summaryrefslogtreecommitdiffstats
path: root/starmath/source
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /starmath/source
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'starmath/source')
-rw-r--r--starmath/source/AccessibleSmElement.cxx286
-rw-r--r--starmath/source/AccessibleSmElementsControl.cxx375
-rw-r--r--starmath/source/ElementsDockingWindow.cxx1230
-rw-r--r--starmath/source/accessibility.cxx1791
-rw-r--r--starmath/source/accessibility.hxx362
-rw-r--r--starmath/source/action.cxx53
-rw-r--r--starmath/source/caret.cxx28
-rw-r--r--starmath/source/cfgitem.cxx1269
-rw-r--r--starmath/source/cfgitem.hxx185
-rw-r--r--starmath/source/cursor.cxx1577
-rw-r--r--starmath/source/dialog.cxx2048
-rw-r--r--starmath/source/document.cxx1291
-rw-r--r--starmath/source/edit.cxx999
-rw-r--r--starmath/source/eqnolefilehdr.cxx53
-rw-r--r--starmath/source/eqnolefilehdr.hxx77
-rw-r--r--starmath/source/format.cxx153
-rw-r--r--starmath/source/mathmlattr.cxx145
-rw-r--r--starmath/source/mathmlattr.hxx78
-rw-r--r--starmath/source/mathmlexport.cxx1526
-rw-r--r--starmath/source/mathmlexport.hxx116
-rw-r--r--starmath/source/mathmlimport.cxx2744
-rw-r--r--starmath/source/mathmlimport.hxx119
-rw-r--r--starmath/source/mathtype.cxx3345
-rw-r--r--starmath/source/mathtype.hxx193
-rw-r--r--starmath/source/node.cxx2940
-rw-r--r--starmath/source/ooxmlexport.cxx600
-rw-r--r--starmath/source/ooxmlexport.hxx49
-rw-r--r--starmath/source/ooxmlimport.cxx681
-rw-r--r--starmath/source/ooxmlimport.hxx54
-rw-r--r--starmath/source/parse.cxx2502
-rw-r--r--starmath/source/rect.cxx623
-rw-r--r--starmath/source/register.cxx72
-rw-r--r--starmath/source/register.hxx37
-rw-r--r--starmath/source/rtfexport.cxx505
-rw-r--r--starmath/source/rtfexport.hxx45
-rw-r--r--starmath/source/smdetect.cxx149
-rw-r--r--starmath/source/smdetect.hxx61
-rw-r--r--starmath/source/smdll.cxx88
-rw-r--r--starmath/source/smmod.cxx235
-rw-r--r--starmath/source/symbol.cxx274
-rw-r--r--starmath/source/tmpdevice.cxx66
-rw-r--r--starmath/source/tmpdevice.hxx49
-rw-r--r--starmath/source/typemap.cxx39
-rw-r--r--starmath/source/uiobject.cxx109
-rw-r--r--starmath/source/uiobject.hxx62
-rw-r--r--starmath/source/unodoc.cxx50
-rw-r--r--starmath/source/unofilter.cxx119
-rw-r--r--starmath/source/unomodel.cxx1100
-rw-r--r--starmath/source/utility.cxx240
-rw-r--r--starmath/source/view.cxx2034
-rw-r--r--starmath/source/visitors.cxx2425
-rw-r--r--starmath/source/wordexportbase.cxx185
-rw-r--r--starmath/source/wordexportbase.hxx57
53 files changed, 35493 insertions, 0 deletions
diff --git a/starmath/source/AccessibleSmElement.cxx b/starmath/source/AccessibleSmElement.cxx
new file mode 100644
index 000000000..9903ce75d
--- /dev/null
+++ b/starmath/source/AccessibleSmElement.cxx
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AccessibleSmElement.hxx>
+#include <ElementsDockingWindow.hxx>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star;
+using OContextEntryGuard = ::comphelper::OContextEntryGuard;
+using OExternalLockGuard = ::comphelper::OExternalLockGuard;
+
+AccessibleSmElement::AccessibleSmElement(SmElementsControl* pSmElementsControl, sal_uInt16 nItemId,
+ sal_Int32 nIndexInParent)
+ : m_pSmElementsControl(pSmElementsControl)
+ , m_nIndexInParent(nIndexInParent)
+ , m_nItemId(nItemId)
+ , m_bHasFocus(false)
+{
+ assert(m_pSmElementsControl);
+ m_nRole = m_pSmElementsControl->itemIsSeparator(m_nItemId) ? AccessibleRole::SEPARATOR
+ : AccessibleRole::PUSH_BUTTON;
+}
+
+AccessibleSmElement::~AccessibleSmElement() {}
+
+void AccessibleSmElement::SetFocus(bool bFocus)
+{
+ if (m_bHasFocus == bFocus)
+ return;
+
+ uno::Any aOldValue;
+ uno::Any aNewValue;
+ if (m_bHasFocus)
+ aOldValue <<= AccessibleStateType::FOCUSED;
+ else
+ aNewValue <<= AccessibleStateType::FOCUSED;
+ m_bHasFocus = bFocus;
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue);
+}
+
+awt::Rectangle AccessibleSmElement::implGetBounds()
+{
+ awt::Rectangle aRect;
+ if (m_pSmElementsControl)
+ aRect = AWTRectangle(m_pSmElementsControl->itemPosRect(m_nItemId));
+ return aRect;
+}
+
+// XInterface
+
+IMPLEMENT_FORWARD_REFCOUNT(AccessibleSmElement, comphelper::OAccessibleComponentHelper)
+
+uno::Any AccessibleSmElement::queryInterface(const uno::Type& _rType)
+{
+ if (_rType == cppu::UnoType<XAccessibleAction>::get()
+ && (!m_pSmElementsControl || m_pSmElementsControl->itemIsSeparator(m_nItemId)))
+ return uno::Any();
+
+ uno::Any aReturn = comphelper::OAccessibleComponentHelper::queryInterface(_rType);
+ if (!aReturn.hasValue())
+ aReturn = AccessibleSmElement_BASE::queryInterface(_rType);
+ return aReturn;
+}
+
+// XTypeProvider
+
+IMPLEMENT_FORWARD_XTYPEPROVIDER2(AccessibleSmElement, comphelper::OAccessibleComponentHelper,
+ AccessibleSmElement_BASE)
+
+// XComponent
+
+void AccessibleSmElement::disposing()
+{
+ comphelper::OAccessibleComponentHelper::disposing();
+ m_pSmElementsControl = nullptr;
+}
+
+// XServiceInfo
+
+OUString AccessibleSmElement::getImplementationName()
+{
+ return "com.sun.star.comp.toolkit.AccessibleSmElement";
+}
+
+sal_Bool AccessibleSmElement::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> AccessibleSmElement::getSupportedServiceNames()
+{
+ return { "com.sun.star.accessibility.AccessibleContext",
+ "com.sun.star.accessibility.AccessibleComponent",
+ "com.sun.star.accessibility.AccessibleSmElement" };
+}
+
+// XAccessible
+
+uno::Reference<XAccessibleContext> AccessibleSmElement::getAccessibleContext() { return this; }
+
+// XAccessibleContext
+
+sal_Int32 AccessibleSmElement::getAccessibleChildCount() { return 0; }
+
+uno::Reference<accessibility::XAccessible> AccessibleSmElement::getAccessibleChild(sal_Int32)
+{
+ return uno::Reference<XAccessible>();
+}
+
+uno::Reference<XAccessible> AccessibleSmElement::getAccessibleParent()
+{
+ OContextEntryGuard aGuard(this);
+ uno::Reference<XAccessible> xParent;
+ if (m_pSmElementsControl)
+ xParent.set(m_pSmElementsControl->GetAccessible().get());
+ return xParent;
+}
+
+sal_Int32 AccessibleSmElement::getAccessibleIndexInParent()
+{
+ OContextEntryGuard aGuard(this);
+ return m_nIndexInParent;
+}
+
+sal_Int16 AccessibleSmElement::getAccessibleRole()
+{
+ OContextEntryGuard aGuard(this);
+ return m_nRole;
+}
+
+OUString AccessibleSmElement::getAccessibleDescription() { return getAccessibleName(); }
+
+OUString AccessibleSmElement::getAccessibleName()
+{
+ OExternalLockGuard aGuard(this);
+ OUString aName;
+ if (m_pSmElementsControl)
+ aName = m_pSmElementsControl->itemName(m_nItemId);
+ return aName;
+}
+
+uno::Reference<XAccessibleRelationSet> AccessibleSmElement::getAccessibleRelationSet()
+{
+ OContextEntryGuard aGuard(this);
+
+ utl::AccessibleRelationSetHelper* pRelationSetHelper = new utl::AccessibleRelationSetHelper;
+ uno::Reference<XAccessibleRelationSet> xSet = pRelationSetHelper;
+ return xSet;
+}
+
+uno::Reference<XAccessibleStateSet> AccessibleSmElement::getAccessibleStateSet()
+{
+ OExternalLockGuard aGuard(this);
+
+ utl::AccessibleStateSetHelper* pStateSetHelper = new utl::AccessibleStateSetHelper;
+ uno::Reference<XAccessibleStateSet> xStateSet = pStateSetHelper;
+
+ if (m_pSmElementsControl && !rBHelper.bDisposed && !rBHelper.bInDispose)
+ {
+ if (m_pSmElementsControl->itemIsVisible(m_nItemId))
+ pStateSetHelper->AddState(AccessibleStateType::VISIBLE);
+ if (!m_pSmElementsControl->itemIsSeparator(m_nItemId))
+ {
+ if (m_pSmElementsControl->IsEnabled())
+ {
+ pStateSetHelper->AddState(AccessibleStateType::ENABLED);
+ pStateSetHelper->AddState(AccessibleStateType::SENSITIVE);
+ }
+ pStateSetHelper->AddState(AccessibleStateType::FOCUSABLE);
+ if (m_bHasFocus)
+ pStateSetHelper->AddState(AccessibleStateType::FOCUSED);
+ }
+ }
+ else
+ pStateSetHelper->AddState(AccessibleStateType::DEFUNC);
+
+ return xStateSet;
+}
+
+// XAccessibleComponent
+
+uno::Reference<XAccessible> AccessibleSmElement::getAccessibleAtPoint(const awt::Point&)
+{
+ return uno::Reference<XAccessible>();
+}
+
+void AccessibleSmElement::grabFocus()
+{
+ uno::Reference<XAccessible> xParent(getAccessibleParent());
+
+ if (xParent.is())
+ {
+ uno::Reference<XAccessibleSelection> rxAccessibleSelection(xParent->getAccessibleContext(),
+ uno::UNO_QUERY);
+ if (rxAccessibleSelection.is())
+ rxAccessibleSelection->selectAccessibleChild(getAccessibleIndexInParent());
+ }
+}
+
+sal_Int32 AccessibleSmElement::getForeground()
+{
+ OExternalLockGuard aGuard(this);
+
+ Color nColor = SmElementsControl::GetTextColor();
+ return sal_Int32(nColor);
+}
+
+sal_Int32 AccessibleSmElement::getBackground()
+{
+ OExternalLockGuard aGuard(this);
+
+ Color nColor = SmElementsControl::GetControlBackground();
+ return sal_Int32(nColor);
+}
+
+// XAccessibleAction
+
+sal_Int32 AccessibleSmElement::getAccessibleActionCount()
+{
+ // only one action -> "Press"
+ return m_pSmElementsControl->itemIsSeparator(m_nItemId) ? 0 : 1;
+}
+
+void AccessibleSmElement::testAction(sal_Int32 nIndex) const
+{
+ if (!m_pSmElementsControl || m_pSmElementsControl->itemIsSeparator(m_nItemId) || (nIndex != 0))
+ throw lang::IndexOutOfBoundsException();
+}
+
+sal_Bool AccessibleSmElement::doAccessibleAction(sal_Int32 nIndex)
+{
+ OExternalLockGuard aGuard(this);
+
+ testAction(nIndex);
+
+ return m_pSmElementsControl->itemTrigger(m_nItemId);
+}
+
+OUString AccessibleSmElement::getAccessibleActionDescription(sal_Int32 nIndex)
+{
+ OExternalLockGuard aGuard(this);
+
+ testAction(nIndex);
+
+ return "press";
+}
+
+uno::Reference<XAccessibleKeyBinding>
+AccessibleSmElement::getAccessibleActionKeyBinding(sal_Int32 nIndex)
+{
+ OContextEntryGuard aGuard(this);
+
+ testAction(nIndex);
+
+ return uno::Reference<XAccessibleKeyBinding>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/AccessibleSmElementsControl.cxx b/starmath/source/AccessibleSmElementsControl.cxx
new file mode 100644
index 000000000..29701ce81
--- /dev/null
+++ b/starmath/source/AccessibleSmElementsControl.cxx
@@ -0,0 +1,375 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AccessibleSmElementsControl.hxx>
+#include <AccessibleSmElement.hxx>
+#include <ElementsDockingWindow.hxx>
+#include <smmod.hxx>
+
+#include <comphelper/accessiblewrapper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/types.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <o3tl/safeint.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/vclevent.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using OContextEntryGuard = ::comphelper::OContextEntryGuard;
+using OExternalLockGuard = ::comphelper::OExternalLockGuard;
+
+// AccessibleSmElementsControl
+
+AccessibleSmElementsControl::AccessibleSmElementsControl(SmElementsControl& rControl)
+ : m_pControl(&rControl)
+{
+}
+
+AccessibleSmElementsControl::~AccessibleSmElementsControl() {}
+
+void AccessibleSmElementsControl::UpdateFocus(sal_uInt16 nPos)
+{
+ const bool bSetFocus = (nPos == SAL_MAX_UINT16);
+
+ // submit events only if the widget has the focus to avoid sending events due to mouse move
+ if (!m_pControl || (bSetFocus && !m_pControl->HasFocus()))
+ return;
+
+ if (bSetFocus)
+ nPos = m_pControl->itemHighlighted() - m_pControl->itemOffset();
+
+ if (nPos < m_aAccessibleChildren.size())
+ {
+ const auto& rxChild = m_aAccessibleChildren[nPos];
+ if (rxChild.is())
+ rxChild->SetFocus(bSetFocus);
+ }
+}
+
+void AccessibleSmElementsControl::ReleaseAllItems()
+{
+ if (m_aAccessibleChildren.empty())
+ return;
+
+ m_aAccessibleChildren.clear();
+
+ // The original toolbox accessibility code uses individual NAME_CHANGED
+ // events in a loop. We can't do this, because on each remove event the
+ // list of known children is rebuild. But since we rely on the child
+ // count of the SmElementsControl, we'll always have no or all items.
+ // In the latter case this would automatically recreate all items!
+ assert(m_pControl && m_pControl->itemCount() == 0);
+ NotifyAccessibleEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN, uno::Any(), uno::Any());
+}
+
+void AccessibleSmElementsControl::AddAllItems()
+{
+ assert(m_pControl);
+ if (!m_pControl)
+ return;
+
+ uno::Any aNewName(getAccessibleName());
+ NotifyAccessibleEvent(AccessibleEventId::NAME_CHANGED, uno::Any(), aNewName);
+
+ // register the new items
+ sal_uInt16 nCount = getAccessibleChildCount();
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ uno::Any aNewValue;
+ aNewValue <<= getAccessibleChild(static_cast<sal_Int32>(i));
+ NotifyAccessibleEvent(AccessibleEventId::CHILD, uno::Any(), aNewValue);
+ }
+}
+
+IMPLEMENT_FORWARD_XINTERFACE2(AccessibleSmElementsControl, comphelper::OAccessibleComponentHelper,
+ AccessibleSmElementsControl_BASE)
+
+IMPLEMENT_FORWARD_XTYPEPROVIDER2(AccessibleSmElementsControl,
+ comphelper::OAccessibleComponentHelper,
+ AccessibleSmElementsControl_BASE)
+
+// XAccessible
+uno::Reference<XAccessibleContext> AccessibleSmElementsControl::getAccessibleContext()
+{
+ return this;
+}
+
+// XComponent
+void AccessibleSmElementsControl::disposing()
+{
+ comphelper::OAccessibleComponentHelper::disposing();
+ m_aAccessibleChildren.clear();
+}
+
+void AccessibleSmElementsControl::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ if (!m_pControl)
+ throw uno::RuntimeException();
+
+ m_pControl->GrabFocus();
+}
+
+sal_Int32 AccessibleSmElementsControl::getForeground()
+{
+ SolarMutexGuard aGuard;
+
+ return static_cast<sal_Int32>(SmElementsControl::GetTextColor());
+}
+
+sal_Int32 AccessibleSmElementsControl::getBackground()
+{
+ SolarMutexGuard aGuard;
+
+ Color nCol = SmElementsControl::GetControlBackground();
+ return static_cast<sal_Int32>(nCol);
+}
+
+// XServiceInfo
+OUString AccessibleSmElementsControl::getImplementationName()
+{
+ return "com.sun.star.comp.toolkit.AccessibleSmElementsControl";
+}
+
+sal_Bool AccessibleSmElementsControl::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> AccessibleSmElementsControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.accessibility.AccessibleContext",
+ "com.sun.star.accessibility.AccessibleComponent",
+ "com.sun.star.accessibility.AccessibleSelection",
+ "com.sun.star.accessibility.AccessibleSmElementsControl" };
+}
+
+// XAccessibleContext
+sal_Int32 AccessibleSmElementsControl::getAccessibleChildCount()
+{
+ comphelper::OExternalLockGuard aGuard(this);
+ sal_Int32 nCount = 0;
+ if (m_pControl)
+ {
+ nCount = m_pControl->itemCount();
+ if (m_aAccessibleChildren.size() != sal_uInt16(nCount))
+ m_aAccessibleChildren.resize(nCount);
+ }
+ return nCount;
+}
+
+uno::Reference<XAccessible> AccessibleSmElementsControl::getAccessibleChild(sal_Int32 c)
+{
+ comphelper::OExternalLockGuard aGuard(this);
+
+ if (c < 0 || c >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+
+ rtl::Reference<AccessibleSmElement> xChild = m_aAccessibleChildren[c];
+ const sal_uInt16 nItemId = m_pControl->itemOffset() + c;
+ if (xChild.is() && xChild->itemId() != nItemId)
+ xChild.clear();
+ if (!xChild.is())
+ {
+ sal_uInt16 nHighlightItemId = m_pControl->itemHighlighted();
+ AccessibleSmElement* pChild = new AccessibleSmElement(m_pControl, nItemId, c);
+ if (pChild->itemId() == nHighlightItemId)
+ pChild->SetFocus(true);
+ m_aAccessibleChildren[c] = pChild;
+ xChild = pChild;
+ }
+ return xChild.get();
+}
+
+uno::Reference<XAccessible> AccessibleSmElementsControl::getAccessibleParent()
+{
+ SolarMutexGuard aGuard;
+ if (!m_pControl)
+ throw uno::RuntimeException();
+
+ return m_pControl->GetDrawingArea()->get_accessible_parent();
+}
+
+uno::Reference<XAccessible>
+AccessibleSmElementsControl::getAccessibleAtPoint(const awt::Point& rPoint)
+{
+ comphelper::OExternalLockGuard aGuard(this);
+
+ uno::Reference<XAccessible> xAccessible;
+ if (m_pControl)
+ {
+ sal_uInt16 nPos = m_pControl->itemAtPos(VCLPoint(rPoint));
+ nPos -= m_pControl->itemOffset();
+ if (nPos <= m_aAccessibleChildren.size())
+ xAccessible = getAccessibleChild(nPos);
+ }
+ return xAccessible;
+}
+
+sal_Int16 AccessibleSmElementsControl::getAccessibleRole() { return AccessibleRole::SCROLL_PANE; }
+
+OUString AccessibleSmElementsControl::getAccessibleDescription() { return OUString(); }
+
+OUString AccessibleSmElementsControl::getAccessibleName()
+{
+ SolarMutexGuard aGuard;
+ OUString aName;
+ if (m_pControl)
+ aName = SmResId(m_pControl->elementSetId().getStr());
+ return aName;
+}
+
+// XAccessibleSelection
+void AccessibleSmElementsControl::selectAccessibleChild(sal_Int32 nChildIndex)
+{
+ OExternalLockGuard aGuard(this);
+
+ if ((!m_pControl) || nChildIndex < 0
+ || o3tl::make_unsigned(nChildIndex) >= m_aAccessibleChildren.size())
+ throw lang::IndexOutOfBoundsException();
+
+ m_pControl->setItemHighlighted(nChildIndex);
+}
+
+sal_Bool AccessibleSmElementsControl::isAccessibleChildSelected(sal_Int32 nChildIndex)
+{
+ OExternalLockGuard aGuard(this);
+ if ((!m_pControl) || nChildIndex < 0
+ || o3tl::make_unsigned(nChildIndex) >= m_aAccessibleChildren.size())
+ throw lang::IndexOutOfBoundsException();
+
+ return (m_pControl->itemHighlighted() == nChildIndex);
+}
+
+void AccessibleSmElementsControl::clearAccessibleSelection()
+{
+ OExternalLockGuard aGuard(this);
+ if (m_pControl)
+ m_pControl->setItemHighlighted(SAL_MAX_UINT16);
+}
+
+void AccessibleSmElementsControl::selectAllAccessibleChildren()
+{
+ // intentionally empty
+}
+
+sal_Int32 AccessibleSmElementsControl::getSelectedAccessibleChildCount()
+{
+ OExternalLockGuard aGuard(this);
+
+ sal_Int32 nRet = 0;
+ if (m_pControl
+ && (m_pControl->itemHighlighted() - m_pControl->itemOffset()) < getAccessibleChildCount())
+ nRet = 1;
+ return nRet;
+}
+
+uno::Reference<XAccessible>
+AccessibleSmElementsControl::getSelectedAccessibleChild(sal_Int32 nSelectedChildIndex)
+{
+ OExternalLockGuard aGuard(this);
+ if (nSelectedChildIndex != 0 || !m_pControl)
+ throw lang::IndexOutOfBoundsException();
+ return getAccessibleChild(m_pControl->itemHighlighted() - m_pControl->itemOffset());
+}
+
+void AccessibleSmElementsControl::deselectAccessibleChild(sal_Int32 nChildIndex)
+{
+ OExternalLockGuard aGuard(this);
+ if (nChildIndex != 0 || nChildIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+ clearAccessibleSelection(); // there can be just one selected child
+}
+
+void AccessibleSmElementsControl::TestControl()
+{
+ if (!m_pControl)
+ throw uno::RuntimeException();
+}
+
+awt::Rectangle AccessibleSmElementsControl::implGetBounds()
+{
+ SolarMutexGuard aGuard;
+ TestControl();
+
+ awt::Rectangle aRet;
+
+ const Point aOutPos;
+ Size aOutSize(m_pControl->GetOutputSizePixel());
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+
+ return aRet;
+}
+
+sal_Bool AccessibleSmElementsControl::containsPoint(const awt::Point& aPoint)
+{
+ SolarMutexGuard aGuard;
+ TestControl();
+ Size aSz(m_pControl->GetOutputSizePixel());
+ return aPoint.X >= 0 && aPoint.Y >= 0 && aPoint.X < aSz.Width() && aPoint.Y < aSz.Height();
+}
+
+uno::Reference<XAccessibleRelationSet> AccessibleSmElementsControl::getAccessibleRelationSet()
+{
+ uno::Reference<XAccessibleRelationSet> xRelSet = new utl::AccessibleRelationSetHelper();
+ return xRelSet; // empty relation set
+}
+
+uno::Reference<XAccessibleStateSet> AccessibleSmElementsControl::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ ::utl::AccessibleStateSetHelper* pStateSet = new ::utl::AccessibleStateSetHelper;
+
+ uno::Reference<XAccessibleStateSet> xStateSet(pStateSet);
+
+ if (!m_pControl)
+ pStateSet->AddState(AccessibleStateType::DEFUNC);
+ else
+ {
+ pStateSet->AddState(AccessibleStateType::ENABLED);
+ pStateSet->AddState(AccessibleStateType::FOCUSABLE);
+ if (m_pControl->HasFocus())
+ pStateSet->AddState(AccessibleStateType::FOCUSED);
+ if (m_pControl->IsActive())
+ pStateSet->AddState(AccessibleStateType::ACTIVE);
+ if (m_pControl->IsVisible())
+ pStateSet->AddState(AccessibleStateType::SHOWING);
+ if (m_pControl->IsReallyVisible())
+ pStateSet->AddState(AccessibleStateType::VISIBLE);
+ if (COL_TRANSPARENT != SmElementsControl::GetControlBackground())
+ pStateSet->AddState(AccessibleStateType::OPAQUE);
+ }
+
+ return xStateSet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/ElementsDockingWindow.cxx b/starmath/source/ElementsDockingWindow.cxx
new file mode 100644
index 000000000..c21933fe1
--- /dev/null
+++ b/starmath/source/ElementsDockingWindow.cxx
@@ -0,0 +1,1230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <ElementsDockingWindow.hxx>
+
+#include <starmath.hrc>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <view.hxx>
+#include <visitors.hxx>
+#include <document.hxx>
+#include <node.hxx>
+#include "uiobject.hxx"
+#include <strings.hxx>
+
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxmodelfactory.hxx>
+#include <svl/stritem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <vcl/event.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <vcl/uitest/logger.hxx>
+
+SmElement::SmElement(std::unique_ptr<SmNode>&& pNode, const OUString& aText, const OUString& aHelpText) :
+ mpNode(std::move(pNode)),
+ maText(aText),
+ maHelpText(aHelpText)
+{}
+
+SmElement::~SmElement()
+{}
+
+const std::unique_ptr<SmNode>& SmElement::getNode() const { return mpNode; }
+
+SmElementSeparator::SmElementSeparator() :
+ SmElement(std::unique_ptr<SmNode>(), OUString(), OUString())
+{}
+
+const SmElementDescr SmElementsControl::m_aUnaryBinaryOperatorsList[] =
+{
+ {RID_PLUSX, RID_PLUSX_HELP}, {RID_MINUSX, RID_MINUSX_HELP},
+ {RID_PLUSMINUSX, RID_PLUSMINUSX_HELP}, {RID_MINUSPLUSX, RID_MINUSPLUSX_HELP},
+ {nullptr, nullptr},
+ {RID_XPLUSY, RID_XPLUSY_HELP}, {RID_XMINUSY, RID_XMINUSY_HELP},
+ {RID_XCDOTY, RID_XCDOTY_HELP}, {RID_XTIMESY, RID_XTIMESY_HELP},
+ {RID_XSYMTIMESY, RID_XSYMTIMESY_HELP}, {RID_XOVERY, RID_XOVERY_HELP},
+ {RID_XDIVY, RID_XDIVY_HELP}, {RID_XSYMDIVIDEY, RID_XSYMDIVIDEY_HELP},
+ {RID_XOPLUSY, RID_XOPLUSY_HELP}, {RID_XOMINUSY, RID_XOMINUSY_HELP},
+ {RID_XODOTY, RID_XODOTY_HELP}, {RID_XOTIMESY, RID_XOTIMESY_HELP},
+ {RID_XODIVIDEY, RID_XODIVIDEY_HELP}, {RID_XCIRCY, RID_XCIRCY_HELP},
+ {RID_XWIDESLASHY, RID_XWIDESLASHY_HELP}, {RID_XWIDEBSLASHY, RID_XWIDEBSLASHY_HELP},
+ {nullptr, nullptr},
+ {RID_NEGX, RID_NEGX_HELP}, {RID_XANDY, RID_XANDY_HELP}, {RID_XORY, RID_XORY_HELP},
+};
+
+const SmElementDescr SmElementsControl::m_aRelationsList[] =
+{
+ {RID_XEQY, RID_XEQY_HELP}, {RID_XNEQY, RID_XNEQY_HELP}, {RID_XLTY, RID_XLTY_HELP},
+ {RID_XLEY, RID_XLEY_HELP}, {RID_XLESLANTY, RID_XLESLANTY_HELP}, {RID_XGTY, RID_XGTY_HELP},
+ {RID_XGEY, RID_XGEY_HELP}, {RID_XGESLANTY, RID_XGESLANTY_HELP},
+ {RID_XLLY, RID_XLLY_HELP}, {RID_XGGY, RID_XGGY_HELP},
+ {nullptr, nullptr},
+ {RID_XAPPROXY, RID_XAPPROXY_HELP}, {RID_XSIMY, RID_XSIMY_HELP}, {RID_XSIMEQY, RID_XSIMEQY_HELP},
+ {RID_XEQUIVY, RID_XEQUIVY_HELP}, {RID_XPROPY, RID_XPROPY_HELP}, {RID_XPARALLELY, RID_XPARALLELY_HELP},
+ {RID_XORTHOY, RID_XORTHOY_HELP}, {RID_XDIVIDESY, RID_XDIVIDESY_HELP}, {RID_XNDIVIDESY, RID_XNDIVIDESY_HELP},
+ {RID_XTOWARDY, RID_XTOWARDY_HELP}, {RID_XTRANSLY, RID_XTRANSLY_HELP}, {RID_XTRANSRY, RID_XTRANSRY_HELP},
+ {RID_XDEFY, RID_XDEFY_HELP},
+ {nullptr, nullptr},
+ {RID_DLARROW, RID_DLARROW_HELP}, {RID_DLRARROW, RID_DLRARROW_HELP}, {RID_DRARROW, RID_DRARROW_HELP},
+ {nullptr, nullptr},
+ {RID_XPRECEDESY, RID_XPRECEDESY_HELP}, {RID_XSUCCEEDSY, RID_XSUCCEEDSY_HELP},
+ {RID_XPRECEDESEQUALY, RID_XPRECEDESEQUALY_HELP}, {RID_XSUCCEEDSEQUALY, RID_XSUCCEEDSEQUALY_HELP},
+ {RID_XPRECEDESEQUIVY, RID_XPRECEDESEQUIVY_HELP}, {RID_XSUCCEEDSEQUIVY, RID_XSUCCEEDSEQUIVY_HELP},
+ {RID_XNOTPRECEDESY, RID_XNOTPRECEDESY_HELP}, {RID_XNOTSUCCEEDSY, RID_XNOTSUCCEEDSY_HELP},
+};
+
+const SmElementDescr SmElementsControl::m_aSetOperationsList[] =
+{
+ {RID_XINY, RID_XINY_HELP}, {RID_XNOTINY, RID_XNOTINY_HELP}, {RID_XOWNSY, RID_XOWNSY_HELP},
+ {nullptr, nullptr},
+ {RID_XINTERSECTIONY, RID_XINTERSECTIONY_HELP}, {RID_XUNIONY, RID_XUNIONY_HELP},
+ {RID_XSETMINUSY, RID_XSETMINUSY_HELP}, {RID_XSLASHY, RID_XSLASHY_HELP},
+ {RID_XSUBSETY, RID_XSUBSETY_HELP}, {RID_XSUBSETEQY, RID_XSUBSETEQY_HELP},
+ {RID_XSUPSETY, RID_XSUPSETY_HELP}, {RID_XSUPSETEQY, RID_XSUPSETEQY_HELP},
+ {RID_XNSUBSETY, RID_XNSUBSETY_HELP}, {RID_XNSUBSETEQY, RID_XNSUBSETEQY_HELP},
+ {RID_XNSUPSETY, RID_XNSUPSETY_HELP}, {RID_XNSUPSETEQY, RID_XNSUPSETEQY_HELP},
+ {nullptr, nullptr},
+ {RID_EMPTYSET, RID_EMPTYSET_HELP}, {RID_ALEPH, RID_ALEPH_HELP}, {RID_SETN, RID_SETN_HELP},
+ {RID_SETZ, RID_SETZ_HELP}, {RID_SETQ, RID_SETQ_HELP}, {RID_SETR, RID_SETR_HELP}, {RID_SETC, RID_SETC_HELP}
+};
+
+const SmElementDescr SmElementsControl::m_aFunctionsList[] =
+{
+ {RID_ABSX, RID_ABSX_HELP}, {RID_FACTX, RID_FACTX_HELP}, {RID_SQRTX, RID_SQRTX_HELP},
+ {RID_NROOTXY, RID_NROOTXY_HELP}, {RID_RSUPX, RID_RSUPX_HELP}, {RID_EX, RID_EX_HELP},
+ {RID_LNX, RID_LNX_HELP}, {RID_EXPX, RID_EXPX_HELP}, {RID_LOGX, RID_LOGX_HELP},
+ {nullptr, nullptr},
+ {RID_SINX, RID_SINX_HELP}, {RID_COSX, RID_COSX_HELP}, {RID_TANX, RID_TANX_HELP}, {RID_COTX, RID_COTX_HELP},
+ {RID_SINHX, RID_SINHX_HELP}, {RID_COSHX, RID_COSHX_HELP}, {RID_TANHX, RID_TANHX_HELP},
+ {RID_COTHX, RID_COTHX_HELP},
+ {nullptr, nullptr},
+ {RID_ARCSINX, RID_ARCSINX_HELP}, {RID_ARCCOSX, RID_ARCCOSX_HELP}, {RID_ARCTANX, RID_ARCTANX_HELP},
+ {RID_ARCCOTX, RID_ARCCOTX_HELP}, {RID_ARSINHX, RID_ARSINHX_HELP}, {RID_ARCOSHX, RID_ARCOSHX_HELP},
+ {RID_ARTANHX, RID_ARTANHX_HELP}, {RID_ARCOTHX, RID_ARCOTHX_HELP}
+};
+
+const SmElementDescr SmElementsControl::m_aOperatorsList[] =
+{
+ {RID_LIMX, RID_LIMX_HELP}, {RID_LIM_FROMX, RID_LIM_FROMX_HELP},
+ {RID_LIM_TOX, RID_LIM_TOX_HELP}, {RID_LIM_FROMTOX, RID_LIM_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_LIMINFX, RID_LIMINFX_HELP}, {RID_LIMINF_FROMX, RID_LIMINF_FROMX_HELP},
+ {RID_LIMINF_TOX, RID_LIMINF_TOX_HELP}, {RID_LIMINF_FROMTOX, RID_LIMINF_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_LIMSUPX, RID_LIMSUPX_HELP}, {RID_LIMSUP_FROMX, RID_LIMSUP_FROMX_HELP},
+ {RID_LIMSUP_TOX, RID_LIMSUP_TOX_HELP}, {RID_LIMSUP_FROMTOX, RID_LIMSUP_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_SUMX, RID_SUMX_HELP}, {RID_SUM_FROMX, RID_SUM_FROMX_HELP},
+ {RID_SUM_TOX, RID_SUM_TOX_HELP}, {RID_SUM_FROMTOX, RID_SUM_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_PRODX, RID_PRODX_HELP}, {RID_PROD_FROMX, RID_PROD_FROMX_HELP},
+ {RID_PROD_TOX, RID_PROD_TOX_HELP}, {RID_PROD_FROMTOX, RID_PROD_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_COPRODX, RID_COPRODX_HELP}, {RID_COPROD_FROMX, RID_COPROD_FROMX_HELP},
+ {RID_COPROD_TOX, RID_COPROD_TOX_HELP}, {RID_COPROD_FROMTOX, RID_COPROD_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_INTX, RID_INTX_HELP}, {RID_INT_FROMX, RID_INT_FROMX_HELP},
+ {RID_INT_TOX, RID_INT_TOX_HELP}, {RID_INT_FROMTOX, RID_INT_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_IINTX, RID_IINTX_HELP}, {RID_IINT_FROMX, RID_IINT_FROMX_HELP},
+ {RID_IINT_TOX, RID_IINT_TOX_HELP}, {RID_IINT_FROMTOX, RID_IINT_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_IIINTX, RID_IIINTX_HELP}, {RID_IIINT_FROMX, RID_IIINT_FROMX_HELP},
+ {RID_IIINT_TOX, RID_IIINT_TOX_HELP}, {RID_IIINT_FROMTOX, RID_IIINT_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_LINTX, RID_LINTX_HELP}, {RID_LINT_FROMX, RID_LINT_FROMX_HELP},
+ {RID_LINT_TOX, RID_LINT_TOX_HELP}, {RID_LINT_FROMTOX, RID_LINT_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_LLINTX, RID_LLINTX_HELP}, {RID_LLINT_FROMX, RID_LLINT_FROMX_HELP},
+ {RID_LLINT_TOX, RID_LLINT_TOX_HELP}, {RID_LLINT_FROMTOX, RID_LLINT_FROMTOX_HELP},
+ {nullptr, nullptr},
+ {RID_LLLINTX, RID_LLLINTX_HELP}, {RID_LLLINT_FROMX, RID_LLLINT_FROMX_HELP},
+ {RID_LLLINT_TOX, RID_LLLINT_TOX_HELP}, {RID_LLLINT_FROMTOX, RID_LLLINT_FROMTOX_HELP},
+};
+
+const SmElementDescr SmElementsControl::m_aAttributesList[] =
+{
+ {RID_ACUTEX, RID_ACUTEX_HELP}, {RID_GRAVEX, RID_GRAVEX_HELP}, {RID_BREVEX, RID_BREVEX_HELP},
+ {RID_CIRCLEX, RID_CIRCLEX_HELP}, {RID_DOTX, RID_DOTX_HELP}, {RID_DDOTX, RID_DDOTX_HELP},
+ {RID_DDDOTX, RID_DDDOTX_HELP}, {RID_BARX, RID_BARX_HELP}, {RID_VECX, RID_VECX_HELP},
+ {RID_HARPOONX, RID_HARPOONX_HELP},
+ {RID_TILDEX, RID_TILDEX_HELP}, {RID_HATX, RID_HATX_HELP}, {RID_CHECKX, RID_CHECKX_HELP},
+ {nullptr, nullptr},
+ {RID_WIDEVECX, RID_WIDEVECX_HELP}, {RID_WIDEHARPOONX, RID_WIDEHARPOONX_HELP},
+ {RID_WIDETILDEX, RID_WIDETILDEX_HELP}, {RID_WIDEHATX, RID_WIDEHATX_HELP},
+ {RID_OVERLINEX, RID_OVERLINEX_HELP}, {RID_UNDERLINEX, RID_UNDERLINEX_HELP}, {RID_OVERSTRIKEX, RID_OVERSTRIKEX_HELP},
+ {nullptr, nullptr},
+ {RID_PHANTOMX, RID_PHANTOMX_HELP}, {RID_BOLDX, RID_BOLDX_HELP}, {RID_ITALX, RID_ITALX_HELP},
+ {RID_SIZEXY, RID_SIZEXY_HELP}, {RID_FONTXY, RID_FONTXY_HELP},
+ {nullptr, nullptr},
+ {RID_COLORX_BLACK, RID_COLORX_BLACK_HELP}, {RID_COLORX_BLUE, RID_COLORX_BLUE_HELP},
+ {RID_COLORX_GREEN, RID_COLORX_GREEN_HELP}, {RID_COLORX_RED, RID_COLORX_RED_HELP},
+ {RID_COLORX_CYAN, RID_COLORX_CYAN_HELP}, {RID_COLORX_MAGENTA, RID_COLORX_MAGENTA_HELP},
+ {RID_COLORX_YELLOW, RID_COLORX_YELLOW_HELP}, {RID_COLORX_GRAY, RID_COLORX_GRAY_HELP},
+ {RID_COLORX_LIME, RID_COLORX_LIME_HELP}, {RID_COLORX_MAROON, RID_COLORX_MAROON_HELP},
+ {RID_COLORX_NAVY, RID_COLORX_NAVY_HELP}, {RID_COLORX_OLIVE, RID_COLORX_OLIVE_HELP},
+ {RID_COLORX_PURPLE, RID_COLORX_PURPLE_HELP}, {RID_COLORX_SILVER, RID_COLORX_SILVER_HELP},
+ {RID_COLORX_TEAL, RID_COLORX_TEAL_HELP},{RID_COLORX_RGB, RID_COLORX_RGB_HELP}
+};
+
+const SmElementDescr SmElementsControl::m_aBracketsList[] =
+{
+ {RID_LRGROUPX, RID_LRGROUPX_HELP},
+ {nullptr, nullptr},
+ {RID_LRPARENTX, RID_LRPARENTX_HELP}, {RID_LRBRACKETX, RID_LRBRACKETX_HELP},
+ {RID_LRDBRACKETX, RID_LRDBRACKETX_HELP}, {RID_LRBRACEX, RID_LRBRACEX_HELP},
+ {RID_LRANGLEX, RID_LRANGLEX_HELP}, {RID_LMRANGLEXY, RID_LMRANGLEXY_HELP},
+ {RID_LRCEILX, RID_LRCEILX_HELP}, {RID_LRFLOORX, RID_LRFLOORX_HELP},
+ {RID_LRLINEX, RID_LRLINEX_HELP}, {RID_LRDLINEX, RID_LRDLINEX_HELP},
+ {nullptr, nullptr},
+ {RID_SLRPARENTX, RID_SLRPARENTX_HELP}, {RID_SLRBRACKETX, RID_SLRBRACKETX_HELP},
+ {RID_SLRDBRACKETX, RID_SLRDBRACKETX_HELP}, {RID_SLRBRACEX, RID_SLRBRACEX_HELP},
+ {RID_SLRANGLEX, RID_SLRANGLEX_HELP}, {RID_SLMRANGLEXY, RID_SLMRANGLEXY_HELP},
+ {RID_SLRCEILX, RID_SLRCEILX_HELP}, {RID_SLRFLOORX, RID_SLRFLOORX_HELP},
+ {RID_SLRLINEX, RID_SLRLINEX_HELP}, {RID_SLRDLINEX, RID_SLRDLINEX_HELP},
+ {RID_XEVALUATEDATY, RID_XEVALUATEDATY_HELP},
+ {nullptr, nullptr},
+ {RID_XOVERBRACEY, RID_XOVERBRACEY_HELP}, {RID_XUNDERBRACEY, RID_XUNDERBRACEY_HELP},
+};
+
+const SmElementDescr SmElementsControl::m_aFormatsList[] =
+{
+ {RID_RSUPX, RID_RSUPX_HELP}, {RID_RSUBX, RID_RSUBX_HELP}, {RID_LSUPX, RID_LSUPX_HELP},
+ {RID_LSUBX, RID_LSUBX_HELP}, {RID_CSUPX, RID_CSUPX_HELP}, {RID_CSUBX, RID_CSUBX_HELP},
+ {nullptr, nullptr},
+ {RID_NEWLINE, RID_NEWLINE_HELP}, {RID_SBLANK, RID_SBLANK_HELP}, {RID_BLANK, RID_BLANK_HELP},
+ {RID_NOSPACE, RID_NOSPACE_HELP},
+ {RID_ALIGNLX, RID_ALIGNLX_HELP}, {RID_ALIGNCX, RID_ALIGNCX_HELP}, {RID_ALIGNRX, RID_ALIGNRX_HELP},
+ {nullptr, nullptr},
+ {RID_BINOMXY, RID_BINOMXY_HELP}, {RID_STACK, RID_STACK_HELP},
+ {RID_MATRIX, RID_MATRIX_HELP},
+};
+
+const SmElementDescr SmElementsControl::m_aOthersList[] =
+{
+ {RID_INFINITY, RID_INFINITY_HELP}, {RID_PARTIAL, RID_PARTIAL_HELP}, {RID_NABLA, RID_NABLA_HELP},
+ {RID_EXISTS, RID_EXISTS_HELP}, {RID_NOTEXISTS, RID_NOTEXISTS_HELP}, {RID_FORALL, RID_FORALL_HELP},
+ {RID_HBAR, RID_HBAR_HELP}, {RID_LAMBDABAR, RID_LAMBDABAR_HELP}, {RID_RE, RID_RE_HELP},
+ {RID_IM, RID_IM_HELP}, {RID_WP, RID_WP_HELP}, {RID_LAPLACE, RID_LAPLACE_HELP},
+ {nullptr, nullptr},
+ {RID_LEFTARROW, RID_LEFTARROW_HELP}, {RID_RIGHTARROW, RID_RIGHTARROW_HELP}, {RID_UPARROW, RID_UPARROW_HELP},
+ {RID_DOWNARROW, RID_DOWNARROW_HELP},
+ {nullptr, nullptr},
+ {RID_DOTSLOW, RID_DOTSLOW_HELP}, {RID_DOTSAXIS, RID_DOTSAXIS_HELP}, {RID_DOTSVERT, RID_DOTSVERT_HELP},
+ {RID_DOTSUP, RID_DOTSUP_HELP}, {RID_DOTSDOWN, RID_DOTSDOWN_HELP}
+};
+
+const SmElementDescr SmElementsControl::m_aExamplesList[] =
+{
+ {"C=%pi cdot d = 2 cdot %pi cdot r", RID_EXAMPLE_CIRCUMFERENCE_HELP},
+ {"E=mc^2", RID_EXAMPLE_MASS_ENERGY_EQUIV_HELP},
+ {"a^2 + b^2 = c^2", RID_EXAMPLE_PYTHAGOREAN_THEO_HELP},
+ {"f ( x ) = sum from { { i = 0 } } to { infinity } { {f^{(i)}(0)} over {i!} x^i}", RID_EXAMPLE_A_SIMPLE_SERIES_HELP},
+ {"f ( x ) = {1} over {%sigma sqrt{2%pi} }func e^-{{(x-%mu)^2} over {2%sigma^2}}", RID_EXAMPLE_GAUSS_DISTRIBUTION_HELP},
+};
+
+#define AS_PAIR(a) a, SAL_N_ELEMENTS(a)
+const std::tuple<const char*, const SmElementDescr*, size_t> SmElementsControl::m_aCategories[] =
+{
+ {RID_CATEGORY_UNARY_BINARY_OPERATORS, AS_PAIR(m_aUnaryBinaryOperatorsList)},
+ {RID_CATEGORY_RELATIONS, AS_PAIR(m_aRelationsList)},
+ {RID_CATEGORY_SET_OPERATIONS, AS_PAIR(m_aSetOperationsList)},
+ {RID_CATEGORY_FUNCTIONS, AS_PAIR(m_aFunctionsList)},
+ {RID_CATEGORY_OPERATORS, AS_PAIR(m_aOperatorsList)},
+ {RID_CATEGORY_ATTRIBUTES, AS_PAIR(m_aAttributesList)},
+ {RID_CATEGORY_BRACKETS, AS_PAIR(m_aBracketsList)},
+ {RID_CATEGORY_FORMATS, AS_PAIR(m_aFormatsList)},
+ {RID_CATEGORY_OTHERS, AS_PAIR(m_aOthersList)},
+ {RID_CATEGORY_EXAMPLES, AS_PAIR(m_aExamplesList)},
+};
+
+const size_t SmElementsControl::m_aCategoriesSize = SAL_N_ELEMENTS(m_aCategories);
+
+SmElementsControl::SmElementsControl(std::unique_ptr<weld::ScrolledWindow> xScrolledWindow)
+ : mpDocShell(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT))
+ , m_nCurrentElement(SAL_MAX_UINT16)
+ , m_nCurrentRolloverElement(SAL_MAX_UINT16)
+ , m_nCurrentOffset(1) // Default offset of 1 due to the ScrollBar child
+ , mbVerticalMode(true)
+ , mxScroll(std::move(xScrolledWindow))
+ , m_bFirstPaintAfterLayout(false)
+{
+ mxScroll->set_user_managed_scrolling();
+ mxScroll->connect_hadjustment_changed( LINK(this, SmElementsControl, ScrollHdl) );
+ mxScroll->connect_vadjustment_changed( LINK(this, SmElementsControl, ScrollHdl) );
+}
+
+SmElementsControl::~SmElementsControl()
+{
+ mpDocShell->DoClose();
+}
+
+void SmElementsControl::setVerticalMode(bool bVerticalMode)
+{
+ if (mbVerticalMode == bVerticalMode)
+ return;
+ mbVerticalMode = bVerticalMode;
+ // turn off scrollbars, LayoutOrPaintContents will enable whichever one
+ // might be needed
+ mxScroll->set_vpolicy(VclPolicyType::NEVER);
+ mxScroll->set_hpolicy(VclPolicyType::NEVER);
+ LayoutOrPaintContents(GetDrawingArea()->get_ref_device(), false);
+ Invalidate();
+}
+
+SmElement* SmElementsControl::current() const
+{
+ sal_uInt16 nCur = (m_nCurrentRolloverElement != SAL_MAX_UINT16)
+ ? m_nCurrentRolloverElement
+ : (HasFocus() ? m_nCurrentElement : SAL_MAX_UINT16);
+ return (nCur < maElementList.size()) ? maElementList[nCur].get() : nullptr;
+}
+
+void SmElementsControl::setCurrentElement(sal_uInt16 nPos)
+{
+ if (m_nCurrentElement == nPos)
+ return;
+ if (nPos != SAL_MAX_UINT16 && nPos >= maElementList.size())
+ return;
+ if (m_xAccessible.is() && m_nCurrentElement != SAL_MAX_UINT16)
+ m_xAccessible->ReleaseFocus(m_nCurrentElement);
+ m_nCurrentElement = nPos;
+ if (m_xAccessible.is() && m_nCurrentElement != SAL_MAX_UINT16)
+ m_xAccessible->AcquireFocus();
+}
+
+Color SmElementsControl::GetTextColor()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ return rStyleSettings.GetFieldTextColor();
+}
+
+Color SmElementsControl::GetControlBackground()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ return rStyleSettings.GetFieldColor();
+}
+
+/**
+ * !bDraw => layout only
+ *
+ * Layouting is always done without a scrollbar and will show or hide it.
+ * The first paint (m_bFirstPaintAfterLayout) therefore needs to update a
+ * visible scrollbar, because the layouting was wrong.
+ **/
+void SmElementsControl::LayoutOrPaintContents(vcl::RenderContext& rContext, bool bDraw)
+{
+ rContext.Push();
+
+ rContext.SetMapMode( MapMode(MapUnit::Map100thMM) );
+ rContext.SetDrawMode( DrawModeFlags::Default );
+ rContext.SetLayoutMode( ComplexTextLayoutFlags::Default );
+ rContext.SetDigitLanguage( LANGUAGE_ENGLISH );
+ if (bDraw)
+ {
+ rContext.SetBackground(GetControlBackground());
+ rContext.SetTextColor(GetTextColor());
+ rContext.Erase();
+ }
+
+ const sal_Int32 nControlHeight = GetOutputSizePixel().Height();
+ const sal_Int32 nControlWidth = GetOutputSizePixel().Width();
+
+ sal_Int32 boxX = maMaxElementDimensions.Width() + 10;
+ sal_Int32 boxY = maMaxElementDimensions.Height() + 10;
+
+ sal_Int32 x = mbVerticalMode ? -mxScroll->hadjustment_get_value() : 0;
+ sal_Int32 y = !mbVerticalMode ? -mxScroll->vadjustment_get_value() : 0;
+
+ sal_Int32 perLine = 0;
+
+ if (mbVerticalMode)
+ perLine = nControlHeight / boxY;
+ else
+ perLine = nControlWidth / boxX;
+ if (perLine <= 0)
+ perLine = 1;
+
+ if (mbVerticalMode)
+ boxY = nControlHeight / perLine;
+ else
+ boxX = nControlWidth / perLine;
+
+ const SmElement* pCurrentElement = current();
+ for (const std::unique_ptr<SmElement> & i : maElementList)
+ {
+ SmElement* element = i.get();
+ if (element->isSeparator())
+ {
+ if (mbVerticalMode)
+ {
+ x += boxX;
+ y = 0;
+
+ element->mBoxLocation = Point(x, y);
+ element->mBoxSize = Size(10, nControlHeight);
+
+ tools::Rectangle aSelectionRectangle(x + 5 - 1, y + 5,
+ x + 5 + 1, nControlHeight - 5);
+
+ if (bDraw)
+ rContext.DrawRect(rContext.PixelToLogic(aSelectionRectangle));
+ x += 10;
+ }
+ else
+ {
+ x = 0;
+ y += boxY;
+
+ element->mBoxLocation = Point(x, y);
+ element->mBoxSize = Size(nControlWidth, 10);
+
+ tools::Rectangle aSelectionRectangle(x + 5, y + 5 - 1,
+ nControlWidth - 5, y + 5 + 1);
+
+ if (bDraw)
+ rContext.DrawRect(rContext.PixelToLogic(aSelectionRectangle));
+ y += 10;
+ }
+ }
+ else
+ {
+ if (mbVerticalMode)
+ {
+ if (y + boxY > nControlHeight)
+ {
+ x += boxX;
+ y = 0;
+ }
+ }
+ else
+ {
+ if ( x + boxX > nControlWidth)
+ {
+ x = 0;
+ y += boxY;
+ }
+ }
+
+ element->mBoxLocation = Point(x,y);
+ element->mBoxSize = Size(boxX, boxY);
+
+ if (bDraw)
+ {
+ if (pCurrentElement == element)
+ {
+ rContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
+ const StyleSettings& rStyleSettings = rContext.GetSettings().GetStyleSettings();
+ rContext.SetLineColor(rStyleSettings.GetHighlightColor());
+ rContext.SetFillColor(COL_TRANSPARENT);
+ rContext.DrawRect(rContext.PixelToLogic(tools::Rectangle(x + 1, y + 1, x + boxX - 1, y + boxY - 1)));
+ rContext.DrawRect(rContext.PixelToLogic(tools::Rectangle(x + 2, y + 2, x + boxX - 2, y + boxY - 2)));
+ rContext.Pop();
+ }
+
+ Size aSizePixel = rContext.LogicToPixel(Size(element->getNode()->GetWidth(),
+ element->getNode()->GetHeight()));
+ Point location(x + ((boxX - aSizePixel.Width()) / 2),
+ y + ((boxY - aSizePixel.Height()) / 2));
+ SmDrawingVisitor(rContext, rContext.PixelToLogic(location), element->getNode().get());
+ }
+
+ if (mbVerticalMode)
+ y += boxY;
+ else
+ x += boxX;
+ }
+ }
+
+ if (bDraw)
+ {
+ if (!m_bFirstPaintAfterLayout)
+ {
+ rContext.Pop();
+ return;
+ }
+ m_bFirstPaintAfterLayout = false;
+ }
+ else
+ m_bFirstPaintAfterLayout = true;
+
+ if (mbVerticalMode)
+ {
+ sal_Int32 nTotalControlWidth = x + boxX + mxScroll->hadjustment_get_value();
+ if (nTotalControlWidth > GetOutputSizePixel().Width())
+ {
+ mxScroll->hadjustment_set_upper(nTotalControlWidth);
+ mxScroll->hadjustment_set_page_size(nControlWidth);
+ mxScroll->hadjustment_set_page_increment(nControlWidth);
+ mxScroll->set_hpolicy(VclPolicyType::ALWAYS);
+ }
+ else
+ {
+ mxScroll->hadjustment_set_value(0);
+ mxScroll->set_hpolicy(VclPolicyType::NEVER);
+ }
+ }
+ else
+ {
+ sal_Int32 nTotalControlHeight = y + boxY + mxScroll->vadjustment_get_value();
+ if (nTotalControlHeight > GetOutputSizePixel().Height())
+ {
+ mxScroll->vadjustment_set_upper(nTotalControlHeight);
+ mxScroll->vadjustment_set_page_size(nControlHeight);
+ mxScroll->vadjustment_set_page_increment(nControlHeight);
+ mxScroll->set_vpolicy(VclPolicyType::ALWAYS);
+ }
+ else
+ {
+ mxScroll->vadjustment_set_value(0);
+ mxScroll->set_vpolicy(VclPolicyType::NEVER);
+ }
+ }
+ rContext.Pop();
+}
+
+void SmElementsControl::Resize()
+{
+ CustomWidgetController::Resize();
+ LayoutOrPaintContents(GetDrawingArea()->get_ref_device(), false);
+}
+
+void SmElementsControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ LayoutOrPaintContents(rRenderContext, true);
+}
+
+OUString SmElementsControl::RequestHelp(tools::Rectangle& rRect)
+{
+ if (!hasRollover())
+ return OUString();
+
+ const SmElement* pHelpElement = current();
+ if (!pHelpElement)
+ return OUString();
+
+ rRect = tools::Rectangle(pHelpElement->mBoxLocation, pHelpElement->mBoxSize);
+
+ // get text and display it
+ return pHelpElement->getHelpText();
+}
+
+bool SmElementsControl::MouseMove( const MouseEvent& rMouseEvent )
+{
+ if (rMouseEvent.IsLeaveWindow())
+ {
+ m_nCurrentRolloverElement = SAL_MAX_UINT16;
+ Invalidate();
+ return false;
+ }
+
+ if (tools::Rectangle(Point(0, 0), GetOutputSizePixel()).IsInside(rMouseEvent.GetPosPixel()))
+ {
+ const SmElement* pPrevElement = current();
+ if (pPrevElement)
+ {
+ const tools::Rectangle rect(pPrevElement->mBoxLocation, pPrevElement->mBoxSize);
+ if (rect.IsInside(rMouseEvent.GetPosPixel()))
+ return true;
+ }
+
+ const sal_uInt16 nElementCount = maElementList.size();
+ for (sal_uInt16 n = 0; n < nElementCount; n++)
+ {
+ const SmElement* element = maElementList[n].get();
+ if (pPrevElement == element)
+ continue;
+
+ const tools::Rectangle rect(element->mBoxLocation, element->mBoxSize);
+ if (rect.IsInside(rMouseEvent.GetPosPixel()))
+ {
+ m_nCurrentRolloverElement = n;
+ Invalidate();
+ return true;
+ }
+ }
+ if (pPrevElement && hasRollover())
+ Invalidate();
+ m_nCurrentRolloverElement = SAL_MAX_UINT16;
+ return true;
+ }
+
+ return false;
+}
+
+namespace {
+
+void collectUIInformation(const OUString& aID)
+{
+ EventDescription aDescription;
+ aDescription.aID = aID;
+ aDescription.aParent = "element_selector";
+ aDescription.aAction = "SELECT";
+ aDescription.aKeyWord = "ElementUIObject";
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+bool SmElementsControl::MouseButtonDown(const MouseEvent& rMouseEvent)
+{
+ GrabFocus();
+
+ if (rMouseEvent.IsLeft() && tools::Rectangle(Point(0, 0), GetOutputSizePixel()).IsInside(rMouseEvent.GetPosPixel()) && maSelectHdlLink.IsSet())
+ {
+ const SmElement* pPrevElement = hasRollover() ? current() : nullptr;
+ if (pPrevElement)
+ {
+ tools::Rectangle rect(pPrevElement->mBoxLocation, pPrevElement->mBoxSize);
+ if (rect.IsInside(rMouseEvent.GetPosPixel()))
+ {
+ setCurrentElement(m_nCurrentRolloverElement);
+ maSelectHdlLink.Call(*const_cast<SmElement*>(pPrevElement));
+ collectUIInformation(OUString::number(m_nCurrentRolloverElement));
+ return true;
+ }
+ }
+
+ const sal_uInt16 nElementCount = maElementList.size();
+ for (sal_uInt16 n = 0; n < nElementCount; n++)
+ {
+ SmElement* element = maElementList[n].get();
+ tools::Rectangle rect(element->mBoxLocation, element->mBoxSize);
+ if (rect.IsInside(rMouseEvent.GetPosPixel()))
+ {
+ setCurrentElement(n);
+ maSelectHdlLink.Call(*element);
+ collectUIInformation(OUString::number(n));
+ return true;
+ }
+ }
+
+ return true;
+ }
+ return false;
+}
+
+void SmElementsControl::GetFocus()
+{
+ CustomWidgetController::GetFocus();
+ Invalidate();
+}
+
+void SmElementsControl::LoseFocus()
+{
+ CustomWidgetController::LoseFocus();
+ Invalidate();
+}
+
+sal_uInt16 SmElementsControl::nextElement(const bool bBackward, const sal_uInt16 nStartPos, const sal_uInt16 nLastElement)
+{
+ sal_uInt16 nPos = nStartPos;
+
+ while (true)
+ {
+ if (bBackward)
+ {
+ if (nPos == 0)
+ break;
+ nPos--;
+ }
+ else
+ {
+ if (nPos == nLastElement)
+ break;
+ nPos++;
+ }
+
+ if (nStartPos == nPos)
+ break;
+ if (!maElementList[nPos]->isSeparator())
+ break;
+ }
+
+ return nPos;
+}
+
+void SmElementsControl::scrollToElement(const bool bBackward, const SmElement *pCur)
+{
+ if (mbVerticalMode)
+ {
+ auto nScrollPos = mxScroll->hadjustment_get_value();
+ nScrollPos += pCur->mBoxLocation.X();
+ if (!bBackward)
+ nScrollPos += pCur->mBoxSize.Width() - GetOutputSizePixel().Width();
+ mxScroll->hadjustment_set_value(nScrollPos);
+ }
+ else
+ {
+ auto nScrollPos = mxScroll->vadjustment_get_value();
+ nScrollPos += pCur->mBoxLocation.Y();
+ if (!bBackward)
+ nScrollPos += pCur->mBoxSize.Height() - GetOutputSizePixel().Height();
+ mxScroll->vadjustment_set_value(nScrollPos);
+ }
+}
+
+void SmElementsControl::stepFocus(const bool bBackward)
+{
+ const sal_uInt16 nStartPos = m_nCurrentElement;
+ const sal_uInt16 nLastElement = (maElementList.size() ? maElementList.size() - 1 : 0);
+ assert(nStartPos <= nLastElement);
+
+ sal_uInt16 nPos = nextElement(bBackward, nStartPos, nLastElement);
+ if (nStartPos != nPos)
+ {
+ m_nCurrentRolloverElement = SAL_MAX_UINT16;
+ setCurrentElement(nPos);
+
+ const tools::Rectangle outputRect(Point(0,0), GetOutputSizePixel());
+ const SmElement *pCur = maElementList[nPos].get();
+ tools::Rectangle elementRect(pCur->mBoxLocation, pCur->mBoxSize);
+ if (!outputRect.IsInside(elementRect))
+ scrollToElement(bBackward, pCur);
+ Invalidate();
+ }
+}
+
+void SmElementsControl::pageFocus(const bool bBackward)
+{
+ const sal_uInt16 nStartPos = m_nCurrentElement;
+ const sal_uInt16 nLastElement = (maElementList.size() ? maElementList.size() - 1 : 0);
+ assert(nStartPos <= nLastElement);
+ tools::Rectangle outputRect(Point(0,0), GetOutputSizePixel());
+ sal_uInt16 nPrevPos = nStartPos;
+ sal_uInt16 nPos = nPrevPos;
+
+ bool bMoved = false;
+ while (true)
+ {
+ nPrevPos = nPos;
+ nPos = nextElement(bBackward, nPrevPos, nLastElement);
+ if (nPrevPos == nPos)
+ break;
+
+ m_nCurrentRolloverElement = SAL_MAX_UINT16;
+
+ SmElement *pCur = maElementList[nPos].get();
+ tools::Rectangle elementRect(pCur->mBoxLocation, pCur->mBoxSize);
+ if (!outputRect.IsInside(elementRect))
+ {
+ if (nPrevPos != nStartPos)
+ {
+ nPos = nPrevPos;
+ break;
+ }
+ if (bMoved)
+ break;
+ pCur = maElementList[nPrevPos].get();
+
+ elementRect = tools::Rectangle(pCur->mBoxLocation, pCur->mBoxSize);
+ if (mbVerticalMode)
+ outputRect.Move(bBackward ? -outputRect.GetWidth() + elementRect.Right() : elementRect.Left(), 0);
+ else
+ outputRect.Move(0, bBackward ? -outputRect.GetHeight() + elementRect.Bottom() : elementRect.Top());
+ bMoved = true;
+ }
+ }
+
+ if (nStartPos != nPos)
+ {
+ setCurrentElement(nPos);
+ if (bMoved)
+ scrollToElement(bBackward, maElementList[nPos].get());
+ Invalidate();
+ }
+}
+
+bool SmElementsControl::KeyInput(const KeyEvent& rKEvt)
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+
+ if (aKeyCode.GetModifier())
+ {
+ return false;
+ }
+
+ switch(aKeyCode.GetCode())
+ {
+ case KEY_RETURN:
+ [[fallthrough]];
+ case KEY_SPACE:
+ assert(m_nCurrentElement < maElementList.size());
+ assert(maSelectHdlLink.IsSet());
+ maSelectHdlLink.Call(*maElementList[m_nCurrentElement]);
+ collectUIInformation(OUString::number(m_nCurrentElement));
+ break;
+
+ case KEY_DOWN:
+ [[fallthrough]];
+ case KEY_RIGHT:
+ stepFocus(false);
+ break;
+
+ case KEY_LEFT:
+ [[fallthrough]];
+ case KEY_UP:
+ stepFocus(true);
+ break;
+
+ case KEY_HOME:
+ if (!maElementList.empty())
+ {
+ setCurrentElement(0);
+ mxScroll->vadjustment_set_value(0);
+ }
+ break;
+ case KEY_END:
+ if (!maElementList.empty())
+ {
+ setCurrentElement(maElementList.size() - 1);
+ mxScroll->vadjustment_set_value(mxScroll->vadjustment_get_upper());
+ }
+ break;
+
+ case KEY_PAGEUP:
+ pageFocus(true);
+ break;
+ case KEY_PAGEDOWN:
+ pageFocus(false);
+ break;
+
+ default:
+ return false;
+ break;
+ }
+ return true;
+}
+
+IMPL_LINK_NOARG( SmElementsControl, ScrollHdl, weld::ScrolledWindow&, void )
+{
+ Invalidate();
+}
+
+void SmElementsControl::addElement(SmParser &rParser, const OUString& aElementVisual, const OUString& aElementSource, const OUString& aHelpText)
+{
+ // SAL_MAX_UINT16 is invalid, zero is the scrollbar
+ assert(maElementList.size() < SAL_MAX_UINT16 - 2);
+ auto pNode = rParser.ParseExpression(aElementVisual);
+
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ rDevice.Push(PushFlags::MAPMODE);
+ rDevice.SetMapMode( MapMode(MapUnit::Map100thMM) );
+
+ pNode->Prepare(maFormat, *mpDocShell, 0);
+ pNode->SetSize(Fraction(10,8));
+ pNode->Arrange(rDevice, maFormat);
+
+ Size aSizePixel = rDevice.LogicToPixel(Size(pNode->GetWidth(), pNode->GetHeight()), MapMode(MapUnit::Map100thMM));
+ if (aSizePixel.Width() > maMaxElementDimensions.Width()) {
+ maMaxElementDimensions.setWidth( aSizePixel.Width() );
+ }
+
+ if (aSizePixel.Height() > maMaxElementDimensions.Height()) {
+ maMaxElementDimensions.setHeight( aSizePixel.Height() );
+ }
+
+ maElementList.push_back(std::make_unique<SmElement>(std::move(pNode), aElementSource, aHelpText));
+
+ rDevice.Pop();
+}
+
+void SmElementsControl::setElementSetId(const char* pSetId)
+{
+ if (msCurrentSetId == pSetId)
+ return;
+ msCurrentSetId = pSetId;
+ maMaxElementDimensions = Size();
+ build();
+}
+
+void SmElementsControl::addElements(const SmElementDescr aElementsArray[], sal_uInt16 aElementsArraySize)
+{
+ SmParser aParser;
+ aParser.SetImportSymbolNames(true);
+
+ for (sal_uInt16 i = 0; i < aElementsArraySize ; i++)
+ {
+ const char* pElement = aElementsArray[i].first;
+ const char* pElementHelp = aElementsArray[i].second;
+ if (!pElement) {
+ maElementList.push_back(std::make_unique<SmElementSeparator>());
+ } else {
+ OUString aElement(OUString::createFromAscii(pElement));
+ if (aElement == RID_NEWLINE)
+ addElement(aParser, OUString(u"\u21B5"), aElement, SmResId(pElementHelp));
+ else if (aElement == RID_SBLANK)
+ addElement(aParser, "\"`\"", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_BLANK)
+ addElement(aParser, "\"~\"", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_PHANTOMX)
+ addElement(aParser, "\"" + SmResId(STR_HIDE) +"\"", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_BOLDX)
+ addElement(aParser, "bold B", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_ITALX)
+ addElement(aParser, "ital I", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_SIZEXY)
+ addElement(aParser, "\"" + SmResId(STR_SIZE) + "\"", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_FONTXY)
+ addElement(aParser, "\"" + SmResId(STR_FONT) + "\"", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_BLACK)
+ addElement(aParser, "color black { \"" + SmResId(STR_BLACK) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_BLUE)
+ addElement(aParser, "color blue { \"" + SmResId(STR_BLUE) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_GREEN)
+ addElement(aParser, "color green { \"" + SmResId(STR_GREEN) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_RED)
+ addElement(aParser, "color red { \"" + SmResId(STR_RED) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_CYAN)
+ addElement(aParser, "color cyan { \"" + SmResId(STR_CYAN) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_MAGENTA)
+ addElement(aParser, "color magenta { \"" + SmResId(STR_MAGENTA) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_YELLOW)
+ addElement(aParser, "color yellow { \"" + SmResId(STR_YELLOW) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_GRAY)
+ addElement(aParser, "color gray { \"" + SmResId(STR_GRAY) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_LIME)
+ addElement(aParser, "color lime { \"" + SmResId(STR_LIME) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_MAROON)
+ addElement(aParser, "color maroon { \"" + SmResId(STR_MAROON) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_NAVY)
+ addElement(aParser, "color navy { \"" + SmResId(STR_NAVY) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_OLIVE)
+ addElement(aParser, "color olive { \"" + SmResId(STR_OLIVE) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_PURPLE)
+ addElement(aParser, "color purple { \"" + SmResId(STR_PURPLE) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_SILVER)
+ addElement(aParser, "color silver { \"" + SmResId(STR_SILVER) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_TEAL)
+ addElement(aParser, "color teal { \"" + SmResId(STR_TEAL) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_COLORX_RGB)
+ addElement(aParser, "color rgb 0 0 0 { \"" + SmResId(STR_RGB) + "\" }", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_ALIGNLX)
+ addElement(aParser, "\"" + SmResId(STR_ALIGN_LEFT) + "\"", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_ALIGNCX)
+ addElement(aParser, "\"" + SmResId(STR_ALIGN_CENTER) + "\"", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_ALIGNRX)
+ addElement(aParser, "\"" + SmResId(STR_ALIGN_RIGHT) + "\"", aElement, SmResId(pElementHelp));
+
+ else if (aElement == RID_SLRPARENTX)
+ addElement(aParser, "left ( binom{<?>}{<?>} right ) ", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_SLRBRACKETX)
+ addElement(aParser, "left [ binom{<?>}{<?>} right ] ", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_SLRDBRACKETX)
+ addElement(aParser, "left ldbracket binom{<?>}{<?>} right rdbracket ", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_SLRBRACEX)
+ addElement(aParser, "left lbrace binom{<?>}{<?>} right rbrace ", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_SLRANGLEX)
+ addElement(aParser, "left langle binom{<?>}{<?>} right rangle ", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_SLRCEILX)
+ addElement(aParser, "left lceil binom{<?>}{<?>} right rceil ", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_SLRFLOORX)
+ addElement(aParser, "left lfloor binom{<?>}{<?>} right rfloor ", aElement, SmResId(pElementHelp));
+
+ else if (aElement == RID_SLRLINEX)
+ addElement(aParser, "left lline binom{<?>}{<?>} right rline ", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_SLRDLINEX)
+ addElement(aParser, "left ldline binom{<?>}{<?>} right rdline ", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_SLMRANGLEXY)
+ addElement(aParser, "left langle binom{<?>}{<?>} mline binom{<?>}{<?>} right rangle ", aElement, SmResId(pElementHelp));
+
+ else if (aElement == RID_XOVERBRACEY)
+ addElement(aParser, "{<?><?><?>} overbrace {<?>} ", aElement, SmResId(pElementHelp));
+ else if (aElement == RID_XUNDERBRACEY)
+ addElement(aParser, "{<?><?><?>} underbrace {<?>} ", aElement, SmResId(pElementHelp));
+ else
+ addElement(aParser, aElement, aElement, pElementHelp ? SmResId(pElementHelp) : "");
+ }
+ }
+}
+
+void SmElementsControl::build()
+{
+ // The order is important!
+ // 1. Ensure there are no items left, including the default scrollbar!
+ // 2. Release all the current accessible items.
+ // This will check for new items after releasing them!
+ // 3. Set the cursor element
+ maElementList.clear();
+ mxScroll->hadjustment_set_value(0);
+ mxScroll->vadjustment_set_value(0);
+ mxScroll->set_hpolicy(VclPolicyType::NEVER);
+ mxScroll->set_vpolicy(VclPolicyType::NEVER);
+
+ if (m_xAccessible.is())
+ m_xAccessible->ReleaseAllItems();
+
+ setCurrentElement(SAL_MAX_UINT16);
+
+ // The first element is the scrollbar. We can't change its indexInParent
+ // value, as this is set by being a child of the SmElementsControl.
+ m_nCurrentOffset = 1;
+ for (sal_uInt16 n = 0; n < SAL_N_ELEMENTS(m_aCategories); ++n)
+ {
+ if (msCurrentSetId == std::get<0>(m_aCategories[n]))
+ {
+ addElements(std::get<1>(m_aCategories[n]), std::get<2>(m_aCategories[n]));
+ break;
+ }
+ else
+ m_nCurrentOffset += std::get<2>(m_aCategories[n]);
+ }
+
+ m_nCurrentRolloverElement = SAL_MAX_UINT16;
+ LayoutOrPaintContents(GetDrawingArea()->get_ref_device(), false);
+
+ if (m_xAccessible.is())
+ m_xAccessible->AddAllItems();
+
+ setCurrentElement(0);
+ Invalidate();
+}
+
+void SmElementsControl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ maFormat.SetBaseSize(rDevice.PixelToLogic(Size(0, SmPtsTo100th_mm(12))));
+ Size aSize(rDevice.LogicToPixel(Size(10, 100), MapMode(MapUnit::MapAppFont)));
+ // give it an arbitrary small width request so it can shrink in the sidebar
+ pDrawingArea->set_size_request(42, aSize.Height());
+ SetOutputSizePixel(aSize);
+}
+
+FactoryFunction SmElementsControl::GetUITestFactory() const
+{
+ return ElementSelectorUIObject::create;
+}
+
+bool SmElementsControl::itemIsSeparator(sal_uInt16 nPos) const
+{
+ if (nPos < m_nCurrentOffset)
+ return true;
+ nPos -= m_nCurrentOffset;
+ if (nPos >= maElementList.size())
+ return true;
+ return maElementList[nPos]->isSeparator();
+}
+
+css::uno::Reference<css::accessibility::XAccessible> SmElementsControl::CreateAccessible()
+{
+ if (!m_xAccessible.is())
+ {
+ m_xAccessible = new AccessibleSmElementsControl(*this);
+ m_xAccessible->AddAllItems();
+ }
+ return m_xAccessible.get();
+}
+
+bool SmElementsControl::itemTrigger(sal_uInt16 nPos)
+{
+ if (nPos < m_nCurrentOffset)
+ return false;
+ nPos -= m_nCurrentOffset;
+ if (nPos >= maElementList.size())
+ return false;
+
+ maSelectHdlLink.Call(*maElementList[nPos]);
+ collectUIInformation(OUString::number(nPos));
+ return true;
+}
+
+tools::Rectangle SmElementsControl::itemPosRect(sal_uInt16 nPos) const
+{
+ if (nPos < m_nCurrentOffset)
+ return tools::Rectangle();
+ nPos -= m_nCurrentOffset;
+ if (nPos >= maElementList.size())
+ return tools::Rectangle();
+
+ SmElement* pItem = maElementList[nPos].get();
+ return tools::Rectangle(pItem->mBoxLocation, pItem->mBoxSize);
+}
+
+bool SmElementsControl::itemIsVisible(sal_uInt16 nPos) const
+{
+ tools::Rectangle elementRect = itemPosRect(nPos);
+ if (elementRect.IsEmpty())
+ return false;
+
+ tools::Rectangle outputRect(Point(0, 0), GetOutputSizePixel());
+ return outputRect.IsInside(elementRect);
+}
+
+sal_uInt16 SmElementsControl::itemCount() const { return maElementList.size(); }
+
+sal_uInt16 SmElementsControl::itemHighlighted() const { return m_nCurrentElement; }
+
+void SmElementsControl::setItemHighlighted(sal_uInt16 nPos)
+{
+ if (m_nCurrentRolloverElement == nPos)
+ return;
+ if (nPos != SAL_MAX_UINT16 && nPos >= maElementList.size())
+ return;
+
+ if (maElementList[nPos]->isSeparator())
+ m_nCurrentRolloverElement = SAL_MAX_UINT16;
+ else
+ m_nCurrentRolloverElement = nPos;
+ Invalidate();
+}
+
+OUString SmElementsControl::itemName(sal_uInt16 nPos) const
+{
+ if (nPos < m_nCurrentOffset)
+ return OUString();
+ nPos -= m_nCurrentOffset;
+ if (nPos >= maElementList.size())
+ return OUString();
+
+ return maElementList[nPos]->getHelpText();
+}
+
+sal_uInt16 SmElementsControl::itemAtPos(const Point& rPoint) const
+{
+ sal_uInt16 nElementCount = maElementList.size();
+ for (sal_uInt16 n = 0; n < nElementCount; n++)
+ {
+ const SmElement* pItem = maElementList[n].get();
+ tools::Rectangle elementRect(pItem->mBoxLocation, pItem->mBoxSize);
+ if (elementRect.IsInside(rPoint))
+ return n;
+ }
+ return SAL_MAX_UINT16;
+}
+
+SmElementsDockingWindow::SmElementsDockingWindow(SfxBindings* pInputBindings, SfxChildWindow* pChildWindow, vcl::Window* pParent)
+ : SfxDockingWindow(pInputBindings, pChildWindow, pParent, "DockingElements",
+ "modules/smath/ui/dockingelements.ui")
+ , mxElementsControl(new SmElementsControl(m_xBuilder->weld_scrolled_window("scrolledwindow")))
+ , mxElementsControlWin(new weld::CustomWeld(*m_xBuilder, "element_selector", *mxElementsControl))
+ , mxElementListBox(m_xBuilder->weld_combo_box("listbox"))
+{
+ // give it an arbitrary small width request so it can shrink in the sidebar
+ mxElementListBox->set_size_request(42, -1);
+
+ for (size_t i = 0; i < SmElementsControl::categoriesSize(); ++i)
+ mxElementListBox->append_text(SmResId(std::get<0>(SmElementsControl::categories()[i])));
+
+ mxElementListBox->connect_changed(LINK(this, SmElementsDockingWindow, ElementSelectedHandle));
+ mxElementListBox->set_active_text(SmResId(RID_CATEGORY_UNARY_BINARY_OPERATORS));
+
+ mxElementsControl->setElementSetId(RID_CATEGORY_UNARY_BINARY_OPERATORS);
+ mxElementsControl->SetSelectHdl(LINK(this, SmElementsDockingWindow, SelectClickHandler));
+}
+
+SmElementsDockingWindow::~SmElementsDockingWindow ()
+{
+ disposeOnce();
+}
+
+void SmElementsDockingWindow::dispose()
+{
+ mxElementsControlWin.reset();
+ mxElementsControl.reset();
+ mxElementListBox.reset();
+ SfxDockingWindow::dispose();
+}
+
+void SmElementsDockingWindow::ToggleFloatingMode()
+{
+ SfxDockingWindow::ToggleFloatingMode();
+
+ if (GetFloatingWindow())
+ GetFloatingWindow()->SetMinOutputSizePixel( Size(100, 100) );
+
+ Invalidate();
+}
+
+void SmElementsDockingWindow::EndDocking( const tools::Rectangle& rReactangle, bool bFloatMode)
+{
+ SfxDockingWindow::EndDocking(rReactangle, bFloatMode);
+ bool bVertical = ( GetAlignment() == SfxChildAlignment::TOP || GetAlignment() == SfxChildAlignment::BOTTOM );
+ mxElementsControl->setVerticalMode(bVertical);
+}
+
+IMPL_LINK(SmElementsDockingWindow, SelectClickHandler, SmElement&, rElement, void)
+{
+ SmViewShell* pViewSh = GetView();
+
+ if (pViewSh)
+ {
+ std::unique_ptr<SfxStringItem> pInsertCommand = std::make_unique<SfxStringItem>(SID_INSERTCOMMANDTEXT, rElement.getText());
+ pViewSh->GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_INSERTCOMMANDTEXT, SfxCallMode::RECORD,
+ { pInsertCommand.get() });
+ }
+}
+
+IMPL_LINK( SmElementsDockingWindow, ElementSelectedHandle, weld::ComboBox&, rList, void)
+{
+ for (size_t i = 0; i < SmElementsControl::categoriesSize(); ++i)
+ {
+ const char *pCurrentCategory = std::get<0>(SmElementsControl::categories()[i]);
+ OUString aCurrentCategoryString = SmResId(pCurrentCategory);
+ if (aCurrentCategoryString == rList.get_active_text())
+ {
+ mxElementsControl->setElementSetId(pCurrentCategory);
+ return;
+ }
+ }
+}
+
+SmViewShell* SmElementsDockingWindow::GetView()
+{
+ SfxViewShell* pView = GetBindings().GetDispatcher()->GetFrame()->GetViewShell();
+ return dynamic_cast<SmViewShell*>( pView);
+}
+
+void SmElementsDockingWindow::Resize()
+{
+ bool bVertical = ( GetAlignment() == SfxChildAlignment::TOP || GetAlignment() == SfxChildAlignment::BOTTOM );
+ mxElementsControl->setVerticalMode(bVertical);
+
+ SfxDockingWindow::Resize();
+ Invalidate();
+}
+
+SFX_IMPL_DOCKINGWINDOW_WITHID(SmElementsDockingWindowWrapper, SID_ELEMENTSDOCKINGWINDOW);
+
+SmElementsDockingWindowWrapper::SmElementsDockingWindowWrapper(
+ vcl::Window *pParentWindow, sal_uInt16 nId,
+ SfxBindings *pBindings, SfxChildWinInfo *pInfo) :
+ SfxChildWindow(pParentWindow, nId)
+{
+ VclPtrInstance<SmElementsDockingWindow> pDialog(pBindings, this, pParentWindow);
+ SetWindow(pDialog);
+ pDialog->setDeferredProperties();
+ pDialog->SetPosSizePixel(Point(0, 0), Size(300, 0));
+ pDialog->Show();
+
+ SetAlignment(SfxChildAlignment::LEFT);
+
+ pDialog->Initialize( pInfo );
+}
+
+SmElementsDockingWindowWrapper::~SmElementsDockingWindowWrapper()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/accessibility.cxx b/starmath/source/accessibility.cxx
new file mode 100644
index 000000000..854f09443
--- /dev/null
+++ b/starmath/source/accessibility.cxx
@@ -0,0 +1,1791 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <memory>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventObject.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <unotools/accessiblerelationsethelper.hxx>
+
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <svx/AccessibleTextHelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <vcl/unohelp2.hxx>
+#include <vcl/settings.hxx>
+
+#include <tools/gen.hxx>
+#include <svl/itemset.hxx>
+
+#include <editeng/editobj.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/unoedhlp.hxx>
+
+
+#include "accessibility.hxx"
+#include <document.hxx>
+#include <view.hxx>
+#include <strings.hrc>
+#include <smmod.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::accessibility;
+
+
+static awt::Rectangle lcl_GetBounds( vcl::Window const *pWin )
+{
+ // !! see VCLXAccessibleComponent::implGetBounds()
+
+ //! the coordinates returned are relative to the parent window !
+ //! Thus the top-left point may be different from (0, 0) !
+
+ awt::Rectangle aBounds;
+ if (pWin)
+ {
+ tools::Rectangle aRect = pWin->GetWindowExtentsRelative( nullptr );
+ aBounds.X = aRect.Left();
+ aBounds.Y = aRect.Top();
+ aBounds.Width = aRect.GetWidth();
+ aBounds.Height = aRect.GetHeight();
+ vcl::Window* pParent = pWin->GetAccessibleParentWindow();
+ if (pParent)
+ {
+ tools::Rectangle aParentRect = pParent->GetWindowExtentsRelative( nullptr );
+ awt::Point aParentScreenLoc( aParentRect.Left(), aParentRect.Top() );
+ aBounds.X -= aParentScreenLoc.X;
+ aBounds.Y -= aParentScreenLoc.Y;
+ }
+ }
+ return aBounds;
+}
+
+static awt::Point lcl_GetLocationOnScreen( vcl::Window const *pWin )
+{
+ // !! see VCLXAccessibleComponent::getLocationOnScreen()
+
+ awt::Point aPos;
+ if (pWin)
+ {
+ tools::Rectangle aRect = pWin->GetWindowExtentsRelative( nullptr );
+ aPos.X = aRect.Left();
+ aPos.Y = aRect.Top();
+ }
+ return aPos;
+}
+
+
+SmGraphicAccessible::SmGraphicAccessible( SmGraphicWindow *pGraphicWin ) :
+ aAccName (SmResId(RID_DOCUMENTSTR)),
+ nClientId (0),
+ pWin (pGraphicWin)
+{
+ OSL_ENSURE( pWin, "SmGraphicAccessible: window missing" );
+}
+
+SmGraphicAccessible::~SmGraphicAccessible()
+{
+}
+
+
+SmDocShell * SmGraphicAccessible::GetDoc_Impl()
+{
+ SmViewShell *pView = pWin ? pWin->GetView() : nullptr;
+ return pView ? pView->GetDoc() : nullptr;
+}
+
+OUString SmGraphicAccessible::GetAccessibleText_Impl()
+{
+ OUString aTxt;
+ SmDocShell *pDoc = GetDoc_Impl();
+ if (pDoc)
+ aTxt = pDoc->GetAccessibleText();
+ return aTxt;
+}
+
+void SmGraphicAccessible::ClearWin()
+{
+ pWin = nullptr; // implicitly results in AccessibleStateType::DEFUNC set
+
+ if ( nClientId )
+ {
+ comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nClientId, *this );
+ nClientId = 0;
+ }
+}
+
+void SmGraphicAccessible::LaunchEvent(
+ const sal_Int16 nAccessibleEventId,
+ const uno::Any &rOldVal,
+ const uno::Any &rNewVal)
+{
+ AccessibleEventObject aEvt;
+ aEvt.Source = static_cast<XAccessible *>(this);
+ aEvt.EventId = nAccessibleEventId;
+ aEvt.OldValue = rOldVal;
+ aEvt.NewValue = rNewVal ;
+
+ // pass event on to event-listener's
+ if (nClientId)
+ comphelper::AccessibleEventNotifier::addEvent( nClientId, aEvt );
+}
+
+uno::Reference< XAccessibleContext > SAL_CALL SmGraphicAccessible::getAccessibleContext()
+{
+ return this;
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::containsPoint( const awt::Point& aPoint )
+{
+ //! the arguments coordinates are relative to the current window !
+ //! Thus the top-left point is (0, 0)
+
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ Size aSz( pWin->GetSizePixel() );
+ return aPoint.X >= 0 && aPoint.Y >= 0 &&
+ aPoint.X < aSz.Width() && aPoint.Y < aSz.Height();
+}
+
+uno::Reference< XAccessible > SAL_CALL SmGraphicAccessible::getAccessibleAtPoint(
+ const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+ XAccessible *pRes = nullptr;
+ if (containsPoint( aPoint ))
+ pRes = this;
+ return pRes;
+}
+
+awt::Rectangle SAL_CALL SmGraphicAccessible::getBounds()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+ OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(),
+ "mismatch of window parent and accessible parent" );
+ return lcl_GetBounds( pWin );
+}
+
+awt::Point SAL_CALL SmGraphicAccessible::getLocation()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+ OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(),
+ "mismatch of window parent and accessible parent" );
+ awt::Rectangle aRect( lcl_GetBounds( pWin ) );
+ return awt::Point( aRect.X, aRect.Y );
+}
+
+awt::Point SAL_CALL SmGraphicAccessible::getLocationOnScreen()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+ OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(),
+ "mismatch of window parent and accessible parent" );
+ return lcl_GetLocationOnScreen( pWin );
+}
+
+awt::Size SAL_CALL SmGraphicAccessible::getSize()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+ OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(),
+ "mismatch of window parent and accessible parent" );
+
+ Size aSz( pWin->GetSizePixel() );
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ awt::Rectangle aRect( lcl_GetBounds( pWin ) );
+ Size aSz2( aRect.Width, aRect.Height );
+ assert(aSz == aSz2 && "mismatch in width" );
+#endif
+ return awt::Size( aSz.Width(), aSz.Height() );
+}
+
+void SAL_CALL SmGraphicAccessible::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ pWin->GrabFocus();
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getForeground()
+{
+ SolarMutexGuard aGuard;
+
+ if (!pWin)
+ throw RuntimeException();
+ return static_cast<sal_Int32>(pWin->GetTextColor());
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getBackground()
+{
+ SolarMutexGuard aGuard;
+
+ if (!pWin)
+ throw RuntimeException();
+ Wallpaper aWall( pWin->GetDisplayBackground() );
+ Color nCol;
+ if (aWall.IsBitmap() || aWall.IsGradient())
+ nCol = pWin->GetSettings().GetStyleSettings().GetWindowColor();
+ else
+ nCol = aWall.GetColor();
+ return static_cast<sal_Int32>(nCol);
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getAccessibleChildCount()
+{
+ return 0;
+}
+
+Reference< XAccessible > SAL_CALL SmGraphicAccessible::getAccessibleChild(
+ sal_Int32 /*i*/ )
+{
+ throw IndexOutOfBoundsException(); // there is no child...
+}
+
+Reference< XAccessible > SAL_CALL SmGraphicAccessible::getAccessibleParent()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ vcl::Window *pAccParent = pWin->GetAccessibleParentWindow();
+ OSL_ENSURE( pAccParent, "accessible parent missing" );
+ return pAccParent ? pAccParent->GetAccessible() : Reference< XAccessible >();
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nIdx = -1;
+ vcl::Window *pAccParent = pWin ? pWin->GetAccessibleParentWindow() : nullptr;
+ if (pAccParent)
+ {
+ sal_uInt16 nCnt = pAccParent->GetAccessibleChildWindowCount();
+ for (sal_uInt16 i = 0; i < nCnt && nIdx == -1; ++i)
+ if (pAccParent->GetAccessibleChildWindow( i ) == pWin)
+ nIdx = i;
+ }
+ return nIdx;
+}
+
+sal_Int16 SAL_CALL SmGraphicAccessible::getAccessibleRole()
+{
+ return AccessibleRole::DOCUMENT;
+}
+
+OUString SAL_CALL SmGraphicAccessible::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+ SmDocShell *pDoc = GetDoc_Impl();
+ return pDoc ? pDoc->GetText() : OUString();
+}
+
+OUString SAL_CALL SmGraphicAccessible::getAccessibleName()
+{
+ SolarMutexGuard aGuard;
+ return aAccName;
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL SmGraphicAccessible::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ Reference< XAccessibleRelationSet > xRelSet = new utl::AccessibleRelationSetHelper();
+ return xRelSet; // empty relation set
+}
+
+Reference< XAccessibleStateSet > SAL_CALL SmGraphicAccessible::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ ::utl::AccessibleStateSetHelper *pStateSet =
+ new ::utl::AccessibleStateSetHelper;
+
+ Reference<XAccessibleStateSet> xStateSet( pStateSet );
+
+ if (!pWin)
+ pStateSet->AddState( AccessibleStateType::DEFUNC );
+ else
+ {
+ pStateSet->AddState( AccessibleStateType::ENABLED );
+ pStateSet->AddState( AccessibleStateType::FOCUSABLE );
+ if (pWin->HasFocus())
+ pStateSet->AddState( AccessibleStateType::FOCUSED );
+ if (pWin->IsActive())
+ pStateSet->AddState( AccessibleStateType::ACTIVE );
+ if (pWin->IsVisible())
+ pStateSet->AddState( AccessibleStateType::SHOWING );
+ if (pWin->IsReallyVisible())
+ pStateSet->AddState( AccessibleStateType::VISIBLE );
+ if (COL_TRANSPARENT != pWin->GetBackground().GetColor())
+ pStateSet->AddState( AccessibleStateType::OPAQUE );
+ }
+
+ return xStateSet;
+}
+
+Locale SAL_CALL SmGraphicAccessible::getLocale()
+{
+ SolarMutexGuard aGuard;
+ // should be the document language...
+ // We use the language of the localized symbol names here.
+ return Application::GetSettings().GetUILanguageTag().getLocale();
+}
+
+
+void SAL_CALL SmGraphicAccessible::addAccessibleEventListener(
+ const Reference< XAccessibleEventListener >& xListener )
+{
+ if (xListener.is())
+ {
+ SolarMutexGuard aGuard;
+ if (pWin)
+ {
+ if (!nClientId)
+ nClientId = comphelper::AccessibleEventNotifier::registerClient( );
+ comphelper::AccessibleEventNotifier::addEventListener( nClientId, xListener );
+ }
+ }
+}
+
+void SAL_CALL SmGraphicAccessible::removeAccessibleEventListener(
+ const Reference< XAccessibleEventListener >& xListener )
+{
+ if (!(xListener.is() && nClientId))
+ return;
+
+ SolarMutexGuard aGuard;
+ sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( nClientId, 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::revokeClient( nClientId );
+ nClientId = 0;
+ }
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getCaretPosition()
+{
+ return 0;
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::setCaretPosition( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ if (nIndex >= aTxt.getLength())
+ throw IndexOutOfBoundsException();
+ return false;
+}
+
+sal_Unicode SAL_CALL SmGraphicAccessible::getCharacter( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ if (nIndex >= aTxt.getLength())
+ throw IndexOutOfBoundsException();
+ return aTxt[nIndex];
+}
+
+Sequence< beans::PropertyValue > SAL_CALL SmGraphicAccessible::getCharacterAttributes(
+ sal_Int32 nIndex,
+ const uno::Sequence< OUString > & /*rRequestedAttributes*/ )
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nLen = GetAccessibleText_Impl().getLength();
+ if (!(0 <= nIndex && nIndex < nLen))
+ throw IndexOutOfBoundsException();
+ return Sequence< beans::PropertyValue >();
+}
+
+awt::Rectangle SAL_CALL SmGraphicAccessible::getCharacterBounds( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ awt::Rectangle aRes;
+
+ if (!pWin)
+ throw RuntimeException();
+
+ // get accessible text
+ SmViewShell *pView = pWin->GetView();
+ SmDocShell *pDoc = pView ? pView->GetDoc() : nullptr;
+ if (!pDoc)
+ throw RuntimeException();
+ OUString aTxt( GetAccessibleText_Impl() );
+ if (!(0 <= nIndex && nIndex <= aTxt.getLength())) // aTxt.getLength() is valid
+ throw IndexOutOfBoundsException();
+
+ // find a reasonable rectangle for position aTxt.getLength().
+ bool bWasBehindText = (nIndex == aTxt.getLength());
+ if (bWasBehindText && nIndex)
+ --nIndex;
+
+ const SmNode *pTree = pDoc->GetFormulaTree();
+ const SmNode *pNode = pTree->FindNodeWithAccessibleIndex( nIndex );
+ //! pNode may be 0 if the index belongs to a char that was inserted
+ //! only for the accessible text!
+ if (pNode)
+ {
+ sal_Int32 nAccIndex = pNode->GetAccessibleIndex();
+ OSL_ENSURE( nAccIndex >= 0, "invalid accessible index" );
+ OSL_ENSURE( nIndex >= nAccIndex, "index out of range" );
+
+ OUStringBuffer aBuf;
+ pNode->GetAccessibleText(aBuf);
+ OUString aNodeText = aBuf.makeStringAndClear();
+ sal_Int32 nNodeIndex = nIndex - nAccIndex;
+ if (0 <= nNodeIndex && nNodeIndex < aNodeText.getLength())
+ {
+ // get appropriate rectangle
+ Point aOffset(pNode->GetTopLeft() - pTree->GetTopLeft());
+ Point aTLPos (pWin->GetFormulaDrawPos() + aOffset);
+ Size aSize (pNode->GetSize());
+
+ std::unique_ptr<long[]> pXAry(new long[ aNodeText.getLength() ]);
+ pWin->SetFont( pNode->GetFont() );
+ pWin->GetTextArray( aNodeText, pXAry.get(), 0, aNodeText.getLength() );
+ aTLPos.AdjustX(nNodeIndex > 0 ? pXAry[nNodeIndex - 1] : 0 );
+ aSize.setWidth( nNodeIndex > 0 ? pXAry[nNodeIndex] - pXAry[nNodeIndex - 1] : pXAry[nNodeIndex] );
+ pXAry.reset();
+
+ aTLPos = pWin->LogicToPixel( aTLPos );
+ aSize = pWin->LogicToPixel( aSize );
+ aRes.X = aTLPos.X();
+ aRes.Y = aTLPos.Y();
+ aRes.Width = aSize.Width();
+ aRes.Height = aSize.Height();
+ }
+ }
+
+ // take rectangle from last character and move it to the right
+ if (bWasBehindText)
+ aRes.X += aRes.Width;
+
+ return aRes;
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getCharacterCount()
+{
+ SolarMutexGuard aGuard;
+ return GetAccessibleText_Impl().getLength();
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getIndexAtPoint( const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nRes = -1;
+ if (pWin)
+ {
+ const SmNode *pTree = pWin->GetView()->GetDoc()->GetFormulaTree();
+ // can be NULL! e.g. if one clicks within the window already during loading of the
+ // document (before the parser even started)
+ if (!pTree)
+ return nRes;
+
+ // get position relative to formula draw position
+ Point aPos( aPoint.X, aPoint.Y );
+ aPos = pWin->PixelToLogic( aPos );
+ aPos -= pWin->GetFormulaDrawPos();
+
+ // if it was inside the formula then get the appropriate node
+ const SmNode *pNode = nullptr;
+ if (pTree->OrientedDist(aPos) <= 0)
+ pNode = pTree->FindRectClosestTo(aPos);
+
+ if (pNode)
+ {
+ // get appropriate rectangle
+ Point aOffset( pNode->GetTopLeft() - pTree->GetTopLeft() );
+ Point aTLPos ( aOffset );
+ Size aSize( pNode->GetSize() );
+
+ tools::Rectangle aRect( aTLPos, aSize );
+ if (aRect.IsInside( aPos ))
+ {
+ OSL_ENSURE( pNode->IsVisible(), "node is not a leaf" );
+ OUStringBuffer aBuf;
+ pNode->GetAccessibleText(aBuf);
+ OUString aTxt = aBuf.makeStringAndClear();
+ OSL_ENSURE( !aTxt.isEmpty(), "no accessible text available" );
+
+ long nNodeX = pNode->GetLeft();
+
+ std::unique_ptr<long[]> pXAry(new long[ aTxt.getLength() ]);
+ pWin->SetFont( pNode->GetFont() );
+ pWin->GetTextArray( aTxt, pXAry.get(), 0, aTxt.getLength() );
+ for (sal_Int32 i = 0; i < aTxt.getLength() && nRes == -1; ++i)
+ {
+ if (pXAry[i] + nNodeX > aPos.X())
+ nRes = i;
+ }
+ pXAry.reset();
+ OSL_ENSURE( nRes >= 0 && nRes < aTxt.getLength(), "index out of range" );
+ OSL_ENSURE( pNode->GetAccessibleIndex() >= 0,
+ "invalid accessible index" );
+
+ nRes = pNode->GetAccessibleIndex() + nRes;
+ }
+ }
+ }
+ return nRes;
+}
+
+OUString SAL_CALL SmGraphicAccessible::getSelectedText()
+{
+ return OUString();
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getSelectionStart()
+{
+ return -1;
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getSelectionEnd()
+{
+ return -1;
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::setSelection(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nLen = GetAccessibleText_Impl().getLength();
+ if (!(0 <= nStartIndex && nStartIndex < nLen) ||
+ !(0 <= nEndIndex && nEndIndex < nLen))
+ throw IndexOutOfBoundsException();
+ return false;
+}
+
+OUString SAL_CALL SmGraphicAccessible::getText()
+{
+ SolarMutexGuard aGuard;
+ return GetAccessibleText_Impl();
+}
+
+OUString SAL_CALL SmGraphicAccessible::getTextRange(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex )
+{
+ //!! nEndIndex may be the string length per definition of the interface !!
+ //!! text should be copied exclusive that end index though. And arguments
+ //!! may be switched.
+
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ sal_Int32 nStart = std::min(nStartIndex, nEndIndex);
+ sal_Int32 nEnd = std::max(nStartIndex, nEndIndex);
+ if ((nStart > aTxt.getLength()) ||
+ (nEnd > aTxt.getLength()))
+ throw IndexOutOfBoundsException();
+ return aTxt.copy( nStart, nEnd - nStart );
+}
+
+css::accessibility::TextSegment SAL_CALL SmGraphicAccessible::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+{
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ //!! nIndex is allowed to be the string length
+ if (nIndex > aTxt.getLength())
+ throw IndexOutOfBoundsException();
+
+ css::accessibility::TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+ if ( (AccessibleTextType::CHARACTER == aTextType) && (nIndex < aTxt.getLength()) )
+ {
+ aResult.SegmentText = aTxt.copy(nIndex, 1);
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndex+1;
+ }
+ return aResult;
+}
+
+css::accessibility::TextSegment SAL_CALL SmGraphicAccessible::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+{
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ //!! nIndex is allowed to be the string length
+ if (nIndex > aTxt.getLength())
+ throw IndexOutOfBoundsException();
+
+ css::accessibility::TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ if ( (AccessibleTextType::CHARACTER == aTextType) && nIndex )
+ {
+ aResult.SegmentText = aTxt.copy(nIndex-1, 1);
+ aResult.SegmentStart = nIndex-1;
+ aResult.SegmentEnd = nIndex;
+ }
+ return aResult;
+}
+
+css::accessibility::TextSegment SAL_CALL SmGraphicAccessible::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+{
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ //!! nIndex is allowed to be the string length
+ if (nIndex > aTxt.getLength())
+ throw IndexOutOfBoundsException();
+
+ css::accessibility::TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ nIndex++; // text *behind*
+ if ( (AccessibleTextType::CHARACTER == aTextType) && (nIndex < aTxt.getLength()) )
+ {
+ aResult.SegmentText = aTxt.copy(nIndex, 1);
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndex+1;
+ }
+ return aResult;
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::copyText(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+ bool bReturn = false;
+
+ if (!pWin)
+ throw RuntimeException();
+
+ Reference< datatransfer::clipboard::XClipboard > xClipboard = pWin->GetClipboard();
+ if ( xClipboard.is() )
+ {
+ OUString sText( getTextRange(nStartIndex, nEndIndex) );
+
+ vcl::unohelper::TextDataObject* pDataObj = new vcl::unohelper::TextDataObject( sText );
+ SolarMutexReleaser aReleaser;
+ xClipboard->setContents( pDataObj, nullptr );
+
+ Reference< datatransfer::clipboard::XFlushableClipboard > xFlushableClipboard( xClipboard, uno::UNO_QUERY );
+ if( xFlushableClipboard.is() )
+ xFlushableClipboard->flushClipboard();
+
+ bReturn = true;
+ }
+
+
+ return bReturn;
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType )
+{
+ return false;
+}
+
+OUString SAL_CALL SmGraphicAccessible::getImplementationName()
+{
+ return "SmGraphicAccessible";
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::supportsService(
+ const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SAL_CALL SmGraphicAccessible::getSupportedServiceNames()
+{
+ return {
+ "css::accessibility::Accessible",
+ "css::accessibility::AccessibleComponent",
+ "css::accessibility::AccessibleContext",
+ "css::accessibility::AccessibleText"
+ };
+}
+
+
+SmEditSource::SmEditSource( SmEditAccessible &rAcc ) :
+ aViewFwd (rAcc),
+ aTextFwd (rAcc, *this),
+ aEditViewFwd(rAcc),
+ rEditAcc (rAcc)
+{
+}
+
+SmEditSource::SmEditSource( const SmEditSource &rSrc ) :
+ SvxEditSource(),
+ aViewFwd (rSrc.rEditAcc),
+ aTextFwd (rSrc.rEditAcc, *this),
+ aEditViewFwd(rSrc.rEditAcc),
+ rEditAcc (rSrc.rEditAcc)
+{
+}
+
+SmEditSource::~SmEditSource()
+{
+}
+
+std::unique_ptr<SvxEditSource> SmEditSource::Clone() const
+{
+ return std::unique_ptr<SvxEditSource>(new SmEditSource( *this ));
+}
+
+SvxTextForwarder* SmEditSource::GetTextForwarder()
+{
+ return &aTextFwd;
+}
+
+SvxViewForwarder* SmEditSource::GetViewForwarder()
+{
+ return &aViewFwd;
+}
+
+SvxEditViewForwarder* SmEditSource::GetEditViewForwarder( bool /*bCreate*/ )
+{
+ return &aEditViewFwd;
+}
+
+void SmEditSource::UpdateData()
+{
+ // would possibly only by needed if the XText interface is implemented
+ // and its text needs to be updated.
+}
+
+SfxBroadcaster & SmEditSource::GetBroadcaster() const
+{
+ return const_cast<SmEditSource*>(this)->aBroadCaster;
+}
+
+SmViewForwarder::SmViewForwarder( SmEditAccessible &rAcc ) :
+ rEditAcc(rAcc)
+{
+}
+
+SmViewForwarder::~SmViewForwarder()
+{
+}
+
+bool SmViewForwarder::IsValid() const
+{
+ return rEditAcc.GetEditView() != nullptr;
+}
+
+Point SmViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ EditView *pEditView = rEditAcc.GetEditView();
+ OutputDevice* pOutDev = pEditView ? pEditView->GetWindow() : nullptr;
+
+ if( pOutDev )
+ {
+ MapMode aMapMode(pOutDev->GetMapMode());
+ Point aPoint( OutputDevice::LogicToLogic( rPoint, rMapMode,
+ MapMode(aMapMode.GetMapUnit())) );
+ aMapMode.SetOrigin(Point());
+ return pOutDev->LogicToPixel( aPoint, aMapMode );
+ }
+
+ return Point();
+}
+
+Point SmViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ EditView *pEditView = rEditAcc.GetEditView();
+ OutputDevice* pOutDev = pEditView ? pEditView->GetWindow() : nullptr;
+
+ if( pOutDev )
+ {
+ MapMode aMapMode(pOutDev->GetMapMode());
+ aMapMode.SetOrigin(Point());
+ Point aPoint( pOutDev->PixelToLogic( rPoint, aMapMode ) );
+ return OutputDevice::LogicToLogic( aPoint,
+ MapMode(aMapMode.GetMapUnit()),
+ rMapMode );
+ }
+
+ return Point();
+}
+
+
+SmTextForwarder::SmTextForwarder( SmEditAccessible& rAcc, SmEditSource & rSource) :
+ rEditAcc ( rAcc ),
+ rEditSource (rSource)
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl( LINK(this, SmTextForwarder, NotifyHdl) );
+}
+
+SmTextForwarder::~SmTextForwarder()
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl( Link<EENotify&,void>() );
+}
+
+IMPL_LINK(SmTextForwarder, NotifyHdl, EENotify&, rNotify, void)
+{
+ ::std::unique_ptr< SfxHint > aHint = SvxEditSourceHelper::EENotification2Hint( &rNotify );
+ if (aHint)
+ rEditSource.GetBroadcaster().Broadcast(*aHint);
+}
+
+sal_Int32 SmTextForwarder::GetParagraphCount() const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetParagraphCount() : 0;
+}
+
+sal_Int32 SmTextForwarder::GetTextLen( sal_Int32 nParagraph ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetTextLen( nParagraph ) : 0;
+}
+
+OUString SmTextForwarder::GetText( const ESelection& rSel ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ OUString aRet;
+ if (pEditEngine)
+ aRet = pEditEngine->GetText( rSel );
+ return convertLineEnd(aRet, GetSystemLineEnd());
+}
+
+SfxItemSet SmTextForwarder::GetAttribs( const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ assert(pEditEngine && "EditEngine missing");
+ 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:
+ SAL_WARN("starmath", "unknown flags for SmTextForwarder::GetAttribs");
+ }
+
+ return pEditEngine->GetAttribs( rSel.nStartPara, rSel.nStartPos, rSel.nEndPos, nFlags );
+ }
+ else
+ {
+ return pEditEngine->GetAttribs( rSel, nOnlyHardAttrib );
+ }
+}
+
+SfxItemSet SmTextForwarder::GetParaAttribs( sal_Int32 nPara ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ assert(pEditEngine && "EditEngine missing");
+
+ SfxItemSet aSet( pEditEngine->GetParaAttribs( nPara ) );
+
+ sal_uInt16 nWhich = EE_PARA_START;
+ while( nWhich <= EE_PARA_END )
+ {
+ if( aSet.GetItemState( nWhich ) != SfxItemState::SET )
+ {
+ if( pEditEngine->HasParaAttrib( nPara, nWhich ) )
+ aSet.Put( pEditEngine->GetParaAttrib( nPara, nWhich ) );
+ }
+ nWhich++;
+ }
+
+ return aSet;
+}
+
+void SmTextForwarder::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetParaAttribs( nPara, rSet );
+}
+
+SfxItemPool* SmTextForwarder::GetPool() const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetEmptyItemSet().GetPool() : nullptr;
+}
+
+void SmTextForwarder::RemoveAttribs( const ESelection& rSelection )
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->RemoveAttribs( rSelection, false/*bRemoveParaAttribs*/, 0 );
+}
+
+void SmTextForwarder::GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& rList ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->GetPortions( nPara, rList );
+}
+
+void SmTextForwarder::QuickInsertText( const OUString& rText, const ESelection& rSel )
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->QuickInsertText( rText, rSel );
+}
+
+void SmTextForwarder::QuickInsertLineBreak( const ESelection& rSel )
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->QuickInsertLineBreak( rSel );
+}
+
+void SmTextForwarder::QuickInsertField( const SvxFieldItem& rFld, const ESelection& rSel )
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->QuickInsertField( rFld, rSel );
+}
+
+void SmTextForwarder::QuickSetAttribs( const SfxItemSet& rSet, const ESelection& rSel )
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->QuickSetAttribs( rSet, rSel );
+}
+
+bool SmTextForwarder::IsValid() const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ // cannot reliably query EditEngine state
+ // while in the middle of an update
+ return pEditEngine && pEditEngine->GetUpdateMode();
+}
+
+OUString SmTextForwarder::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor )
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->CalcFieldValue(rField, nPara, nPos, rpTxtColor, rpFldColor) : OUString();
+}
+
+void SmTextForwarder::FieldClicked(const SvxFieldItem&)
+{
+}
+
+static 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(const auto& rAttrib : aAttribs)
+ {
+ OSL_ENSURE( rAttrib.pAttr, "GetCharAttribs gives corrupt data" );
+
+ const bool bEmptyPortion = (rAttrib.nStart == rAttrib.nEnd);
+ if( (!bEmptyPortion && (rAttrib.nStart >= nEndPos)) || (bEmptyPortion && (rAttrib.nStart > nEndPos)) )
+ break; // break if we are already behind our selection
+
+ if( (!bEmptyPortion && (rAttrib.nEnd <= nPos)) || (bEmptyPortion && (rAttrib.nEnd < nPos)) )
+ continue; // or if the attribute ends before our selection
+
+ if( rAttrib.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 != *(rAttrib.pAttr) )
+ return SfxItemState::DONTCARE;
+ }
+ else
+ {
+ pParaItem = rAttrib.pAttr;
+ }
+
+ if( bEmpty )
+ bEmpty = false;
+
+ if( !bGaps && rAttrib.nStart > nLastEnd )
+ bGaps = true;
+
+ nLastEnd = rAttrib.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 SmTextForwarder::GetItemState( const ESelection& rSel, sal_uInt16 nWhich ) const
+{
+ SfxItemState nState = SfxItemState::DISABLED;
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ nState = GetSvxEditEngineItemState( *pEditEngine, rSel, nWhich );
+ return nState;
+}
+
+SfxItemState SmTextForwarder::GetItemState( sal_Int32 nPara, sal_uInt16 nWhich ) const
+{
+ SfxItemState nState = SfxItemState::DISABLED;
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ const SfxItemSet& rSet = pEditEngine->GetParaAttribs( nPara );
+ nState = rSet.GetItemState( nWhich );
+ }
+ return nState;
+}
+
+LanguageType SmTextForwarder::GetLanguage( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetLanguage(nPara, nIndex) : LANGUAGE_NONE;
+}
+
+sal_Int32 SmTextForwarder::GetFieldCount( sal_Int32 nPara ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetFieldCount(nPara) : 0;
+}
+
+EFieldInfo SmTextForwarder::GetFieldInfo( sal_Int32 nPara, sal_uInt16 nField ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetFieldInfo( nPara, nField ) : EFieldInfo();
+}
+
+EBulletInfo SmTextForwarder::GetBulletInfo( sal_Int32 /*nPara*/ ) const
+{
+ return EBulletInfo();
+}
+
+tools::Rectangle SmTextForwarder::GetCharBounds( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ tools::Rectangle aRect(0,0,0,0);
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+
+ if (pEditEngine)
+ {
+ // Handle virtual position one-past-the end of the string
+ if( nIndex >= pEditEngine->GetTextLen(nPara) )
+ {
+ if( nIndex )
+ aRect = pEditEngine->GetCharacterBounds( EPosition(nPara, nIndex-1) );
+
+ aRect.Move( aRect.Right() - aRect.Left(), 0 );
+ aRect.SetSize( Size(1, pEditEngine->GetTextHeight()) );
+ }
+ else
+ {
+ aRect = pEditEngine->GetCharacterBounds( EPosition(nPara, nIndex) );
+ }
+ }
+ return aRect;
+}
+
+tools::Rectangle SmTextForwarder::GetParaBounds( sal_Int32 nPara ) const
+{
+ tools::Rectangle aRect(0,0,0,0);
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+
+ if (pEditEngine)
+ {
+ const Point aPnt = pEditEngine->GetDocPosTopLeft( nPara );
+ const sal_uLong nWidth = pEditEngine->CalcTextWidth();
+ const sal_uLong nHeight = pEditEngine->GetTextHeight( nPara );
+ aRect = tools::Rectangle( aPnt.X(), aPnt.Y(), aPnt.X() + nWidth, aPnt.Y() + nHeight );
+ }
+
+ return aRect;
+}
+
+MapMode SmTextForwarder::GetMapMode() const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetRefMapMode() : MapMode( MapUnit::Map100thMM );
+}
+
+OutputDevice* SmTextForwarder::GetRefDevice() const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetRefDevice() : nullptr;
+}
+
+bool SmTextForwarder::GetIndexAtPoint( const Point& rPos, sal_Int32& nPara, sal_Int32& nIndex ) const
+{
+ bool bRes = false;
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ EPosition aDocPos = pEditEngine->FindDocPosition( rPos );
+ nPara = aDocPos.nPara;
+ nIndex = aDocPos.nIndex;
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool SmTextForwarder::GetWordIndices( sal_Int32 nPara, sal_Int32 nIndex, sal_Int32& nStart, sal_Int32& nEnd ) const
+{
+ bool bRes = false;
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ ESelection aRes = pEditEngine->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;
+
+ bRes = true;
+ }
+ }
+
+ return bRes;
+}
+
+bool SmTextForwarder::GetAttributeRun( sal_Int32& nStartIndex, sal_Int32& nEndIndex, sal_Int32 nPara, sal_Int32 nIndex, bool bInCell ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (!pEditEngine)
+ return false;
+ SvxEditSourceHelper::GetAttributeRun( nStartIndex, nEndIndex, *pEditEngine, nPara, nIndex, bInCell );
+ return true;
+}
+
+sal_Int32 SmTextForwarder::GetLineCount( sal_Int32 nPara ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetLineCount(nPara) : 0;
+}
+
+sal_Int32 SmTextForwarder::GetLineLen( sal_Int32 nPara, sal_Int32 nLine ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetLineLen(nPara, nLine) : 0;
+}
+
+void SmTextForwarder::GetLineBoundaries( /*out*/sal_Int32 &rStart, /*out*/sal_Int32 &rEnd, sal_Int32 nPara, sal_Int32 nLine ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->GetLineBoundaries(rStart, rEnd, nPara, nLine);
+ else
+ rStart = rEnd = 0;
+}
+
+sal_Int32 SmTextForwarder::GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetLineNumberAtIndex(nPara, nIndex) : 0;
+}
+
+bool SmTextForwarder::QuickFormatDoc( bool /*bFull*/ )
+{
+ bool bRes = false;
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ pEditEngine->QuickFormatDoc();
+ bRes = true;
+ }
+ return bRes;
+}
+
+sal_Int16 SmTextForwarder::GetDepth( sal_Int32 /*nPara*/ ) const
+{
+ // math has no outliner...
+ return -1;
+}
+
+bool SmTextForwarder::SetDepth( sal_Int32 /*nPara*/, sal_Int16 nNewDepth )
+{
+ // math has no outliner...
+ return -1 == nNewDepth; // is it the value from 'GetDepth' ?
+}
+
+bool SmTextForwarder::Delete( const ESelection& rSelection )
+{
+ bool bRes = false;
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ pEditEngine->QuickDelete( rSelection );
+ pEditEngine->QuickFormatDoc();
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool SmTextForwarder::InsertText( const OUString& rStr, const ESelection& rSelection )
+{
+ bool bRes = false;
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ pEditEngine->QuickInsertText( rStr, rSelection );
+ pEditEngine->QuickFormatDoc();
+ bRes = true;
+ }
+ return bRes;
+}
+
+const SfxItemSet* SmTextForwarder::GetEmptyItemSetPtr()
+{
+ const SfxItemSet *pItemSet = nullptr;
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ pItemSet = &pEditEngine->GetEmptyItemSet();
+ }
+ return pItemSet;
+}
+
+void SmTextForwarder::AppendParagraph()
+{
+ // append an empty paragraph
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ sal_Int32 nParaCount = pEditEngine->GetParagraphCount();
+ pEditEngine->InsertParagraph( nParaCount, OUString() );
+ }
+}
+
+sal_Int32 SmTextForwarder::AppendTextPortion( sal_Int32 nPara, const OUString &rText, const SfxItemSet &rSet )
+{
+ sal_uInt16 nRes = 0;
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine && nPara < pEditEngine->GetParagraphCount())
+ {
+ // append text
+ ESelection aSel( nPara, pEditEngine->GetTextLen( nPara ) );
+ pEditEngine->QuickInsertText( rText, aSel );
+
+ // set attributes for new appended text
+ nRes = aSel.nEndPos = pEditEngine->GetTextLen( nPara );
+ pEditEngine->QuickSetAttribs( rSet, aSel );
+ }
+ return nRes;
+}
+
+void SmTextForwarder::CopyText(const SvxTextForwarder& rSource)
+{
+
+ const SmTextForwarder* pSourceForwarder = dynamic_cast< const SmTextForwarder* >( &rSource );
+ if( !pSourceForwarder )
+ return;
+ EditEngine* pSourceEditEngine = pSourceForwarder->rEditAcc.GetEditEngine();
+ EditEngine *pEditEngine = rEditAcc.GetEditEngine();
+ if (pEditEngine && pSourceEditEngine )
+ {
+ std::unique_ptr<EditTextObject> pNewTextObject = pSourceEditEngine->CreateTextObject();
+ pEditEngine->SetText( *pNewTextObject );
+ }
+}
+
+
+SmEditViewForwarder::SmEditViewForwarder( SmEditAccessible& rAcc ) :
+ rEditAcc( rAcc )
+{
+}
+
+SmEditViewForwarder::~SmEditViewForwarder()
+{
+}
+
+bool SmEditViewForwarder::IsValid() const
+{
+ return rEditAcc.GetEditView() != nullptr;
+}
+
+
+Point SmEditViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ EditView *pEditView = rEditAcc.GetEditView();
+ OutputDevice* pOutDev = pEditView ? pEditView->GetWindow() : nullptr;
+
+ if( pOutDev )
+ {
+ MapMode aMapMode(pOutDev->GetMapMode());
+ Point aPoint( OutputDevice::LogicToLogic( rPoint, rMapMode,
+ MapMode(aMapMode.GetMapUnit())));
+ aMapMode.SetOrigin(Point());
+ return pOutDev->LogicToPixel( aPoint, aMapMode );
+ }
+
+ return Point();
+}
+
+Point SmEditViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ EditView *pEditView = rEditAcc.GetEditView();
+ OutputDevice* pOutDev = pEditView ? pEditView->GetWindow() : nullptr;
+
+ if( pOutDev )
+ {
+ MapMode aMapMode(pOutDev->GetMapMode());
+ aMapMode.SetOrigin(Point());
+ Point aPoint( pOutDev->PixelToLogic( rPoint, aMapMode ) );
+ return OutputDevice::LogicToLogic( aPoint,
+ MapMode(aMapMode.GetMapUnit()),
+ rMapMode );
+ }
+
+ return Point();
+}
+
+bool SmEditViewForwarder::GetSelection( ESelection& rSelection ) const
+{
+ bool bRes = false;
+ EditView *pEditView = rEditAcc.GetEditView();
+ if (pEditView)
+ {
+ rSelection = pEditView->GetSelection();
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool SmEditViewForwarder::SetSelection( const ESelection& rSelection )
+{
+ bool bRes = false;
+ EditView *pEditView = rEditAcc.GetEditView();
+ if (pEditView)
+ {
+ pEditView->SetSelection( rSelection );
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool SmEditViewForwarder::Copy()
+{
+ bool bRes = false;
+ EditView *pEditView = rEditAcc.GetEditView();
+ if (pEditView)
+ {
+ pEditView->Copy();
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool SmEditViewForwarder::Cut()
+{
+ bool bRes = false;
+ EditView *pEditView = rEditAcc.GetEditView();
+ if (pEditView)
+ {
+ pEditView->Cut();
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool SmEditViewForwarder::Paste()
+{
+ bool bRes = false;
+ EditView *pEditView = rEditAcc.GetEditView();
+ if (pEditView)
+ {
+ pEditView->Paste();
+ bRes = true;
+ }
+ return bRes;
+}
+
+
+SmEditAccessible::SmEditAccessible( SmEditWindow *pEditWin ) :
+ aAccName (SmResId(STR_CMDBOXWINDOW)),
+ pTextHelper (),
+ pWin (pEditWin)
+{
+ OSL_ENSURE( pWin, "SmEditAccessible: window missing" );
+}
+
+SmEditAccessible::~SmEditAccessible()
+{
+}
+
+::accessibility::AccessibleTextHelper *SmEditAccessible::GetTextHelper()
+{
+ return pTextHelper.get();
+}
+
+void SmEditAccessible::Init()
+{
+ OSL_ENSURE( pWin, "SmEditAccessible: window missing" );
+ if (pWin)
+ {
+ EditEngine *pEditEngine = pWin->GetEditEngine();
+ EditView *pEditView = pWin->GetEditView();
+ if (pEditEngine && pEditView)
+ {
+ assert(!pTextHelper);
+ pTextHelper.reset(new ::accessibility::AccessibleTextHelper( std::make_unique<SmEditSource>( *this ) ));
+ pTextHelper->SetEventSource( this );
+ }
+ }
+}
+
+void SmEditAccessible::ClearWin()
+{
+ // remove handler before current object gets destroyed
+ // (avoid handler being called for already dead object)
+ EditEngine *pEditEngine = GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl( Link<EENotify&,void>() );
+
+ pWin = nullptr; // implicitly results in AccessibleStateType::DEFUNC set
+
+ //! make TextHelper implicitly release C++ references to some core objects
+ pTextHelper->SetEditSource( ::std::unique_ptr<SvxEditSource>() );
+ //! make TextHelper release references
+ //! (e.g. the one set by the 'SetEventSource' call)
+ pTextHelper->Dispose();
+ pTextHelper.reset();
+}
+
+// XAccessible
+uno::Reference< XAccessibleContext > SAL_CALL SmEditAccessible::getAccessibleContext( )
+{
+ return this;
+}
+
+// XAccessibleComponent
+sal_Bool SAL_CALL SmEditAccessible::containsPoint( const awt::Point& aPoint )
+{
+ //! the arguments coordinates are relative to the current window !
+ //! Thus the top left-point is (0, 0)
+
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ Size aSz( pWin->GetSizePixel() );
+ return aPoint.X >= 0 && aPoint.Y >= 0 &&
+ aPoint.X < aSz.Width() && aPoint.Y < aSz.Height();
+}
+
+uno::Reference< XAccessible > SAL_CALL SmEditAccessible::getAccessibleAtPoint( const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+ if (!pTextHelper)
+ throw RuntimeException();
+ return pTextHelper->GetAt( aPoint );
+}
+
+awt::Rectangle SAL_CALL SmEditAccessible::getBounds( )
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+ OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(),
+ "mismatch of window parent and accessible parent" );
+ return lcl_GetBounds( pWin );
+}
+
+awt::Point SAL_CALL SmEditAccessible::getLocation( )
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+ OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(),
+ "mismatch of window parent and accessible parent" );
+ awt::Rectangle aRect( lcl_GetBounds( pWin ) );
+ return awt::Point( aRect.X, aRect.Y );
+}
+
+awt::Point SAL_CALL SmEditAccessible::getLocationOnScreen( )
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+ OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(),
+ "mismatch of window parent and accessible parent" );
+ return lcl_GetLocationOnScreen( pWin );
+}
+
+awt::Size SAL_CALL SmEditAccessible::getSize( )
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+ OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(),
+ "mismatch of window parent and accessible parent" );
+
+ Size aSz( pWin->GetSizePixel() );
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ awt::Rectangle aRect( lcl_GetBounds( pWin ) );
+ Size aSz2( aRect.Width, aRect.Height );
+ assert(aSz == aSz2 && "mismatch in width");
+#endif
+ return awt::Size( aSz.Width(), aSz.Height() );
+}
+
+void SAL_CALL SmEditAccessible::grabFocus( )
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ pWin->GrabFocus();
+}
+
+sal_Int32 SAL_CALL SmEditAccessible::getForeground()
+{
+ SolarMutexGuard aGuard;
+
+ if (!pWin)
+ throw RuntimeException();
+ return static_cast<sal_Int32>(pWin->GetTextColor());
+}
+
+sal_Int32 SAL_CALL SmEditAccessible::getBackground()
+{
+ SolarMutexGuard aGuard;
+
+ if (!pWin)
+ throw RuntimeException();
+ Wallpaper aWall( pWin->GetDisplayBackground() );
+ Color nCol;
+ if (aWall.IsBitmap() || aWall.IsGradient())
+ nCol = pWin->GetSettings().GetStyleSettings().GetWindowColor();
+ else
+ nCol = aWall.GetColor();
+ return static_cast<sal_Int32>(nCol);
+}
+
+// XAccessibleContext
+sal_Int32 SAL_CALL SmEditAccessible::getAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+ if (!pTextHelper)
+ return 0;
+ return pTextHelper->GetChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL SmEditAccessible::getAccessibleChild( sal_Int32 i )
+{
+ SolarMutexGuard aGuard;
+ if (!pTextHelper)
+ throw RuntimeException();
+ return pTextHelper->GetChild( i );
+}
+
+uno::Reference< XAccessible > SAL_CALL SmEditAccessible::getAccessibleParent( )
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ vcl::Window *pAccParent = pWin->GetAccessibleParentWindow();
+ OSL_ENSURE( pAccParent, "accessible parent missing" );
+ return pAccParent ? pAccParent->GetAccessible() : Reference< XAccessible >();
+}
+
+sal_Int32 SAL_CALL SmEditAccessible::getAccessibleIndexInParent( )
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nIdx = -1;
+ vcl::Window *pAccParent = pWin ? pWin->GetAccessibleParentWindow() : nullptr;
+ if (pAccParent)
+ {
+ sal_uInt16 nCnt = pAccParent->GetAccessibleChildWindowCount();
+ for (sal_uInt16 i = 0; i < nCnt && nIdx == -1; ++i)
+ if (pAccParent->GetAccessibleChildWindow( i ) == pWin)
+ nIdx = i;
+ }
+ return nIdx;
+}
+
+sal_Int16 SAL_CALL SmEditAccessible::getAccessibleRole( )
+{
+ return AccessibleRole::TEXT_FRAME;
+}
+
+OUString SAL_CALL SmEditAccessible::getAccessibleDescription( )
+{
+ return OUString(); // empty as agreed with product-management
+}
+
+OUString SAL_CALL SmEditAccessible::getAccessibleName( )
+{
+ SolarMutexGuard aGuard;
+ // same name as displayed by the window when not docked
+ return aAccName;
+}
+
+uno::Reference< XAccessibleRelationSet > SAL_CALL SmEditAccessible::getAccessibleRelationSet( )
+{
+ Reference< XAccessibleRelationSet > xRelSet = new utl::AccessibleRelationSetHelper();
+ return xRelSet; // empty relation set
+}
+
+uno::Reference< XAccessibleStateSet > SAL_CALL SmEditAccessible::getAccessibleStateSet( )
+{
+ SolarMutexGuard aGuard;
+ ::utl::AccessibleStateSetHelper *pStateSet =
+ new ::utl::AccessibleStateSetHelper;
+
+ Reference<XAccessibleStateSet> xStateSet( pStateSet );
+
+ if (!pWin || !pTextHelper)
+ pStateSet->AddState( AccessibleStateType::DEFUNC );
+ else
+ {
+ pStateSet->AddState( AccessibleStateType::MULTI_LINE );
+ pStateSet->AddState( AccessibleStateType::ENABLED );
+ pStateSet->AddState( AccessibleStateType::EDITABLE );
+ pStateSet->AddState( AccessibleStateType::FOCUSABLE );
+ if (pWin->HasFocus())
+ pStateSet->AddState( AccessibleStateType::FOCUSED );
+ if (pWin->IsActive())
+ pStateSet->AddState( AccessibleStateType::ACTIVE );
+ if (pWin->IsVisible())
+ pStateSet->AddState( AccessibleStateType::SHOWING );
+ if (pWin->IsReallyVisible())
+ pStateSet->AddState( AccessibleStateType::VISIBLE );
+ if (COL_TRANSPARENT != pWin->GetBackground().GetColor())
+ pStateSet->AddState( AccessibleStateType::OPAQUE );
+ }
+
+ return xStateSet;
+}
+
+Locale SAL_CALL SmEditAccessible::getLocale( )
+{
+ SolarMutexGuard aGuard;
+ // should be the document language...
+ // We use the language of the localized symbol names here.
+ return Application::GetSettings().GetUILanguageTag().getLocale();
+}
+
+
+// XAccessibleEventBroadcaster
+void SAL_CALL SmEditAccessible::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+{
+ if (pTextHelper) // not disposing (about to destroy view shell)
+ pTextHelper->AddEventListener( xListener );
+}
+
+void SAL_CALL SmEditAccessible::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+{
+ if (pTextHelper) // not disposing (about to destroy view shell)
+ pTextHelper->RemoveEventListener( xListener );
+}
+
+OUString SAL_CALL SmEditAccessible::getImplementationName()
+{
+ return "SmEditAccessible";
+}
+
+sal_Bool SAL_CALL SmEditAccessible::supportsService(
+ const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SAL_CALL SmEditAccessible::getSupportedServiceNames()
+{
+ return {
+ "css::accessibility::Accessible",
+ "css::accessibility::AccessibleComponent",
+ "css::accessibility::AccessibleContext"
+ };
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/accessibility.hxx b/starmath/source/accessibility.hxx
new file mode 100644
index 000000000..8cccdc916
--- /dev/null
+++ b/starmath/source/accessibility.hxx
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_ACCESSIBILITY_HXX
+#define INCLUDED_STARMATH_SOURCE_ACCESSIBILITY_HXX
+
+#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <cppuhelper/implbase.hxx>
+#include <svl/SfxBroadcaster.hxx>
+
+#include <editeng/editeng.hxx>
+#include <editeng/unoedsrc.hxx>
+#include <edit.hxx>
+#include <view.hxx>
+#include <memory>
+
+class SmDocShell;
+
+namespace accessibility { class AccessibleTextHelper; }
+
+// classes and helper-classes used for accessibility in the graphic-window
+
+
+typedef
+cppu::WeakImplHelper
+ <
+ css::lang::XServiceInfo,
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleText,
+ css::accessibility::XAccessibleEventBroadcaster
+ >
+SmGraphicAccessibleBaseClass;
+
+class SmGraphicAccessible final :
+ public SmGraphicAccessibleBaseClass
+{
+ OUString aAccName;
+ /// client id in the AccessibleEventNotifier queue
+ sal_uInt32 nClientId;
+
+ VclPtr<SmGraphicWindow> pWin;
+
+ SmGraphicAccessible( const SmGraphicAccessible & ) = delete;
+ SmGraphicAccessible & operator = ( const SmGraphicAccessible & ) = delete;
+
+ SmDocShell * GetDoc_Impl();
+ OUString GetAccessibleText_Impl();
+
+public:
+ explicit SmGraphicAccessible( SmGraphicWindow *pGraphicWin );
+ virtual ~SmGraphicAccessible() override;
+
+ void ClearWin(); // to be called when view is destroyed
+ void LaunchEvent(
+ const sal_Int16 nAccessibleEventId,
+ const css::uno::Any &rOldVal,
+ const css::uno::Any &rNewVal);
+
+ // XAccessible
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) 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;
+
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+ virtual css::lang::Locale SAL_CALL getLocale( ) override;
+
+ // 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;
+
+ // XAccessibleText
+ virtual sal_Int32 SAL_CALL getCaretPosition( ) override;
+ virtual sal_Bool SAL_CALL setCaretPosition ( sal_Int32 nIndex ) override;
+ virtual sal_Unicode SAL_CALL getCharacter( sal_Int32 nIndex ) override;
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+ virtual css::awt::Rectangle SAL_CALL getCharacterBounds( sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getCharacterCount( ) override;
+ virtual sal_Int32 SAL_CALL getIndexAtPoint( const css::awt::Point& aPoint ) override;
+ virtual OUString SAL_CALL getSelectedText( ) override;
+ virtual sal_Int32 SAL_CALL getSelectionStart( ) override;
+ virtual sal_Int32 SAL_CALL getSelectionEnd( ) override;
+ virtual sal_Bool SAL_CALL setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual OUString SAL_CALL getText( ) override;
+ virtual OUString SAL_CALL getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual sal_Bool SAL_CALL copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual sal_Bool SAL_CALL scrollSubstringTo( sal_Int32 nStartIndex, sal_Int32 nEndIndex, css::accessibility::AccessibleScrollType aScrollType) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+
+// classes and helper-classes used for accessibility in the command-window
+
+
+class SmEditAccessible;
+class SmEditSource;
+class EditView;
+class SvxFieldItem;
+
+
+class SmViewForwarder :
+ public SvxViewForwarder
+{
+ SmEditAccessible & rEditAcc;
+
+ SmViewForwarder( const SmViewForwarder & ) = delete;
+ SmViewForwarder & operator = ( const SmViewForwarder & ) = delete;
+
+public:
+ explicit SmViewForwarder( SmEditAccessible &rAcc );
+ virtual ~SmViewForwarder() override;
+
+ virtual bool IsValid() const override;
+ virtual Point LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
+ virtual Point PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;
+};
+
+
+class SmTextForwarder : /* analog to SvxEditEngineForwarder */
+ public SvxTextForwarder
+{
+ SmEditAccessible & rEditAcc;
+ SmEditSource & rEditSource;
+
+ DECL_LINK( NotifyHdl, EENotify&, void );
+
+ SmTextForwarder( const SmTextForwarder & ) = delete;
+ SmTextForwarder & operator = ( const SmTextForwarder & ) = delete;
+
+public:
+ SmTextForwarder( SmEditAccessible& rAcc, SmEditSource & rSource );
+ virtual ~SmTextForwarder() override;
+
+ virtual sal_Int32 GetParagraphCount() const override;
+ virtual sal_Int32 GetTextLen( sal_Int32 nParagraph ) const override;
+ virtual OUString GetText( const ESelection& rSel ) const override;
+ virtual SfxItemSet GetAttribs( const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib = EditEngineAttribs::All ) const override;
+ virtual SfxItemSet GetParaAttribs( sal_Int32 nPara ) const override;
+ virtual void SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet ) override;
+ virtual void RemoveAttribs( const ESelection& rSelection ) override;
+ virtual void GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& rList ) const override;
+
+ virtual SfxItemState GetItemState( const ESelection& rSel, sal_uInt16 nWhich ) const override;
+ virtual SfxItemState GetItemState( sal_Int32 nPara, sal_uInt16 nWhich ) const override;
+
+ virtual void QuickInsertText( const OUString& rText, const ESelection& rSel ) override;
+ virtual void QuickInsertField( const SvxFieldItem& rFld, const ESelection& rSel ) override;
+ virtual void QuickSetAttribs( const SfxItemSet& rSet, const ESelection& rSel ) override;
+ virtual void QuickInsertLineBreak( const ESelection& rSel ) override;
+
+ virtual SfxItemPool* GetPool() const override;
+
+ virtual OUString CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor ) override;
+ virtual void FieldClicked(const SvxFieldItem&) override;
+ virtual bool IsValid() const override;
+
+ virtual LanguageType GetLanguage( sal_Int32, sal_Int32 ) const override;
+ virtual sal_Int32 GetFieldCount( sal_Int32 nPara ) const override;
+ virtual EFieldInfo GetFieldInfo( sal_Int32 nPara, sal_uInt16 nField ) const override;
+ virtual EBulletInfo GetBulletInfo( sal_Int32 nPara ) const override;
+ virtual tools::Rectangle GetCharBounds( sal_Int32 nPara, sal_Int32 nIndex ) const override;
+ virtual tools::Rectangle GetParaBounds( sal_Int32 nPara ) const override;
+ virtual MapMode GetMapMode() const override;
+ virtual OutputDevice* GetRefDevice() const override;
+ virtual bool GetIndexAtPoint( const Point&, sal_Int32& nPara, sal_Int32& nIndex ) const override;
+ virtual bool GetWordIndices( sal_Int32 nPara, sal_Int32 nIndex, sal_Int32& nStart, sal_Int32& nEnd ) const override;
+ virtual bool GetAttributeRun( sal_Int32& nStartIndex, sal_Int32& nEndIndex, sal_Int32 nPara, sal_Int32 nIndex, bool bInCell = false ) const override;
+ virtual sal_Int32 GetLineCount( sal_Int32 nPara ) const override;
+ virtual sal_Int32 GetLineLen( sal_Int32 nPara, sal_Int32 nLine ) const override;
+ virtual void GetLineBoundaries( /*out*/sal_Int32 &rStart, /*out*/sal_Int32 &rEnd, sal_Int32 nParagraph, sal_Int32 nLine ) const override;
+ virtual sal_Int32 GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nLine ) const override;
+ virtual bool Delete( const ESelection& ) override;
+ virtual bool InsertText( const OUString&, const ESelection& ) override;
+ virtual bool QuickFormatDoc( bool bFull = false ) override;
+
+ virtual sal_Int16 GetDepth( sal_Int32 nPara ) const override;
+ virtual bool SetDepth( sal_Int32 nPara, sal_Int16 nNewDepth ) override;
+
+ virtual const SfxItemSet* GetEmptyItemSetPtr() override;
+ // implementation functions for XParagraphAppend and XTextPortionAppend
+ virtual void AppendParagraph() override;
+ virtual sal_Int32 AppendTextPortion( sal_Int32 nPara, const OUString &rText, const SfxItemSet &rSet ) override;
+
+ virtual void CopyText(const SvxTextForwarder& rSource) override;
+};
+
+
+class SmEditViewForwarder : /* analog to SvxEditEngineViewForwarder */
+ public SvxEditViewForwarder
+{
+ SmEditAccessible& rEditAcc;
+
+ SmEditViewForwarder( const SmEditViewForwarder & ) = delete;
+ SmEditViewForwarder & operator = ( const SmEditViewForwarder & ) = delete;
+
+public:
+ explicit SmEditViewForwarder( SmEditAccessible& rAcc );
+ virtual ~SmEditViewForwarder() override;
+
+ virtual bool IsValid() const override;
+
+ virtual Point LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
+ virtual Point PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;
+
+ virtual bool GetSelection( ESelection& rSelection ) const override;
+ virtual bool SetSelection( const ESelection& rSelection ) override;
+ virtual bool Copy() override;
+ virtual bool Cut() override;
+ virtual bool Paste() override;
+};
+
+
+class SmEditSource :
+ public SvxEditSource
+{
+ SfxBroadcaster aBroadCaster;
+ SmViewForwarder aViewFwd;
+ SmTextForwarder aTextFwd;
+ SmEditViewForwarder aEditViewFwd;
+
+ SmEditAccessible& rEditAcc;
+
+ SmEditSource( const SmEditSource &rSrc );
+ SmEditSource & operator = ( const SmEditSource & ) = delete;
+
+public:
+ SmEditSource( SmEditAccessible &rAcc );
+ virtual ~SmEditSource() override;
+
+ virtual std::unique_ptr<SvxEditSource> Clone() const override;
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual SvxViewForwarder* GetViewForwarder() override;
+ virtual SvxEditViewForwarder* GetEditViewForwarder( bool bCreate = false ) override;
+ virtual void UpdateData() override;
+ virtual SfxBroadcaster& GetBroadcaster() const override;
+};
+
+
+typedef
+cppu::WeakImplHelper
+ <
+ css::lang::XServiceInfo,
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleEventBroadcaster
+ >
+SmEditAccessibleBaseClass;
+
+class SmEditAccessible :
+ public SmEditAccessibleBaseClass
+{
+ OUString aAccName;
+ std::unique_ptr<::accessibility::AccessibleTextHelper> pTextHelper;
+ VclPtr<SmEditWindow> pWin;
+
+ SmEditAccessible( const SmEditAccessible & ) = delete;
+ SmEditAccessible & operator = ( const SmEditAccessible & ) = delete;
+
+public:
+ explicit SmEditAccessible( SmEditWindow *pEditWin );
+ virtual ~SmEditAccessible() override;
+
+ ::accessibility::AccessibleTextHelper * GetTextHelper();
+
+ void Init();
+ void ClearWin(); // to be called when view is destroyed
+
+ //! access EditEngine and EditView via the functions in the respective window
+ //! pointers may be 0 (e.g. during reload)
+ EditEngine * GetEditEngine() { return pWin ? pWin->GetEditEngine() : nullptr; }
+ EditView * GetEditView() { return pWin ? pWin->GetEditView() : nullptr; }
+
+ // XAccessible
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) 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;
+
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+ virtual css::lang::Locale SAL_CALL getLocale( ) override;
+
+ // XAccessibleEventBroadcaster
+ virtual void SAL_CALL addAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+ virtual void SAL_CALL removeAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/action.cxx b/starmath/source/action.cxx
new file mode 100644
index 000000000..37e643c85
--- /dev/null
+++ b/starmath/source/action.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 <action.hxx>
+#include <document.hxx>
+#include <strings.hxx>
+
+SmFormatAction::SmFormatAction(SmDocShell *pDocSh,
+ const SmFormat& rOldFormat,
+ const SmFormat& rNewFormat) :
+ pDoc( pDocSh ),
+ aOldFormat( rOldFormat ),
+ aNewFormat( rNewFormat )
+{
+}
+
+void SmFormatAction::Undo()
+{
+ pDoc->SetFormat(aOldFormat);
+}
+
+void SmFormatAction::Redo()
+{
+ pDoc->SetFormat(aNewFormat);
+}
+
+void SmFormatAction::Repeat(SfxRepeatTarget& rDocSh)
+{
+ dynamic_cast< SmDocShell & >(rDocSh).SetFormat(aNewFormat);
+}
+
+OUString SmFormatAction::GetComment() const
+{
+ return RID_UNDOFORMATNAME;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/caret.cxx b/starmath/source/caret.cxx
new file mode 100644
index 000000000..4e2effaa2
--- /dev/null
+++ b/starmath/source/caret.cxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#include <caret.hxx>
+
+SmCaretPosGraph::SmCaretPosGraph() = default;
+
+SmCaretPosGraph::~SmCaretPosGraph() = default;
+
+SmCaretPosGraphEntry* SmCaretPosGraph::Add(SmCaretPos pos,
+ SmCaretPosGraphEntry* left)
+{
+ assert(pos.nIndex >= 0);
+ auto entry = std::make_unique<SmCaretPosGraphEntry>(pos, left, nullptr);
+ SmCaretPosGraphEntry* e = entry.get();
+ //Set Left and Right to point to the entry itself if they are NULL
+ entry->Left = entry->Left ? entry->Left : e;
+ entry->Right = entry->Right ? entry->Right : e;
+ mvEntries.push_back(std::move(entry));
+ return e;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/cfgitem.cxx b/starmath/source/cfgitem.cxx
new file mode 100644
index 000000000..f3c0ec3e9
--- /dev/null
+++ b/starmath/source/cfgitem.cxx
@@ -0,0 +1,1269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cassert>
+#include <memory>
+#include <vector>
+
+#include <svl/itemset.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/eitem.hxx>
+#include <svl/languageoptions.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <officecfg/Office/Math.hxx>
+#include "cfgitem.hxx"
+
+#include <starmath.hrc>
+#include <smmod.hxx>
+#include <symbol.hxx>
+#include <format.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+
+#define SYMBOL_LIST "SymbolList"
+#define FONT_FORMAT_LIST "FontFormatList"
+
+static Sequence< OUString > lcl_GetFontPropertyNames()
+{
+ return Sequence< OUString > {
+ "Name",
+ "CharSet",
+ "Family",
+ "Pitch",
+ "Weight",
+ "Italic"
+ };
+}
+
+static Sequence< OUString > lcl_GetSymbolPropertyNames()
+{
+ return Sequence< OUString > {
+ "Char",
+ "Set",
+ "Predefined",
+ "FontFormatId"
+ };
+}
+
+static Sequence< OUString > lcl_GetFormatPropertyNames()
+{
+ //! Beware of order according to *_BEGIN *_END defines in format.hxx !
+ //! see respective load/save routines here
+ return Sequence< OUString > {
+ "StandardFormat/Textmode",
+ "StandardFormat/GreekCharStyle",
+ "StandardFormat/ScaleNormalBracket",
+ "StandardFormat/HorizontalAlignment",
+ "StandardFormat/BaseSize",
+ "StandardFormat/TextSize",
+ "StandardFormat/IndexSize",
+ "StandardFormat/FunctionSize",
+ "StandardFormat/OperatorSize",
+ "StandardFormat/LimitsSize",
+ "StandardFormat/Distance/Horizontal",
+ "StandardFormat/Distance/Vertical",
+ "StandardFormat/Distance/Root",
+ "StandardFormat/Distance/SuperScript",
+ "StandardFormat/Distance/SubScript",
+ "StandardFormat/Distance/Numerator",
+ "StandardFormat/Distance/Denominator",
+ "StandardFormat/Distance/Fraction",
+ "StandardFormat/Distance/StrokeWidth",
+ "StandardFormat/Distance/UpperLimit",
+ "StandardFormat/Distance/LowerLimit",
+ "StandardFormat/Distance/BracketSize",
+ "StandardFormat/Distance/BracketSpace",
+ "StandardFormat/Distance/MatrixRow",
+ "StandardFormat/Distance/MatrixColumn",
+ "StandardFormat/Distance/OrnamentSize",
+ "StandardFormat/Distance/OrnamentSpace",
+ "StandardFormat/Distance/OperatorSize",
+ "StandardFormat/Distance/OperatorSpace",
+ "StandardFormat/Distance/LeftSpace",
+ "StandardFormat/Distance/RightSpace",
+ "StandardFormat/Distance/TopSpace",
+ "StandardFormat/Distance/BottomSpace",
+ "StandardFormat/Distance/NormalBracketSize",
+ "StandardFormat/VariableFont",
+ "StandardFormat/FunctionFont",
+ "StandardFormat/NumberFont",
+ "StandardFormat/TextFont",
+ "StandardFormat/SerifFont",
+ "StandardFormat/SansFont",
+ "StandardFormat/FixedFont"
+ };
+}
+
+struct SmCfgOther
+{
+ SmPrintSize ePrintSize;
+ sal_uInt16 nPrintZoomFactor;
+ bool bPrintTitle;
+ bool bPrintFormulaText;
+ bool bPrintFrame;
+ bool bIsSaveOnlyUsedSymbols;
+ bool bIsAutoCloseBrackets;
+ bool bIgnoreSpacesRight;
+ bool bToolboxVisible;
+ bool bAutoRedraw;
+ bool bFormulaCursor;
+
+ SmCfgOther();
+};
+
+
+SmCfgOther::SmCfgOther()
+ : ePrintSize(PRINT_SIZE_NORMAL)
+ , nPrintZoomFactor(100)
+ , bPrintTitle(true)
+ , bPrintFormulaText(true)
+ , bPrintFrame(true)
+ , bIsSaveOnlyUsedSymbols(true)
+ , bIsAutoCloseBrackets(true)
+ , bIgnoreSpacesRight(true)
+ , bToolboxVisible(true)
+ , bAutoRedraw(true)
+ , bFormulaCursor(true)
+{
+}
+
+
+SmFontFormat::SmFontFormat()
+ : aName(FONTNAME_MATH)
+ , nCharSet(RTL_TEXTENCODING_UNICODE)
+ , nFamily(FAMILY_DONTKNOW)
+ , nPitch(PITCH_DONTKNOW)
+ , nWeight(WEIGHT_DONTKNOW)
+ , nItalic(ITALIC_NONE)
+{
+}
+
+
+SmFontFormat::SmFontFormat( const vcl::Font &rFont )
+ : aName(rFont.GetFamilyName())
+ , nCharSet(static_cast<sal_Int16>(rFont.GetCharSet()))
+ , nFamily(static_cast<sal_Int16>(rFont.GetFamilyType()))
+ , nPitch(static_cast<sal_Int16>(rFont.GetPitch()))
+ , nWeight(static_cast<sal_Int16>(rFont.GetWeight()))
+ , nItalic(static_cast<sal_Int16>(rFont.GetItalic()))
+{
+}
+
+
+vcl::Font SmFontFormat::GetFont() const
+{
+ vcl::Font aRes;
+ aRes.SetFamilyName( aName );
+ aRes.SetCharSet( static_cast<rtl_TextEncoding>(nCharSet) );
+ aRes.SetFamily( static_cast<FontFamily>(nFamily) );
+ aRes.SetPitch( static_cast<FontPitch>(nPitch) );
+ aRes.SetWeight( static_cast<FontWeight>(nWeight) );
+ aRes.SetItalic( static_cast<FontItalic>(nItalic) );
+ return aRes;
+}
+
+
+bool SmFontFormat::operator == ( const SmFontFormat &rFntFmt ) const
+{
+ return aName == rFntFmt.aName &&
+ nCharSet == rFntFmt.nCharSet &&
+ nFamily == rFntFmt.nFamily &&
+ nPitch == rFntFmt.nPitch &&
+ nWeight == rFntFmt.nWeight &&
+ nItalic == rFntFmt.nItalic;
+}
+
+
+SmFntFmtListEntry::SmFntFmtListEntry( const OUString &rId, const SmFontFormat &rFntFmt ) :
+ aId (rId),
+ aFntFmt (rFntFmt)
+{
+}
+
+
+SmFontFormatList::SmFontFormatList()
+ : bModified(false)
+{
+}
+
+
+void SmFontFormatList::Clear()
+{
+ if (!aEntries.empty())
+ {
+ aEntries.clear();
+ SetModified( true );
+ }
+}
+
+
+void SmFontFormatList::AddFontFormat( const OUString &rFntFmtId,
+ const SmFontFormat &rFntFmt )
+{
+ const SmFontFormat *pFntFmt = GetFontFormat( rFntFmtId );
+ OSL_ENSURE( !pFntFmt, "FontFormatId already exists" );
+ if (!pFntFmt)
+ {
+ SmFntFmtListEntry aEntry( rFntFmtId, rFntFmt );
+ aEntries.push_back( aEntry );
+ SetModified( true );
+ }
+}
+
+
+void SmFontFormatList::RemoveFontFormat( const OUString &rFntFmtId )
+{
+
+ // search for entry
+ for (size_t i = 0; i < aEntries.size(); ++i)
+ {
+ if (aEntries[i].aId == rFntFmtId)
+ {
+ // remove entry if found
+ aEntries.erase( aEntries.begin() + i );
+ SetModified( true );
+ break;
+ }
+ }
+}
+
+
+const SmFontFormat * SmFontFormatList::GetFontFormat( const OUString &rFntFmtId ) const
+{
+ const SmFontFormat *pRes = nullptr;
+
+ for (const auto & rEntry : aEntries)
+ {
+ if (rEntry.aId == rFntFmtId)
+ {
+ pRes = &rEntry.aFntFmt;
+ break;
+ }
+ }
+
+ return pRes;
+}
+
+
+const SmFontFormat * SmFontFormatList::GetFontFormat( size_t nPos ) const
+{
+ const SmFontFormat *pRes = nullptr;
+ if (nPos < aEntries.size())
+ pRes = &aEntries[nPos].aFntFmt;
+ return pRes;
+}
+
+
+OUString SmFontFormatList::GetFontFormatId( const SmFontFormat &rFntFmt ) const
+{
+ OUString aRes;
+
+ for (const auto & rEntry : aEntries)
+ {
+ if (rEntry.aFntFmt == rFntFmt)
+ {
+ aRes = rEntry.aId;
+ break;
+ }
+ }
+
+ return aRes;
+}
+
+
+OUString SmFontFormatList::GetFontFormatId( const SmFontFormat &rFntFmt, bool bAdd )
+{
+ OUString aRes( GetFontFormatId( rFntFmt) );
+ if (aRes.isEmpty() && bAdd)
+ {
+ aRes = GetNewFontFormatId();
+ AddFontFormat( aRes, rFntFmt );
+ }
+ return aRes;
+}
+
+
+OUString SmFontFormatList::GetFontFormatId( size_t nPos ) const
+{
+ OUString aRes;
+ if (nPos < aEntries.size())
+ aRes = aEntries[nPos].aId;
+ return aRes;
+}
+
+
+OUString SmFontFormatList::GetNewFontFormatId() const
+{
+ // returns first unused FormatId
+
+ sal_Int32 nCnt = GetCount();
+ for (sal_Int32 i = 1; i <= nCnt + 1; ++i)
+ {
+ OUString aTmpId = "Id" + OUString::number(i);
+ if (!GetFontFormat(aTmpId))
+ return aTmpId;
+ }
+ OSL_ENSURE( false, "failed to create new FontFormatId" );
+
+ return OUString();
+}
+
+
+SmMathConfig::SmMathConfig() :
+ ConfigItem("Office.Math")
+ , pFormat()
+ , pOther()
+ , pFontFormatList()
+ , pSymbolMgr()
+ , bIsOtherModified(false)
+ , bIsFormatModified(false)
+{
+}
+
+
+SmMathConfig::~SmMathConfig()
+{
+ Save();
+}
+
+
+void SmMathConfig::SetOtherModified( bool bVal )
+{
+ bIsOtherModified = bVal;
+}
+
+
+void SmMathConfig::SetFormatModified( bool bVal )
+{
+ bIsFormatModified = bVal;
+}
+
+
+void SmMathConfig::ReadSymbol( SmSym &rSymbol,
+ const OUString &rSymbolName,
+ const OUString &rBaseNode ) const
+{
+ Sequence< OUString > aNames = lcl_GetSymbolPropertyNames();
+ sal_Int32 nProps = aNames.getLength();
+
+ OUString aDelim( "/" );
+ for (auto& rName : aNames)
+ rName = rBaseNode + aDelim + rSymbolName + aDelim + rName;
+
+ const Sequence< Any > aValues = const_cast<SmMathConfig*>(this)->GetProperties(aNames);
+
+ if (!(nProps && aValues.getLength() == nProps))
+ return;
+
+ const Any * pValue = aValues.getConstArray();
+ vcl::Font aFont;
+ sal_UCS4 cChar = '\0';
+ OUString aSet;
+ bool bPredefined = false;
+
+ OUString aTmpStr;
+ sal_Int32 nTmp32 = 0;
+ bool bTmp = false;
+
+ bool bOK = true;
+ if (pValue->hasValue() && (*pValue >>= nTmp32))
+ cChar = static_cast< sal_UCS4 >( nTmp32 );
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= aTmpStr))
+ aSet = aTmpStr;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= bTmp))
+ bPredefined = bTmp;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= aTmpStr))
+ {
+ const SmFontFormat *pFntFmt = GetFontFormatList().GetFontFormat( aTmpStr );
+ OSL_ENSURE( pFntFmt, "unknown FontFormat" );
+ if (pFntFmt)
+ aFont = pFntFmt->GetFont();
+ }
+ else
+ bOK = false;
+ ++pValue;
+
+ if (bOK)
+ {
+ OUString aUiName( rSymbolName );
+ OUString aUiSetName( aSet );
+ if (bPredefined)
+ {
+ OUString aTmp;
+ aTmp = SmLocalizedSymbolData::GetUiSymbolName( rSymbolName );
+ OSL_ENSURE( !aTmp.isEmpty(), "localized symbol-name not found" );
+ if (!aTmp.isEmpty())
+ aUiName = aTmp;
+ aTmp = SmLocalizedSymbolData::GetUiSymbolSetName( aSet );
+ OSL_ENSURE( !aTmp.isEmpty(), "localized symbolset-name not found" );
+ if (!aTmp.isEmpty())
+ aUiSetName = aTmp;
+ }
+
+ rSymbol = SmSym( aUiName, aFont, cChar, aUiSetName, bPredefined );
+ if (aUiName != rSymbolName)
+ rSymbol.SetExportName( rSymbolName );
+ }
+ else
+ {
+ SAL_WARN("starmath", "symbol read error");
+ }
+}
+
+
+SmSymbolManager & SmMathConfig::GetSymbolManager()
+{
+ if (!pSymbolMgr)
+ {
+ pSymbolMgr.reset(new SmSymbolManager);
+ pSymbolMgr->Load();
+ }
+ return *pSymbolMgr;
+}
+
+
+void SmMathConfig::ImplCommit()
+{
+ Save();
+}
+
+
+void SmMathConfig::Save()
+{
+ SaveOther();
+ SaveFormat();
+ SaveFontFormatList();
+}
+
+
+void SmMathConfig::GetSymbols( std::vector< SmSym > &rSymbols ) const
+{
+ Sequence< OUString > aNodes(const_cast<SmMathConfig*>(this)->GetNodeNames(SYMBOL_LIST));
+ const OUString *pNode = aNodes.getConstArray();
+ sal_Int32 nNodes = aNodes.getLength();
+
+ rSymbols.resize( nNodes );
+ for (auto& rSymbol : rSymbols)
+ {
+ ReadSymbol( rSymbol, *pNode++, SYMBOL_LIST );
+ }
+}
+
+
+void SmMathConfig::SetSymbols( const std::vector< SmSym > &rNewSymbols )
+{
+ auto nCount = sal::static_int_cast<sal_Int32>(rNewSymbols.size());
+
+ Sequence< OUString > aNames = lcl_GetSymbolPropertyNames();
+ const OUString *pNames = aNames.getConstArray();
+ sal_Int32 nSymbolProps = aNames.getLength();
+
+ Sequence< PropertyValue > aValues( nCount * nSymbolProps );
+ PropertyValue *pValues = aValues.getArray();
+
+ PropertyValue *pVal = pValues;
+ OUString aDelim( "/" );
+ for (const SmSym& rSymbol : rNewSymbols)
+ {
+ OUString aNodeNameDelim = SYMBOL_LIST +
+ aDelim +
+ rSymbol.GetExportName() +
+ aDelim;
+
+ const OUString *pName = pNames;
+
+ // Char
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= rSymbol.GetCharacter();
+ pVal++;
+ // Set
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ OUString aTmp( rSymbol.GetSymbolSetName() );
+ if (rSymbol.IsPredefined())
+ aTmp = SmLocalizedSymbolData::GetExportSymbolSetName( aTmp );
+ pVal->Value <<= aTmp;
+ pVal++;
+ // Predefined
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= rSymbol.IsPredefined();
+ pVal++;
+ // FontFormatId
+ SmFontFormat aFntFmt( rSymbol.GetFace() );
+ OUString aFntFmtId( GetFontFormatList().GetFontFormatId( aFntFmt, true ) );
+ OSL_ENSURE( !aFntFmtId.isEmpty(), "FontFormatId not found" );
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmtId;
+ pVal++;
+ }
+ OSL_ENSURE( pVal - pValues == sal::static_int_cast< ptrdiff_t >(nCount * nSymbolProps), "properties missing" );
+ ReplaceSetProperties( SYMBOL_LIST, aValues );
+
+ StripFontFormatList( rNewSymbols );
+ SaveFontFormatList();
+}
+
+
+SmFontFormatList & SmMathConfig::GetFontFormatList()
+{
+ if (!pFontFormatList)
+ {
+ LoadFontFormatList();
+ }
+ return *pFontFormatList;
+}
+
+
+void SmMathConfig::LoadFontFormatList()
+{
+ if (!pFontFormatList)
+ pFontFormatList.reset(new SmFontFormatList);
+ else
+ pFontFormatList->Clear();
+
+ const Sequence< OUString > aNodes( GetNodeNames( FONT_FORMAT_LIST ) );
+
+ for (const OUString& rNode : aNodes)
+ {
+ SmFontFormat aFntFmt;
+ ReadFontFormat( aFntFmt, rNode, FONT_FORMAT_LIST );
+ if (!pFontFormatList->GetFontFormat( rNode ))
+ pFontFormatList->AddFontFormat( rNode, aFntFmt );
+ }
+ pFontFormatList->SetModified( false );
+}
+
+
+void SmMathConfig::ReadFontFormat( SmFontFormat &rFontFormat,
+ const OUString &rSymbolName, const OUString &rBaseNode ) const
+{
+ Sequence< OUString > aNames = lcl_GetFontPropertyNames();
+ sal_Int32 nProps = aNames.getLength();
+
+ OUString aDelim( "/" );
+ for (auto& rName : aNames)
+ rName = rBaseNode + aDelim + rSymbolName + aDelim + rName;
+
+ const Sequence< Any > aValues = const_cast<SmMathConfig*>(this)->GetProperties(aNames);
+
+ if (!(nProps && aValues.getLength() == nProps))
+ return;
+
+ const Any * pValue = aValues.getConstArray();
+
+ OUString aTmpStr;
+ sal_Int16 nTmp16 = 0;
+
+ bool bOK = true;
+ if (pValue->hasValue() && (*pValue >>= aTmpStr))
+ rFontFormat.aName = aTmpStr;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= nTmp16))
+ rFontFormat.nCharSet = nTmp16; // 6.0 file-format GetSOLoadTextEncoding not needed
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= nTmp16))
+ rFontFormat.nFamily = nTmp16;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= nTmp16))
+ rFontFormat.nPitch = nTmp16;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= nTmp16))
+ rFontFormat.nWeight = nTmp16;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= nTmp16))
+ rFontFormat.nItalic = nTmp16;
+ else
+ bOK = false;
+ ++pValue;
+
+ OSL_ENSURE( bOK, "read FontFormat failed" );
+}
+
+
+void SmMathConfig::SaveFontFormatList()
+{
+ SmFontFormatList &rFntFmtList = GetFontFormatList();
+
+ if (!rFntFmtList.IsModified())
+ return;
+
+ Sequence< OUString > aNames = lcl_GetFontPropertyNames();
+ sal_Int32 nSymbolProps = aNames.getLength();
+
+ size_t nCount = rFntFmtList.GetCount();
+
+ Sequence< PropertyValue > aValues( nCount * nSymbolProps );
+ PropertyValue *pValues = aValues.getArray();
+
+ PropertyValue *pVal = pValues;
+ OUString aDelim( "/" );
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ OUString aFntFmtId(rFntFmtList.GetFontFormatId(i));
+ const SmFontFormat *pFntFmt = rFntFmtList.GetFontFormat(i);
+ assert(pFntFmt);
+ const SmFontFormat aFntFmt(*pFntFmt);
+
+ OUString aNodeNameDelim = FONT_FORMAT_LIST +
+ aDelim +
+ aFntFmtId +
+ aDelim;
+
+ const OUString *pName = aNames.getConstArray();
+
+ // Name
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.aName;
+ pVal++;
+ // CharSet
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.nCharSet; // 6.0 file-format GetSOStoreTextEncoding not needed
+ pVal++;
+ // Family
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.nFamily;
+ pVal++;
+ // Pitch
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.nPitch;
+ pVal++;
+ // Weight
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.nWeight;
+ pVal++;
+ // Italic
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.nItalic;
+ pVal++;
+ }
+ OSL_ENSURE( sal::static_int_cast<size_t>(pVal - pValues) == nCount * nSymbolProps, "properties missing" );
+ ReplaceSetProperties( FONT_FORMAT_LIST, aValues );
+
+ rFntFmtList.SetModified( false );
+}
+
+
+void SmMathConfig::StripFontFormatList( const std::vector< SmSym > &rSymbols )
+{
+ size_t i;
+
+ // build list of used font-formats only
+ //!! font-format IDs may be different !!
+ SmFontFormatList aUsedList;
+ for (i = 0; i < rSymbols.size(); ++i)
+ {
+ OSL_ENSURE( rSymbols[i].GetName().getLength() > 0, "non named symbol" );
+ aUsedList.GetFontFormatId( SmFontFormat( rSymbols[i].GetFace() ) , true );
+ }
+ const SmFormat & rStdFmt = GetStandardFormat();
+ for (i = FNT_BEGIN; i <= FNT_END; ++i)
+ {
+ aUsedList.GetFontFormatId( SmFontFormat( rStdFmt.GetFont( i ) ) , true );
+ }
+
+ // remove unused font-formats from list
+ SmFontFormatList &rFntFmtList = GetFontFormatList();
+ size_t nCnt = rFntFmtList.GetCount();
+ std::unique_ptr<SmFontFormat[]> pTmpFormat(new SmFontFormat[ nCnt ]);
+ std::unique_ptr<OUString[]> pId(new OUString[ nCnt ]);
+ size_t k;
+ for (k = 0; k < nCnt; ++k)
+ {
+ pTmpFormat[k] = *rFntFmtList.GetFontFormat( k );
+ pId[k] = rFntFmtList.GetFontFormatId( k );
+ }
+ for (k = 0; k < nCnt; ++k)
+ {
+ if (aUsedList.GetFontFormatId( pTmpFormat[k] ).isEmpty())
+ {
+ rFntFmtList.RemoveFontFormat( pId[k] );
+ }
+ }
+}
+
+
+void SmMathConfig::LoadOther()
+{
+ if (!pOther)
+ pOther.reset(new SmCfgOther);
+
+ pOther->bPrintTitle = officecfg::Office::Math::Print::Title::get();
+ pOther->bPrintFormulaText = officecfg::Office::Math::Print::FormulaText::get();
+ pOther->bPrintFrame = officecfg::Office::Math::Print::Frame::get();
+ pOther->ePrintSize = static_cast<SmPrintSize>(officecfg::Office::Math::Print::Size::get());
+ pOther->nPrintZoomFactor = officecfg::Office::Math::Print::ZoomFactor::get();
+ pOther->bIsSaveOnlyUsedSymbols = officecfg::Office::Math::LoadSave::IsSaveOnlyUsedSymbols::get();
+ pOther->bIsAutoCloseBrackets = officecfg::Office::Math::Misc::AutoCloseBrackets::get();
+ pOther->bIgnoreSpacesRight = officecfg::Office::Math::Misc::IgnoreSpacesRight::get();
+ pOther->bToolboxVisible = officecfg::Office::Math::View::ToolboxVisible::get();
+ pOther->bAutoRedraw = officecfg::Office::Math::View::AutoRedraw::get();
+ pOther->bFormulaCursor = officecfg::Office::Math::View::FormulaCursor::get();
+ SetOtherModified( false );
+}
+
+
+void SmMathConfig::SaveOther()
+{
+ if (!pOther || !IsOtherModified())
+ return;
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+
+ officecfg::Office::Math::Print::Title::set(pOther->bPrintTitle, batch);
+ officecfg::Office::Math::Print::FormulaText::set(pOther->bPrintFormulaText, batch);
+ officecfg::Office::Math::Print::Frame::set(pOther->bPrintFrame, batch);
+ officecfg::Office::Math::Print::Size::set(pOther->ePrintSize, batch);
+ officecfg::Office::Math::Print::ZoomFactor::set(pOther->nPrintZoomFactor, batch);
+ officecfg::Office::Math::LoadSave::IsSaveOnlyUsedSymbols::set(pOther->bIsSaveOnlyUsedSymbols, batch);
+ officecfg::Office::Math::Misc::AutoCloseBrackets::set(pOther->bIsAutoCloseBrackets, batch);
+ officecfg::Office::Math::Misc::IgnoreSpacesRight::set(pOther->bIgnoreSpacesRight, batch);
+ officecfg::Office::Math::View::ToolboxVisible::set(pOther->bToolboxVisible, batch);
+ officecfg::Office::Math::View::AutoRedraw::set(pOther->bAutoRedraw, batch);
+ officecfg::Office::Math::View::FormulaCursor::set(pOther->bFormulaCursor, batch);
+
+ batch->commit();
+ SetOtherModified( false );
+}
+
+namespace {
+
+// Latin default-fonts
+const DefaultFontType aLatinDefFnts[FNT_END] =
+{
+ DefaultFontType::SERIF, // FNT_VARIABLE
+ DefaultFontType::SERIF, // FNT_FUNCTION
+ DefaultFontType::SERIF, // FNT_NUMBER
+ DefaultFontType::SERIF, // FNT_TEXT
+ DefaultFontType::SERIF, // FNT_SERIF
+ DefaultFontType::SANS, // FNT_SANS
+ DefaultFontType::FIXED // FNT_FIXED
+ //OpenSymbol, // FNT_MATH
+};
+
+// CJK default-fonts
+//! we use non-asian fonts for variables, functions and numbers since they
+//! look better and even in asia only latin letters will be used for those.
+//! At least that's what I was told...
+const DefaultFontType aCJKDefFnts[FNT_END] =
+{
+ DefaultFontType::SERIF, // FNT_VARIABLE
+ DefaultFontType::SERIF, // FNT_FUNCTION
+ DefaultFontType::SERIF, // FNT_NUMBER
+ DefaultFontType::CJK_TEXT, // FNT_TEXT
+ DefaultFontType::CJK_TEXT, // FNT_SERIF
+ DefaultFontType::CJK_DISPLAY, // FNT_SANS
+ DefaultFontType::CJK_TEXT // FNT_FIXED
+ //OpenSymbol, // FNT_MATH
+};
+
+// CTL default-fonts
+const DefaultFontType aCTLDefFnts[FNT_END] =
+{
+ DefaultFontType::CTL_TEXT, // FNT_VARIABLE
+ DefaultFontType::CTL_TEXT, // FNT_FUNCTION
+ DefaultFontType::CTL_TEXT, // FNT_NUMBER
+ DefaultFontType::CTL_TEXT, // FNT_TEXT
+ DefaultFontType::CTL_TEXT, // FNT_SERIF
+ DefaultFontType::CTL_TEXT, // FNT_SANS
+ DefaultFontType::CTL_TEXT // FNT_FIXED
+ //OpenSymbol, // FNT_MATH
+};
+
+
+OUString lcl_GetDefaultFontName( LanguageType nLang, sal_uInt16 nIdent )
+{
+ assert(nIdent < FNT_END);
+ const DefaultFontType *pTable;
+ switch ( SvtLanguageOptions::GetScriptTypeOfLanguage( nLang ) )
+ {
+ case SvtScriptType::LATIN : pTable = aLatinDefFnts; break;
+ case SvtScriptType::ASIAN : pTable = aCJKDefFnts; break;
+ case SvtScriptType::COMPLEX : pTable = aCTLDefFnts; break;
+ default :
+ pTable = aLatinDefFnts;
+ SAL_WARN("starmath", "unknown script-type");
+ }
+
+ return OutputDevice::GetDefaultFont(pTable[ nIdent ], nLang,
+ GetDefaultFontFlags::OnlyOne ).GetFamilyName();
+}
+
+}
+
+
+void SmMathConfig::LoadFormat()
+{
+ if (!pFormat)
+ pFormat.reset(new SmFormat);
+
+
+ Sequence< OUString > aNames = lcl_GetFormatPropertyNames();
+
+ sal_Int32 nProps = aNames.getLength();
+
+ Sequence< Any > aValues( GetProperties( aNames ) );
+ if (!(nProps && aValues.getLength() == nProps))
+ return;
+
+ const Any *pValues = aValues.getConstArray();
+ const Any *pVal = pValues;
+
+ OUString aTmpStr;
+ sal_Int16 nTmp16 = 0;
+ bool bTmp = false;
+
+ // StandardFormat/Textmode
+ if (pVal->hasValue() && (*pVal >>= bTmp))
+ pFormat->SetTextmode( bTmp );
+ ++pVal;
+ // StandardFormat/GreekCharStyle
+ if (pVal->hasValue() && (*pVal >>= nTmp16))
+ pFormat->SetGreekCharStyle( nTmp16 );
+ ++pVal;
+ // StandardFormat/ScaleNormalBracket
+ if (pVal->hasValue() && (*pVal >>= bTmp))
+ pFormat->SetScaleNormalBrackets( bTmp );
+ ++pVal;
+ // StandardFormat/HorizontalAlignment
+ if (pVal->hasValue() && (*pVal >>= nTmp16))
+ pFormat->SetHorAlign( static_cast<SmHorAlign>(nTmp16) );
+ ++pVal;
+ // StandardFormat/BaseSize
+ if (pVal->hasValue() && (*pVal >>= nTmp16))
+ pFormat->SetBaseSize( Size(0, SmPtsTo100th_mm( nTmp16 )) );
+ ++pVal;
+
+ sal_uInt16 i;
+ for (i = SIZ_BEGIN; i <= SIZ_END; ++i)
+ {
+ if (pVal->hasValue() && (*pVal >>= nTmp16))
+ pFormat->SetRelSize( i, nTmp16 );
+ ++pVal;
+ }
+
+ for (i = DIS_BEGIN; i <= DIS_END; ++i)
+ {
+ if (pVal->hasValue() && (*pVal >>= nTmp16))
+ pFormat->SetDistance( i, nTmp16 );
+ ++pVal;
+ }
+
+ LanguageType nLang = Application::GetSettings().GetUILanguageTag().getLanguageType();
+ for (i = FNT_BEGIN; i < FNT_END; ++i)
+ {
+ vcl::Font aFnt;
+ bool bUseDefaultFont = true;
+ if (pVal->hasValue() && (*pVal >>= aTmpStr))
+ {
+ bUseDefaultFont = aTmpStr.isEmpty();
+ if (bUseDefaultFont)
+ {
+ aFnt = pFormat->GetFont( i );
+ aFnt.SetFamilyName( lcl_GetDefaultFontName( nLang, i ) );
+ }
+ else
+ {
+ const SmFontFormat *pFntFmt = GetFontFormatList().GetFontFormat( aTmpStr );
+ OSL_ENSURE( pFntFmt, "unknown FontFormat" );
+ if (pFntFmt)
+ aFnt = pFntFmt->GetFont();
+ }
+ }
+ ++pVal;
+
+ aFnt.SetFontSize( pFormat->GetBaseSize() );
+ pFormat->SetFont( i, aFnt, bUseDefaultFont );
+ }
+
+ OSL_ENSURE( pVal - pValues == nProps, "property mismatch" );
+ SetFormatModified( false );
+}
+
+
+void SmMathConfig::SaveFormat()
+{
+ if (!pFormat || !IsFormatModified())
+ return;
+
+ const Sequence< OUString > aNames = lcl_GetFormatPropertyNames();
+ sal_Int32 nProps = aNames.getLength();
+
+ Sequence< Any > aValues( nProps );
+ Any *pValues = aValues.getArray();
+ Any *pValue = pValues;
+
+ // StandardFormat/Textmode
+ *pValue++ <<= pFormat->IsTextmode();
+ // StandardFormat/GreekCharStyle
+ *pValue++ <<= pFormat->GetGreekCharStyle();
+ // StandardFormat/ScaleNormalBracket
+ *pValue++ <<= pFormat->IsScaleNormalBrackets();
+ // StandardFormat/HorizontalAlignment
+ *pValue++ <<= static_cast<sal_Int16>(pFormat->GetHorAlign());
+ // StandardFormat/BaseSize
+ *pValue++ <<= static_cast<sal_Int16>(SmRoundFraction( Sm100th_mmToPts(
+ pFormat->GetBaseSize().Height() ) ));
+
+ sal_uInt16 i;
+ for (i = SIZ_BEGIN; i <= SIZ_END; ++i)
+ *pValue++ <<= static_cast<sal_Int16>(pFormat->GetRelSize( i ));
+
+ for (i = DIS_BEGIN; i <= DIS_END; ++i)
+ *pValue++ <<= static_cast<sal_Int16>(pFormat->GetDistance( i ));
+
+ for (i = FNT_BEGIN; i < FNT_END; ++i)
+ {
+ OUString aFntFmtId;
+
+ if (!pFormat->IsDefaultFont( i ))
+ {
+ SmFontFormat aFntFmt( pFormat->GetFont( i ) );
+ aFntFmtId = GetFontFormatList().GetFontFormatId( aFntFmt, true );
+ OSL_ENSURE( !aFntFmtId.isEmpty(), "FontFormatId not found" );
+ }
+
+ *pValue++ <<= aFntFmtId;
+ }
+
+ OSL_ENSURE( pValue - pValues == nProps, "property mismatch" );
+ PutProperties( aNames , aValues );
+
+ SetFormatModified( false );
+}
+
+
+const SmFormat & SmMathConfig::GetStandardFormat() const
+{
+ if (!pFormat)
+ const_cast<SmMathConfig*>(this)->LoadFormat();
+ return *pFormat;
+}
+
+
+void SmMathConfig::SetStandardFormat( const SmFormat &rFormat, bool bSaveFontFormatList )
+{
+ if (!pFormat)
+ LoadFormat();
+ if (rFormat == *pFormat)
+ return;
+
+ *pFormat = rFormat;
+ SetFormatModified( true );
+ SaveFormat();
+
+ if (bSaveFontFormatList)
+ {
+ // needed for SmFontTypeDialog's DefaultButtonClickHdl
+ if (pFontFormatList)
+ pFontFormatList->SetModified( true );
+ SaveFontFormatList();
+ }
+}
+
+
+SmPrintSize SmMathConfig::GetPrintSize() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->ePrintSize;
+}
+
+
+void SmMathConfig::SetPrintSize( SmPrintSize eSize )
+{
+ if (!pOther)
+ LoadOther();
+ if (eSize != pOther->ePrintSize)
+ {
+ pOther->ePrintSize = eSize;
+ SetOtherModified( true );
+ }
+}
+
+
+sal_uInt16 SmMathConfig::GetPrintZoomFactor() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->nPrintZoomFactor;
+}
+
+
+void SmMathConfig::SetPrintZoomFactor( sal_uInt16 nVal )
+{
+ if (!pOther)
+ LoadOther();
+ if (nVal != pOther->nPrintZoomFactor)
+ {
+ pOther->nPrintZoomFactor = nVal;
+ SetOtherModified( true );
+ }
+}
+
+
+void SmMathConfig::SetOtherIfNotEqual( bool &rbItem, bool bNewVal )
+{
+ if (bNewVal != rbItem)
+ {
+ rbItem = bNewVal;
+ SetOtherModified( true );
+ }
+}
+
+
+bool SmMathConfig::IsPrintTitle() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bPrintTitle;
+}
+
+
+void SmMathConfig::SetPrintTitle( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bPrintTitle, bVal );
+}
+
+
+bool SmMathConfig::IsPrintFormulaText() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bPrintFormulaText;
+}
+
+
+void SmMathConfig::SetPrintFormulaText( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bPrintFormulaText, bVal );
+}
+
+bool SmMathConfig::IsSaveOnlyUsedSymbols() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bIsSaveOnlyUsedSymbols;
+}
+
+bool SmMathConfig::IsAutoCloseBrackets() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bIsAutoCloseBrackets;
+}
+
+bool SmMathConfig::IsPrintFrame() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bPrintFrame;
+}
+
+
+void SmMathConfig::SetPrintFrame( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bPrintFrame, bVal );
+}
+
+
+void SmMathConfig::SetSaveOnlyUsedSymbols( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bIsSaveOnlyUsedSymbols, bVal );
+}
+
+
+void SmMathConfig::SetAutoCloseBrackets( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bIsAutoCloseBrackets, bVal );
+}
+
+
+bool SmMathConfig::IsIgnoreSpacesRight() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bIgnoreSpacesRight;
+}
+
+
+void SmMathConfig::SetIgnoreSpacesRight( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bIgnoreSpacesRight, bVal );
+}
+
+
+bool SmMathConfig::IsAutoRedraw() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bAutoRedraw;
+}
+
+
+void SmMathConfig::SetAutoRedraw( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bAutoRedraw, bVal );
+}
+
+
+bool SmMathConfig::IsShowFormulaCursor() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bFormulaCursor;
+}
+
+
+void SmMathConfig::SetShowFormulaCursor( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bFormulaCursor, bVal );
+}
+
+void SmMathConfig::Notify( const css::uno::Sequence< OUString >& )
+{}
+
+
+void SmMathConfig::ItemSetToConfig(const SfxItemSet &rSet)
+{
+ const SfxPoolItem *pItem = nullptr;
+
+ sal_uInt16 nU16;
+ bool bVal;
+ if (rSet.GetItemState(SID_PRINTSIZE, true, &pItem) == SfxItemState::SET)
+ { nU16 = static_cast<const SfxUInt16Item *>(pItem)->GetValue();
+ SetPrintSize( static_cast<SmPrintSize>(nU16) );
+ }
+ if (rSet.GetItemState(SID_PRINTZOOM, true, &pItem) == SfxItemState::SET)
+ { nU16 = static_cast<const SfxUInt16Item *>(pItem)->GetValue();
+ SetPrintZoomFactor( nU16 );
+ }
+ if (rSet.GetItemState(SID_PRINTTITLE, true, &pItem) == SfxItemState::SET)
+ { bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+ SetPrintTitle( bVal );
+ }
+ if (rSet.GetItemState(SID_PRINTTEXT, true, &pItem) == SfxItemState::SET)
+ { bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+ SetPrintFormulaText( bVal );
+ }
+ if (rSet.GetItemState(SID_PRINTFRAME, true, &pItem) == SfxItemState::SET)
+ { bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+ SetPrintFrame( bVal );
+ }
+ if (rSet.GetItemState(SID_AUTOREDRAW, true, &pItem) == SfxItemState::SET)
+ { bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+ SetAutoRedraw( bVal );
+ }
+ if (rSet.GetItemState(SID_NO_RIGHT_SPACES, true, &pItem) == SfxItemState::SET)
+ { bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+ if (IsIgnoreSpacesRight() != bVal)
+ {
+ SetIgnoreSpacesRight( bVal );
+
+ // reformat (displayed) formulas accordingly
+ Broadcast(SfxHint(SfxHintId::MathFormatChanged));
+ }
+ }
+ if (rSet.GetItemState(SID_SAVE_ONLY_USED_SYMBOLS, true, &pItem) == SfxItemState::SET)
+ { bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+ SetSaveOnlyUsedSymbols( bVal );
+ }
+
+ if (rSet.GetItemState(SID_AUTO_CLOSE_BRACKETS, true, &pItem) == SfxItemState::SET)
+ {
+ bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+ SetAutoCloseBrackets( bVal );
+ }
+
+ SaveOther();
+}
+
+
+void SmMathConfig::ConfigToItemSet(SfxItemSet &rSet) const
+{
+ const SfxItemPool *pPool = rSet.GetPool();
+
+ rSet.Put(SfxUInt16Item(pPool->GetWhich(SID_PRINTSIZE),
+ sal::static_int_cast<sal_uInt16>(GetPrintSize())));
+ rSet.Put(SfxUInt16Item(pPool->GetWhich(SID_PRINTZOOM),
+ GetPrintZoomFactor()));
+
+ rSet.Put(SfxBoolItem(pPool->GetWhich(SID_PRINTTITLE), IsPrintTitle()));
+ rSet.Put(SfxBoolItem(pPool->GetWhich(SID_PRINTTEXT), IsPrintFormulaText()));
+ rSet.Put(SfxBoolItem(pPool->GetWhich(SID_PRINTFRAME), IsPrintFrame()));
+ rSet.Put(SfxBoolItem(pPool->GetWhich(SID_AUTOREDRAW), IsAutoRedraw()));
+ rSet.Put(SfxBoolItem(pPool->GetWhich(SID_NO_RIGHT_SPACES), IsIgnoreSpacesRight()));
+ rSet.Put(SfxBoolItem(pPool->GetWhich(SID_SAVE_ONLY_USED_SYMBOLS), IsSaveOnlyUsedSymbols()));
+ rSet.Put(SfxBoolItem(pPool->GetWhich(SID_AUTO_CLOSE_BRACKETS), IsAutoCloseBrackets()));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/cfgitem.hxx b/starmath/source/cfgitem.hxx
new file mode 100644
index 000000000..8fa101187
--- /dev/null
+++ b/starmath/source/cfgitem.hxx
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_CFGITEM_HXX
+#define INCLUDED_STARMATH_SOURCE_CFGITEM_HXX
+
+#include <utility.hxx>
+
+#include <vector>
+
+#include <rtl/ustring.hxx>
+#include <svl/SfxBroadcaster.hxx>
+#include <unotools/configitem.hxx>
+
+#include <types.hxx>
+#include <memory>
+
+namespace com::sun::star::uno { template <class E> class Sequence; }
+
+class SmSym;
+class SmSymbolManager;
+class SmFormat;
+namespace vcl { class Font; }
+struct SmCfgOther;
+class SfxItemSet;
+
+struct SmFontFormat
+{
+ OUString aName;
+ sal_Int16 nCharSet;
+ sal_Int16 nFamily;
+ sal_Int16 nPitch;
+ sal_Int16 nWeight;
+ sal_Int16 nItalic;
+
+ SmFontFormat();
+ explicit SmFontFormat( const vcl::Font &rFont );
+
+ vcl::Font GetFont() const;
+ bool operator == ( const SmFontFormat &rFntFmt ) const;
+};
+
+struct SmFntFmtListEntry
+{
+ OUString aId;
+ SmFontFormat aFntFmt;
+
+ SmFntFmtListEntry( const OUString &rId, const SmFontFormat &rFntFmt );
+};
+
+class SmFontFormatList
+{
+ std::vector<SmFntFmtListEntry> aEntries;
+ bool bModified;
+
+ SmFontFormatList(const SmFontFormatList&) = delete;
+ SmFontFormatList& operator=(const SmFontFormatList&) = delete;
+
+public:
+ SmFontFormatList();
+
+ void Clear();
+ void AddFontFormat( const OUString &rFntFmtId, const SmFontFormat &rFntFmt );
+ void RemoveFontFormat( const OUString &rFntFmtId );
+
+ const SmFontFormat * GetFontFormat( const OUString &rFntFmtId ) const;
+ const SmFontFormat * GetFontFormat( size_t nPos ) const;
+ OUString GetFontFormatId( const SmFontFormat &rFntFmt ) const;
+ OUString GetFontFormatId( const SmFontFormat &rFntFmt, bool bAdd );
+ OUString GetFontFormatId( size_t nPos ) const;
+ OUString GetNewFontFormatId() const;
+ size_t GetCount() const { return aEntries.size(); }
+
+ bool IsModified() const { return bModified; }
+ void SetModified( bool bVal ) { bModified = bVal; }
+};
+
+class SmMathConfig final : public utl::ConfigItem, public SfxBroadcaster
+{
+ std::unique_ptr<SmFormat> pFormat;
+ std::unique_ptr<SmCfgOther> pOther;
+ std::unique_ptr<SmFontFormatList> pFontFormatList;
+ std::unique_ptr<SmSymbolManager> pSymbolMgr;
+ bool bIsOtherModified;
+ bool bIsFormatModified;
+ SmFontPickList vFontPickList[7];
+
+ SmMathConfig(const SmMathConfig&) = delete;
+ SmMathConfig& operator=(const SmMathConfig&) = delete;
+
+ void StripFontFormatList( const std::vector< SmSym > &rSymbols );
+
+
+ void Save();
+
+ void ReadSymbol( SmSym &rSymbol,
+ const OUString &rSymbolName,
+ const OUString &rBaseNode ) const;
+ void ReadFontFormat( SmFontFormat &rFontFormat,
+ const OUString &rSymbolName,
+ const OUString &rBaseNode ) const;
+
+ void SetOtherIfNotEqual( bool &rbItem, bool bNewVal );
+
+ void LoadOther();
+ void SaveOther();
+ void LoadFormat();
+ void SaveFormat();
+ void LoadFontFormatList();
+ void SaveFontFormatList();
+
+ void SetOtherModified( bool bVal );
+ bool IsOtherModified() const { return bIsOtherModified; }
+ void SetFormatModified( bool bVal );
+ bool IsFormatModified() const { return bIsFormatModified; }
+
+ SmFontFormatList & GetFontFormatList();
+ const SmFontFormatList & GetFontFormatList() const
+ {
+ return const_cast<SmMathConfig*>(this)->GetFontFormatList();
+ }
+
+ virtual void ImplCommit() override;
+
+public:
+ SmMathConfig();
+ virtual ~SmMathConfig() override;
+
+ // utl::ConfigItem
+ virtual void Notify( const css::uno::Sequence< OUString > &rPropertyNames ) override;
+
+ SmSymbolManager & GetSymbolManager();
+ void GetSymbols( std::vector< SmSym > &rSymbols ) const;
+ void SetSymbols( const std::vector< SmSym > &rNewSymbols );
+
+ const SmFormat & GetStandardFormat() const;
+ void SetStandardFormat( const SmFormat &rFormat, bool bSaveFontFormatList = false );
+
+ bool IsPrintTitle() const;
+ void SetPrintTitle( bool bVal );
+ bool IsPrintFormulaText() const;
+ void SetPrintFormulaText( bool bVal );
+ bool IsPrintFrame() const;
+ void SetPrintFrame( bool bVal );
+ SmPrintSize GetPrintSize() const;
+ void SetPrintSize( SmPrintSize eSize );
+ sal_uInt16 GetPrintZoomFactor() const;
+ void SetPrintZoomFactor( sal_uInt16 nVal );
+
+ bool IsSaveOnlyUsedSymbols() const;
+ void SetSaveOnlyUsedSymbols( bool bVal );
+ bool IsAutoCloseBrackets() const;
+ void SetAutoCloseBrackets( bool bVal );
+ bool IsIgnoreSpacesRight() const;
+ void SetIgnoreSpacesRight( bool bVal );
+ bool IsAutoRedraw() const;
+ void SetAutoRedraw( bool bVal );
+ bool IsShowFormulaCursor() const;
+ void SetShowFormulaCursor( bool bVal );
+
+ SmFontPickList & GetFontPickList(sal_uInt16 nIdent) { return vFontPickList[nIdent]; }
+
+ void ItemSetToConfig(const SfxItemSet &rSet);
+ void ConfigToItemSet(SfxItemSet &rSet) const;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/cursor.cxx b/starmath/source/cursor.cxx
new file mode 100644
index 000000000..2aae7adb5
--- /dev/null
+++ b/starmath/source/cursor.cxx
@@ -0,0 +1,1577 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <cursor.hxx>
+#include <visitors.hxx>
+#include <document.hxx>
+#include <view.hxx>
+#include <comphelper/string.hxx>
+#include <editeng/editeng.hxx>
+#include <osl/diagnose.h>
+#include <cassert>
+
+void SmCursor::Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor){
+ SmCaretPosGraphEntry* NewPos = nullptr;
+ switch(direction)
+ {
+ case MoveLeft:
+ if (mpPosition)
+ NewPos = mpPosition->Left;
+ OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
+ break;
+ case MoveRight:
+ if (mpPosition)
+ NewPos = mpPosition->Right;
+ OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
+ break;
+ case MoveUp:
+ //Implementation is practically identical to MoveDown, except for a single if statement
+ //so I've implemented them together and added a direction == MoveDown to the if statements.
+ case MoveDown:
+ if (mpPosition)
+ {
+ SmCaretLine from_line = SmCaretPos2LineVisitor(pDev, mpPosition->CaretPos).GetResult(),
+ best_line, //Best approximated line found so far
+ curr_line; //Current line
+ long dbp_sq = 0; //Distance squared to best line
+ for(const auto &pEntry : *mpGraph)
+ {
+ //Reject it if it's the current position
+ if(pEntry->CaretPos == mpPosition->CaretPos) continue;
+ //Compute caret line
+ curr_line = SmCaretPos2LineVisitor(pDev, pEntry->CaretPos).GetResult();
+ //Reject anything above if we're moving down
+ if(curr_line.GetTop() <= from_line.GetTop() && direction == MoveDown) continue;
+ //Reject anything below if we're moving up
+ if(curr_line.GetTop() + curr_line.GetHeight() >= from_line.GetTop() + from_line.GetHeight()
+ && direction == MoveUp) continue;
+ //Compare if it to what we have, if we have anything yet
+ if(NewPos){
+ //Compute distance to current line squared, multiplied with a horizontal factor
+ long dp_sq = curr_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
+ curr_line.SquaredDistanceY(from_line);
+ //Discard current line if best line is closer
+ if(dbp_sq <= dp_sq) continue;
+ }
+ //Take current line as the best
+ best_line = curr_line;
+ NewPos = pEntry.get();
+ //Update distance to best line
+ dbp_sq = best_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
+ best_line.SquaredDistanceY(from_line);
+ }
+ }
+ break;
+ default:
+ assert(false);
+ }
+ if(NewPos){
+ mpPosition = NewPos;
+ if(bMoveAnchor)
+ mpAnchor = NewPos;
+ RequestRepaint();
+ }
+}
+
+void SmCursor::MoveTo(OutputDevice* pDev, const Point& pos, bool bMoveAnchor)
+{
+ SmCaretPosGraphEntry* NewPos = nullptr;
+ long dp_sq = 0, //Distance to current line squared
+ dbp_sq = 1; //Distance to best line squared
+ for(const auto &pEntry : *mpGraph)
+ {
+ OSL_ENSURE(pEntry->CaretPos.IsValid(), "The caret position graph may not have invalid positions!");
+ //Compute current line
+ SmCaretLine curr_line = SmCaretPos2LineVisitor(pDev, pEntry->CaretPos).GetResult();
+ //Compute squared distance to current line
+ dp_sq = curr_line.SquaredDistanceX(pos) + curr_line.SquaredDistanceY(pos);
+ //If we have a position compare to it
+ if(NewPos){
+ //If best line is closer, reject current line
+ if(dbp_sq <= dp_sq) continue;
+ }
+ //Accept current position as the best
+ NewPos = pEntry.get();
+ //Update distance to best line
+ dbp_sq = dp_sq;
+ }
+ if(NewPos){
+ mpPosition = NewPos;
+ if(bMoveAnchor)
+ mpAnchor = NewPos;
+ RequestRepaint();
+ }
+}
+
+void SmCursor::BuildGraph(){
+ //Save the current anchor and position
+ SmCaretPos _anchor, _position;
+ //Release mpGraph if allocated
+ if(mpGraph){
+ if(mpAnchor)
+ _anchor = mpAnchor->CaretPos;
+ if(mpPosition)
+ _position = mpPosition->CaretPos;
+ mpGraph.reset();
+ //Reset anchor and position as they point into an old graph
+ mpAnchor = nullptr;
+ mpPosition = nullptr;
+ }
+
+ //Build the new graph
+ mpGraph.reset(SmCaretPosGraphBuildingVisitor(mpTree).takeGraph());
+
+ //Restore anchor and position pointers
+ if(_anchor.IsValid() || _position.IsValid()){
+ for(const auto &pEntry : *mpGraph)
+ {
+ if(_anchor == pEntry->CaretPos)
+ mpAnchor = pEntry.get();
+ if(_position == pEntry->CaretPos)
+ mpPosition = pEntry.get();
+ }
+ }
+ //Set position and anchor to first caret position
+ auto it = mpGraph->begin();
+ assert(it != mpGraph->end());
+ if(!mpPosition)
+ mpPosition = it->get();
+ if(!mpAnchor)
+ mpAnchor = mpPosition;
+
+ assert(mpPosition);
+ assert(mpAnchor);
+ OSL_ENSURE(mpPosition->CaretPos.IsValid(), "Position must be valid");
+ OSL_ENSURE(mpAnchor->CaretPos.IsValid(), "Anchor must be valid");
+}
+
+bool SmCursor::SetCaretPosition(SmCaretPos pos){
+ for(const auto &pEntry : *mpGraph)
+ {
+ if(pEntry->CaretPos == pos)
+ {
+ mpPosition = pEntry.get();
+ mpAnchor = pEntry.get();
+ return true;
+ }
+ }
+ return false;
+}
+
+void SmCursor::AnnotateSelection(){
+ //TODO: Manage a state, reset it upon modification and optimize this call
+ SmSetSelectionVisitor(mpAnchor->CaretPos, mpPosition->CaretPos, mpTree);
+}
+
+void SmCursor::Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible){
+ SmCaretDrawingVisitor(pDev, GetPosition(), Offset, isCaretVisible);
+}
+
+void SmCursor::DeletePrev(OutputDevice* pDev){
+ //Delete only a selection if there's a selection
+ if(HasSelection()){
+ Delete();
+ return;
+ }
+
+ SmNode* pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
+ SmStructureNode* pLineParent = pLine->GetParent();
+ int nLineOffsetIdx = pLineParent->IndexOfSubNode(pLine);
+ assert(nLineOffsetIdx >= 0);
+
+ //If we're in front of a node who's parent is a TABLE
+ if (pLineParent->GetType() == SmNodeType::Table && mpPosition->CaretPos.nIndex == 0 && nLineOffsetIdx > 0)
+ {
+ size_t nLineOffset = nLineOffsetIdx;
+ //Now we can merge with nLineOffset - 1
+ BeginEdit();
+ //Line to merge things into, so we can delete pLine
+ SmNode* pMergeLine = pLineParent->GetSubNode(nLineOffset-1);
+ OSL_ENSURE(pMergeLine, "pMergeLine cannot be NULL!");
+ SmCaretPos PosAfterDelete;
+ //Convert first line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pMergeLine, *pLineList);
+ if(!pLineList->empty()){
+ //Find iterator to patch
+ SmNodeList::iterator patchPoint = pLineList->end();
+ --patchPoint;
+ //Convert second line to list
+ NodeToList(pLine, *pLineList);
+ //Patch the line list
+ ++patchPoint;
+ PosAfterDelete = PatchLineList(pLineList.get(), patchPoint);
+ //Parse the line
+ pLine = SmNodeListParser().Parse(pLineList.get());
+ }
+ pLineList.reset();
+ pLineParent->SetSubNode(nLineOffset-1, pLine);
+ //Delete the removed line slot
+ SmNodeArray lines(pLineParent->GetNumSubNodes()-1);
+ for (size_t i = 0; i < pLineParent->GetNumSubNodes(); ++i)
+ {
+ if(i < nLineOffset)
+ lines[i] = pLineParent->GetSubNode(i);
+ else if(i > nLineOffset)
+ lines[i-1] = pLineParent->GetSubNode(i);
+ }
+ pLineParent->SetSubNodes(std::move(lines));
+ //Rebuild graph
+ mpAnchor = nullptr;
+ mpPosition = nullptr;
+ BuildGraph();
+ AnnotateSelection();
+ //Set caret position
+ if(!SetCaretPosition(PosAfterDelete))
+ SetCaretPosition(SmCaretPos(pLine, 0));
+ //Finish editing
+ EndEdit();
+
+ //TODO: If we're in an empty (sub/super/*) script
+ /*}else if(pLineParent->GetType() == SmNodeType::SubSup &&
+ nLineOffset != 0 &&
+ pLine->GetType() == SmNodeType::Expression &&
+ pLine->GetNumSubNodes() == 0){
+ //There's a (sub/super) script we can delete
+ //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != SmNodeType::Expression
+ //TODO: Handle case where we delete a limit
+ */
+
+ //Else move select, and delete if not complex
+ }else{
+ Move(pDev, MoveLeft, false);
+ if(!HasComplexSelection())
+ Delete();
+ }
+}
+
+void SmCursor::Delete(){
+ //Return if we don't have a selection to delete
+ if(!HasSelection())
+ return;
+
+ //Enter edit section
+ BeginEdit();
+
+ //Set selected on nodes
+ AnnotateSelection();
+
+ //Find an arbitrary selected node
+ SmNode* pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+
+ //Find the topmost node of the line that holds the selection
+ SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
+ OSL_ENSURE(pLine != mpTree, "Shouldn't be able to select the entire tree");
+
+ //Get the parent of the line
+ SmStructureNode* pLineParent = pLine->GetParent();
+ //Find line offset in parent
+ int nLineOffset = pLineParent->IndexOfSubNode(pLine);
+ assert(nLineOffset >= 0);
+
+ //Position after delete
+ SmCaretPos PosAfterDelete;
+
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Take the selected nodes and delete them...
+ SmNodeList::iterator patchIt = TakeSelectedNodesFromList(pLineList.get());
+
+ //Get the position to set after delete
+ PosAfterDelete = PatchLineList(pLineList.get(), patchIt);
+
+ //Finish editing
+ FinishEdit(std::move(pLineList), pLineParent, nLineOffset, PosAfterDelete);
+}
+
+void SmCursor::InsertNodes(std::unique_ptr<SmNodeList> pNewNodes){
+ if(pNewNodes->empty()){
+ return;
+ }
+
+ //Begin edit section
+ BeginEdit();
+
+ //Get the current position
+ const SmCaretPos pos = mpPosition->CaretPos;
+
+ //Find top most of line that holds position
+ SmNode* pLine = FindTopMostNodeInLine(pos.pSelectedNode);
+
+ //Find line parent and line index in parent
+ SmStructureNode* pLineParent = pLine->GetParent();
+ int nParentIndex = pLineParent->IndexOfSubNode(pLine);
+ assert(nParentIndex >= 0);
+
+ //Convert line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Find iterator for place to insert nodes
+ SmNodeList::iterator it = FindPositionInLineList(pLineList.get(), pos);
+
+ //Insert all new nodes
+ SmNodeList::iterator newIt,
+ patchIt = it, // (pointless default value, fixes compiler warnings)
+ insIt;
+ for(newIt = pNewNodes->begin(); newIt != pNewNodes->end(); ++newIt){
+ insIt = pLineList->insert(it, *newIt);
+ if(newIt == pNewNodes->begin())
+ patchIt = insIt;
+ }
+ //Patch the places we've changed stuff
+ PatchLineList(pLineList.get(), patchIt);
+ SmCaretPos PosAfterInsert = PatchLineList(pLineList.get(), it);
+ //Release list, we've taken the nodes
+ pNewNodes.reset();
+
+ //Finish editing
+ FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
+}
+
+SmNodeList::iterator SmCursor::FindPositionInLineList(SmNodeList* pLineList,
+ const SmCaretPos& rCaretPos)
+{
+ //Find iterator for position
+ SmNodeList::iterator it = std::find(pLineList->begin(), pLineList->end(), rCaretPos.pSelectedNode);
+ if (it != pLineList->end())
+ {
+ if((*it)->GetType() == SmNodeType::Text)
+ {
+ //Split textnode if needed
+ if(rCaretPos.nIndex > 0)
+ {
+ SmTextNode* pText = static_cast<SmTextNode*>(rCaretPos.pSelectedNode);
+ if (rCaretPos.nIndex == pText->GetText().getLength())
+ return ++it;
+ OUString str1 = pText->GetText().copy(0, rCaretPos.nIndex);
+ OUString str2 = pText->GetText().copy(rCaretPos.nIndex);
+ pText->ChangeText(str1);
+ ++it;
+ //Insert str2 as new text node
+ assert(!str2.isEmpty());
+ SmTextNode* pNewText = new SmTextNode(pText->GetToken(), pText->GetFontDesc());
+ pNewText->ChangeText(str2);
+ it = pLineList->insert(it, pNewText);
+ }
+ }else
+ ++it;
+ //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
+ return it;
+ }
+ //If we didn't find pSelectedNode, it must be because the caret is in front of the line
+ return pLineList->begin();
+}
+
+SmCaretPos SmCursor::PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter) {
+ //The nodes we should consider merging
+ SmNode *prev = nullptr,
+ *next = nullptr;
+ if(aIter != pLineList->end())
+ next = *aIter;
+ if(aIter != pLineList->begin()) {
+ --aIter;
+ prev = *aIter;
+ ++aIter;
+ }
+
+ //Check if there's textnodes to merge
+ if( prev &&
+ next &&
+ prev->GetType() == SmNodeType::Text &&
+ next->GetType() == SmNodeType::Text &&
+ ( prev->GetToken().eType != TNUMBER ||
+ next->GetToken().eType == TNUMBER) ){
+ SmTextNode *pText = static_cast<SmTextNode*>(prev),
+ *pOldN = static_cast<SmTextNode*>(next);
+ SmCaretPos retval(pText, pText->GetText().getLength());
+ OUString newText = pText->GetText() + pOldN->GetText();
+ pText->ChangeText(newText);
+ delete pOldN;
+ pLineList->erase(aIter);
+ return retval;
+ }
+
+ //Check if there's a SmPlaceNode to remove:
+ if(prev && next && prev->GetType() == SmNodeType::Place && !SmNodeListParser::IsOperator(next->GetToken())){
+ --aIter;
+ aIter = pLineList->erase(aIter);
+ delete prev;
+ //Return caret pos in front of aIter
+ if(aIter != pLineList->begin())
+ --aIter; //Thus find node before aIter
+ if(aIter == pLineList->begin())
+ return SmCaretPos();
+ return SmCaretPos::GetPosAfter(*aIter);
+ }
+ if(prev && next && next->GetType() == SmNodeType::Place && !SmNodeListParser::IsOperator(prev->GetToken())){
+ aIter = pLineList->erase(aIter);
+ delete next;
+ return SmCaretPos::GetPosAfter(prev);
+ }
+
+ //If we didn't do anything return
+ if(!prev) //return an invalid to indicate we're in front of line
+ return SmCaretPos();
+ return SmCaretPos::GetPosAfter(prev);
+}
+
+SmNodeList::iterator SmCursor::TakeSelectedNodesFromList(SmNodeList *pLineList,
+ SmNodeList *pSelectedNodes) {
+ SmNodeList::iterator retval;
+ SmNodeList::iterator it = pLineList->begin();
+ while(it != pLineList->end()){
+ if((*it)->IsSelected()){
+ //Split text nodes
+ if((*it)->GetType() == SmNodeType::Text) {
+ SmTextNode* pText = static_cast<SmTextNode*>(*it);
+ OUString aText = pText->GetText();
+ //Start and lengths of the segments, 2 is the selected segment
+ int start2 = pText->GetSelectionStart(),
+ start3 = pText->GetSelectionEnd(),
+ len1 = start2 - 0,
+ len2 = start3 - start2,
+ len3 = aText.getLength() - start3;
+ SmToken aToken = pText->GetToken();
+ sal_uInt16 eFontDesc = pText->GetFontDesc();
+ //If we need make segment 1
+ if(len1 > 0) {
+ OUString str = aText.copy(0, len1);
+ pText->ChangeText(str);
+ ++it;
+ } else {//Remove it if not needed
+ it = pLineList->erase(it);
+ delete pText;
+ }
+ //Set retval to be right after the selection
+ retval = it;
+ //if we need make segment 3
+ if(len3 > 0) {
+ OUString str = aText.copy(start3, len3);
+ SmTextNode* pSeg3 = new SmTextNode(aToken, eFontDesc);
+ pSeg3->ChangeText(str);
+ retval = pLineList->insert(it, pSeg3);
+ }
+ //If we need to save the selected text
+ if(pSelectedNodes && len2 > 0) {
+ OUString str = aText.copy(start2, len2);
+ SmTextNode* pSeg2 = new SmTextNode(aToken, eFontDesc);
+ pSeg2->ChangeText(str);
+ pSelectedNodes->push_back(pSeg2);
+ }
+ } else { //if it's not textnode
+ SmNode* pNode = *it;
+ retval = it = pLineList->erase(it);
+ if(pSelectedNodes)
+ pSelectedNodes->push_back(pNode);
+ else
+ delete pNode;
+ }
+ } else
+ ++it;
+ }
+ return retval;
+}
+
+void SmCursor::InsertSubSup(SmSubSup eSubSup) {
+ AnnotateSelection();
+
+ //Find line
+ SmNode *pLine;
+ if(HasSelection()) {
+ SmNode *pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+ pLine = FindTopMostNodeInLine(pSNode, true);
+ } else
+ pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
+
+ //Find Parent and offset in parent
+ SmStructureNode *pLineParent = pLine->GetParent();
+ int nParentIndex = pLineParent->IndexOfSubNode(pLine);
+ assert(nParentIndex >= 0);
+
+ //TODO: Consider handling special cases where parent is an SmOperNode,
+ // Maybe this method should be able to add limits to an SmOperNode...
+
+ //We begin modifying the tree here
+ BeginEdit();
+
+ //Convert line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Take the selection, and/or find iterator for current position
+ std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
+ SmNodeList::iterator it;
+ if(HasSelection())
+ it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
+ else
+ it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
+
+ //Find node that this should be applied to
+ SmNode* pSubject;
+ bool bPatchLine = !pSelectedNodesList->empty(); //If the line should be patched later
+ if(it != pLineList->begin()) {
+ --it;
+ pSubject = *it;
+ ++it;
+ } else {
+ //Create a new place node
+ pSubject = new SmPlaceNode();
+ pSubject->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+ it = pLineList->insert(it, pSubject);
+ ++it;
+ bPatchLine = true; //We've modified the line it should be patched later.
+ }
+
+ //Wrap the subject in a SmSubSupNode
+ SmSubSupNode* pSubSup;
+ if(pSubject->GetType() != SmNodeType::SubSup){
+ SmToken token;
+ token.nGroup = TG::Power;
+ pSubSup = new SmSubSupNode(token);
+ pSubSup->SetBody(pSubject);
+ *(--it) = pSubSup;
+ ++it;
+ }else
+ pSubSup = static_cast<SmSubSupNode*>(pSubject);
+ //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
+ //and it pointer to the element following pSubSup in pLineList.
+ pSubject = nullptr;
+
+ //Patch the line if we noted that was needed previously
+ if(bPatchLine)
+ PatchLineList(pLineList.get(), it);
+
+ //Convert existing, if any, sub-/superscript line to list
+ SmNode *pScriptLine = pSubSup->GetSubSup(eSubSup);
+ std::unique_ptr<SmNodeList> pScriptLineList(new SmNodeList);
+ NodeToList(pScriptLine, *pScriptLineList);
+
+ //Add selection to pScriptLineList
+ unsigned int nOldSize = pScriptLineList->size();
+ pScriptLineList->insert(pScriptLineList->end(), pSelectedNodesList->begin(), pSelectedNodesList->end());
+ pSelectedNodesList.reset();
+
+ //Patch pScriptLineList if needed
+ if(0 < nOldSize && nOldSize < pScriptLineList->size()) {
+ SmNodeList::iterator iPatchPoint = pScriptLineList->begin();
+ std::advance(iPatchPoint, nOldSize);
+ PatchLineList(pScriptLineList.get(), iPatchPoint);
+ }
+
+ //Find caret pos, that should be used after sub-/superscription.
+ SmCaretPos PosAfterScript; //Leave invalid for first position
+ if (!pScriptLineList->empty())
+ PosAfterScript = SmCaretPos::GetPosAfter(pScriptLineList->back());
+
+ //Parse pScriptLineList
+ pScriptLine = SmNodeListParser().Parse(pScriptLineList.get());
+ pScriptLineList.reset();
+
+ //Insert pScriptLine back into the tree
+ pSubSup->SetSubSup(eSubSup, pScriptLine);
+
+ //Finish editing
+ FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterScript, pScriptLine);
+}
+
+void SmCursor::InsertBrackets(SmBracketType eBracketType) {
+ BeginEdit();
+
+ AnnotateSelection();
+
+ //Find line
+ SmNode *pLine;
+ if(HasSelection()) {
+ SmNode *pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+ pLine = FindTopMostNodeInLine(pSNode, true);
+ } else
+ pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
+
+ //Find parent and offset in parent
+ SmStructureNode *pLineParent = pLine->GetParent();
+ int nParentIndex = pLineParent->IndexOfSubNode(pLine);
+ assert(nParentIndex >= 0);
+
+ //Convert line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Take the selection, and/or find iterator for current position
+ std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
+ SmNodeList::iterator it;
+ if(HasSelection())
+ it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
+ else
+ it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
+
+ //If there's no selected nodes, create a place node
+ std::unique_ptr<SmNode> pBodyNode;
+ SmCaretPos PosAfterInsert;
+ if(pSelectedNodesList->empty()) {
+ pBodyNode.reset(new SmPlaceNode());
+ PosAfterInsert = SmCaretPos(pBodyNode.get(), 1);
+ } else
+ pBodyNode.reset(SmNodeListParser().Parse(pSelectedNodesList.get()));
+
+ pSelectedNodesList.reset();
+
+ //Create SmBraceNode
+ SmToken aTok(TLEFT, '\0', "left", TG::NONE, 5);
+ SmBraceNode *pBrace = new SmBraceNode(aTok);
+ pBrace->SetScaleMode(SmScaleMode::Height);
+ std::unique_ptr<SmNode> pLeft( CreateBracket(eBracketType, true) ),
+ pRight( CreateBracket(eBracketType, false) );
+ std::unique_ptr<SmBracebodyNode> pBody(new SmBracebodyNode(SmToken()));
+ pBody->SetSubNodes(std::move(pBodyNode), nullptr);
+ pBrace->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ pBrace->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+
+ //Insert into line
+ pLineList->insert(it, pBrace);
+ //Patch line (I think this is good enough)
+ SmCaretPos aAfter = PatchLineList(pLineList.get(), it);
+ if( !PosAfterInsert.IsValid() )
+ PosAfterInsert = aAfter;
+
+ //Finish editing
+ FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
+}
+
+SmNode *SmCursor::CreateBracket(SmBracketType eBracketType, bool bIsLeft) {
+ SmToken aTok;
+ if(bIsLeft){
+ switch(eBracketType){
+ case SmBracketType::Round:
+ aTok = SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5);
+ break;
+ case SmBracketType::Square:
+ aTok = SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5);
+ break;
+ case SmBracketType::Curly:
+ aTok = SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5);
+ break;
+ }
+ } else {
+ switch(eBracketType) {
+ case SmBracketType::Round:
+ aTok = SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5);
+ break;
+ case SmBracketType::Square:
+ aTok = SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5);
+ break;
+ case SmBracketType::Curly:
+ aTok = SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5);
+ break;
+ }
+ }
+ SmNode* pRetVal = new SmMathSymbolNode(aTok);
+ pRetVal->SetScaleMode(SmScaleMode::Height);
+ return pRetVal;
+}
+
+bool SmCursor::InsertRow() {
+ AnnotateSelection();
+
+ //Find line
+ SmNode *pLine;
+ if(HasSelection()) {
+ SmNode *pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+ pLine = FindTopMostNodeInLine(pSNode, true);
+ } else
+ pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
+
+ //Find parent and offset in parent
+ SmStructureNode *pLineParent = pLine->GetParent();
+ int nParentIndex = pLineParent->IndexOfSubNode(pLine);
+ assert(nParentIndex >= 0);
+
+ //Discover the context of this command
+ SmTableNode *pTable = nullptr;
+ SmMatrixNode *pMatrix = nullptr;
+ int nTableIndex = nParentIndex;
+ if(pLineParent->GetType() == SmNodeType::Table)
+ pTable = static_cast<SmTableNode*>(pLineParent);
+ //If it's wrapped in a SmLineNode, we can still insert a newline
+ else if(pLineParent->GetType() == SmNodeType::Line &&
+ pLineParent->GetParent() &&
+ pLineParent->GetParent()->GetType() == SmNodeType::Table) {
+ //NOTE: This hack might give problems if we stop ignoring SmAlignNode
+ pTable = static_cast<SmTableNode*>(pLineParent->GetParent());
+ nTableIndex = pTable->IndexOfSubNode(pLineParent);
+ assert(nTableIndex >= 0);
+ }
+ if(pLineParent->GetType() == SmNodeType::Matrix)
+ pMatrix = static_cast<SmMatrixNode*>(pLineParent);
+
+ //If we're not in a context that supports InsertRow, return sal_False
+ if(!pTable && !pMatrix)
+ return false;
+
+ //Now we start editing
+ BeginEdit();
+
+ //Convert line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Find position in line
+ SmNodeList::iterator it;
+ if(HasSelection()) {
+ //Take the selected nodes and delete them...
+ it = TakeSelectedNodesFromList(pLineList.get());
+ } else
+ it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
+
+ //New caret position after inserting the newline/row in whatever context
+ SmCaretPos PosAfterInsert;
+
+ //If we're in the context of a table
+ if(pTable) {
+ std::unique_ptr<SmNodeList> pNewLineList(new SmNodeList);
+ //Move elements from pLineList to pNewLineList
+ SmNodeList& rLineList = *pLineList;
+ pNewLineList->splice(pNewLineList->begin(), rLineList, it, rLineList.end());
+ //Make sure it is valid again
+ it = pLineList->end();
+ if(it != pLineList->begin())
+ --it;
+ if(pNewLineList->empty())
+ pNewLineList->push_front(new SmPlaceNode());
+ //Parse new line
+ std::unique_ptr<SmNode> pNewLine(SmNodeListParser().Parse(pNewLineList.get()));
+ pNewLineList.reset();
+ //Wrap pNewLine in SmLineNode if needed
+ if(pLineParent->GetType() == SmNodeType::Line) {
+ std::unique_ptr<SmLineNode> pNewLineNode(new SmLineNode(SmToken(TNEWLINE, '\0', "newline")));
+ pNewLineNode->SetSubNodes(std::move(pNewLine), nullptr);
+ pNewLine = std::move(pNewLineNode);
+ }
+ //Get position
+ PosAfterInsert = SmCaretPos(pNewLine.get(), 0);
+ //Move other nodes if needed
+ for( int i = pTable->GetNumSubNodes(); i > nTableIndex + 1; i--)
+ pTable->SetSubNode(i, pTable->GetSubNode(i-1));
+
+ //Insert new line
+ pTable->SetSubNode(nTableIndex + 1, pNewLine.get());
+
+ //Check if we need to change token type:
+ if(pTable->GetNumSubNodes() > 2 && pTable->GetToken().eType == TBINOM) {
+ SmToken tok = pTable->GetToken();
+ tok.eType = TSTACK;
+ pTable->SetToken(tok);
+ }
+ }
+ //If we're in the context of a matrix
+ else {
+ //Find position after insert and patch the list
+ PosAfterInsert = PatchLineList(pLineList.get(), it);
+ //Move other children
+ sal_uInt16 rows = pMatrix->GetNumRows();
+ sal_uInt16 cols = pMatrix->GetNumCols();
+ int nRowStart = (nParentIndex - nParentIndex % cols) + cols;
+ for( int i = pMatrix->GetNumSubNodes() + cols - 1; i >= nRowStart + cols; i--)
+ pMatrix->SetSubNode(i, pMatrix->GetSubNode(i - cols));
+ for( int i = nRowStart; i < nRowStart + cols; i++) {
+ SmPlaceNode *pNewLine = new SmPlaceNode();
+ if(i == nParentIndex + cols)
+ PosAfterInsert = SmCaretPos(pNewLine, 0);
+ pMatrix->SetSubNode(i, pNewLine);
+ }
+ pMatrix->SetRowCol(rows + 1, cols);
+ }
+
+ //Finish editing
+ FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
+ //FinishEdit is actually used to handle situations where parent is an instance of
+ //SmSubSupNode. In this case parent should always be a table or matrix, however, for
+ //code reuse we just use FinishEdit() here too.
+ return true;
+}
+
+void SmCursor::InsertFraction() {
+ AnnotateSelection();
+
+ //Find line
+ SmNode *pLine;
+ if(HasSelection()) {
+ SmNode *pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+ pLine = FindTopMostNodeInLine(pSNode, true);
+ } else
+ pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
+
+ //Find Parent and offset in parent
+ SmStructureNode *pLineParent = pLine->GetParent();
+ int nParentIndex = pLineParent->IndexOfSubNode(pLine);
+ assert(nParentIndex >= 0);
+
+ //We begin modifying the tree here
+ BeginEdit();
+
+ //Convert line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Take the selection, and/or find iterator for current position
+ std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
+ SmNodeList::iterator it;
+ if(HasSelection())
+ it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
+ else
+ it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
+
+ //Create pNum, and pDenom
+ bool bEmptyFraction = pSelectedNodesList->empty();
+ std::unique_ptr<SmNode> pNum( bEmptyFraction
+ ? new SmPlaceNode()
+ : SmNodeListParser().Parse(pSelectedNodesList.get()) );
+ std::unique_ptr<SmNode> pDenom(new SmPlaceNode());
+ pSelectedNodesList.reset();
+
+ //Create new fraction
+ SmBinVerNode *pFrac = new SmBinVerNode(SmToken(TOVER, '\0', "over", TG::Product, 0));
+ std::unique_ptr<SmNode> pRect(new SmRectangleNode(SmToken()));
+ pFrac->SetSubNodes(std::move(pNum), std::move(pRect), std::move(pDenom));
+
+ //Insert in pLineList
+ SmNodeList::iterator patchIt = pLineList->insert(it, pFrac);
+ PatchLineList(pLineList.get(), patchIt);
+ PatchLineList(pLineList.get(), it);
+
+ //Finish editing
+ SmNode *pSelectedNode = bEmptyFraction ? pFrac->GetSubNode(0) : pFrac->GetSubNode(2);
+ FinishEdit(std::move(pLineList), pLineParent, nParentIndex, SmCaretPos(pSelectedNode, 1));
+}
+
+void SmCursor::InsertText(const OUString& aString)
+{
+ BeginEdit();
+
+ Delete();
+
+ SmToken token;
+ token.eType = TIDENT;
+ token.cMathChar = '\0';
+ token.nGroup = TG::NONE;
+ token.nLevel = 5;
+ token.aText = aString;
+
+ SmTextNode* pText = new SmTextNode(token, FNT_VARIABLE);
+ pText->SetText(aString);
+ pText->AdjustFontDesc();
+ pText->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+
+ std::unique_ptr<SmNodeList> pList(new SmNodeList);
+ pList->push_front(pText);
+ InsertNodes(std::move(pList));
+
+ EndEdit();
+}
+
+void SmCursor::InsertElement(SmFormulaElement element){
+ BeginEdit();
+
+ Delete();
+
+ //Create new node
+ SmNode* pNewNode = nullptr;
+ switch(element){
+ case BlankElement:
+ {
+ SmToken token;
+ token.eType = TBLANK;
+ token.nGroup = TG::Blank;
+ token.aText = "~";
+ SmBlankNode* pBlankNode = new SmBlankNode(token);
+ pBlankNode->IncreaseBy(token);
+ pNewNode = pBlankNode;
+ }break;
+ case FactorialElement:
+ {
+ SmToken token(TFACT, MS_FACT, "fact", TG::UnOper, 5);
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case PlusElement:
+ {
+ SmToken token;
+ token.eType = TPLUS;
+ token.cMathChar = MS_PLUS;
+ token.nGroup = TG::UnOper | TG::Sum;
+ token.nLevel = 5;
+ token.aText = "+";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case MinusElement:
+ {
+ SmToken token;
+ token.eType = TMINUS;
+ token.cMathChar = MS_MINUS;
+ token.nGroup = TG::UnOper | TG::Sum;
+ token.nLevel = 5;
+ token.aText = "-";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case CDotElement:
+ {
+ SmToken token;
+ token.eType = TCDOT;
+ token.cMathChar = MS_CDOT;
+ token.nGroup = TG::Product;
+ token.aText = "cdot";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case EqualElement:
+ {
+ SmToken token;
+ token.eType = TASSIGN;
+ token.cMathChar = MS_ASSIGN;
+ token.nGroup = TG::Relation;
+ token.aText = "=";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case LessThanElement:
+ {
+ SmToken token;
+ token.eType = TLT;
+ token.cMathChar = MS_LT;
+ token.nGroup = TG::Relation;
+ token.aText = "<";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case GreaterThanElement:
+ {
+ SmToken token;
+ token.eType = TGT;
+ token.cMathChar = MS_GT;
+ token.nGroup = TG::Relation;
+ token.aText = ">";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case PercentElement:
+ {
+ SmToken token;
+ token.eType = TTEXT;
+ token.cMathChar = MS_PERCENT;
+ token.nGroup = TG::NONE;
+ token.aText = "\"%\"";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ }
+ assert(pNewNode);
+
+ //Prepare the new node
+ pNewNode->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+
+ //Insert new node
+ std::unique_ptr<SmNodeList> pList(new SmNodeList);
+ pList->push_front(pNewNode);
+ InsertNodes(std::move(pList));
+
+ EndEdit();
+}
+
+void SmCursor::InsertSpecial(const OUString& _aString)
+{
+ BeginEdit();
+ Delete();
+
+ OUString aString = comphelper::string::strip(_aString, ' ');
+
+ //Create instance of special node
+ SmToken token;
+ token.eType = TSPECIAL;
+ token.cMathChar = '\0';
+ token.nGroup = TG::NONE;
+ token.nLevel = 5;
+ token.aText = aString;
+ SmSpecialNode* pSpecial = new SmSpecialNode(token);
+
+ //Prepare the special node
+ pSpecial->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+
+ //Insert the node
+ std::unique_ptr<SmNodeList> pList(new SmNodeList);
+ pList->push_front(pSpecial);
+ InsertNodes(std::move(pList));
+
+ EndEdit();
+}
+
+void SmCursor::InsertCommandText(const OUString& aCommandText) {
+ //Parse the sub expression
+ auto xSubExpr = SmParser().ParseExpression(aCommandText);
+
+ //Prepare the subtree
+ xSubExpr->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+
+ //Convert subtree to list
+ SmNode* pSubExpr = xSubExpr.release();
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pSubExpr, *pLineList);
+
+ BeginEdit();
+
+ //Delete any selection
+ Delete();
+
+ //Insert it
+ InsertNodes(std::move(pLineList));
+
+ EndEdit();
+}
+
+void SmCursor::Copy(){
+ if(!HasSelection())
+ return;
+
+ AnnotateSelection();
+ //Find selected node
+ SmNode* pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+ //Find visual line
+ SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
+ assert(pLine);
+
+ //Clone selected nodes
+ SmClipboard aClipboard;
+ if(IsLineCompositionNode(pLine))
+ CloneLineToClipboard(static_cast<SmStructureNode*>(pLine), &aClipboard);
+ else{
+ //Special care to only clone selected text
+ if(pLine->GetType() == SmNodeType::Text) {
+ SmTextNode *pText = static_cast<SmTextNode*>(pLine);
+ std::unique_ptr<SmTextNode> pClone(new SmTextNode( pText->GetToken(), pText->GetFontDesc() ));
+ int start = pText->GetSelectionStart(),
+ length = pText->GetSelectionEnd() - pText->GetSelectionStart();
+ pClone->ChangeText(pText->GetText().copy(start, length));
+ pClone->SetScaleMode(pText->GetScaleMode());
+ aClipboard.push_front(std::move(pClone));
+ } else {
+ SmCloningVisitor aCloneFactory;
+ aClipboard.push_front(std::unique_ptr<SmNode>(aCloneFactory.Clone(pLine)));
+ }
+ }
+
+ //Set clipboard
+ if (!aClipboard.empty())
+ maClipboard = std::move(aClipboard);
+}
+
+void SmCursor::Paste() {
+ BeginEdit();
+ Delete();
+
+ if (!maClipboard.empty())
+ InsertNodes(CloneList(maClipboard));
+
+ EndEdit();
+}
+
+std::unique_ptr<SmNodeList> SmCursor::CloneList(SmClipboard &rClipboard){
+ SmCloningVisitor aCloneFactory;
+ std::unique_ptr<SmNodeList> pClones(new SmNodeList);
+
+ for(auto &xNode : rClipboard){
+ SmNode *pClone = aCloneFactory.Clone(xNode.get());
+ pClones->push_back(pClone);
+ }
+
+ return pClones;
+}
+
+SmNode* SmCursor::FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected){
+ assert(pSNode);
+ //Move up parent until we find a node who's
+ //parent is NULL or isn't selected and not a type of:
+ // SmExpressionNode
+ // SmLineNode
+ // SmBinHorNode
+ // SmUnHorNode
+ // SmAlignNode
+ // SmFontNode
+ while(pSNode->GetParent() &&
+ ((MoveUpIfSelected &&
+ pSNode->GetParent()->IsSelected()) ||
+ IsLineCompositionNode(pSNode->GetParent())))
+ pSNode = pSNode->GetParent();
+ //Now we have the selection line node
+ return pSNode;
+}
+
+SmNode* SmCursor::FindSelectedNode(SmNode* pNode){
+ if(pNode->GetNumSubNodes() == 0)
+ return nullptr;
+ for(auto pChild : *static_cast<SmStructureNode*>(pNode))
+ {
+ if(!pChild)
+ continue;
+ if(pChild->IsSelected())
+ return pChild;
+ SmNode* pRetVal = FindSelectedNode(pChild);
+ if(pRetVal)
+ return pRetVal;
+ }
+ return nullptr;
+}
+
+void SmCursor::LineToList(SmStructureNode* pLine, SmNodeList& list){
+ for(auto pChild : *pLine)
+ {
+ if (!pChild)
+ continue;
+ switch(pChild->GetType()){
+ case SmNodeType::Line:
+ case SmNodeType::UnHor:
+ case SmNodeType::Expression:
+ case SmNodeType::BinHor:
+ case SmNodeType::Align:
+ case SmNodeType::Font:
+ LineToList(static_cast<SmStructureNode*>(pChild), list);
+ break;
+ case SmNodeType::Error:
+ delete pChild;
+ break;
+ default:
+ list.push_back(pChild);
+ }
+ }
+ pLine->ClearSubNodes();
+ delete pLine;
+}
+
+void SmCursor::CloneLineToClipboard(SmStructureNode* pLine, SmClipboard* pClipboard){
+ SmCloningVisitor aCloneFactory;
+ for(auto pChild : *pLine)
+ {
+ if (!pChild)
+ continue;
+ if( IsLineCompositionNode( pChild ) )
+ CloneLineToClipboard( static_cast<SmStructureNode*>(pChild), pClipboard );
+ else if( pChild->IsSelected() && pChild->GetType() != SmNodeType::Error ) {
+ //Only clone selected text from SmTextNode
+ if(pChild->GetType() == SmNodeType::Text) {
+ SmTextNode *pText = static_cast<SmTextNode*>(pChild);
+ std::unique_ptr<SmTextNode> pClone(new SmTextNode( pChild->GetToken(), pText->GetFontDesc() ));
+ int start = pText->GetSelectionStart(),
+ length = pText->GetSelectionEnd() - pText->GetSelectionStart();
+ pClone->ChangeText(pText->GetText().copy(start, length));
+ pClone->SetScaleMode(pText->GetScaleMode());
+ pClipboard->push_back(std::move(pClone));
+ } else
+ pClipboard->push_back(std::unique_ptr<SmNode>(aCloneFactory.Clone(pChild)));
+ }
+ }
+}
+
+bool SmCursor::IsLineCompositionNode(SmNode const * pNode){
+ switch(pNode->GetType()){
+ case SmNodeType::Line:
+ case SmNodeType::UnHor:
+ case SmNodeType::Expression:
+ case SmNodeType::BinHor:
+ case SmNodeType::Align:
+ case SmNodeType::Font:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int SmCursor::CountSelectedNodes(SmNode* pNode){
+ if(pNode->GetNumSubNodes() == 0)
+ return 0;
+ int nCount = 0;
+ for(auto pChild : *static_cast<SmStructureNode*>(pNode))
+ {
+ if (!pChild)
+ continue;
+ if(pChild->IsSelected() && !IsLineCompositionNode(pChild))
+ nCount++;
+ nCount += CountSelectedNodes(pChild);
+ }
+ return nCount;
+}
+
+bool SmCursor::HasComplexSelection(){
+ if(!HasSelection())
+ return false;
+ AnnotateSelection();
+
+ return CountSelectedNodes(mpTree) > 1;
+}
+
+void SmCursor::FinishEdit(std::unique_ptr<SmNodeList> pLineList,
+ SmStructureNode* pParent,
+ int nParentIndex,
+ SmCaretPos PosAfterEdit,
+ SmNode* pStartLine) {
+ //Store number of nodes in line for later
+ int entries = pLineList->size();
+
+ //Parse list of nodes to a tree
+ SmNodeListParser parser;
+ std::unique_ptr<SmNode> pLine(parser.Parse(pLineList.get()));
+ pLineList.reset();
+
+ //Check if we're making the body of a subsup node bigger than one
+ if(pParent->GetType() == SmNodeType::SubSup &&
+ nParentIndex == 0 &&
+ entries > 1) {
+ //Wrap pLine in scalable round brackets
+ SmToken aTok(TLEFT, '\0', "left", TG::NONE, 5);
+ std::unique_ptr<SmBraceNode> pBrace(new SmBraceNode(aTok));
+ pBrace->SetScaleMode(SmScaleMode::Height);
+ std::unique_ptr<SmNode> pLeft( CreateBracket(SmBracketType::Round, true) ),
+ pRight( CreateBracket(SmBracketType::Round, false) );
+ std::unique_ptr<SmBracebodyNode> pBody(new SmBracebodyNode(SmToken()));
+ pBody->SetSubNodes(std::move(pLine), nullptr);
+ pBrace->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ pBrace->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+ pLine = std::move(pBrace);
+ //TODO: Consider the following alternative behavior:
+ //Consider the line: A + {B + C}^D lsub E
+ //Here pLineList is B, + and C and pParent is a subsup node with
+ //both RSUP and LSUB set. Imagine the user just inserted "B +" in
+ //the body of the subsup node...
+ //The most natural thing to do would be to make the line like this:
+ //A + B lsub E + C ^ D
+ //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
+ //and RSUB to the last element in pLineList. But how should this act
+ //for CSUP and CSUB ???
+ //For this reason and because brackets was faster to implement, this solution
+ //have been chosen. It might be worth working on the other solution later...
+ }
+
+ //Set pStartLine if NULL
+ if(!pStartLine)
+ pStartLine = pLine.get();
+
+ //Insert it back into the parent
+ pParent->SetSubNode(nParentIndex, pLine.release());
+
+ //Rebuild graph of caret position
+ mpAnchor = nullptr;
+ mpPosition = nullptr;
+ BuildGraph();
+ AnnotateSelection(); //Update selection annotation!
+
+ //Set caret position
+ if(!SetCaretPosition(PosAfterEdit))
+ SetCaretPosition(SmCaretPos(pStartLine, 0));
+
+ //End edit section
+ EndEdit();
+}
+
+void SmCursor::BeginEdit(){
+ if(mnEditSections++ > 0) return;
+
+ mbIsEnabledSetModifiedSmDocShell = mpDocShell->IsEnableSetModified();
+ if( mbIsEnabledSetModifiedSmDocShell )
+ mpDocShell->EnableSetModified( false );
+}
+
+void SmCursor::EndEdit(){
+ if(--mnEditSections > 0) return;
+
+ mpDocShell->SetFormulaArranged(false);
+ //Okay, I don't know what this does... :)
+ //It's used in SmDocShell::SetText and with places where everything is modified.
+ //I think it does some magic, with sfx, but everything is totally undocumented so
+ //it's kinda hard to tell...
+ if ( mbIsEnabledSetModifiedSmDocShell )
+ mpDocShell->EnableSetModified( mbIsEnabledSetModifiedSmDocShell );
+ //I think this notifies people around us that we've modified this document...
+ mpDocShell->SetModified();
+ //I think SmDocShell uses this value when it sends an update graphics event
+ //Anyway comments elsewhere suggests it needs to be updated...
+ mpDocShell->mnModifyCount++;
+
+ //TODO: Consider copying the update accessibility code from SmDocShell::SetText in here...
+ //This somehow updates the size of SmGraphicView if it is running in embedded mode
+ if( mpDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ mpDocShell->OnDocumentPrinterChanged(nullptr);
+
+ //Request a repaint...
+ RequestRepaint();
+
+ //Update the edit engine and text of the document
+ OUString formula;
+ SmNodeToTextVisitor(mpTree, formula);
+ //mpTree->CreateTextFromNode(formula);
+ mpDocShell->maText = formula;
+ mpDocShell->GetEditEngine().QuickInsertText( formula, ESelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL ) );
+ mpDocShell->GetEditEngine().QuickFormatDoc();
+}
+
+void SmCursor::RequestRepaint(){
+ SmViewShell *pViewSh = SmGetActiveView();
+ if( pViewSh ) {
+ if ( SfxObjectCreateMode::EMBEDDED == mpDocShell->GetCreateMode() )
+ mpDocShell->Repaint();
+ else
+ pViewSh->GetGraphicWindow().Invalidate();
+ }
+}
+
+bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType, SmBraceNode** ppBraceNode) const {
+ const SmCaretPos pos = GetPosition();
+ if (!pos.IsValid()) {
+ return false;
+ }
+
+ SmNode* pNode = pos.pSelectedNode;
+
+ if (pNode->GetType() == SmNodeType::Text) {
+ SmTextNode* pTextNode = static_cast<SmTextNode*>(pNode);
+ if (pos.nIndex < pTextNode->GetText().getLength()) {
+ // The cursor is on a text node and at the middle of it.
+ return false;
+ }
+ } else {
+ if (pos.nIndex < 1) {
+ return false;
+ }
+ }
+
+ while (true) {
+ SmStructureNode* pParentNode = pNode->GetParent();
+ if (!pParentNode) {
+ // There's no brace body node in the ancestors.
+ return false;
+ }
+
+ int index = pParentNode->IndexOfSubNode(pNode);
+ assert(index >= 0);
+ if (static_cast<size_t>(index + 1) != pParentNode->GetNumSubNodes()) {
+ // The cursor is not at the tail at one of ancestor nodes.
+ return false;
+ }
+
+ pNode = pParentNode;
+ if (pNode->GetType() == SmNodeType::Bracebody) {
+ // Found the brace body node.
+ break;
+ }
+ }
+
+ SmStructureNode* pBraceNodeTmp = pNode->GetParent();
+ if (!pBraceNodeTmp || pBraceNodeTmp->GetType() != SmNodeType::Brace) {
+ // Brace node is invalid.
+ return false;
+ }
+
+ SmBraceNode* pBraceNode = static_cast<SmBraceNode*>(pBraceNodeTmp);
+ SmMathSymbolNode* pClosingNode = pBraceNode->ClosingBrace();
+ if (!pClosingNode) {
+ // Couldn't get closing symbol node.
+ return false;
+ }
+
+ // Check if the closing brace matches eBracketType.
+ SmTokenType eClosingTokenType = pClosingNode->GetToken().eType;
+ switch (eBracketType) {
+ case SmBracketType::Round: if (eClosingTokenType != TRPARENT) { return false; } break;
+ case SmBracketType::Square: if (eClosingTokenType != TRBRACKET) { return false; } break;
+ case SmBracketType::Curly: if (eClosingTokenType != TRBRACE) { return false; } break;
+ default:
+ return false;
+ }
+
+ if (ppBraceNode) {
+ *ppBraceNode = pBraceNode;
+ }
+
+ return true;
+}
+
+void SmCursor::MoveAfterBracket(SmBraceNode* pBraceNode)
+{
+ mpPosition->CaretPos.pSelectedNode = pBraceNode;
+ mpPosition->CaretPos.nIndex = 1;
+ mpAnchor->CaretPos.pSelectedNode = pBraceNode;
+ mpAnchor->CaretPos.nIndex = 1;
+ RequestRepaint();
+}
+
+
+/////////////////////////////////////// SmNodeListParser
+
+SmNode* SmNodeListParser::Parse(SmNodeList* list){
+ pList = list;
+ //Delete error nodes
+ SmNodeList::iterator it = pList->begin();
+ while(it != pList->end()) {
+ if((*it)->GetType() == SmNodeType::Error){
+ //Delete and erase
+ delete *it;
+ it = pList->erase(it);
+ }else
+ ++it;
+ }
+ SmNode* retval = Expression();
+ pList = nullptr;
+ return retval;
+}
+
+SmNode* SmNodeListParser::Expression(){
+ SmNodeArray NodeArray;
+ //Accept as many relations as there is
+ while(Terminal())
+ NodeArray.push_back(Relation());
+
+ //Create SmExpressionNode, I hope SmToken() will do :)
+ SmStructureNode* pExpr = new SmExpressionNode(SmToken());
+ pExpr->SetSubNodes(std::move(NodeArray));
+ return pExpr;
+}
+
+SmNode* SmNodeListParser::Relation(){
+ //Read a sum
+ std::unique_ptr<SmNode> pLeft(Sum());
+ //While we have tokens and the next is a relation
+ while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
+ //Take the operator
+ std::unique_ptr<SmNode> pOper(Take());
+ //Find the right side of the relation
+ std::unique_ptr<SmNode> pRight(Sum());
+ //Create new SmBinHorNode
+ std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
+ pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
+ pLeft = std::move(pNewNode);
+ }
+ return pLeft.release();
+}
+
+SmNode* SmNodeListParser::Sum(){
+ //Read a product
+ std::unique_ptr<SmNode> pLeft(Product());
+ //While we have tokens and the next is a sum
+ while(Terminal() && IsSumOperator(Terminal()->GetToken())){
+ //Take the operator
+ std::unique_ptr<SmNode> pOper(Take());
+ //Find the right side of the sum
+ std::unique_ptr<SmNode> pRight(Product());
+ //Create new SmBinHorNode
+ std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
+ pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
+ pLeft = std::move(pNewNode);
+ }
+ return pLeft.release();
+}
+
+SmNode* SmNodeListParser::Product(){
+ //Read a Factor
+ std::unique_ptr<SmNode> pLeft(Factor());
+ //While we have tokens and the next is a product
+ while(Terminal() && IsProductOperator(Terminal()->GetToken())){
+ //Take the operator
+ std::unique_ptr<SmNode> pOper(Take());
+ //Find the right side of the operation
+ std::unique_ptr<SmNode> pRight(Factor());
+ //Create new SmBinHorNode
+ std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
+ pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
+ pLeft = std::move(pNewNode);
+ }
+ return pLeft.release();
+}
+
+SmNode* SmNodeListParser::Factor(){
+ //Read unary operations
+ if(!Terminal())
+ return Error();
+ //Take care of unary operators
+ else if(IsUnaryOperator(Terminal()->GetToken()))
+ {
+ SmStructureNode *pUnary = new SmUnHorNode(SmToken());
+ std::unique_ptr<SmNode> pOper(Terminal()),
+ pArg;
+
+ if(Next())
+ pArg.reset(Factor());
+ else
+ pArg.reset(Error());
+
+ pUnary->SetSubNodes(std::move(pOper), std::move(pArg));
+ return pUnary;
+ }
+ return Postfix();
+}
+
+SmNode* SmNodeListParser::Postfix(){
+ if(!Terminal())
+ return Error();
+ std::unique_ptr<SmNode> pArg;
+ if(IsPostfixOperator(Terminal()->GetToken()))
+ pArg.reset(Error());
+ else if(IsOperator(Terminal()->GetToken()))
+ return Error();
+ else
+ pArg.reset(Take());
+ while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
+ std::unique_ptr<SmStructureNode> pUnary(new SmUnHorNode(SmToken()) );
+ std::unique_ptr<SmNode> pOper(Take());
+ pUnary->SetSubNodes(std::move(pArg), std::move(pOper));
+ pArg = std::move(pUnary);
+ }
+ return pArg.release();
+}
+
+SmNode* SmNodeListParser::Error(){
+ return new SmErrorNode(SmToken());
+}
+
+bool SmNodeListParser::IsOperator(const SmToken &token) {
+ return IsRelationOperator(token) ||
+ IsSumOperator(token) ||
+ IsProductOperator(token) ||
+ IsUnaryOperator(token) ||
+ IsPostfixOperator(token);
+}
+
+bool SmNodeListParser::IsRelationOperator(const SmToken &token) {
+ return bool(token.nGroup & TG::Relation);
+}
+
+bool SmNodeListParser::IsSumOperator(const SmToken &token) {
+ return bool(token.nGroup & TG::Sum);
+}
+
+bool SmNodeListParser::IsProductOperator(const SmToken &token) {
+ return token.nGroup & TG::Product &&
+ token.eType != TWIDESLASH &&
+ token.eType != TWIDEBACKSLASH &&
+ token.eType != TUNDERBRACE &&
+ token.eType != TOVERBRACE &&
+ token.eType != TOVER;
+}
+
+bool SmNodeListParser::IsUnaryOperator(const SmToken &token) {
+ return token.nGroup & TG::UnOper &&
+ (token.eType == TPLUS ||
+ token.eType == TMINUS ||
+ token.eType == TPLUSMINUS ||
+ token.eType == TMINUSPLUS ||
+ token.eType == TNEG ||
+ token.eType == TUOPER);
+}
+
+bool SmNodeListParser::IsPostfixOperator(const SmToken &token) {
+ return token.eType == TFACT;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/dialog.cxx b/starmath/source/dialog.cxx
new file mode 100644
index 000000000..77fc823ba
--- /dev/null
+++ b/starmath/source/dialog.cxx
@@ -0,0 +1,2048 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <cassert>
+
+#include <comphelper/string.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <svtools/ctrltool.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/wall.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svx/charmap.hxx>
+#include <svx/ucsubset.hxx>
+
+#include <dialog.hxx>
+#include <starmath.hrc>
+#include <strings.hrc>
+#include <helpids.h>
+#include "cfgitem.hxx"
+#include <smmod.hxx>
+#include <symbol.hxx>
+#include <view.hxx>
+
+#include <algorithm>
+
+namespace
+{
+
+void lclGetSettingColors(Color& rBackgroundColor, Color& rTextColor)
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ if (rStyleSettings.GetHighContrastMode())
+ {
+ rBackgroundColor = rStyleSettings.GetFieldColor();
+ rTextColor = rStyleSettings.GetFieldTextColor();
+ }
+ else
+ {
+ rBackgroundColor = COL_WHITE;
+ rTextColor = COL_BLACK;
+ }
+}
+
+// Since it's better to set/query the FontStyle via its attributes rather
+// than via the StyleName we create a way to translate
+// Attribute <-> StyleName
+
+class SmFontStyles
+{
+ OUString aNormal;
+ OUString aBold;
+ OUString aItalic;
+ OUString aBoldItalic;
+
+public:
+ SmFontStyles();
+
+ static sal_uInt16 GetCount() { return 4; }
+ const OUString& GetStyleName(const vcl::Font& rFont) const;
+ const OUString& GetStyleName(sal_uInt16 nIdx) const;
+};
+
+} // end anonymous namespace
+
+SmFontStyles::SmFontStyles()
+ : aNormal(SmResId(RID_FONTREGULAR))
+ , aBold(SmResId(RID_FONTBOLD))
+ , aItalic(SmResId(RID_FONTITALIC))
+{
+ aBoldItalic = aBold;
+ aBoldItalic += ", ";
+ aBoldItalic += aItalic;
+}
+
+const OUString& SmFontStyles::GetStyleName(const vcl::Font& rFont) const
+{
+ //! compare also SmSpecialNode::Prepare
+ bool bBold = IsBold( rFont ),
+ bItalic = IsItalic( rFont );
+
+ if (bBold && bItalic)
+ return aBoldItalic;
+ else if (bItalic)
+ return aItalic;
+ else if (bBold)
+ return aBold;
+ return aNormal;
+}
+
+const OUString& SmFontStyles::GetStyleName( sal_uInt16 nIdx ) const
+{
+ // 0 = "normal", 1 = "italic",
+ // 2 = "bold", 3 = "bold italic"
+
+ assert( nIdx < GetCount() );
+ switch (nIdx)
+ {
+ case 0 : return aNormal;
+ case 1 : return aItalic;
+ case 2 : return aBold;
+ default: /*case 3:*/ return aBoldItalic;
+ }
+}
+
+static const SmFontStyles & GetFontStyles()
+{
+ static const SmFontStyles aImpl;
+ return aImpl;
+}
+
+void SetFontStyle(const OUString &rStyleName, vcl::Font &rFont)
+{
+ // Find index related to StyleName. For an empty StyleName it's assumed to be
+ // 0 (neither bold nor italic).
+ sal_uInt16 nIndex = 0;
+ if (!rStyleName.isEmpty())
+ {
+ sal_uInt16 i;
+ const SmFontStyles &rStyles = GetFontStyles();
+ for (i = 0; i < SmFontStyles::GetCount(); ++i)
+ if (rStyleName == rStyles.GetStyleName(i))
+ break;
+ assert(i < SmFontStyles::GetCount() && "style-name unknown");
+ nIndex = i;
+ }
+
+ rFont.SetItalic((nIndex & 0x1) ? ITALIC_NORMAL : ITALIC_NONE);
+ rFont.SetWeight((nIndex & 0x2) ? WEIGHT_BOLD : WEIGHT_NORMAL);
+}
+
+IMPL_LINK_NOARG(SmPrintOptionsTabPage, SizeButtonClickHdl, weld::ToggleButton&, void)
+{
+ m_xZoom->set_sensitive(m_xSizeZoomed->get_active());
+}
+
+SmPrintOptionsTabPage::SmPrintOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rOptions)
+ : SfxTabPage(pPage, pController, "modules/smath/ui/smathsettings.ui", "SmathSettings", &rOptions)
+ , m_xTitle(m_xBuilder->weld_check_button("title"))
+ , m_xText(m_xBuilder->weld_check_button("text"))
+ , m_xFrame(m_xBuilder->weld_check_button("frame"))
+ , m_xSizeNormal(m_xBuilder->weld_radio_button("sizenormal"))
+ , m_xSizeScaled(m_xBuilder->weld_radio_button("sizescaled"))
+ , m_xSizeZoomed(m_xBuilder->weld_radio_button("sizezoomed"))
+ , m_xZoom(m_xBuilder->weld_metric_spin_button("zoom", FieldUnit::PERCENT))
+ , m_xNoRightSpaces(m_xBuilder->weld_check_button("norightspaces"))
+ , m_xSaveOnlyUsedSymbols(m_xBuilder->weld_check_button("saveonlyusedsymbols"))
+ , m_xAutoCloseBrackets(m_xBuilder->weld_check_button("autoclosebrackets"))
+{
+ m_xSizeNormal->connect_toggled(LINK(this, SmPrintOptionsTabPage, SizeButtonClickHdl));
+ m_xSizeScaled->connect_toggled(LINK(this, SmPrintOptionsTabPage, SizeButtonClickHdl));
+ m_xSizeZoomed->connect_toggled(LINK(this, SmPrintOptionsTabPage, SizeButtonClickHdl));
+
+ Reset(&rOptions);
+}
+
+SmPrintOptionsTabPage::~SmPrintOptionsTabPage()
+{
+}
+
+bool SmPrintOptionsTabPage::FillItemSet(SfxItemSet* rSet)
+{
+ sal_uInt16 nPrintSize;
+ if (m_xSizeNormal->get_active())
+ nPrintSize = PRINT_SIZE_NORMAL;
+ else if (m_xSizeScaled->get_active())
+ nPrintSize = PRINT_SIZE_SCALED;
+ else
+ nPrintSize = PRINT_SIZE_ZOOMED;
+
+ rSet->Put(SfxUInt16Item(GetWhich(SID_PRINTSIZE), nPrintSize));
+ rSet->Put(SfxUInt16Item(GetWhich(SID_PRINTZOOM), sal::static_int_cast<sal_uInt16>(m_xZoom->get_value(FieldUnit::PERCENT))));
+ rSet->Put(SfxBoolItem(GetWhich(SID_PRINTTITLE), m_xTitle->get_active()));
+ rSet->Put(SfxBoolItem(GetWhich(SID_PRINTTEXT), m_xText->get_active()));
+ rSet->Put(SfxBoolItem(GetWhich(SID_PRINTFRAME), m_xFrame->get_active()));
+ rSet->Put(SfxBoolItem(GetWhich(SID_NO_RIGHT_SPACES), m_xNoRightSpaces->get_active()));
+ rSet->Put(SfxBoolItem(GetWhich(SID_SAVE_ONLY_USED_SYMBOLS), m_xSaveOnlyUsedSymbols->get_active()));
+ rSet->Put(SfxBoolItem(GetWhich(SID_AUTO_CLOSE_BRACKETS), m_xAutoCloseBrackets->get_active()));
+
+ return true;
+}
+
+void SmPrintOptionsTabPage::Reset(const SfxItemSet* rSet)
+{
+ SmPrintSize ePrintSize = static_cast<SmPrintSize>(static_cast<const SfxUInt16Item &>(rSet->Get(GetWhich(SID_PRINTSIZE))).GetValue());
+
+ m_xSizeNormal->set_active(ePrintSize == PRINT_SIZE_NORMAL);
+ m_xSizeScaled->set_active(ePrintSize == PRINT_SIZE_SCALED);
+ m_xSizeZoomed->set_active(ePrintSize == PRINT_SIZE_ZOOMED);
+
+ m_xZoom->set_sensitive(m_xSizeZoomed->get_active());
+
+ m_xZoom->set_value(static_cast<const SfxUInt16Item &>(rSet->Get(GetWhich(SID_PRINTZOOM))).GetValue(), FieldUnit::PERCENT);
+
+ m_xTitle->set_active(static_cast<const SfxBoolItem &>(rSet->Get(GetWhich(SID_PRINTTITLE))).GetValue());
+ m_xNoRightSpaces->set_active(static_cast<const SfxBoolItem &>(rSet->Get(GetWhich(SID_NO_RIGHT_SPACES))).GetValue());
+ m_xSaveOnlyUsedSymbols->set_active(static_cast<const SfxBoolItem &>(rSet->Get(GetWhich(SID_SAVE_ONLY_USED_SYMBOLS))).GetValue());
+ m_xAutoCloseBrackets->set_active(static_cast<const SfxBoolItem &>(rSet->Get(GetWhich(SID_AUTO_CLOSE_BRACKETS))).GetValue());
+}
+
+std::unique_ptr<SfxTabPage> SmPrintOptionsTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
+{
+ return std::make_unique<SmPrintOptionsTabPage>(pPage, pController, rSet);
+}
+
+void SmShowFont::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
+{
+ Color aBackColor;
+ Color aTextColor;
+ lclGetSettingColors(aBackColor, aTextColor);
+
+ rRenderContext.SetBackground(Wallpaper(aBackColor));
+
+ vcl::Font aFont(maFont);
+ aFont.SetFontSize(Size(0, 24 * rRenderContext.GetDPIScaleFactor()));
+ aFont.SetAlignment(ALIGN_TOP);
+ rRenderContext.SetFont(aFont);
+ rRenderContext.SetTextColor(aTextColor);
+
+ OUString sText(rRenderContext.GetFont().GetFamilyName());
+ Size aTextSize(rRenderContext.GetTextWidth(sText), rRenderContext.GetTextHeight());
+
+ rRenderContext.DrawText(Point((rRenderContext.GetOutputSize().Width() - aTextSize.Width()) / 2,
+ (rRenderContext.GetOutputSize().Height() - aTextSize.Height()) / 2), sText);
+}
+
+void SmShowFont::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(111 , 31), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+}
+
+void SmShowFont::SetFont(const vcl::Font& rFont)
+{
+ maFont = rFont;
+ Invalidate();
+}
+
+IMPL_LINK( SmFontDialog, FontSelectHdl, weld::ComboBox&, rComboBox, void )
+{
+ maFont.SetFamilyName(rComboBox.get_active_text());
+ m_aShowFont.SetFont(maFont);
+}
+
+IMPL_LINK_NOARG(SmFontDialog, AttrChangeHdl, weld::ToggleButton&, void)
+{
+ if (m_xBoldCheckBox->get_active())
+ maFont.SetWeight(WEIGHT_BOLD);
+ else
+ maFont.SetWeight(WEIGHT_NORMAL);
+
+ if (m_xItalicCheckBox->get_active())
+ maFont.SetItalic(ITALIC_NORMAL);
+ else
+ maFont.SetItalic(ITALIC_NONE);
+
+ m_aShowFont.SetFont(maFont);
+}
+
+void SmFontDialog::SetFont(const vcl::Font &rFont)
+{
+ maFont = rFont;
+
+ m_xFontBox->set_active_text(maFont.GetFamilyName());
+ m_xBoldCheckBox->set_active(IsBold(maFont));
+ m_xItalicCheckBox->set_active(IsItalic(maFont));
+ m_aShowFont.SetFont(maFont);
+}
+
+SmFontDialog::SmFontDialog(weld::Window * pParent, OutputDevice *pFntListDevice, bool bHideCheckboxes)
+ : GenericDialogController(pParent, "modules/smath/ui/fontdialog.ui", "FontDialog")
+ , m_xFontBox(m_xBuilder->weld_entry_tree_view("fontgrid", "font", "fonts"))
+ , m_xAttrFrame(m_xBuilder->weld_widget("attrframe"))
+ , m_xBoldCheckBox(m_xBuilder->weld_check_button("bold"))
+ , m_xItalicCheckBox(m_xBuilder->weld_check_button("italic"))
+ , m_xShowFont(new weld::CustomWeld(*m_xBuilder, "preview", m_aShowFont))
+{
+ m_xFontBox->set_height_request_by_rows(8);
+
+ {
+ weld::WaitObject aWait(pParent);
+
+ FontList aFontList( pFntListDevice );
+
+ sal_uInt16 nCount = aFontList.GetFontNameCount();
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ m_xFontBox->append_text(aFontList.GetFontName(i).GetFamilyName());
+ }
+ maFont.SetFontSize(Size(0, 24));
+ maFont.SetWeight(WEIGHT_NORMAL);
+ maFont.SetItalic(ITALIC_NONE);
+ maFont.SetFamily(FAMILY_DONTKNOW);
+ maFont.SetPitch(PITCH_DONTKNOW);
+ maFont.SetCharSet(RTL_TEXTENCODING_DONTKNOW);
+ maFont.SetTransparent(true);
+ }
+
+ m_xFontBox->connect_changed(LINK(this, SmFontDialog, FontSelectHdl));
+ m_xBoldCheckBox->connect_toggled(LINK(this, SmFontDialog, AttrChangeHdl));
+ m_xItalicCheckBox->connect_toggled(LINK(this, SmFontDialog, AttrChangeHdl));
+
+ if (bHideCheckboxes)
+ {
+ m_xBoldCheckBox->set_active(false);
+ m_xBoldCheckBox->set_sensitive(false);
+ m_xItalicCheckBox->set_active(false);
+ m_xItalicCheckBox->set_sensitive(false);
+ m_xAttrFrame->hide();
+ }
+}
+
+SmFontDialog::~SmFontDialog()
+{
+}
+
+namespace {
+
+class SaveDefaultsQuery : public weld::MessageDialogController
+{
+public:
+ explicit SaveDefaultsQuery(weld::Widget* pParent)
+ : MessageDialogController(pParent, "modules/smath/ui/savedefaultsdialog.ui",
+ "SaveDefaultsDialog")
+ {
+ }
+};
+
+}
+
+IMPL_LINK_NOARG( SmFontSizeDialog, DefaultButtonClickHdl, weld::Button&, void )
+{
+ SaveDefaultsQuery aQuery(m_xDialog.get());
+ if (aQuery.run() == RET_YES)
+ {
+ SmModule *pp = SM_MOD();
+ SmFormat aFmt( pp->GetConfig()->GetStandardFormat() );
+ WriteTo( aFmt );
+ pp->GetConfig()->SetStandardFormat( aFmt );
+ }
+}
+
+SmFontSizeDialog::SmFontSizeDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/smath/ui/fontsizedialog.ui", "FontSizeDialog")
+ , m_xBaseSize(m_xBuilder->weld_metric_spin_button("spinB_baseSize", FieldUnit::POINT))
+ , m_xTextSize(m_xBuilder->weld_metric_spin_button("spinB_text", FieldUnit::PERCENT))
+ , m_xIndexSize(m_xBuilder->weld_metric_spin_button("spinB_index", FieldUnit::PERCENT))
+ , m_xFunctionSize(m_xBuilder->weld_metric_spin_button("spinB_function", FieldUnit::PERCENT))
+ , m_xOperatorSize(m_xBuilder->weld_metric_spin_button("spinB_operator", FieldUnit::PERCENT))
+ , m_xBorderSize(m_xBuilder->weld_metric_spin_button("spinB_limit", FieldUnit::PERCENT))
+ , m_xDefaultButton(m_xBuilder->weld_button("default"))
+{
+ m_xDefaultButton->connect_clicked(LINK(this, SmFontSizeDialog, DefaultButtonClickHdl));
+}
+
+SmFontSizeDialog::~SmFontSizeDialog()
+{
+}
+
+void SmFontSizeDialog::ReadFrom(const SmFormat &rFormat)
+{
+ //! watch out: round properly!
+ m_xBaseSize->set_value( SmRoundFraction(
+ Sm100th_mmToPts( rFormat.GetBaseSize().Height() ) ), FieldUnit::NONE );
+
+ m_xTextSize->set_value( rFormat.GetRelSize(SIZ_TEXT), FieldUnit::NONE );
+ m_xIndexSize->set_value( rFormat.GetRelSize(SIZ_INDEX), FieldUnit::NONE );
+ m_xFunctionSize->set_value( rFormat.GetRelSize(SIZ_FUNCTION), FieldUnit::NONE );
+ m_xOperatorSize->set_value( rFormat.GetRelSize(SIZ_OPERATOR), FieldUnit::NONE );
+ m_xBorderSize->set_value( rFormat.GetRelSize(SIZ_LIMITS), FieldUnit::NONE );
+}
+
+void SmFontSizeDialog::WriteTo(SmFormat &rFormat) const
+{
+ rFormat.SetBaseSize( Size(0, SmPtsTo100th_mm( static_cast< long >(m_xBaseSize->get_value(FieldUnit::NONE)))) );
+
+ rFormat.SetRelSize(SIZ_TEXT, sal::static_int_cast<sal_uInt16>(m_xTextSize->get_value(FieldUnit::NONE)));
+ rFormat.SetRelSize(SIZ_INDEX, sal::static_int_cast<sal_uInt16>(m_xIndexSize->get_value(FieldUnit::NONE)));
+ rFormat.SetRelSize(SIZ_FUNCTION, sal::static_int_cast<sal_uInt16>(m_xFunctionSize->get_value(FieldUnit::NONE)));
+ rFormat.SetRelSize(SIZ_OPERATOR, sal::static_int_cast<sal_uInt16>(m_xOperatorSize->get_value(FieldUnit::NONE)));
+ rFormat.SetRelSize(SIZ_LIMITS, sal::static_int_cast<sal_uInt16>(m_xBorderSize->get_value(FieldUnit::NONE)));
+
+ const Size aTmp (rFormat.GetBaseSize());
+ for (sal_uInt16 i = FNT_BEGIN; i <= FNT_END; i++)
+ rFormat.SetFontSize(i, aTmp);
+
+ rFormat.RequestApplyChanges();
+}
+
+IMPL_LINK(SmFontTypeDialog, MenuSelectHdl, const OString&, rIdent, void)
+{
+ SmFontPickListBox *pActiveListBox;
+
+ bool bHideCheckboxes = false;
+ if (rIdent == "variables")
+ pActiveListBox = m_xVariableFont.get();
+ else if (rIdent == "functions")
+ pActiveListBox = m_xFunctionFont.get();
+ else if (rIdent == "numbers")
+ pActiveListBox = m_xNumberFont.get();
+ else if (rIdent == "text")
+ pActiveListBox = m_xTextFont.get();
+ else if (rIdent == "serif")
+ {
+ pActiveListBox = m_xSerifFont.get();
+ bHideCheckboxes = true;
+ }
+ else if (rIdent == "sansserif")
+ {
+ pActiveListBox = m_xSansFont.get();
+ bHideCheckboxes = true;
+ }
+ else if (rIdent == "fixedwidth")
+ {
+ pActiveListBox = m_xFixedFont.get();
+ bHideCheckboxes = true;
+ }
+ else
+ pActiveListBox = nullptr;
+
+ if (pActiveListBox)
+ {
+ SmFontDialog aFontDialog(m_xDialog.get(), pFontListDev, bHideCheckboxes);
+
+ pActiveListBox->WriteTo(aFontDialog);
+ if (aFontDialog.run() == RET_OK)
+ pActiveListBox->ReadFrom(aFontDialog);
+ }
+}
+
+IMPL_LINK_NOARG(SmFontTypeDialog, DefaultButtonClickHdl, weld::Button&, void)
+{
+ SaveDefaultsQuery aQuery(m_xDialog.get());
+ if (aQuery.run() == RET_YES)
+ {
+ SmModule *pp = SM_MOD();
+ SmFormat aFmt( pp->GetConfig()->GetStandardFormat() );
+ WriteTo( aFmt );
+ pp->GetConfig()->SetStandardFormat( aFmt, true );
+ }
+}
+
+SmFontTypeDialog::SmFontTypeDialog(weld::Window* pParent, OutputDevice *pFntListDevice)
+ : GenericDialogController(pParent, "modules/smath/ui/fonttypedialog.ui", "FontsDialog")
+ , pFontListDev(pFntListDevice)
+ , m_xVariableFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("variableCB")))
+ , m_xFunctionFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("functionCB")))
+ , m_xNumberFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("numberCB")))
+ , m_xTextFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("textCB")))
+ , m_xSerifFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("serifCB")))
+ , m_xSansFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("sansCB")))
+ , m_xFixedFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("fixedCB")))
+ , m_xMenuButton(m_xBuilder->weld_menu_button("modify"))
+ , m_xDefaultButton(m_xBuilder->weld_button("default"))
+{
+ m_xDefaultButton->connect_clicked(LINK(this, SmFontTypeDialog, DefaultButtonClickHdl));
+ m_xMenuButton->connect_selected(LINK(this, SmFontTypeDialog, MenuSelectHdl));
+}
+
+SmFontTypeDialog::~SmFontTypeDialog()
+{
+}
+
+void SmFontTypeDialog::ReadFrom(const SmFormat &rFormat)
+{
+ SmModule *pp = SM_MOD();
+
+ *m_xVariableFont = pp->GetConfig()->GetFontPickList(FNT_VARIABLE);
+ *m_xFunctionFont = pp->GetConfig()->GetFontPickList(FNT_FUNCTION);
+ *m_xNumberFont = pp->GetConfig()->GetFontPickList(FNT_NUMBER);
+ *m_xTextFont = pp->GetConfig()->GetFontPickList(FNT_TEXT);
+ *m_xSerifFont = pp->GetConfig()->GetFontPickList(FNT_SERIF);
+ *m_xSansFont = pp->GetConfig()->GetFontPickList(FNT_SANS);
+ *m_xFixedFont = pp->GetConfig()->GetFontPickList(FNT_FIXED);
+
+ m_xVariableFont->Insert( rFormat.GetFont(FNT_VARIABLE) );
+ m_xFunctionFont->Insert( rFormat.GetFont(FNT_FUNCTION) );
+ m_xNumberFont->Insert( rFormat.GetFont(FNT_NUMBER) );
+ m_xTextFont->Insert( rFormat.GetFont(FNT_TEXT) );
+ m_xSerifFont->Insert( rFormat.GetFont(FNT_SERIF) );
+ m_xSansFont->Insert( rFormat.GetFont(FNT_SANS) );
+ m_xFixedFont->Insert( rFormat.GetFont(FNT_FIXED) );
+}
+
+
+void SmFontTypeDialog::WriteTo(SmFormat &rFormat) const
+{
+ SmModule *pp = SM_MOD();
+
+ pp->GetConfig()->GetFontPickList(FNT_VARIABLE) = *m_xVariableFont;
+ pp->GetConfig()->GetFontPickList(FNT_FUNCTION) = *m_xFunctionFont;
+ pp->GetConfig()->GetFontPickList(FNT_NUMBER) = *m_xNumberFont;
+ pp->GetConfig()->GetFontPickList(FNT_TEXT) = *m_xTextFont;
+ pp->GetConfig()->GetFontPickList(FNT_SERIF) = *m_xSerifFont;
+ pp->GetConfig()->GetFontPickList(FNT_SANS) = *m_xSansFont;
+ pp->GetConfig()->GetFontPickList(FNT_FIXED) = *m_xFixedFont;
+
+ rFormat.SetFont( FNT_VARIABLE, m_xVariableFont->Get() );
+ rFormat.SetFont( FNT_FUNCTION, m_xFunctionFont->Get() );
+ rFormat.SetFont( FNT_NUMBER, m_xNumberFont->Get() );
+ rFormat.SetFont( FNT_TEXT, m_xTextFont->Get() );
+ rFormat.SetFont( FNT_SERIF, m_xSerifFont->Get() );
+ rFormat.SetFont( FNT_SANS, m_xSansFont->Get() );
+ rFormat.SetFont( FNT_FIXED, m_xFixedFont->Get() );
+
+ rFormat.RequestApplyChanges();
+}
+
+/**************************************************************************/
+
+namespace {
+
+struct FieldMinMax
+{
+ sal_uInt16 nMin, nMax;
+};
+
+}
+
+// Data for min and max values of the 4 metric fields
+// for each of the 10 categories
+static const FieldMinMax pMinMaxData[10][4] =
+{
+ // 0
+ {{ 0, 200 }, { 0, 200 }, { 0, 100 }, { 0, 0 }},
+ // 1
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 0 }},
+ // 2
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 0 }},
+ // 3
+ {{ 0, 100 }, { 1, 100 }, { 0, 0 }, { 0, 0 }},
+ // 4
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 0 }},
+ // 5
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 100 }},
+ // 6
+ {{ 0, 300 }, { 0, 300 }, { 0, 0 }, { 0, 0 }},
+ // 7
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 0 }},
+ // 8
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 0 }},
+ // 9
+ {{ 0, 10000 }, { 0, 10000 }, { 0, 10000 }, { 0, 10000 }}
+};
+
+SmCategoryDesc::SmCategoryDesc(weld::Builder& rBuilder, sal_uInt16 nCategoryIdx)
+{
+ ++nCategoryIdx;
+ std::unique_ptr<weld::Label> xTitle(rBuilder.weld_label(OString::number(nCategoryIdx)+"title"));
+ if (xTitle)
+ {
+ Name = xTitle->get_label();
+ }
+ for (int i = 0; i < 4; ++i)
+ {
+ std::unique_ptr<weld::Label> xLabel(rBuilder.weld_label(OString::number(nCategoryIdx)+"label"+OString::number(i+1)));
+
+ if (xLabel)
+ {
+ Strings[i] = xLabel->get_label();
+ Graphics[i] = rBuilder.weld_widget(OString::number(nCategoryIdx)+"image"+OString::number(i+1));
+ }
+ else
+ {
+ Strings[i].clear();
+ Graphics[i].reset();
+ }
+
+ const FieldMinMax& rMinMax = pMinMaxData[ nCategoryIdx-1 ][i];
+ Value[i] = Minimum[i] = rMinMax.nMin;
+ Maximum[i] = rMinMax.nMax;
+ }
+}
+
+SmCategoryDesc::~SmCategoryDesc()
+{
+}
+
+/**************************************************************************/
+
+IMPL_LINK( SmDistanceDialog, GetFocusHdl, weld::Widget&, rControl, void )
+{
+ if (!m_xCategories[nActiveCategory])
+ return;
+
+ sal_uInt16 i;
+
+ if (&rControl == &m_xMetricField1->get_widget())
+ i = 0;
+ else if (&rControl == &m_xMetricField2->get_widget())
+ i = 1;
+ else if (&rControl == &m_xMetricField3->get_widget())
+ i = 2;
+ else if (&rControl == &m_xMetricField4->get_widget())
+ i = 3;
+ else
+ return;
+ if (m_pCurrentImage)
+ m_pCurrentImage->hide();
+ m_pCurrentImage = m_xCategories[nActiveCategory]->GetGraphic(i);
+ m_pCurrentImage->show();
+}
+
+IMPL_LINK(SmDistanceDialog, MenuSelectHdl, const OString&, rId, void)
+{
+ assert(rId.startsWith("menuitem"));
+ SetCategory(rId.replaceFirst("menuitem", "").toInt32() - 1);
+}
+
+IMPL_LINK_NOARG( SmDistanceDialog, DefaultButtonClickHdl, weld::Button&, void )
+{
+ SaveDefaultsQuery aQuery(m_xDialog.get());
+ if (aQuery.run() == RET_YES)
+ {
+ SmModule *pp = SM_MOD();
+ SmFormat aFmt( pp->GetConfig()->GetStandardFormat() );
+ WriteTo( aFmt );
+ pp->GetConfig()->SetStandardFormat( aFmt );
+ }
+}
+
+IMPL_LINK( SmDistanceDialog, CheckBoxClickHdl, weld::ToggleButton&, rCheckBox, void )
+{
+ if (&rCheckBox == m_xCheckBox1.get())
+ {
+ bool bChecked = m_xCheckBox1->get_active();
+ m_xFixedText4->set_sensitive( bChecked );
+ m_xMetricField4->set_sensitive( bChecked );
+ }
+}
+
+void SmDistanceDialog::SetCategory(sal_uInt16 nCategory)
+{
+ assert(nCategory < NOCATEGORIES && "Sm: wrong category number in SmDistanceDialog");
+
+ // array to convert category- and metricfield-number in help ids.
+ // 0 is used in case of unused combinations.
+ assert(NOCATEGORIES == 10 && "Sm : array doesn't fit into the number of categories");
+ static const char * aCatMf2Hid[10][4] =
+ {
+ { HID_SMA_DEFAULT_DIST, HID_SMA_LINE_DIST, HID_SMA_ROOT_DIST, nullptr },
+ { HID_SMA_SUP_DIST, HID_SMA_SUB_DIST , nullptr, nullptr },
+ { HID_SMA_NUMERATOR_DIST, HID_SMA_DENOMINATOR_DIST, nullptr, nullptr },
+ { HID_SMA_FRACLINE_EXCWIDTH, HID_SMA_FRACLINE_LINEWIDTH, nullptr, nullptr },
+ { HID_SMA_UPPERLIMIT_DIST, HID_SMA_LOWERLIMIT_DIST, nullptr, nullptr },
+ { HID_SMA_BRACKET_EXCHEIGHT, HID_SMA_BRACKET_DIST, nullptr, HID_SMA_BRACKET_EXCHEIGHT2 },
+ { HID_SMA_MATRIXROW_DIST, HID_SMA_MATRIXCOL_DIST, nullptr, nullptr },
+ { HID_SMA_ATTRIBUT_DIST, HID_SMA_INTERATTRIBUT_DIST, nullptr, nullptr },
+ { HID_SMA_OPERATOR_EXCHEIGHT, HID_SMA_OPERATOR_DIST, nullptr, nullptr },
+ { HID_SMA_LEFTBORDER_DIST, HID_SMA_RIGHTBORDER_DIST, HID_SMA_UPPERBORDER_DIST, HID_SMA_LOWERBORDER_DIST }
+ };
+
+ // array to help iterate over the controls
+ std::pair<weld::Label*, weld::MetricSpinButton*> const aWin[4] =
+ {
+ { m_xFixedText1.get(), m_xMetricField1.get() },
+ { m_xFixedText2.get(), m_xMetricField2.get() },
+ { m_xFixedText3.get(), m_xMetricField3.get() },
+ { m_xFixedText4.get(), m_xMetricField4.get() }
+ };
+
+ SmCategoryDesc *pCat;
+
+ // remember the (maybe new) settings of the active SmCategoryDesc
+ // before switching to the new one
+ if (nActiveCategory != CATEGORY_NONE)
+ {
+ pCat = m_xCategories[nActiveCategory].get();
+ pCat->SetValue(0, sal::static_int_cast<sal_uInt16>(m_xMetricField1->get_value(FieldUnit::NONE)));
+ pCat->SetValue(1, sal::static_int_cast<sal_uInt16>(m_xMetricField2->get_value(FieldUnit::NONE)));
+ pCat->SetValue(2, sal::static_int_cast<sal_uInt16>(m_xMetricField3->get_value(FieldUnit::NONE)));
+ pCat->SetValue(3, sal::static_int_cast<sal_uInt16>(m_xMetricField4->get_value(FieldUnit::NONE)));
+
+ if (nActiveCategory == 5)
+ bScaleAllBrackets = m_xCheckBox1->get_active();
+
+ m_xMenuButton->set_item_active("menuitem" + OString::number(nActiveCategory + 1), false);
+ }
+
+ // activation/deactivation of the associated controls depending on the chosen category
+ bool bActive;
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ weld::Label *pFT = aWin[i].first;
+ weld::MetricSpinButton *pMF = aWin[i].second;
+
+ // To determine which Controls should be active, the existence
+ // of an associated HelpID is checked
+ bActive = aCatMf2Hid[nCategory][i] != nullptr;
+
+ pFT->set_visible(bActive);
+ pFT->set_sensitive(bActive);
+ pMF->set_visible(bActive);
+ pMF->set_sensitive(bActive);
+
+ // set measurement unit and number of decimal places
+ FieldUnit eUnit;
+ sal_uInt16 nDigits;
+ if (nCategory < 9)
+ {
+ eUnit = FieldUnit::PERCENT;
+ nDigits = 0;
+ }
+ else
+ {
+ eUnit = FieldUnit::MM_100TH;
+ nDigits = 2;
+ }
+ pMF->set_unit(eUnit); // changes the value
+ pMF->set_digits(nDigits);
+
+ if (bActive)
+ {
+ pCat = m_xCategories[nCategory].get();
+ pFT->set_label(pCat->GetString(i));
+
+ pMF->set_range(pCat->GetMinimum(i), pCat->GetMaximum(i), FieldUnit::NONE);
+ pMF->set_value(pCat->GetValue(i), FieldUnit::NONE);
+
+ pMF->set_help_id(aCatMf2Hid[nCategory][i]);
+ }
+ }
+ // activate the CheckBox and the associated MetricField if we're dealing with the brackets menu
+ bActive = nCategory == 5;
+ m_xCheckBox1->set_visible(bActive);
+ m_xCheckBox1->set_sensitive(bActive);
+ if (bActive)
+ {
+ m_xCheckBox1->set_active(bScaleAllBrackets);
+
+ bool bChecked = m_xCheckBox1->get_active();
+ m_xFixedText4->set_sensitive( bChecked );
+ m_xMetricField4->set_sensitive( bChecked );
+ }
+
+ m_xMenuButton->set_item_active("menuitem" + OString::number(nCategory + 1), true);
+ m_xFrame->set_label(m_xCategories[nCategory]->GetName());
+
+ nActiveCategory = nCategory;
+
+ m_xMetricField1->grab_focus();
+}
+
+SmDistanceDialog::SmDistanceDialog(weld::Window *pParent)
+ : GenericDialogController(pParent, "modules/smath/ui/spacingdialog.ui", "SpacingDialog")
+ , m_xFrame(m_xBuilder->weld_frame("template"))
+ , m_xFixedText1(m_xBuilder->weld_label("label1"))
+ , m_xMetricField1(m_xBuilder->weld_metric_spin_button("spinbutton1", FieldUnit::CM))
+ , m_xFixedText2(m_xBuilder->weld_label("label2"))
+ , m_xMetricField2(m_xBuilder->weld_metric_spin_button("spinbutton2", FieldUnit::CM))
+ , m_xFixedText3(m_xBuilder->weld_label("label3"))
+ , m_xMetricField3(m_xBuilder->weld_metric_spin_button("spinbutton3", FieldUnit::CM))
+ , m_xCheckBox1(m_xBuilder->weld_check_button("checkbutton"))
+ , m_xFixedText4(m_xBuilder->weld_label("label4"))
+ , m_xMetricField4(m_xBuilder->weld_metric_spin_button("spinbutton4", FieldUnit::CM))
+ , m_xMenuButton(m_xBuilder->weld_menu_button("category"))
+ , m_xDefaultButton(m_xBuilder->weld_button("default"))
+ , m_xBitmap(m_xBuilder->weld_widget("image"))
+ , m_pCurrentImage(m_xBitmap.get())
+{
+ for (sal_uInt16 i = 0; i < NOCATEGORIES; ++i)
+ m_xCategories[i].reset( new SmCategoryDesc(*m_xBuilder, i) );
+ nActiveCategory = CATEGORY_NONE;
+ bScaleAllBrackets = false;
+
+ m_xMetricField1->connect_focus_in(LINK(this, SmDistanceDialog, GetFocusHdl));
+ m_xMetricField2->connect_focus_in(LINK(this, SmDistanceDialog, GetFocusHdl));
+ m_xMetricField3->connect_focus_in(LINK(this, SmDistanceDialog, GetFocusHdl));
+ m_xMetricField4->connect_focus_in(LINK(this, SmDistanceDialog, GetFocusHdl));
+ m_xCheckBox1->connect_toggled(LINK(this, SmDistanceDialog, CheckBoxClickHdl));
+ m_xMenuButton->connect_selected(LINK(this, SmDistanceDialog, MenuSelectHdl));
+ m_xDefaultButton->connect_clicked(LINK(this, SmDistanceDialog, DefaultButtonClickHdl));
+
+ //set the initial size, with max visible widgets visible, as preferred size
+ m_xDialog->set_size_request(-1, m_xDialog->get_preferred_size().Height());
+}
+
+SmDistanceDialog::~SmDistanceDialog()
+{
+}
+
+void SmDistanceDialog::ReadFrom(const SmFormat &rFormat)
+{
+ m_xCategories[0]->SetValue(0, rFormat.GetDistance(DIS_HORIZONTAL));
+ m_xCategories[0]->SetValue(1, rFormat.GetDistance(DIS_VERTICAL));
+ m_xCategories[0]->SetValue(2, rFormat.GetDistance(DIS_ROOT));
+ m_xCategories[1]->SetValue(0, rFormat.GetDistance(DIS_SUPERSCRIPT));
+ m_xCategories[1]->SetValue(1, rFormat.GetDistance(DIS_SUBSCRIPT));
+ m_xCategories[2]->SetValue(0, rFormat.GetDistance(DIS_NUMERATOR));
+ m_xCategories[2]->SetValue(1, rFormat.GetDistance(DIS_DENOMINATOR));
+ m_xCategories[3]->SetValue(0, rFormat.GetDistance(DIS_FRACTION));
+ m_xCategories[3]->SetValue(1, rFormat.GetDistance(DIS_STROKEWIDTH));
+ m_xCategories[4]->SetValue(0, rFormat.GetDistance(DIS_UPPERLIMIT));
+ m_xCategories[4]->SetValue(1, rFormat.GetDistance(DIS_LOWERLIMIT));
+ m_xCategories[5]->SetValue(0, rFormat.GetDistance(DIS_BRACKETSIZE));
+ m_xCategories[5]->SetValue(1, rFormat.GetDistance(DIS_BRACKETSPACE));
+ m_xCategories[5]->SetValue(3, rFormat.GetDistance(DIS_NORMALBRACKETSIZE));
+ m_xCategories[6]->SetValue(0, rFormat.GetDistance(DIS_MATRIXROW));
+ m_xCategories[6]->SetValue(1, rFormat.GetDistance(DIS_MATRIXCOL));
+ m_xCategories[7]->SetValue(0, rFormat.GetDistance(DIS_ORNAMENTSIZE));
+ m_xCategories[7]->SetValue(1, rFormat.GetDistance(DIS_ORNAMENTSPACE));
+ m_xCategories[8]->SetValue(0, rFormat.GetDistance(DIS_OPERATORSIZE));
+ m_xCategories[8]->SetValue(1, rFormat.GetDistance(DIS_OPERATORSPACE));
+ m_xCategories[9]->SetValue(0, rFormat.GetDistance(DIS_LEFTSPACE));
+ m_xCategories[9]->SetValue(1, rFormat.GetDistance(DIS_RIGHTSPACE));
+ m_xCategories[9]->SetValue(2, rFormat.GetDistance(DIS_TOPSPACE));
+ m_xCategories[9]->SetValue(3, rFormat.GetDistance(DIS_BOTTOMSPACE));
+
+ bScaleAllBrackets = rFormat.IsScaleNormalBrackets();
+
+ // force update (even of category 0) by setting nActiveCategory to a
+ // non-existent category number
+ nActiveCategory = CATEGORY_NONE;
+ SetCategory(0);
+}
+
+
+void SmDistanceDialog::WriteTo(SmFormat &rFormat) /*const*/
+{
+ // TODO can they actually be different?
+ // if that's not the case 'const' could be used above!
+ SetCategory(nActiveCategory);
+
+ rFormat.SetDistance( DIS_HORIZONTAL, m_xCategories[0]->GetValue(0) );
+ rFormat.SetDistance( DIS_VERTICAL, m_xCategories[0]->GetValue(1) );
+ rFormat.SetDistance( DIS_ROOT, m_xCategories[0]->GetValue(2) );
+ rFormat.SetDistance( DIS_SUPERSCRIPT, m_xCategories[1]->GetValue(0) );
+ rFormat.SetDistance( DIS_SUBSCRIPT, m_xCategories[1]->GetValue(1) );
+ rFormat.SetDistance( DIS_NUMERATOR, m_xCategories[2]->GetValue(0) );
+ rFormat.SetDistance( DIS_DENOMINATOR, m_xCategories[2]->GetValue(1) );
+ rFormat.SetDistance( DIS_FRACTION, m_xCategories[3]->GetValue(0) );
+ rFormat.SetDistance( DIS_STROKEWIDTH, m_xCategories[3]->GetValue(1) );
+ rFormat.SetDistance( DIS_UPPERLIMIT, m_xCategories[4]->GetValue(0) );
+ rFormat.SetDistance( DIS_LOWERLIMIT, m_xCategories[4]->GetValue(1) );
+ rFormat.SetDistance( DIS_BRACKETSIZE, m_xCategories[5]->GetValue(0) );
+ rFormat.SetDistance( DIS_BRACKETSPACE, m_xCategories[5]->GetValue(1) );
+ rFormat.SetDistance( DIS_MATRIXROW, m_xCategories[6]->GetValue(0) );
+ rFormat.SetDistance( DIS_MATRIXCOL, m_xCategories[6]->GetValue(1) );
+ rFormat.SetDistance( DIS_ORNAMENTSIZE, m_xCategories[7]->GetValue(0) );
+ rFormat.SetDistance( DIS_ORNAMENTSPACE, m_xCategories[7]->GetValue(1) );
+ rFormat.SetDistance( DIS_OPERATORSIZE, m_xCategories[8]->GetValue(0) );
+ rFormat.SetDistance( DIS_OPERATORSPACE, m_xCategories[8]->GetValue(1) );
+ rFormat.SetDistance( DIS_LEFTSPACE, m_xCategories[9]->GetValue(0) );
+ rFormat.SetDistance( DIS_RIGHTSPACE, m_xCategories[9]->GetValue(1) );
+ rFormat.SetDistance( DIS_TOPSPACE, m_xCategories[9]->GetValue(2) );
+ rFormat.SetDistance( DIS_BOTTOMSPACE, m_xCategories[9]->GetValue(3) );
+ rFormat.SetDistance( DIS_NORMALBRACKETSIZE, m_xCategories[5]->GetValue(3) );
+
+ rFormat.SetScaleNormalBrackets( bScaleAllBrackets );
+
+ rFormat.RequestApplyChanges();
+}
+
+IMPL_LINK_NOARG( SmAlignDialog, DefaultButtonClickHdl, weld::Button&, void )
+{
+ SaveDefaultsQuery aQuery(m_xDialog.get());
+ if (aQuery.run() == RET_YES)
+ {
+ SmModule *pp = SM_MOD();
+ SmFormat aFmt( pp->GetConfig()->GetStandardFormat() );
+ WriteTo( aFmt );
+ pp->GetConfig()->SetStandardFormat( aFmt );
+ }
+}
+
+SmAlignDialog::SmAlignDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/smath/ui/alignmentdialog.ui", "AlignmentDialog")
+ , m_xLeft(m_xBuilder->weld_radio_button("left"))
+ , m_xCenter(m_xBuilder->weld_radio_button("center"))
+ , m_xRight(m_xBuilder->weld_radio_button("right"))
+ , m_xDefaultButton(m_xBuilder->weld_button("default"))
+{
+ m_xDefaultButton->connect_clicked(LINK(this, SmAlignDialog, DefaultButtonClickHdl));
+}
+
+SmAlignDialog::~SmAlignDialog()
+{
+}
+
+void SmAlignDialog::ReadFrom(const SmFormat &rFormat)
+{
+ switch (rFormat.GetHorAlign())
+ {
+ case SmHorAlign::Left:
+ m_xLeft->set_active(true);
+ break;
+ case SmHorAlign::Center:
+ m_xCenter->set_active(true);
+ break;
+ case SmHorAlign::Right:
+ m_xRight->set_active(true);
+ break;
+ }
+}
+
+void SmAlignDialog::WriteTo(SmFormat &rFormat) const
+{
+ if (m_xLeft->get_active())
+ rFormat.SetHorAlign(SmHorAlign::Left);
+ else if (m_xRight->get_active())
+ rFormat.SetHorAlign(SmHorAlign::Right);
+ else
+ rFormat.SetHorAlign(SmHorAlign::Center);
+
+ rFormat.RequestApplyChanges();
+}
+
+SmShowSymbolSet::SmShowSymbolSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow)
+ : nLen(0)
+ , nRows(0)
+ , nColumns(0)
+ , nXOffset(0)
+ , nYOffset(0)
+ , nSelectSymbol(SYMBOL_NONE)
+ , m_xScrolledWindow(std::move(pScrolledWindow))
+{
+ m_xScrolledWindow->set_user_managed_scrolling();
+ m_xScrolledWindow->connect_vadjustment_changed(LINK(this, SmShowSymbolSet, ScrollHdl));
+}
+
+Point SmShowSymbolSet::OffsetPoint(const Point &rPoint) const
+{
+ return Point(rPoint.X() + nXOffset, rPoint.Y() + nYOffset);
+}
+
+void SmShowSymbolSet::Resize()
+{
+ CustomWidgetController::Resize();
+ Size aWinSize(GetOutputSizePixel());
+ if (aWinSize != m_aOldSize)
+ {
+ calccols(GetDrawingArea()->get_ref_device());
+ m_aOldSize = aWinSize;
+ }
+}
+
+void SmShowSymbolSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ Color aBackgroundColor;
+ Color aTextColor;
+ lclGetSettingColors(aBackgroundColor, aTextColor);
+
+ rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
+ rRenderContext.SetTextColor(aTextColor);
+
+ rRenderContext.Push(PushFlags::MAPMODE);
+
+ // set MapUnit for which 'nLen' has been calculated
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
+
+ sal_uInt16 v = sal::static_int_cast< sal_uInt16 >(m_xScrolledWindow->vadjustment_get_value() * nColumns);
+ size_t nSymbols = aSymbolSet.size();
+
+ Color aTxtColor(rRenderContext.GetTextColor());
+ for (size_t i = v; i < nSymbols ; i++)
+ {
+ SmSym aSymbol(*aSymbolSet[i]);
+ vcl::Font aFont(aSymbol.GetFace());
+ aFont.SetAlignment(ALIGN_TOP);
+
+ // taking a FontSize which is a bit smaller (compared to nLen) in order to have a buffer
+ // (hopefully enough for left and right, too)
+ aFont.SetFontSize(Size(0, nLen - (nLen / 3)));
+ rRenderContext.SetFont(aFont);
+ // keep text color
+ rRenderContext.SetTextColor(aTxtColor);
+
+ int nIV = i - v;
+ sal_UCS4 cChar = aSymbol.GetCharacter();
+ OUString aText(&cChar, 1);
+ Size aSize(rRenderContext.GetTextWidth( aText ), rRenderContext.GetTextHeight());
+
+ Point aPoint((nIV % nColumns) * nLen + (nLen - aSize.Width()) / 2,
+ (nIV / nColumns) * nLen + (nLen - aSize.Height()) / 2);
+
+ rRenderContext.DrawText(OffsetPoint(aPoint), aText);
+ }
+
+ if (nSelectSymbol != SYMBOL_NONE)
+ {
+ Point aPoint(((nSelectSymbol - v) % nColumns) * nLen,
+ ((nSelectSymbol - v) / nColumns) * nLen);
+
+ rRenderContext.Invert(tools::Rectangle(OffsetPoint(aPoint), Size(nLen, nLen)));
+
+ }
+
+ rRenderContext.Pop();
+}
+
+bool SmShowSymbolSet::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ GrabFocus();
+
+ Size aOutputSize(nColumns * nLen, nRows * nLen);
+ aOutputSize.AdjustWidth(nXOffset );
+ aOutputSize.AdjustHeight(nYOffset );
+ Point aPoint(rMEvt.GetPosPixel());
+ aPoint.AdjustX( -nXOffset );
+ aPoint.AdjustY( -nYOffset );
+
+ if (rMEvt.IsLeft() && tools::Rectangle(Point(0, 0), aOutputSize).IsInside(rMEvt.GetPosPixel()))
+ {
+ long nPos = (aPoint.Y() / nLen) * nColumns + (aPoint.X() / nLen) +
+ m_xScrolledWindow->vadjustment_get_value() * nColumns;
+ SelectSymbol( sal::static_int_cast< sal_uInt16 >(nPos) );
+
+ aSelectHdlLink.Call(*this);
+
+ if (rMEvt.GetClicks() > 1)
+ aDblClickHdlLink.Call(*this);
+ }
+
+ return true;
+}
+
+bool SmShowSymbolSet::KeyInput(const KeyEvent& rKEvt)
+{
+ sal_uInt16 n = nSelectSymbol;
+
+ if (n != SYMBOL_NONE)
+ {
+ switch (rKEvt.GetKeyCode().GetCode())
+ {
+ case KEY_DOWN: n = n + nColumns; break;
+ case KEY_UP: n = n - nColumns; break;
+ case KEY_LEFT: n -= 1; break;
+ case KEY_RIGHT: n += 1; break;
+ case KEY_HOME: n = 0; break;
+ case KEY_END: n = static_cast< sal_uInt16 >(aSymbolSet.size() - 1); break;
+ case KEY_PAGEUP: n -= nColumns * nRows; break;
+ case KEY_PAGEDOWN: n += nColumns * nRows; break;
+ default:
+ return false;
+ }
+ }
+ else
+ n = 0;
+
+ if (n >= aSymbolSet.size())
+ n = nSelectSymbol;
+
+ // adjust scrollbar
+ if ((n < sal::static_int_cast<sal_uInt16>(m_xScrolledWindow->vadjustment_get_value() * nColumns)) ||
+ (n >= sal::static_int_cast<sal_uInt16>((m_xScrolledWindow->vadjustment_get_value() + nRows) * nColumns)))
+ {
+ m_xScrolledWindow->vadjustment_set_value(n / nColumns);
+ Invalidate();
+ }
+
+ SelectSymbol(n);
+ aSelectHdlLink.Call(*this);
+
+ return true;
+}
+
+void SmShowSymbolSet::calccols(const vcl::RenderContext& rRenderContext)
+{
+ // Height of 16pt in pixels (matching 'aOutputSize')
+ nLen = rRenderContext.LogicToPixel(Size(0, 16), MapMode(MapUnit::MapPoint)).Height();
+
+ Size aOutputSize(GetOutputSizePixel());
+
+ nColumns = aOutputSize.Width() / nLen;
+ nRows = aOutputSize.Height() / nLen;
+ nColumns = std::max<long>(1, nColumns);
+ nRows = std::max<long>(1, nRows);
+
+ nXOffset = (aOutputSize.Width() - (nColumns * nLen)) / 2;
+ nYOffset = (aOutputSize.Height() - (nRows * nLen)) / 2;
+
+ SetScrollBarRange();
+}
+
+void SmShowSymbolSet::SetSymbolSet(const SymbolPtrVec_t& rSymbolSet)
+{
+ aSymbolSet = rSymbolSet;
+ SetScrollBarRange();
+ Invalidate();
+}
+
+void SmShowSymbolSet::SetScrollBarRange()
+{
+ const int nLastRow = (aSymbolSet.size() - 1 + nColumns) / nColumns;
+ m_xScrolledWindow->vadjustment_configure(m_xScrolledWindow->vadjustment_get_value(), 0, nLastRow, 1, nRows - 1, nRows);
+ Invalidate();
+}
+
+void SmShowSymbolSet::SelectSymbol(sal_uInt16 nSymbol)
+{
+ int v = m_xScrolledWindow->vadjustment_get_value() * nColumns;
+
+ if (nSelectSymbol != SYMBOL_NONE && nColumns)
+ {
+ Point aPoint(OffsetPoint(Point(((nSelectSymbol - v) % nColumns) * nLen,
+ ((nSelectSymbol - v) / nColumns) * nLen)));
+ Invalidate(tools::Rectangle(aPoint, Size(nLen, nLen)));
+ }
+
+ if (nSymbol < aSymbolSet.size())
+ nSelectSymbol = nSymbol;
+
+ if (aSymbolSet.empty())
+ nSelectSymbol = SYMBOL_NONE;
+
+ if (nSelectSymbol != SYMBOL_NONE && nColumns)
+ {
+ Point aPoint(OffsetPoint(Point(((nSelectSymbol - v) % nColumns) * nLen,
+ ((nSelectSymbol - v) / nColumns) * nLen)));
+ Invalidate(tools::Rectangle(aPoint, Size(nLen, nLen)));
+ }
+
+ if (!nColumns)
+ Invalidate();
+}
+
+IMPL_LINK_NOARG(SmShowSymbolSet, ScrollHdl, weld::ScrolledWindow&, void)
+{
+ Invalidate();
+}
+
+SmShowSymbol::SmShowSymbol()
+{
+}
+
+void SmShowSymbol::setFontSize(vcl::Font &rFont) const
+{
+ Size aSize(GetOutputSizePixel());
+ rFont.SetFontSize(Size(0, aSize.Height() - aSize.Height() / 3));
+}
+
+void SmShowSymbol::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ Color aBackgroundColor;
+ Color aTextColor;
+ lclGetSettingColors(aBackgroundColor, aTextColor);
+ rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
+ rRenderContext.SetTextColor(aTextColor);
+ rRenderContext.Erase();
+
+ vcl::Font aFont(GetFont());
+ setFontSize(aFont);
+ rRenderContext.SetFont(aFont);
+
+ const OUString &rText = GetText();
+ Size aTextSize(rRenderContext.GetTextWidth(rText), rRenderContext.GetTextHeight());
+
+ rRenderContext.DrawText(Point((rRenderContext.GetOutputSize().Width() - aTextSize.Width()) / 2,
+ (rRenderContext.GetOutputSize().Height() * 7 / 10)), rText);
+}
+
+bool SmShowSymbol::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if (rMEvt.GetClicks() > 1)
+ aDblClickHdlLink.Call(*this);
+ return true;
+}
+
+void SmShowSymbol::SetSymbol(const SmSym *pSymbol)
+{
+ if (pSymbol)
+ {
+ vcl::Font aFont(pSymbol->GetFace());
+ aFont.SetAlignment(ALIGN_BASELINE);
+ SetFont(aFont);
+
+ sal_UCS4 cChar = pSymbol->GetCharacter();
+ OUString aText(&cChar, 1);
+ SetText( aText );
+ }
+
+ Invalidate();
+}
+
+void SmSymbolDialog::FillSymbolSets()
+ // populate the entries of possible SymbolsSets in the dialog with
+ // current values of the SymbolSet manager but selects none of those
+{
+ m_xSymbolSets->clear();
+ m_xSymbolSets->set_active(-1);
+
+ std::set< OUString > aSymbolSetNames( rSymbolMgr.GetSymbolSetNames() );
+ for (const auto& rSymbolSetName : aSymbolSetNames)
+ m_xSymbolSets->append_text(rSymbolSetName);
+}
+
+IMPL_LINK_NOARG( SmSymbolDialog, SymbolSetChangeHdl, weld::ComboBox&, void )
+{
+ SelectSymbolSet(m_xSymbolSets->get_active_text());
+}
+
+IMPL_LINK_NOARG( SmSymbolDialog, SymbolChangeHdl, SmShowSymbolSet&, void )
+{
+ SelectSymbol(m_xSymbolSetDisplay->GetSelectSymbol());
+}
+
+IMPL_LINK_NOARG(SmSymbolDialog, EditClickHdl, weld::Button&, void)
+{
+ SmSymDefineDialog aDialog(m_xDialog.get(), pFontListDev, rSymbolMgr);
+
+ // set current symbol and SymbolSet for the new dialog
+ const OUString aSymSetName (m_xSymbolSets->get_active_text()),
+ aSymName (m_xSymbolName->get_label());
+ aDialog.SelectOldSymbolSet(aSymSetName);
+ aDialog.SelectOldSymbol(aSymName);
+ aDialog.SelectSymbolSet(aSymSetName);
+ aDialog.SelectSymbol(aSymName);
+
+ // remember old SymbolSet
+ OUString aOldSymbolSet (m_xSymbolSets->get_active_text());
+
+ sal_uInt16 nSymPos = m_xSymbolSetDisplay->GetSelectSymbol();
+
+ // adapt dialog to data of the SymbolSet manager, which might have changed
+ if (aDialog.run() == RET_OK && rSymbolMgr.IsModified())
+ {
+ rSymbolMgr.Save();
+ FillSymbolSets();
+ }
+
+ // if the old SymbolSet doesn't exist anymore, go to the first one SymbolSet (if one exists)
+ if (!SelectSymbolSet(aOldSymbolSet) && m_xSymbolSets->get_count() > 0)
+ SelectSymbolSet(m_xSymbolSets->get_text(0));
+ else
+ {
+ // just update display of current symbol set
+ assert(aSymSetName == aSymSetName); //unexpected change in symbol set name
+ aSymbolSet = rSymbolMgr.GetSymbolSet( aSymbolSetName );
+ m_xSymbolSetDisplay->SetSymbolSet( aSymbolSet );
+ }
+
+ if (nSymPos >= aSymbolSet.size())
+ nSymPos = static_cast< sal_uInt16 >(aSymbolSet.size()) - 1;
+ SelectSymbol( nSymPos );
+}
+
+IMPL_LINK_NOARG( SmSymbolDialog, SymbolDblClickHdl2, SmShowSymbolSet&, void )
+{
+ SymbolDblClickHdl();
+}
+
+IMPL_LINK_NOARG( SmSymbolDialog, SymbolDblClickHdl, SmShowSymbol&, void )
+{
+ SymbolDblClickHdl();
+}
+
+void SmSymbolDialog::SymbolDblClickHdl()
+{
+ GetClickHdl(*m_xGetBtn);
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(SmSymbolDialog, GetClickHdl, weld::Button&, void)
+{
+ const SmSym *pSym = GetSymbol();
+ if (pSym)
+ {
+ OUString aText = "%" + pSym->GetName() + " ";
+
+ rViewSh.GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_INSERTSPECIAL, SfxCallMode::RECORD,
+ { new SfxStringItem(SID_INSERTSPECIAL, aText) });
+ }
+}
+
+SmSymbolDialog::SmSymbolDialog(weld::Window *pParent, OutputDevice *pFntListDevice,
+ SmSymbolManager &rMgr, SmViewShell &rViewShell)
+ : GenericDialogController(pParent, "modules/smath/ui/catalogdialog.ui", "CatalogDialog")
+ , rViewSh(rViewShell)
+ , rSymbolMgr(rMgr)
+ , pFontListDev(pFntListDevice)
+ , m_xSymbolSets(m_xBuilder->weld_combo_box("symbolset"))
+ , m_xSymbolSetDisplay(new SmShowSymbolSet(m_xBuilder->weld_scrolled_window("scrolledwindow")))
+ , m_xSymbolSetDisplayArea(new weld::CustomWeld(*m_xBuilder, "symbolsetdisplay", *m_xSymbolSetDisplay))
+ , m_xSymbolName(m_xBuilder->weld_label("symbolname"))
+ , m_xSymbolDisplay(new weld::CustomWeld(*m_xBuilder, "preview", m_aSymbolDisplay))
+ , m_xGetBtn(m_xBuilder->weld_button("ok"))
+ , m_xEditBtn(m_xBuilder->weld_button("edit"))
+{
+ m_xSymbolSets->make_sorted();
+
+ aSymbolSetName.clear();
+ aSymbolSet.clear();
+ FillSymbolSets();
+ if (m_xSymbolSets->get_count() > 0)
+ SelectSymbolSet(m_xSymbolSets->get_text(0));
+
+ m_xSymbolSets->connect_changed(LINK(this, SmSymbolDialog, SymbolSetChangeHdl));
+ m_xSymbolSetDisplay->SetSelectHdl(LINK(this, SmSymbolDialog, SymbolChangeHdl));
+ m_xSymbolSetDisplay->SetDblClickHdl(LINK(this, SmSymbolDialog, SymbolDblClickHdl2));
+ m_aSymbolDisplay.SetDblClickHdl(LINK(this, SmSymbolDialog, SymbolDblClickHdl));
+ m_xEditBtn->connect_clicked(LINK(this, SmSymbolDialog, EditClickHdl));
+ m_xGetBtn->connect_clicked(LINK(this, SmSymbolDialog, GetClickHdl));
+}
+
+SmSymbolDialog::~SmSymbolDialog()
+{
+}
+
+bool SmSymbolDialog::SelectSymbolSet(const OUString &rSymbolSetName)
+{
+ bool bRet = false;
+ sal_Int32 nPos = m_xSymbolSets->find_text(rSymbolSetName);
+
+ aSymbolSetName.clear();
+ aSymbolSet.clear();
+ if (nPos != -1)
+ {
+ m_xSymbolSets->set_active(nPos);
+
+ aSymbolSetName = rSymbolSetName;
+ aSymbolSet = rSymbolMgr.GetSymbolSet( aSymbolSetName );
+
+ // sort symbols by Unicode position (useful for displaying Greek characters alphabetically)
+ std::sort( aSymbolSet.begin(), aSymbolSet.end(),
+ [](const SmSym *pSym1, const SmSym *pSym2)
+ {
+ return pSym1->GetCharacter() < pSym2->GetCharacter();
+ } );
+
+ m_xSymbolSetDisplay->SetSymbolSet( aSymbolSet );
+ if (!aSymbolSet.empty())
+ SelectSymbol(0);
+
+ bRet = true;
+ }
+ else
+ m_xSymbolSets->set_active(-1);
+
+ return bRet;
+}
+
+void SmSymbolDialog::SelectSymbol(sal_uInt16 nSymbolNo)
+{
+ const SmSym *pSym = nullptr;
+ if (!aSymbolSetName.isEmpty() && nSymbolNo < static_cast< sal_uInt16 >(aSymbolSet.size()))
+ pSym = aSymbolSet[ nSymbolNo ];
+
+ m_xSymbolSetDisplay->SelectSymbol(nSymbolNo);
+ m_aSymbolDisplay.SetSymbol(pSym);
+ m_xSymbolName->set_label(pSym ? pSym->GetName() : OUString());
+}
+
+const SmSym* SmSymbolDialog::GetSymbol() const
+{
+ sal_uInt16 nSymbolNo = m_xSymbolSetDisplay->GetSelectSymbol();
+ bool bValid = !aSymbolSetName.isEmpty() && nSymbolNo < static_cast< sal_uInt16 >(aSymbolSet.size());
+ return bValid ? aSymbolSet[ nSymbolNo ] : nullptr;
+}
+
+void SmShowChar::Resize()
+{
+ const OUString &rText = GetText();
+ if (rText.isEmpty())
+ return;
+ sal_Int32 nStrIndex = 0;
+ sal_UCS4 cChar = rText.iterateCodePoints(&nStrIndex);
+ SetSymbol(cChar, GetFont()); //force recalculation of size
+}
+
+void SmShowChar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ Color aTextCol = rRenderContext.GetTextColor();
+ Color aFillCol = rRenderContext.GetFillColor();
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Color aWindowTextColor(rStyleSettings.GetDialogTextColor());
+ const Color aWindowColor(rStyleSettings.GetWindowColor());
+ rRenderContext.SetTextColor(aWindowTextColor);
+ rRenderContext.SetFillColor(aWindowColor);
+
+ Size aSize(GetOutputSizePixel());
+ rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), aSize));
+
+ OUString aText(GetText());
+ if (!aText.isEmpty())
+ {
+ vcl::Font aFont(m_aFont);
+ aFont.SetAlignment(ALIGN_TOP);
+ rRenderContext.SetFont(aFont);
+
+ Size aTextSize(rRenderContext.GetTextWidth(aText), rRenderContext.GetTextHeight());
+
+ rRenderContext.DrawText(Point((aSize.Width() - aTextSize.Width()) / 2,
+ (aSize.Height() - aTextSize.Height()) / 2), aText);
+ }
+
+ rRenderContext.SetTextColor(aTextCol);
+ rRenderContext.SetFillColor(aFillCol);
+}
+
+void SmShowChar::SetSymbol( const SmSym *pSym )
+{
+ if (pSym)
+ SetSymbol( pSym->GetCharacter(), pSym->GetFace() );
+}
+
+
+void SmShowChar::SetSymbol( sal_UCS4 cChar, const vcl::Font &rFont )
+{
+ vcl::Font aFont( rFont );
+ Size aSize(GetOutputSizePixel());
+ aFont.SetFontSize(Size(0, aSize.Height() - aSize.Height() / 3));
+ aFont.SetAlignment(ALIGN_BASELINE);
+ SetFont(aFont);
+
+ OUString aText(&cChar, 1);
+ SetText( aText );
+
+ Invalidate();
+}
+
+void SmSymDefineDialog::FillSymbols(weld::ComboBox& rComboBox, bool bDeleteText)
+{
+ assert((&rComboBox == m_xOldSymbols.get() || &rComboBox == m_xSymbols.get()) && "Sm : wrong ComboBox");
+
+ rComboBox.clear();
+ if (bDeleteText)
+ rComboBox.set_entry_text(OUString());
+
+ weld::ComboBox& rBox = &rComboBox == m_xOldSymbols.get() ? *m_xOldSymbolSets : *m_xSymbolSets;
+ SymbolPtrVec_t aSymSet(m_aSymbolMgrCopy.GetSymbolSet(rBox.get_active_text()));
+ for (const SmSym* i : aSymSet)
+ rComboBox.append_text(i->GetName());
+}
+
+void SmSymDefineDialog::FillSymbolSets(weld::ComboBox& rComboBox, bool bDeleteText)
+{
+ assert((&rComboBox == m_xOldSymbolSets.get() || &rComboBox == m_xSymbolSets.get()) && "Sm : wrong ComboBox");
+
+ rComboBox.clear();
+ if (bDeleteText)
+ rComboBox.set_entry_text(OUString());
+
+ const std::set< OUString > aSymbolSetNames( m_aSymbolMgrCopy.GetSymbolSetNames() );
+ for (const auto& rSymbolSetName : aSymbolSetNames)
+ rComboBox.append_text(rSymbolSetName);
+}
+
+void SmSymDefineDialog::FillFonts()
+{
+ m_xFonts->clear();
+ m_xFonts->set_active(-1);
+
+ // Include all fonts of FontList into the font list.
+ // If there are duplicates, only include one entry of each font since the style will be
+ // already selected using the FontStyleBox.
+ if (m_xFontList)
+ {
+ sal_uInt16 nCount = m_xFontList->GetFontNameCount();
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ m_xFonts->append_text(m_xFontList->GetFontName(i).GetFamilyName());
+ }
+}
+
+void SmSymDefineDialog::FillStyles()
+{
+ m_xStyles->clear();
+// pStyles->SetText(OUString());
+
+ OUString aText(m_xFonts->get_active_text());
+ if (!aText.isEmpty())
+ {
+ // use own StyleNames
+ const SmFontStyles &rStyles = GetFontStyles();
+ for (sal_uInt16 i = 0; i < SmFontStyles::GetCount(); ++i)
+ m_xStyles->append_text(rStyles.GetStyleName(i));
+
+ assert(m_xStyles->get_count() > 0 && "Sm : no styles available");
+ m_xStyles->set_active(0);
+ }
+}
+
+SmSym* SmSymDefineDialog::GetSymbol(const weld::ComboBox& rComboBox)
+{
+ assert((&rComboBox == m_xOldSymbols.get() || &rComboBox == m_xSymbols.get()) && "Sm : wrong combobox");
+ return m_aSymbolMgrCopy.GetSymbolByName(rComboBox.get_active_text());
+}
+
+IMPL_LINK(SmSymDefineDialog, OldSymbolChangeHdl, weld::ComboBox&, rComboBox, void)
+{
+ (void) rComboBox;
+ assert(&rComboBox == m_xOldSymbols.get() && "Sm : wrong argument");
+ SelectSymbol(*m_xOldSymbols, m_xOldSymbols->get_active_text(), false);
+}
+
+IMPL_LINK( SmSymDefineDialog, OldSymbolSetChangeHdl, weld::ComboBox&, rComboBox, void )
+{
+ (void) rComboBox;
+ assert(&rComboBox == m_xOldSymbolSets.get() && "Sm : wrong argument");
+ SelectSymbolSet(*m_xOldSymbolSets, m_xOldSymbolSets->get_active_text(), false);
+}
+
+IMPL_LINK(SmSymDefineDialog, ModifyHdl, weld::ComboBox&, rComboBox, void)
+{
+ // remember cursor position for later restoring of it
+ int nStartPos, nEndPos;
+ rComboBox.get_entry_selection_bounds(nStartPos, nEndPos);
+
+ if (&rComboBox == m_xSymbols.get())
+ SelectSymbol(*m_xSymbols, m_xSymbols->get_active_text(), false);
+ else if (&rComboBox == m_xSymbolSets.get())
+ SelectSymbolSet(*m_xSymbolSets, m_xSymbolSets->get_active_text(), false);
+ else if (&rComboBox == m_xOldSymbols.get())
+ // allow only names from the list
+ SelectSymbol(*m_xOldSymbols, m_xOldSymbols->get_active_text(), true);
+ else if (&rComboBox == m_xOldSymbolSets.get())
+ // allow only names from the list
+ SelectSymbolSet(*m_xOldSymbolSets, m_xOldSymbolSets->get_active_text(), true);
+ else if (&rComboBox == m_xStyles.get())
+ // allow only names from the list (that's the case here anyway)
+ SelectStyle(m_xStyles->get_active_text(), true);
+ else
+ SAL_WARN("starmath", "wrong combobox argument");
+
+ rComboBox.select_entry_region(nStartPos, nEndPos);
+
+ UpdateButtons();
+}
+
+IMPL_LINK(SmSymDefineDialog, FontChangeHdl, weld::ComboBox&, rListBox, void)
+{
+ (void) rListBox;
+ assert(&rListBox == m_xFonts.get() && "Sm : wrong argument");
+
+ SelectFont(m_xFonts->get_active_text());
+}
+
+IMPL_LINK_NOARG(SmSymDefineDialog, SubsetChangeHdl, weld::ComboBox&, void)
+{
+ int nPos = m_xFontsSubsetLB->get_active();
+ if (nPos != -1)
+ {
+ const Subset* pSubset = reinterpret_cast<const Subset*>(m_xFontsSubsetLB->get_active_id().toUInt64());
+ if (pSubset)
+ {
+ m_xCharsetDisplay->SelectCharacter( pSubset->GetRangeMin() );
+ }
+ }
+}
+
+IMPL_LINK( SmSymDefineDialog, StyleChangeHdl, weld::ComboBox&, rComboBox, void )
+{
+ (void) rComboBox;
+ assert(&rComboBox == m_xStyles.get() && "Sm : wrong argument");
+
+ SelectStyle(m_xStyles->get_active_text());
+}
+
+IMPL_LINK_NOARG(SmSymDefineDialog, CharHighlightHdl, SvxShowCharSet*, void)
+{
+ sal_UCS4 cChar = m_xCharsetDisplay->GetSelectCharacter();
+
+ if (m_xSubsetMap)
+ {
+ const Subset* pSubset = m_xSubsetMap->GetSubsetByUnicode(cChar);
+ if (pSubset)
+ m_xFontsSubsetLB->set_active_text(pSubset->GetName());
+ else
+ m_xFontsSubsetLB->set_active(-1);
+ }
+
+ m_aSymbolDisplay.SetSymbol(cChar, m_xCharsetDisplay->GetFont());
+
+ UpdateButtons();
+
+ // display Unicode position as symbol name while iterating over characters
+ const OUString aHex(OUString::number(cChar, 16).toAsciiUpperCase());
+ const OUString aPattern( (aHex.getLength() > 4) ? OUString("Ux000000") : OUString("Ux0000") );
+ OUString aUnicodePos = aPattern.copy( 0, aPattern.getLength() - aHex.getLength() ) +
+ aHex;
+ m_xSymbols->set_entry_text(aUnicodePos);
+ m_xSymbolName->set_label(aUnicodePos);
+}
+
+IMPL_LINK( SmSymDefineDialog, AddClickHdl, weld::Button&, rButton, void )
+{
+ (void) rButton;
+ assert(&rButton == m_xAddBtn.get() && "Sm : wrong argument");
+ assert(rButton.get_sensitive() && "Sm : requirements met ??");
+
+ // add symbol
+ const SmSym aNewSymbol(m_xSymbols->get_active_text(), m_xCharsetDisplay->GetFont(),
+ m_xCharsetDisplay->GetSelectCharacter(), m_xSymbolSets->get_active_text());
+ //OSL_ENSURE( m_aSymbolMgrCopy.GetSymbolByName(aTmpSymbolName) == NULL, "symbol already exists" );
+ m_aSymbolMgrCopy.AddOrReplaceSymbol( aNewSymbol );
+
+ // update display of new symbol
+ m_aSymbolDisplay.SetSymbol( &aNewSymbol );
+ m_xSymbolName->set_label(aNewSymbol.GetName());
+ m_xSymbolSetName->set_label(aNewSymbol.GetSymbolSetName());
+
+ // update list box entries
+ FillSymbolSets(*m_xOldSymbolSets, false);
+ FillSymbolSets(*m_xSymbolSets, false);
+ FillSymbols(*m_xOldSymbols, false);
+ FillSymbols(*m_xSymbols, false);
+
+ UpdateButtons();
+}
+
+IMPL_LINK( SmSymDefineDialog, ChangeClickHdl, weld::Button&, rButton, void )
+{
+ (void) rButton;
+ assert(&rButton == m_xChangeBtn.get() && "Sm : wrong argument");
+ assert(m_xChangeBtn->get_sensitive() && "Sm : requirements met ??");
+
+ // get new Symbol to use
+ //! get font from symbol-disp lay since charset-display does not keep
+ //! the bold attribute.
+ const SmSym aNewSymbol(m_xSymbols->get_active_text(), m_xCharsetDisplay->GetFont(),
+ m_xCharsetDisplay->GetSelectCharacter(), m_xSymbolSets->get_active_text());
+
+ // remove old symbol if the name was changed then add new one
+ const bool bNameChanged = m_xOldSymbols->get_active_text() != m_xSymbols->get_active_text();
+ if (bNameChanged)
+ m_aSymbolMgrCopy.RemoveSymbol(m_xOldSymbols->get_active_text());
+ m_aSymbolMgrCopy.AddOrReplaceSymbol( aNewSymbol, true );
+
+ // clear display for original symbol if necessary
+ if (bNameChanged)
+ SetOrigSymbol(nullptr, OUString());
+
+ // update display of new symbol
+ m_aSymbolDisplay.SetSymbol(&aNewSymbol);
+ m_xSymbolName->set_label(aNewSymbol.GetName());
+ m_xSymbolSetName->set_label(aNewSymbol.GetSymbolSetName());
+
+ // update list box entries
+ FillSymbolSets(*m_xOldSymbolSets, false);
+ FillSymbolSets(*m_xSymbolSets, false);
+ FillSymbols(*m_xOldSymbols, false);
+ FillSymbols(*m_xSymbols, false);
+
+ UpdateButtons();
+}
+
+IMPL_LINK(SmSymDefineDialog, DeleteClickHdl, weld::Button&, rButton, void)
+{
+ (void) rButton;
+ assert(&rButton == m_xDeleteBtn.get() && "Sm : wrong argument");
+ assert(m_xDeleteBtn->get_sensitive() && "Sm : requirements met ??");
+
+ if (m_xOrigSymbol)
+ {
+ m_aSymbolMgrCopy.RemoveSymbol(m_xOrigSymbol->GetName());
+
+ // clear display for original symbol
+ SetOrigSymbol(nullptr, OUString());
+
+ // update list box entries
+ FillSymbolSets(*m_xOldSymbolSets, false);
+ FillSymbolSets(*m_xSymbolSets, false);
+ FillSymbols(*m_xOldSymbols ,false);
+ FillSymbols(*m_xSymbols ,false);
+ }
+
+ UpdateButtons();
+}
+
+void SmSymDefineDialog::UpdateButtons()
+{
+ bool bAdd = false,
+ bChange = false,
+ bDelete = false;
+ OUString aTmpSymbolName(m_xSymbols->get_active_text()),
+ aTmpSymbolSetName(m_xSymbolSets->get_active_text());
+
+ if (!aTmpSymbolName.isEmpty() && !aTmpSymbolSetName.isEmpty())
+ {
+ // are all settings equal?
+ //! (Font-, Style- and SymbolSet name comparison is not case sensitive)
+ bool bEqual = m_xOrigSymbol
+ && aTmpSymbolSetName.equalsIgnoreAsciiCase(m_xOldSymbolSetName->get_label())
+ && aTmpSymbolName == m_xOrigSymbol->GetName()
+ && m_xFonts->get_active_text().equalsIgnoreAsciiCase(
+ m_xOrigSymbol->GetFace().GetFamilyName())
+ && m_xStyles->get_active_text().equalsIgnoreAsciiCase(
+ GetFontStyles().GetStyleName(m_xOrigSymbol->GetFace()))
+ && m_xCharsetDisplay->GetSelectCharacter() == m_xOrigSymbol->GetCharacter();
+
+ // only add it if there isn't already a symbol with the same name
+ bAdd = m_aSymbolMgrCopy.GetSymbolByName(aTmpSymbolName) == nullptr;
+
+ // only delete it if all settings are equal
+ bDelete = bool(m_xOrigSymbol);
+
+ // only change it if the old symbol exists and the new one is different
+ bChange = m_xOrigSymbol && !bEqual;
+ }
+
+ m_xAddBtn->set_sensitive(bAdd);
+ m_xChangeBtn->set_sensitive(bChange);
+ m_xDeleteBtn->set_sensitive(bDelete);
+}
+
+SmSymDefineDialog::SmSymDefineDialog(weld::Window* pParent, OutputDevice *pFntListDevice, SmSymbolManager &rMgr)
+ : GenericDialogController(pParent, "modules/smath/ui/symdefinedialog.ui", "EditSymbols")
+ , m_xVirDev(VclPtr<VirtualDevice>::Create())
+ , m_rSymbolMgr(rMgr)
+ , m_xFontList(new FontList(pFntListDevice))
+ , m_xOldSymbols(m_xBuilder->weld_combo_box("oldSymbols"))
+ , m_xOldSymbolSets(m_xBuilder->weld_combo_box("oldSymbolSets"))
+ , m_xSymbols(m_xBuilder->weld_combo_box("symbols"))
+ , m_xSymbolSets(m_xBuilder->weld_combo_box("symbolSets"))
+ , m_xFonts(m_xBuilder->weld_combo_box("fonts"))
+ , m_xFontsSubsetLB(m_xBuilder->weld_combo_box("fontsSubsetLB"))
+ , m_xStyles(m_xBuilder->weld_combo_box("styles"))
+ , m_xOldSymbolName(m_xBuilder->weld_label("oldSymbolName"))
+ , m_xOldSymbolSetName(m_xBuilder->weld_label("oldSymbolSetName"))
+ , m_xSymbolName(m_xBuilder->weld_label("symbolName"))
+ , m_xSymbolSetName(m_xBuilder->weld_label("symbolSetName"))
+ , m_xAddBtn(m_xBuilder->weld_button("add"))
+ , m_xChangeBtn(m_xBuilder->weld_button("modify"))
+ , m_xDeleteBtn(m_xBuilder->weld_button("delete"))
+ , m_xOldSymbolDisplay(new weld::CustomWeld(*m_xBuilder, "oldSymbolDisplay", m_aOldSymbolDisplay))
+ , m_xSymbolDisplay(new weld::CustomWeld(*m_xBuilder, "symbolDisplay", m_aSymbolDisplay))
+ , m_xCharsetDisplay(new SvxShowCharSet(m_xBuilder->weld_scrolled_window("showscroll"), m_xVirDev))
+ , m_xCharsetDisplayArea(new weld::CustomWeld(*m_xBuilder, "charsetDisplay", *m_xCharsetDisplay))
+{
+ // auto completion is troublesome since that symbols character also gets automatically selected in the
+ // display and if the user previously selected a character to define/redefine that one this is bad
+ m_xOldSymbols->set_entry_completion(false);
+ m_xSymbols->set_entry_completion(false);
+
+ FillFonts();
+ if (m_xFonts->get_count() > 0)
+ SelectFont(m_xFonts->get_text(0));
+
+ SetSymbolSetManager(m_rSymbolMgr);
+
+ m_xOldSymbols->connect_changed(LINK(this, SmSymDefineDialog, OldSymbolChangeHdl));
+ m_xOldSymbolSets->connect_changed(LINK(this, SmSymDefineDialog, OldSymbolSetChangeHdl));
+ m_xSymbolSets->connect_changed(LINK(this, SmSymDefineDialog, ModifyHdl));
+ m_xOldSymbolSets->connect_changed(LINK(this, SmSymDefineDialog, ModifyHdl));
+ m_xSymbols->connect_changed(LINK(this, SmSymDefineDialog, ModifyHdl));
+ m_xOldSymbols->connect_changed(LINK(this, SmSymDefineDialog, ModifyHdl));
+ m_xStyles->connect_changed(LINK(this, SmSymDefineDialog, ModifyHdl));
+ m_xFonts->connect_changed(LINK(this, SmSymDefineDialog, FontChangeHdl));
+ m_xFontsSubsetLB->connect_changed(LINK(this, SmSymDefineDialog, SubsetChangeHdl));
+ m_xStyles->connect_changed(LINK(this, SmSymDefineDialog, StyleChangeHdl));
+ m_xAddBtn->connect_clicked(LINK(this, SmSymDefineDialog, AddClickHdl));
+ m_xChangeBtn->connect_clicked(LINK(this, SmSymDefineDialog, ChangeClickHdl));
+ m_xDeleteBtn->connect_clicked(LINK(this, SmSymDefineDialog, DeleteClickHdl));
+ m_xCharsetDisplay->SetHighlightHdl( LINK( this, SmSymDefineDialog, CharHighlightHdl ) );
+}
+
+SmSymDefineDialog::~SmSymDefineDialog()
+{
+}
+
+short SmSymDefineDialog::run()
+{
+ short nResult = GenericDialogController::run();
+
+ // apply changes if dialog was closed by clicking OK
+ if (m_aSymbolMgrCopy.IsModified() && nResult == RET_OK)
+ m_rSymbolMgr = m_aSymbolMgrCopy;
+
+ return nResult;
+}
+
+void SmSymDefineDialog::SetSymbolSetManager(const SmSymbolManager &rMgr)
+{
+ m_aSymbolMgrCopy = rMgr;
+
+ // Set the modified flag of the copy to false so that
+ // we can check later on if anything has been changed
+ m_aSymbolMgrCopy.SetModified(false);
+
+ FillSymbolSets(*m_xOldSymbolSets);
+ if (m_xOldSymbolSets->get_count() > 0)
+ SelectSymbolSet(m_xOldSymbolSets->get_text(0));
+ FillSymbolSets(*m_xSymbolSets);
+ if (m_xSymbolSets->get_count() > 0)
+ SelectSymbolSet(m_xSymbolSets->get_text(0));
+ FillSymbols(*m_xOldSymbols);
+ if (m_xOldSymbols->get_count() > 0)
+ SelectSymbol(m_xOldSymbols->get_text(0));
+ FillSymbols(*m_xSymbols);
+ if (m_xSymbols->get_count() > 0)
+ SelectSymbol(m_xSymbols->get_text(0));
+
+ UpdateButtons();
+}
+
+bool SmSymDefineDialog::SelectSymbolSet(weld::ComboBox& rComboBox,
+ const OUString &rSymbolSetName, bool bDeleteText)
+{
+ assert((&rComboBox == m_xOldSymbolSets.get() || &rComboBox == m_xSymbolSets.get()) && "Sm : wrong ComboBox");
+
+ // trim SymbolName (no leading and trailing blanks)
+ OUString aNormName = comphelper::string::stripStart(rSymbolSetName, ' ');
+ aNormName = comphelper::string::stripEnd(aNormName, ' ');
+ // and remove possible deviations within the input
+ rComboBox.set_entry_text(aNormName);
+
+ bool bRet = false;
+ int nPos = rComboBox.find_text(aNormName);
+
+ if (nPos != -1)
+ {
+ rComboBox.set_active(nPos);
+ bRet = true;
+ }
+ else if (bDeleteText)
+ rComboBox.set_entry_text(OUString());
+
+ bool bIsOld = &rComboBox == m_xOldSymbolSets.get();
+
+ // setting the SymbolSet name at the associated display
+ weld::Label& rFT = bIsOld ? *m_xOldSymbolSetName : *m_xSymbolSetName;
+ rFT.set_label(rComboBox.get_active_text());
+
+ // set the symbol name which belongs to the SymbolSet at the associated combobox
+ weld::ComboBox& rCB = bIsOld ? *m_xOldSymbols : *m_xSymbols;
+ FillSymbols(rCB, false);
+
+ // display a valid respectively no symbol when changing the SymbolSets
+ if (bIsOld)
+ {
+ OUString aTmpOldSymbolName;
+ if (m_xOldSymbols->get_count() > 0)
+ aTmpOldSymbolName = m_xOldSymbols->get_text(0);
+ SelectSymbol(*m_xOldSymbols, aTmpOldSymbolName, true);
+ }
+
+ UpdateButtons();
+
+ return bRet;
+}
+
+void SmSymDefineDialog::SetOrigSymbol(const SmSym *pSymbol,
+ const OUString &rSymbolSetName)
+{
+ // clear old symbol
+ m_xOrigSymbol.reset();
+
+ OUString aSymName,
+ aSymSetName;
+ if (pSymbol)
+ {
+ // set new symbol
+ m_xOrigSymbol.reset(new SmSym(*pSymbol));
+
+ aSymName = pSymbol->GetName();
+ aSymSetName = rSymbolSetName;
+ m_aOldSymbolDisplay.SetSymbol( pSymbol );
+ }
+ else
+ { // delete displayed symbols
+ m_aOldSymbolDisplay.SetText(OUString());
+ m_aOldSymbolDisplay.Invalidate();
+ }
+ m_xOldSymbolName->set_label(aSymName);
+ m_xOldSymbolSetName->set_label(aSymSetName);
+}
+
+
+bool SmSymDefineDialog::SelectSymbol(weld::ComboBox& rComboBox,
+ const OUString &rSymbolName, bool bDeleteText)
+{
+ assert((&rComboBox == m_xOldSymbols.get() || &rComboBox == m_xSymbols.get()) && "Sm : wrong ComboBox");
+
+ // trim SymbolName (no blanks)
+ OUString aNormName = rSymbolName.replaceAll(" ", "");
+ // and remove possible deviations within the input
+ rComboBox.set_entry_text(aNormName);
+
+ bool bRet = false;
+ int nPos = rComboBox.find_text(aNormName);
+
+ bool bIsOld = &rComboBox == m_xOldSymbols.get();
+
+ if (nPos != -1)
+ {
+ rComboBox.set_active(nPos);
+
+ if (!bIsOld)
+ {
+ const SmSym *pSymbol = GetSymbol(*m_xSymbols);
+ if (pSymbol)
+ {
+ // choose font and style accordingly
+ const vcl::Font &rFont = pSymbol->GetFace();
+ SelectFont(rFont.GetFamilyName(), false);
+ SelectStyle(GetFontStyles().GetStyleName(rFont), false);
+
+ // Since setting the Font via the Style name of the SymbolFonts doesn't
+ // work really well (e.g. it can be empty even though the font itself is
+ // bold or italic) we're manually setting the Font with respect to the Symbol
+ m_xCharsetDisplay->SetFont(rFont);
+ m_aSymbolDisplay.SetFont(rFont);
+
+ // select associated character
+ SelectChar(pSymbol->GetCharacter());
+
+ // since SelectChar will also set the unicode point as text in the
+ // symbols box, we have to set the symbol name again to get that one displayed
+ m_xSymbols->set_entry_text(pSymbol->GetName());
+ }
+ }
+
+ bRet = true;
+ }
+ else if (bDeleteText)
+ rComboBox.set_entry_text(OUString());
+
+ if (bIsOld)
+ {
+ // if there's a change of the old symbol, show only the available ones, otherwise show none
+ const SmSym *pOldSymbol = nullptr;
+ OUString aTmpOldSymbolSetName;
+ if (nPos != -1)
+ {
+ pOldSymbol = m_aSymbolMgrCopy.GetSymbolByName(aNormName);
+ aTmpOldSymbolSetName = m_xOldSymbolSets->get_active_text();
+ }
+ SetOrigSymbol(pOldSymbol, aTmpOldSymbolSetName);
+ }
+ else
+ m_xSymbolName->set_label(rComboBox.get_active_text());
+
+ UpdateButtons();
+
+ return bRet;
+}
+
+
+void SmSymDefineDialog::SetFont(const OUString &rFontName, const OUString &rStyleName)
+{
+ // get Font (FontInfo) matching name and style
+ FontMetric aFontMetric;
+ if (m_xFontList)
+ aFontMetric = m_xFontList->Get(rFontName, WEIGHT_NORMAL, ITALIC_NONE);
+ SetFontStyle(rStyleName, aFontMetric);
+
+ m_xCharsetDisplay->SetFont(aFontMetric);
+ m_aSymbolDisplay.SetFont(aFontMetric);
+
+ // update subset listbox for new font's unicode subsets
+ FontCharMapRef xFontCharMap = m_xCharsetDisplay->GetFontCharMap();
+ m_xSubsetMap.reset(new SubsetMap( xFontCharMap ));
+
+ m_xFontsSubsetLB->clear();
+ bool bFirst = true;
+ for (auto & subset : m_xSubsetMap->GetSubsetMap())
+ {
+ m_xFontsSubsetLB->append(OUString::number(reinterpret_cast<sal_uInt64>(&subset)), subset.GetName());
+ // subset must live at least as long as the selected font !!!
+ if (bFirst)
+ m_xFontsSubsetLB->set_active(0);
+ bFirst = false;
+ }
+ if (bFirst)
+ m_xFontsSubsetLB->set_active(-1);
+ m_xFontsSubsetLB->set_sensitive(!bFirst);
+}
+
+bool SmSymDefineDialog::SelectFont(const OUString &rFontName, bool bApplyFont)
+{
+ bool bRet = false;
+ int nPos = m_xFonts->find_text(rFontName);
+
+ if (nPos != -1)
+ {
+ m_xFonts->set_active(nPos);
+ if (m_xStyles->get_count() > 0)
+ SelectStyle(m_xStyles->get_text(0));
+ if (bApplyFont)
+ {
+ SetFont(m_xFonts->get_active_text(), m_xStyles->get_active_text());
+ m_aSymbolDisplay.SetSymbol(m_xCharsetDisplay->GetSelectCharacter(), m_xCharsetDisplay->GetFont());
+ }
+ bRet = true;
+ }
+ else
+ m_xFonts->set_active(-1);
+ FillStyles();
+
+ UpdateButtons();
+
+ return bRet;
+}
+
+
+bool SmSymDefineDialog::SelectStyle(const OUString &rStyleName, bool bApplyFont)
+{
+ bool bRet = false;
+ int nPos = m_xStyles->find_text(rStyleName);
+
+ // if the style is not available take the first available one (if existent)
+ if (nPos == -1 && m_xStyles->get_count() > 0)
+ nPos = 0;
+
+ if (nPos != -1)
+ {
+ m_xStyles->set_active(nPos);
+ if (bApplyFont)
+ {
+ SetFont(m_xFonts->get_active_text(), m_xStyles->get_active_text());
+ m_aSymbolDisplay.SetSymbol(m_xCharsetDisplay->GetSelectCharacter(), m_xCharsetDisplay->GetFont());
+ }
+ bRet = true;
+ }
+ else
+ m_xStyles->set_entry_text(OUString());
+
+ UpdateButtons();
+
+ return bRet;
+}
+
+void SmSymDefineDialog::SelectChar(sal_Unicode cChar)
+{
+ m_xCharsetDisplay->SelectCharacter( cChar );
+ m_aSymbolDisplay.SetSymbol(cChar, m_xCharsetDisplay->GetFont());
+
+ UpdateButtons();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/document.cxx b/starmath/source/document.cxx
new file mode 100644
index 000000000..8f9925c3c
--- /dev/null
+++ b/starmath/source/document.cxx
@@ -0,0 +1,1291 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/uno/Any.h>
+
+#include <comphelper/fileformat.h>
+#include <comphelper/accessibletexthelper.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <unotools/eventcfg.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <comphelper/classids.hxx>
+#include <sot/formats.hxx>
+#include <sot/storage.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/hint.hxx>
+#include <svl/stritem.hxx>
+#include <svl/undo.hxx>
+#include <svl/whiter.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <tools/mapunit.hxx>
+#include <vcl/settings.hxx>
+
+#include <document.hxx>
+#include <action.hxx>
+#include <dialog.hxx>
+#include <format.hxx>
+#include <starmath.hrc>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <symbol.hxx>
+#include <unomodel.hxx>
+#include <utility.hxx>
+#include <view.hxx>
+#include "mathtype.hxx"
+#include "ooxmlexport.hxx"
+#include "ooxmlimport.hxx"
+#include "rtfexport.hxx"
+#include "mathmlimport.hxx"
+#include "mathmlexport.hxx"
+#include <svx/svxids.hrc>
+#include <cursor.hxx>
+#include <tools/diagnose_ex.h>
+#include <visitors.hxx>
+#include "accessibility.hxx"
+#include "cfgitem.hxx"
+#include <memory>
+#include <utility>
+#include <oox/mathml/export.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+#define ShellClass_SmDocShell
+#include <smslots.hxx>
+
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SmDocShell, SfxObjectShell)
+
+void SmDocShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("view");
+}
+
+SFX_IMPL_OBJECTFACTORY(SmDocShell, SvGlobalName(SO3_SM_CLASSID), "smath" )
+
+void SmDocShell::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::MathFormatChanged)
+ {
+ SetFormulaArranged(false);
+
+ mnModifyCount++; //! see comment for SID_GAPHIC_SM in SmDocShell::GetState
+
+ Repaint();
+ }
+}
+
+void SmDocShell::LoadSymbols()
+{
+ SmModule *pp = SM_MOD();
+ pp->GetSymbolManager().Load();
+}
+
+
+OUString SmDocShell::GetComment() const
+{
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ return xDocProps->getDescription();
+}
+
+
+void SmDocShell::SetText(const OUString& rBuffer)
+{
+ if (rBuffer == maText)
+ return;
+
+ bool bIsEnabled = IsEnableSetModified();
+ if( bIsEnabled )
+ EnableSetModified( false );
+
+ maText = rBuffer;
+ SetFormulaArranged( false );
+
+ Parse();
+
+ SmViewShell *pViewSh = SmGetActiveView();
+ if( pViewSh )
+ {
+ pViewSh->GetViewFrame()->GetBindings().Invalidate(SID_TEXT);
+ if ( SfxObjectCreateMode::EMBEDDED == GetCreateMode() )
+ {
+ // have SwOleClient::FormatChanged() to align the modified formula properly
+ // even if the visible area does not change (e.g. when formula text changes from
+ // "{a over b + c} over d" to "d over {a over b + c}"
+ SfxGetpApp()->NotifyEvent(SfxEventHint( SfxEventHintId::VisAreaChanged, GlobalEventConfig::GetEventName(GlobalEventId::VISAREACHANGED), this));
+
+ Repaint();
+ }
+ else
+ pViewSh->GetGraphicWindow().Invalidate();
+ }
+
+ if ( bIsEnabled )
+ EnableSetModified( bIsEnabled );
+ SetModified();
+
+ // launch accessible event if necessary
+ SmGraphicAccessible *pAcc = pViewSh ? pViewSh->GetGraphicWindow().GetAccessible_Impl() : nullptr;
+ if (pAcc)
+ {
+ Any aOldValue, aNewValue;
+ if ( comphelper::OCommonAccessibleText::implInitTextChangedEvent( maText, rBuffer, aOldValue, aNewValue ) )
+ {
+ pAcc->LaunchEvent( AccessibleEventId::TEXT_CHANGED,
+ aOldValue, aNewValue );
+ }
+ }
+
+ if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ OnDocumentPrinterChanged(nullptr);
+}
+
+void SmDocShell::SetFormat(SmFormat const & rFormat)
+{
+ maFormat = rFormat;
+ SetFormulaArranged( false );
+ SetModified();
+
+ mnModifyCount++; //! see comment for SID_GAPHIC_SM in SmDocShell::GetState
+
+ // don't use SmGetActiveView since the view shell might not be active (0 pointer)
+ // if for example the Basic Macro dialog currently has the focus. Thus:
+ SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this );
+ while (pFrm)
+ {
+ pFrm->GetBindings().Invalidate(SID_GAPHIC_SM);
+ pFrm = SfxViewFrame::GetNext( *pFrm, this );
+ }
+}
+
+OUString const & SmDocShell::GetAccessibleText()
+{
+ ArrangeFormula();
+ if (maAccText.isEmpty())
+ {
+ OSL_ENSURE( mpTree, "Tree missing" );
+ if (mpTree)
+ {
+ OUStringBuffer aBuf;
+ mpTree->GetAccessibleText(aBuf);
+ maAccText = aBuf.makeStringAndClear();
+ }
+ }
+ return maAccText;
+}
+
+void SmDocShell::Parse()
+{
+ mpTree.reset();
+ ReplaceBadChars();
+ mpTree = maParser.Parse(maText);
+ mnModifyCount++; //! see comment for SID_GAPHIC_SM in SmDocShell::GetState
+ SetFormulaArranged( false );
+ InvalidateCursor();
+ maUsedSymbols = maParser.GetUsedSymbols();
+}
+
+
+void SmDocShell::ArrangeFormula()
+{
+ if (mbFormulaArranged)
+ return;
+
+ // Only for the duration of the existence of this object the correct settings
+ // at the printer are guaranteed!
+ SmPrinterAccess aPrtAcc(*this);
+ OutputDevice* pOutDev = aPrtAcc.GetRefDev();
+
+ SAL_WARN_IF( !pOutDev, "starmath", "!! SmDocShell::ArrangeFormula: reference device missing !!");
+
+ // if necessary get another OutputDevice for which we format
+ if (!pOutDev)
+ {
+ SmViewShell *pView = SmGetActiveView();
+ if (pView)
+ pOutDev = &pView->GetGraphicWindow();
+ else
+ {
+ pOutDev = &SM_MOD()->GetDefaultVirtualDev();
+ pOutDev->SetMapMode( MapMode(MapUnit::Map100thMM) );
+ }
+ }
+ OSL_ENSURE(pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM,
+ "Sm : wrong MapMode");
+
+ const SmFormat &rFormat = GetFormat();
+ mpTree->Prepare(rFormat, *this, 0);
+
+ // format/draw formulas always from left to right,
+ // and numbers should not be converted
+ ComplexTextLayoutFlags nLayoutMode = pOutDev->GetLayoutMode();
+ pOutDev->SetLayoutMode( ComplexTextLayoutFlags::Default );
+ LanguageType nDigitLang = pOutDev->GetDigitLanguage();
+ pOutDev->SetDigitLanguage( LANGUAGE_ENGLISH );
+
+ mpTree->Arrange(*pOutDev, rFormat);
+
+ pOutDev->SetLayoutMode( nLayoutMode );
+ pOutDev->SetDigitLanguage( nDigitLang );
+
+ SetFormulaArranged(true);
+
+ // invalidate accessible text
+ maAccText.clear();
+}
+
+void SmDocShell::UpdateEditEngineDefaultFonts(const Color& aTextColor)
+{
+ assert(mpEditEngineItemPool);
+ if (!mpEditEngineItemPool)
+ return;
+
+ // set fonts to be used
+ struct FontDta {
+ LanguageType nFallbackLang;
+ LanguageType nLang;
+ DefaultFontType nFontType;
+ sal_uInt16 nFontInfoId;
+ } aTable[3] =
+ {
+ // info to get western font to be used
+ { LANGUAGE_ENGLISH_US, LANGUAGE_NONE,
+ DefaultFontType::FIXED, EE_CHAR_FONTINFO },
+ // info to get CJK font to be used
+ { LANGUAGE_JAPANESE, LANGUAGE_NONE,
+ DefaultFontType::CJK_TEXT, EE_CHAR_FONTINFO_CJK },
+ // info to get CTL font to be used
+ { LANGUAGE_ARABIC_SAUDI_ARABIA, LANGUAGE_NONE,
+ DefaultFontType::CTL_TEXT, EE_CHAR_FONTINFO_CTL }
+ };
+
+ aTable[0].nLang = maLinguOptions.nDefaultLanguage;
+ aTable[1].nLang = maLinguOptions.nDefaultLanguage_CJK;
+ aTable[2].nLang = maLinguOptions.nDefaultLanguage_CTL;
+
+ for (const FontDta & rFntDta : aTable)
+ {
+ LanguageType nLang = (LANGUAGE_NONE == rFntDta.nLang) ?
+ rFntDta.nFallbackLang : rFntDta.nLang;
+ vcl::Font aFont = OutputDevice::GetDefaultFont(
+ rFntDta.nFontType, nLang, GetDefaultFontFlags::OnlyOne );
+ aFont.SetColor(aTextColor);
+ mpEditEngineItemPool->SetPoolDefaultItem(
+ SvxFontItem( aFont.GetFamilyType(), aFont.GetFamilyName(),
+ aFont.GetStyleName(), aFont.GetPitch(), aFont.GetCharSet(),
+ rFntDta.nFontInfoId ) );
+ }
+
+ // set font heights
+ SvxFontHeightItem aFontHeigt(
+ Application::GetDefaultDevice()->LogicToPixel(
+ Size( 0, 11 ), MapMode( MapUnit::MapPoint ) ).Height(), 100,
+ EE_CHAR_FONTHEIGHT );
+ mpEditEngineItemPool->SetPoolDefaultItem( aFontHeigt );
+ aFontHeigt.SetWhich( EE_CHAR_FONTHEIGHT_CJK );
+ mpEditEngineItemPool->SetPoolDefaultItem( aFontHeigt );
+ aFontHeigt.SetWhich( EE_CHAR_FONTHEIGHT_CTL );
+ mpEditEngineItemPool->SetPoolDefaultItem( aFontHeigt );
+}
+
+EditEngine& SmDocShell::GetEditEngine()
+{
+ if (!mpEditEngine)
+ {
+ //!
+ //! see also SmEditWindow::DataChanged !
+ //!
+
+ mpEditEngineItemPool = EditEngine::CreatePool();
+
+ const StyleSettings& rStyleSettings = Application::GetDefaultDevice()->GetSettings().GetStyleSettings();
+ UpdateEditEngineDefaultFonts(rStyleSettings.GetFieldTextColor());
+
+ mpEditEngine.reset( new EditEngine( mpEditEngineItemPool ) );
+
+ mpEditEngine->SetAddExtLeading(true);
+
+ mpEditEngine->EnableUndo( true );
+ mpEditEngine->SetDefTab( sal_uInt16(
+ Application::GetDefaultDevice()->GetTextWidth("XXXX")) );
+
+ mpEditEngine->SetBackgroundColor(rStyleSettings.GetFieldColor());
+
+ mpEditEngine->SetControlWord(
+ (mpEditEngine->GetControlWord() | EEControlBits::AUTOINDENTING) &
+ EEControlBits(~EEControlBits::UNDOATTRIBS) &
+ EEControlBits(~EEControlBits::PASTESPECIAL) );
+
+ mpEditEngine->SetWordDelimiters(" .=+-*/(){}[];\"");
+ mpEditEngine->SetRefMapMode(MapMode(MapUnit::MapPixel));
+
+ mpEditEngine->SetPaperSize( Size( 800, 0 ) );
+
+ mpEditEngine->EraseVirtualDevice();
+
+ // set initial text if the document already has some...
+ // (may be the case when reloading a doc)
+ OUString aTxt( GetText() );
+ if (!aTxt.isEmpty())
+ mpEditEngine->SetText( aTxt );
+
+ mpEditEngine->ClearModifyFlag();
+
+ }
+ return *mpEditEngine;
+}
+
+
+void SmDocShell::DrawFormula(OutputDevice &rDev, Point &rPosition, bool bDrawSelection)
+{
+ if (!mpTree)
+ Parse();
+ OSL_ENSURE(mpTree, "Sm : NULL pointer");
+
+ ArrangeFormula();
+
+ // Problem: What happens to WYSIWYG? While we're active inplace, we don't have a reference
+ // device and aren't aligned to that either. So now there can be a difference between the
+ // VisArea (i.e. the size within the client) and the current size.
+ // Idea: The difference could be adapted with SmNod::SetSize (no long-term solution)
+
+ rPosition.AdjustX(maFormat.GetDistance( DIS_LEFTSPACE ) );
+ rPosition.AdjustY(maFormat.GetDistance( DIS_TOPSPACE ) );
+
+ //! in case of high contrast-mode (accessibility option!)
+ //! the draw mode needs to be set to default, because when embedding
+ //! Math for example in Calc in "a over b" the fraction bar may not
+ //! be visible else. More generally: the FillColor may have been changed.
+ DrawModeFlags nOldDrawMode = DrawModeFlags::Default;
+ bool bRestoreDrawMode = false;
+ if (OUTDEV_WINDOW == rDev.GetOutDevType() &&
+ static_cast<vcl::Window &>(rDev).GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ nOldDrawMode = rDev.GetDrawMode();
+ rDev.SetDrawMode( DrawModeFlags::Default );
+ bRestoreDrawMode = true;
+ }
+
+ // format/draw formulas always from left to right
+ // and numbers should not be converted
+ ComplexTextLayoutFlags nLayoutMode = rDev.GetLayoutMode();
+ rDev.SetLayoutMode( ComplexTextLayoutFlags::Default );
+ LanguageType nDigitLang = rDev.GetDigitLanguage();
+ rDev.SetDigitLanguage( LANGUAGE_ENGLISH );
+
+ //Set selection if any
+ if(mpCursor && bDrawSelection){
+ mpCursor->AnnotateSelection();
+ SmSelectionDrawingVisitor(rDev, mpTree.get(), rPosition);
+ }
+
+ //Drawing using visitor
+ SmDrawingVisitor(rDev, rPosition, mpTree.get());
+
+
+ rDev.SetLayoutMode( nLayoutMode );
+ rDev.SetDigitLanguage( nDigitLang );
+
+ if (bRestoreDrawMode)
+ rDev.SetDrawMode( nOldDrawMode );
+}
+
+Size SmDocShell::GetSize()
+{
+ Size aRet;
+
+ if (!mpTree)
+ Parse();
+
+ if (mpTree)
+ {
+ ArrangeFormula();
+ aRet = mpTree->GetSize();
+
+ if ( !aRet.Width() )
+ aRet.setWidth( 2000 );
+ else
+ aRet.AdjustWidth(maFormat.GetDistance( DIS_LEFTSPACE ) +
+ maFormat.GetDistance( DIS_RIGHTSPACE ) );
+ if ( !aRet.Height() )
+ aRet.setHeight( 1000 );
+ else
+ aRet.AdjustHeight(maFormat.GetDistance( DIS_TOPSPACE ) +
+ maFormat.GetDistance( DIS_BOTTOMSPACE ) );
+ }
+
+ return aRet;
+}
+
+void SmDocShell::InvalidateCursor(){
+ mpCursor.reset();
+}
+
+SmCursor& SmDocShell::GetCursor(){
+ if(!mpCursor)
+ mpCursor.reset(new SmCursor(mpTree.get(), this));
+ return *mpCursor;
+}
+
+bool SmDocShell::HasCursor() const { return mpCursor != nullptr; }
+
+SmPrinterAccess::SmPrinterAccess( SmDocShell &rDocShell )
+{
+ pPrinter = rDocShell.GetPrt();
+ if ( pPrinter )
+ {
+ pPrinter->Push( PushFlags::MAPMODE );
+ if ( SfxObjectCreateMode::EMBEDDED == rDocShell.GetCreateMode() )
+ {
+ // if it is an embedded object (without its own printer)
+ // we change the MapMode temporarily.
+ //!If it is a document with its own printer the MapMode should
+ //!be set correct (once) elsewhere(!), in order to avoid numerous
+ //!superfluous pushing and popping of the MapMode when using
+ //!this class.
+
+ const MapUnit eOld = pPrinter->GetMapMode().GetMapUnit();
+ if ( MapUnit::Map100thMM != eOld )
+ {
+ MapMode aMap( pPrinter->GetMapMode() );
+ aMap.SetMapUnit( MapUnit::Map100thMM );
+ Point aTmp( aMap.GetOrigin() );
+ aTmp.setX( OutputDevice::LogicToLogic( aTmp.X(), eOld, MapUnit::Map100thMM ) );
+ aTmp.setY( OutputDevice::LogicToLogic( aTmp.Y(), eOld, MapUnit::Map100thMM ) );
+ aMap.SetOrigin( aTmp );
+ pPrinter->SetMapMode( aMap );
+ }
+ }
+ }
+ pRefDev = rDocShell.GetRefDev();
+ if ( !pRefDev || pPrinter.get() == pRefDev.get() )
+ return;
+
+ pRefDev->Push( PushFlags::MAPMODE );
+ if ( SfxObjectCreateMode::EMBEDDED != rDocShell.GetCreateMode() )
+ return;
+
+ // if it is an embedded object (without its own printer)
+ // we change the MapMode temporarily.
+ //!If it is a document with its own printer the MapMode should
+ //!be set correct (once) elsewhere(!), in order to avoid numerous
+ //!superfluous pushing and popping of the MapMode when using
+ //!this class.
+
+ const MapUnit eOld = pRefDev->GetMapMode().GetMapUnit();
+ if ( MapUnit::Map100thMM != eOld )
+ {
+ MapMode aMap( pRefDev->GetMapMode() );
+ aMap.SetMapUnit( MapUnit::Map100thMM );
+ Point aTmp( aMap.GetOrigin() );
+ aTmp.setX( OutputDevice::LogicToLogic( aTmp.X(), eOld, MapUnit::Map100thMM ) );
+ aTmp.setY( OutputDevice::LogicToLogic( aTmp.Y(), eOld, MapUnit::Map100thMM ) );
+ aMap.SetOrigin( aTmp );
+ pRefDev->SetMapMode( aMap );
+ }
+}
+
+SmPrinterAccess::~SmPrinterAccess()
+{
+ if ( pPrinter )
+ pPrinter->Pop();
+ if ( pRefDev && pRefDev != pPrinter )
+ pRefDev->Pop();
+}
+
+Printer* SmDocShell::GetPrt()
+{
+ if (SfxObjectCreateMode::EMBEDDED == GetCreateMode())
+ {
+ // Normally the server provides the printer. But if it doesn't provide one (e.g. because
+ // there is no connection) it still can be the case that we know the printer because it
+ // has been passed on by the server in OnDocumentPrinterChanged and being kept temporarily.
+ Printer* pPrt = GetDocumentPrinter();
+ if (!pPrt && mpTmpPrinter)
+ pPrt = mpTmpPrinter;
+ return pPrt;
+ }
+ else if (!mpPrinter)
+ {
+ auto pOptions = std::make_unique<SfxItemSet>(
+ GetPool(),
+ svl::Items<
+ SID_PRINTTITLE, SID_PRINTZOOM,
+ SID_NO_RIGHT_SPACES, SID_SAVE_ONLY_USED_SYMBOLS,
+ SID_AUTO_CLOSE_BRACKETS, SID_AUTO_CLOSE_BRACKETS>{});
+ SmModule *pp = SM_MOD();
+ pp->GetConfig()->ConfigToItemSet(*pOptions);
+ mpPrinter = VclPtr<SfxPrinter>::Create(std::move(pOptions));
+ mpPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
+ }
+ return mpPrinter;
+}
+
+OutputDevice* SmDocShell::GetRefDev()
+{
+ if (SfxObjectCreateMode::EMBEDDED == GetCreateMode())
+ {
+ OutputDevice* pOutDev = GetDocumentRefDev();
+ if (pOutDev)
+ return pOutDev;
+ }
+
+ return GetPrt();
+}
+
+void SmDocShell::SetPrinter( SfxPrinter *pNew )
+{
+ mpPrinter.disposeAndClear();
+ mpPrinter = pNew; //Transfer ownership
+ mpPrinter->SetMapMode( MapMode(MapUnit::Map100thMM) );
+ SetFormulaArranged(false);
+ Repaint();
+}
+
+void SmDocShell::OnDocumentPrinterChanged( Printer *pPrt )
+{
+ mpTmpPrinter = pPrt;
+ SetFormulaArranged(false);
+ Size aOldSize = GetVisArea().GetSize();
+ Repaint();
+ if( aOldSize != GetVisArea().GetSize() && !maText.isEmpty() )
+ SetModified();
+ mpTmpPrinter = nullptr;
+}
+
+void SmDocShell::Repaint()
+{
+ bool bIsEnabled = IsEnableSetModified();
+ if (bIsEnabled)
+ EnableSetModified( false );
+
+ SetFormulaArranged(false);
+
+ Size aVisSize = GetSize();
+ SetVisAreaSize(aVisSize);
+ SmViewShell* pViewSh = SmGetActiveView();
+ if (pViewSh)
+ pViewSh->GetGraphicWindow().Invalidate();
+
+ if (bIsEnabled)
+ EnableSetModified(bIsEnabled);
+}
+
+SmDocShell::SmDocShell( SfxModelFlags i_nSfxCreationFlags )
+ : SfxObjectShell(i_nSfxCreationFlags)
+ , mpEditEngineItemPool(nullptr)
+ , mpPrinter(nullptr)
+ , mpTmpPrinter(nullptr)
+ , mnModifyCount(0)
+ , mbFormulaArranged(false)
+{
+ SvtLinguConfig().GetOptions(maLinguOptions);
+
+ SetPool(&SfxGetpApp()->GetPool());
+
+ SmModule *pp = SM_MOD();
+ maFormat = pp->GetConfig()->GetStandardFormat();
+
+ StartListening(maFormat);
+ StartListening(*pp->GetConfig());
+
+ SetBaseModel(new SmModel(this));
+}
+
+SmDocShell::~SmDocShell()
+{
+ SmModule *pp = SM_MOD();
+
+ EndListening(maFormat);
+ EndListening(*pp->GetConfig());
+
+ mpCursor.reset();
+ mpEditEngine.reset();
+ SfxItemPool::Free(mpEditEngineItemPool);
+ mpPrinter.disposeAndClear();
+}
+
+bool SmDocShell::ConvertFrom(SfxMedium &rMedium)
+{
+ bool bSuccess = false;
+ const OUString& rFltName = rMedium.GetFilter()->GetFilterName();
+
+ OSL_ENSURE( rFltName != STAROFFICE_XML, "Wrong filter!");
+
+ if ( rFltName == MATHML_XML )
+ {
+ if (mpTree)
+ {
+ mpTree.reset();
+ InvalidateCursor();
+ }
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLImportWrapper aEquation(xModel);
+ bSuccess = ( ERRCODE_NONE == aEquation.Import(rMedium) );
+ }
+ else
+ {
+ SvStream *pStream = rMedium.GetInStream();
+ if ( pStream )
+ {
+ if ( SotStorage::IsStorageFile( pStream ) )
+ {
+ tools::SvRef<SotStorage> aStorage = new SotStorage( pStream, false );
+ if ( aStorage->IsStream("Equation Native") )
+ {
+ // is this a MathType Storage?
+ OUStringBuffer aBuffer;
+ MathType aEquation(aBuffer);
+ bSuccess = aEquation.Parse( aStorage.get() );
+ if ( bSuccess )
+ {
+ maText = aBuffer.makeStringAndClear();
+ Parse();
+ }
+ }
+ }
+ }
+ }
+
+ if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ SetFormulaArranged( false );
+ Repaint();
+ }
+
+ FinishedLoading();
+ return bSuccess;
+}
+
+
+bool SmDocShell::InitNew( const uno::Reference < embed::XStorage >& xStorage )
+{
+ bool bRet = false;
+ if ( SfxObjectShell::InitNew( xStorage ) )
+ {
+ bRet = true;
+ SetVisArea(tools::Rectangle(Point(0, 0), Size(2000, 1000)));
+ }
+ return bRet;
+}
+
+
+bool SmDocShell::Load( SfxMedium& rMedium )
+{
+ bool bRet = false;
+ if( SfxObjectShell::Load( rMedium ))
+ {
+ uno::Reference < embed::XStorage > xStorage = GetMedium()->GetStorage();
+ if (xStorage->hasByName("content.xml") && xStorage->isStreamElement("content.xml"))
+ {
+ // is this a fabulous math package ?
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLImportWrapper aEquation(xModel);
+ auto nError = aEquation.Import(rMedium);
+ bRet = ERRCODE_NONE == nError;
+ SetError(nError);
+ }
+ }
+
+ if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ SetFormulaArranged( false );
+ Repaint();
+ }
+
+ FinishedLoading();
+ return bRet;
+}
+
+
+bool SmDocShell::Save()
+{
+ //! apply latest changes if necessary
+ UpdateText();
+
+ if ( SfxObjectShell::Save() )
+ {
+ if (!mpTree)
+ Parse();
+ if( mpTree )
+ ArrangeFormula();
+
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLExportWrapper aEquation(xModel);
+ aEquation.SetFlat(false);
+ return aEquation.Export(*GetMedium());
+ }
+
+ return false;
+}
+
+/*
+ * replace bad characters that can not be saved. (#i74144)
+ * */
+void SmDocShell::ReplaceBadChars()
+{
+ bool bReplace = false;
+
+ if (!mpEditEngine)
+ return;
+
+ OUStringBuffer aBuf( mpEditEngine->GetText() );
+
+ for (sal_Int32 i = 0; i < aBuf.getLength(); ++i)
+ {
+ if (aBuf[i] < ' ' && aBuf[i] != '\r' && aBuf[i] != '\n' && aBuf[i] != '\t')
+ {
+ aBuf[i] = ' ';
+ bReplace = true;
+ }
+ }
+
+ if (bReplace)
+ maText = aBuf.makeStringAndClear();
+}
+
+
+void SmDocShell::UpdateText()
+{
+ if (mpEditEngine && mpEditEngine->IsModified())
+ {
+ OUString aEngTxt( mpEditEngine->GetText() );
+ if (GetText() != aEngTxt)
+ SetText( aEngTxt );
+ }
+}
+
+
+bool SmDocShell::SaveAs( SfxMedium& rMedium )
+{
+ bool bRet = false;
+
+ //! apply latest changes if necessary
+ UpdateText();
+
+ if ( SfxObjectShell::SaveAs( rMedium ) )
+ {
+ if (!mpTree)
+ Parse();
+ if( mpTree )
+ ArrangeFormula();
+
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLExportWrapper aEquation(xModel);
+ aEquation.SetFlat(false);
+ bRet = aEquation.Export(rMedium);
+ }
+ return bRet;
+}
+
+bool SmDocShell::ConvertTo( SfxMedium &rMedium )
+{
+ bool bRet = false;
+ std::shared_ptr<const SfxFilter> pFlt = rMedium.GetFilter();
+ if( pFlt )
+ {
+ if( !mpTree )
+ Parse();
+ if( mpTree )
+ ArrangeFormula();
+
+ const OUString& rFltName = pFlt->GetFilterName();
+ if(rFltName == STAROFFICE_XML)
+ {
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLExportWrapper aEquation(xModel);
+ aEquation.SetFlat(false);
+ bRet = aEquation.Export(rMedium);
+ }
+ else if(rFltName == MATHML_XML)
+ {
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLExportWrapper aEquation(xModel);
+ aEquation.SetFlat(true);
+ bRet = aEquation.Export(rMedium);
+ }
+ else if (pFlt->GetFilterName() == "MathType 3.x")
+ bRet = WriteAsMathType3( rMedium );
+ }
+ return bRet;
+}
+
+void SmDocShell::writeFormulaOoxml(
+ ::sax_fastparser::FSHelperPtr const& pSerializer,
+ oox::core::OoxmlVersion const version,
+ oox::drawingml::DocumentType const documentType,
+ const sal_Int8 nAlign)
+{
+ if( !mpTree )
+ Parse();
+ if( mpTree )
+ ArrangeFormula();
+ SmOoxmlExport aEquation(mpTree.get(), version, documentType);
+ if(documentType == oox::drawingml::DOCUMENT_DOCX)
+ aEquation.ConvertFromStarMath( pSerializer, nAlign);
+ else
+ aEquation.ConvertFromStarMath(pSerializer, oox::FormulaExportBase::eFormulaAlign::INLINE);
+}
+
+void SmDocShell::writeFormulaRtf(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding)
+{
+ if (!mpTree)
+ Parse();
+ if (mpTree)
+ ArrangeFormula();
+ SmRtfExport aEquation(mpTree.get());
+ aEquation.ConvertFromStarMath(rBuffer, nEncoding);
+}
+
+void SmDocShell::readFormulaOoxml( oox::formulaimport::XmlStream& stream )
+{
+ SmOoxmlImport aEquation( stream );
+ SetText( aEquation.ConvertToStarMath());
+}
+
+void SmDocShell::Execute(SfxRequest& rReq)
+{
+ switch (rReq.GetSlot())
+ {
+ case SID_TEXTMODE:
+ {
+ SmFormat aOldFormat = GetFormat();
+ SmFormat aNewFormat( aOldFormat );
+ aNewFormat.SetTextmode(!aOldFormat.IsTextmode());
+
+ SfxUndoManager *pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat( aNewFormat );
+ Repaint();
+ }
+ break;
+
+ case SID_AUTO_REDRAW :
+ {
+ SmModule *pp = SM_MOD();
+ bool bRedraw = pp->GetConfig()->IsAutoRedraw();
+ pp->GetConfig()->SetAutoRedraw(!bRedraw);
+ }
+ break;
+
+ case SID_LOADSYMBOLS:
+ LoadSymbols();
+ break;
+
+ case SID_SAVESYMBOLS:
+ SaveSymbols();
+ break;
+
+ case SID_FONT:
+ {
+ // get device used to retrieve the FontList
+ OutputDevice *pDev = GetPrinter();
+ if (!pDev || pDev->GetDevFontCount() == 0)
+ pDev = &SM_MOD()->GetDefaultVirtualDev();
+ OSL_ENSURE (pDev, "device for font list missing" );
+
+ SmFontTypeDialog aFontTypeDialog(rReq.GetFrameWeld(), pDev);
+
+ SmFormat aOldFormat = GetFormat();
+ aFontTypeDialog.ReadFrom( aOldFormat );
+ if (aFontTypeDialog.run() == RET_OK)
+ {
+ SmFormat aNewFormat( aOldFormat );
+
+ aFontTypeDialog.WriteTo(aNewFormat);
+ SfxUndoManager *pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat( aNewFormat );
+ Repaint();
+ }
+ }
+ break;
+
+ case SID_FONTSIZE:
+ {
+ SmFontSizeDialog aFontSizeDialog(rReq.GetFrameWeld());
+
+ SmFormat aOldFormat = GetFormat();
+ aFontSizeDialog.ReadFrom( aOldFormat );
+ if (aFontSizeDialog.run() == RET_OK)
+ {
+ SmFormat aNewFormat( aOldFormat );
+
+ aFontSizeDialog.WriteTo(aNewFormat);
+
+ SfxUndoManager *pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat( aNewFormat );
+ Repaint();
+ }
+ }
+ break;
+
+ case SID_DISTANCE:
+ {
+ SmDistanceDialog aDistanceDialog(rReq.GetFrameWeld());
+
+ SmFormat aOldFormat = GetFormat();
+ aDistanceDialog.ReadFrom( aOldFormat );
+ if (aDistanceDialog.run() == RET_OK)
+ {
+ SmFormat aNewFormat( aOldFormat );
+
+ aDistanceDialog.WriteTo(aNewFormat);
+
+ SfxUndoManager *pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat( aNewFormat );
+ Repaint();
+ }
+ }
+ break;
+
+ case SID_ALIGN:
+ {
+ SmAlignDialog aAlignDialog(rReq.GetFrameWeld());
+
+ SmFormat aOldFormat = GetFormat();
+ aAlignDialog.ReadFrom( aOldFormat );
+ if (aAlignDialog.run() == RET_OK)
+ {
+ SmFormat aNewFormat( aOldFormat );
+
+ aAlignDialog.WriteTo(aNewFormat);
+
+ SmModule *pp = SM_MOD();
+ SmFormat aFmt( pp->GetConfig()->GetStandardFormat() );
+ aAlignDialog.WriteTo( aFmt );
+ pp->GetConfig()->SetStandardFormat( aFmt );
+
+ SfxUndoManager *pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat( aNewFormat );
+ Repaint();
+ }
+ }
+ break;
+
+ case SID_TEXT:
+ {
+ const SfxStringItem& rItem = static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_TEXT));
+ if (GetText() != rItem.GetValue())
+ SetText(rItem.GetValue());
+ }
+ break;
+
+ case SID_UNDO:
+ case SID_REDO:
+ {
+ SfxUndoManager* pTmpUndoMgr = GetUndoManager();
+ if( pTmpUndoMgr )
+ {
+ sal_uInt16 nId = rReq.GetSlot(), nCnt = 1;
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem;
+ if( pArgs && SfxItemState::SET == pArgs->GetItemState( nId, false, &pItem ))
+ nCnt = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+
+ bool (SfxUndoManager:: *fnDo)();
+
+ size_t nCount;
+ if( SID_UNDO == rReq.GetSlot() )
+ {
+ nCount = pTmpUndoMgr->GetUndoActionCount();
+ fnDo = &SfxUndoManager::Undo;
+ }
+ else
+ {
+ nCount = pTmpUndoMgr->GetRedoActionCount();
+ fnDo = &SfxUndoManager::Redo;
+ }
+
+ try
+ {
+ for( ; nCnt && nCount; --nCnt, --nCount )
+ (pTmpUndoMgr->*fnDo)();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("starmath");
+ }
+ }
+ Repaint();
+ UpdateText();
+ SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this );
+ while( pFrm )
+ {
+ SfxBindings& rBind = pFrm->GetBindings();
+ rBind.Invalidate(SID_UNDO);
+ rBind.Invalidate(SID_REDO);
+ rBind.Invalidate(SID_REPEAT);
+ rBind.Invalidate(SID_CLEARHISTORY);
+ pFrm = SfxViewFrame::GetNext( *pFrm, this );
+ }
+ }
+ break;
+ }
+
+ rReq.Done();
+}
+
+
+void SmDocShell::GetState(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter(rSet);
+
+ for (sal_uInt16 nWh = aIter.FirstWhich(); 0 != nWh; nWh = aIter.NextWhich())
+ {
+ switch (nWh)
+ {
+ case SID_TEXTMODE:
+ rSet.Put(SfxBoolItem(SID_TEXTMODE, GetFormat().IsTextmode()));
+ break;
+
+ case SID_DOCTEMPLATE :
+ rSet.DisableItem(SID_DOCTEMPLATE);
+ break;
+
+ case SID_AUTO_REDRAW :
+ {
+ SmModule *pp = SM_MOD();
+ bool bRedraw = pp->GetConfig()->IsAutoRedraw();
+
+ rSet.Put(SfxBoolItem(SID_AUTO_REDRAW, bRedraw));
+ }
+ break;
+
+ case SID_MODIFYSTATUS:
+ {
+ sal_Unicode cMod = ' ';
+ if (IsModified())
+ cMod = '*';
+ rSet.Put(SfxStringItem(SID_MODIFYSTATUS, OUString(cMod)));
+ }
+ break;
+
+ case SID_TEXT:
+ rSet.Put(SfxStringItem(SID_TEXT, GetText()));
+ break;
+
+ case SID_GAPHIC_SM:
+ //! very old (pre UNO) and ugly hack to invalidate the SmGraphicWindow.
+ //! If mnModifyCount gets changed then the call below will implicitly notify
+ //! SmGraphicController::StateChanged and there the window gets invalidated.
+ //! Thus all the 'mnModifyCount++' before invalidating this slot.
+ rSet.Put(SfxInt16Item(SID_GAPHIC_SM, mnModifyCount));
+ break;
+
+ case SID_UNDO:
+ case SID_REDO:
+ {
+ SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this );
+ if( pFrm )
+ pFrm->GetSlotState( nWh, nullptr, &rSet );
+ else
+ rSet.DisableItem( nWh );
+ }
+ break;
+
+ case SID_GETUNDOSTRINGS:
+ case SID_GETREDOSTRINGS:
+ {
+ SfxUndoManager* pTmpUndoMgr = GetUndoManager();
+ if( pTmpUndoMgr )
+ {
+ OUString(SfxUndoManager:: *fnGetComment)( size_t, bool const ) const;
+
+ size_t nCount;
+ if( SID_GETUNDOSTRINGS == nWh )
+ {
+ nCount = pTmpUndoMgr->GetUndoActionCount();
+ fnGetComment = &SfxUndoManager::GetUndoActionComment;
+ }
+ else
+ {
+ nCount = pTmpUndoMgr->GetRedoActionCount();
+ fnGetComment = &SfxUndoManager::GetRedoActionComment;
+ }
+ if (nCount)
+ {
+ OUStringBuffer aBuf;
+ for (size_t n = 0; n < nCount; ++n)
+ {
+ aBuf.append((pTmpUndoMgr->*fnGetComment)( n, SfxUndoManager::TopLevel ));
+ aBuf.append('\n');
+ }
+
+ SfxStringListItem aItem( nWh );
+ aItem.SetString( aBuf.makeStringAndClear() );
+ rSet.Put( aItem );
+ }
+ }
+ else
+ rSet.DisableItem( nWh );
+ }
+ break;
+ }
+ }
+}
+
+
+SfxUndoManager *SmDocShell::GetUndoManager()
+{
+ if (!mpEditEngine)
+ GetEditEngine();
+ return &mpEditEngine->GetUndoManager();
+}
+
+
+void SmDocShell::SaveSymbols()
+{
+ SmModule *pp = SM_MOD();
+ pp->GetSymbolManager().Save();
+}
+
+
+void SmDocShell::Draw(OutputDevice *pDevice,
+ const JobSetup &,
+ sal_uInt16 /*nAspect*/)
+{
+ pDevice->IntersectClipRegion(GetVisArea());
+ Point atmppoint;
+ DrawFormula(*pDevice, atmppoint);
+}
+
+SfxItemPool& SmDocShell::GetPool()
+{
+ return SfxGetpApp()->GetPool();
+}
+
+void SmDocShell::SetVisArea(const tools::Rectangle & rVisArea)
+{
+ tools::Rectangle aNewRect(rVisArea);
+
+ aNewRect.SetPos(Point());
+
+ if (aNewRect.IsWidthEmpty())
+ aNewRect.SetRight( 2000 );
+ if (aNewRect.IsHeightEmpty())
+ aNewRect.SetBottom( 1000 );
+
+ bool bIsEnabled = IsEnableSetModified();
+ if ( bIsEnabled )
+ EnableSetModified( false );
+
+ //TODO/LATER: it's unclear how this interacts with the SFX code
+ // If outplace editing, then don't resize the OutplaceWindow. But the
+ // ObjectShell has to resize.
+ bool bUnLockFrame;
+ if( GetCreateMode() == SfxObjectCreateMode::EMBEDDED && !IsInPlaceActive() && GetFrame() )
+ {
+ GetFrame()->LockAdjustPosSizePixel();
+ bUnLockFrame = true;
+ }
+ else
+ bUnLockFrame = false;
+
+ SfxObjectShell::SetVisArea( aNewRect );
+
+ if( bUnLockFrame )
+ GetFrame()->UnlockAdjustPosSizePixel();
+
+ if ( bIsEnabled )
+ EnableSetModified( bIsEnabled );
+}
+
+
+void SmDocShell::FillClass(SvGlobalName* pClassName,
+ SotClipboardFormatId* pFormat,
+ OUString* pFullTypeName,
+ sal_Int32 nFileFormat,
+ bool bTemplate /* = false */) const
+{
+ if (nFileFormat == SOFFICE_FILEFORMAT_60 )
+ {
+ *pClassName = SvGlobalName(SO3_SM_CLASSID_60);
+ *pFormat = SotClipboardFormatId::STARMATH_60;
+ *pFullTypeName = SmResId(STR_MATH_DOCUMENT_FULLTYPE_CURRENT);
+ }
+ else if (nFileFormat == SOFFICE_FILEFORMAT_8 )
+ {
+ *pClassName = SvGlobalName(SO3_SM_CLASSID_60);
+ *pFormat = bTemplate ? SotClipboardFormatId::STARMATH_8_TEMPLATE : SotClipboardFormatId::STARMATH_8;
+ *pFullTypeName = SmResId(STR_MATH_DOCUMENT_FULLTYPE_CURRENT);
+ }
+}
+
+void SmDocShell::SetModified(bool bModified)
+{
+ if( IsEnableSetModified() )
+ {
+ SfxObjectShell::SetModified( bModified );
+ Broadcast(SfxHint(SfxHintId::DocChanged));
+ }
+}
+
+bool SmDocShell::WriteAsMathType3( SfxMedium& rMedium )
+{
+ OUStringBuffer aTextAsBuffer(maText);
+ MathType aEquation(aTextAsBuffer, mpTree.get());
+ return aEquation.ConvertFromStarMath( rMedium );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/edit.cxx b/starmath/source/edit.cxx
new file mode 100644
index 000000000..755bb3b5c
--- /dev/null
+++ b/starmath/source/edit.cxx
@@ -0,0 +1,999 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <starmath.hrc>
+#include <helpids.h>
+
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/scrbar.hxx>
+#include <vcl/settings.hxx>
+
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/stritem.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <svtools/colorcfg.hxx>
+#include <osl/diagnose.h>
+
+#include <edit.hxx>
+#include <smmod.hxx>
+#include <view.hxx>
+#include <document.hxx>
+#include "cfgitem.hxx"
+#include "accessibility.hxx"
+#include <memory>
+
+#define SCROLL_LINE 24
+
+
+using namespace com::sun::star::accessibility;
+using namespace com::sun::star;
+
+
+void SmGetLeftSelectionPart(const ESelection &rSel,
+ sal_Int32 &nPara, sal_uInt16 &nPos)
+ // returns paragraph number and position of the selections left part
+{
+ // compare start and end of selection and use the one that comes first
+ if ( rSel.nStartPara < rSel.nEndPara
+ || (rSel.nStartPara == rSel.nEndPara && rSel.nStartPos < rSel.nEndPos) )
+ { nPara = rSel.nStartPara;
+ nPos = rSel.nStartPos;
+ }
+ else
+ { nPara = rSel.nEndPara;
+ nPos = rSel.nEndPos;
+ }
+}
+
+bool SmEditWindow::IsInlineEditEnabled()
+{
+ SmViewShell *pView = GetView();
+ return pView && pView->IsInlineEditEnabled();
+}
+
+
+SmEditWindow::SmEditWindow( SmCmdBoxWindow &rMyCmdBoxWin ) :
+ Window (&rMyCmdBoxWin, WB_BORDER),
+ DropTargetHelper ( this ),
+ rCmdBox (rMyCmdBoxWin),
+ aModifyIdle ("SmEditWindow ModifyIdle"),
+ aCursorMoveIdle ("SmEditWindow CursorMoveIdle")
+{
+ set_id("math_edit");
+ SetHelpId(HID_SMA_COMMAND_WIN_EDIT);
+ SetMapMode(MapMode(MapUnit::MapPixel));
+
+ // Even RTL languages don't use RTL for math
+ EnableRTL( false );
+
+ // compare DataChanged
+ SetBackground( GetSettings().GetStyleSettings().GetWindowColor() );
+
+ aModifyIdle.SetInvokeHandler(LINK(this, SmEditWindow, ModifyTimerHdl));
+ aModifyIdle.SetPriority(TaskPriority::LOWEST);
+
+ if (!IsInlineEditEnabled())
+ {
+ aCursorMoveIdle.SetInvokeHandler(LINK(this, SmEditWindow, CursorMoveTimerHdl));
+ aCursorMoveIdle.SetPriority(TaskPriority::LOWEST);
+ }
+
+ // if not called explicitly the this edit window within the
+ // command window will just show an empty gray panel.
+ Show();
+}
+
+
+SmEditWindow::~SmEditWindow()
+{
+ disposeOnce();
+}
+
+void SmEditWindow::dispose()
+{
+ aModifyIdle.Stop();
+
+ StartCursorMove();
+
+ // clean up of classes used for accessibility
+ // must be done before EditView (and thus EditEngine) is no longer
+ // available for those classes.
+ if (mxAccessible.is())
+ {
+ mxAccessible->ClearWin(); // make Accessible nonfunctional
+ mxAccessible.clear();
+ }
+
+ if (pEditView)
+ {
+ EditEngine *pEditEngine = pEditView->GetEditEngine();
+ if (pEditEngine)
+ {
+ pEditEngine->SetStatusEventHdl( Link<EditStatus&,void>() );
+ pEditEngine->RemoveView( pEditView.get() );
+ }
+ pEditView.reset();
+ }
+
+ pHScrollBar.disposeAndClear();
+ pVScrollBar.disposeAndClear();
+ pScrollBox.disposeAndClear();
+
+ DropTargetHelper::dispose();
+ vcl::Window::dispose();
+}
+
+void SmEditWindow::StartCursorMove()
+{
+ if (!IsInlineEditEnabled())
+ aCursorMoveIdle.Stop();
+}
+
+void SmEditWindow::InvalidateSlots()
+{
+ SfxBindings& rBind = GetView()->GetViewFrame()->GetBindings();
+ rBind.Invalidate(SID_COPY);
+ rBind.Invalidate(SID_CUT);
+ rBind.Invalidate(SID_DELETE);
+}
+
+SmViewShell * SmEditWindow::GetView()
+{
+ return rCmdBox.GetView();
+}
+
+
+SmDocShell * SmEditWindow::GetDoc()
+{
+ SmViewShell *pView = rCmdBox.GetView();
+ return pView ? pView->GetDoc() : nullptr;
+}
+
+EditView * SmEditWindow::GetEditView()
+{
+ return pEditView.get();
+}
+
+EditEngine * SmEditWindow::GetEditEngine()
+{
+ EditEngine *pEditEng = nullptr;
+ if (pEditView)
+ pEditEng = pEditView->GetEditEngine();
+ else
+ {
+ SmDocShell *pDoc = GetDoc();
+ if (pDoc)
+ pEditEng = &pDoc->GetEditEngine();
+ }
+ return pEditEng;
+}
+
+void SmEditWindow::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ rRenderContext.SetBackground(rStyleSettings.GetWindowColor());
+}
+
+void SmEditWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ if (!((rDCEvt.GetType() == DataChangedEventType::FONTS) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
+ ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))))
+ return;
+
+ EditEngine *pEditEngine = GetEditEngine();
+ SmDocShell *pDoc = GetDoc();
+
+ if (pEditEngine && pDoc)
+ {
+ //!
+ //! see also SmDocShell::GetEditEngine() !
+ //!
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+
+ pDoc->UpdateEditEngineDefaultFonts(rStyleSettings.GetFieldTextColor());
+ pEditEngine->SetBackgroundColor(rStyleSettings.GetFieldColor());
+ pEditEngine->SetDefTab(sal_uInt16(GetTextWidth("XXXX")));
+
+ // forces new settings to be used
+ // unfortunately this resets the whole edit engine
+ // thus we need to save at least the text
+ OUString aTxt( pEditEngine->GetText() );
+ pEditEngine->Clear(); //incorrect font size
+ pEditEngine->SetText( aTxt );
+
+ AdjustScrollBars();
+ Resize();
+ }
+
+ Invalidate();
+}
+
+IMPL_LINK_NOARG( SmEditWindow, ModifyTimerHdl, Timer *, void )
+{
+ UpdateStatus(false);
+ aModifyIdle.Stop();
+}
+
+IMPL_LINK_NOARG(SmEditWindow, CursorMoveTimerHdl, Timer *, void)
+ // every once in a while check cursor position (selection) of edit
+ // window and if it has changed (try to) set the formula-cursor
+ // according to that.
+{
+ if (IsInlineEditEnabled())
+ return;
+
+ ESelection aNewSelection(GetSelection());
+
+ if (aNewSelection != aOldSelection)
+ {
+ SmViewShell *pView = rCmdBox.GetView();
+ if (pView)
+ {
+ // get row and column to look for
+ sal_Int32 nRow;
+ sal_uInt16 nCol;
+ SmGetLeftSelectionPart(aNewSelection, nRow, nCol);
+ nRow++;
+ nCol++;
+ pView->GetGraphicWindow().SetCursorPos(static_cast<sal_uInt16>(nRow), nCol);
+ aOldSelection = aNewSelection;
+ }
+ }
+ aCursorMoveIdle.Stop();
+}
+
+void SmEditWindow::Resize()
+{
+ if (!pEditView)
+ CreateEditView();
+
+ if (pEditView)
+ {
+ pEditView->SetOutputArea(AdjustScrollBars());
+ pEditView->ShowCursor();
+
+ OSL_ENSURE( pEditView->GetEditEngine(), "EditEngine missing" );
+ const long nMaxVisAreaStart = pEditView->GetEditEngine()->GetTextHeight() -
+ pEditView->GetOutputArea().GetHeight();
+ if (pEditView->GetVisArea().Top() > nMaxVisAreaStart)
+ {
+ tools::Rectangle aVisArea(pEditView->GetVisArea() );
+ aVisArea.SetTop( std::max<long>(nMaxVisAreaStart, 0) );
+ aVisArea.SetSize(pEditView->GetOutputArea().GetSize());
+ pEditView->SetVisArea(aVisArea);
+ pEditView->ShowCursor();
+ }
+ InitScrollBars();
+ }
+ Invalidate();
+}
+
+void SmEditWindow::MouseButtonUp(const MouseEvent &rEvt)
+{
+ if (pEditView)
+ pEditView->MouseButtonUp(rEvt);
+ else
+ Window::MouseButtonUp (rEvt);
+
+ if (!IsInlineEditEnabled())
+ CursorMoveTimerHdl(&aCursorMoveIdle);
+ InvalidateSlots();
+}
+
+void SmEditWindow::MouseButtonDown(const MouseEvent &rEvt)
+{
+ if (pEditView)
+ pEditView->MouseButtonDown(rEvt);
+ else
+ Window::MouseButtonDown (rEvt);
+
+ GrabFocus();
+}
+
+void SmEditWindow::Command(const CommandEvent& rCEvt)
+{
+ //pass alt press/release to parent impl
+ if (rCEvt.GetCommand() == CommandEventId::ModKeyChange)
+ {
+ Window::Command(rCEvt);
+ return;
+ }
+
+ bool bForwardEvt = true;
+ if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
+ {
+ GetParent()->ToTop();
+
+ Point aPoint = rCEvt.GetMousePosPixel();
+ SmViewShell* pViewSh = rCmdBox.GetView();
+ if (pViewSh)
+ pViewSh->GetViewFrame()->GetDispatcher()->ExecutePopup("edit", this, &aPoint);
+ bForwardEvt = false;
+ }
+ else if (rCEvt.GetCommand() == CommandEventId::Wheel)
+ bForwardEvt = !HandleWheelCommands( rCEvt );
+
+ if (bForwardEvt)
+ {
+ if (pEditView)
+ pEditView->Command( rCEvt );
+ else
+ Window::Command (rCEvt);
+ }
+}
+
+bool SmEditWindow::HandleWheelCommands( const CommandEvent &rCEvt )
+{
+ bool bCommandHandled = false; // true if the CommandEvent needs not
+ // to be passed on (because it has fully
+ // been taken care of).
+
+ const CommandWheelData* pWData = rCEvt.GetWheelData();
+ if (pWData)
+ {
+ if (CommandWheelMode::ZOOM == pWData->GetMode())
+ bCommandHandled = true; // no zooming in Command window
+ else
+ bCommandHandled = HandleScrollCommand( rCEvt, pHScrollBar.get(), pVScrollBar.get());
+ }
+
+ return bCommandHandled;
+}
+
+void SmEditWindow::KeyInput(const KeyEvent& rKEvt)
+{
+ if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)
+ {
+ bool bCallBase = true;
+ SfxViewShell* pViewShell = GetView();
+ if ( dynamic_cast<const SmViewShell *>(pViewShell) )
+ {
+ // Terminate possible InPlace mode
+ bCallBase = !pViewShell->Escape();
+ }
+ if ( bCallBase )
+ Window::KeyInput( rKEvt );
+ }
+ else
+ {
+ StartCursorMove();
+
+ bool autoClose = false;
+ if (!pEditView)
+ CreateEditView();
+ ESelection aSelection = pEditView->GetSelection();
+ // as we don't support RTL in Math, we need to swap values from selection when they were done
+ // in RTL form
+ aSelection.Adjust();
+ OUString selected = pEditView->GetEditEngine()->GetText(aSelection);
+
+ // Check is auto close brackets/braces is disabled
+ SmModule *pMod = SM_MOD();
+ if (pMod && !pMod->GetConfig()->IsAutoCloseBrackets())
+ autoClose = false;
+ else if (selected.trim() == "<?>")
+ autoClose = true;
+ else if (selected.isEmpty() && !aSelection.HasRange())
+ {
+ selected = pEditView->GetEditEngine()->GetText(aSelection.nEndPara);
+ if (!selected.isEmpty())
+ {
+ sal_Int32 index = selected.indexOf("\n", aSelection.nEndPos);
+ if (index != -1)
+ {
+ selected = selected.copy(index, sal_Int32(aSelection.nEndPos-index));
+ if (selected.trim().isEmpty())
+ autoClose = true;
+ }
+ else
+ {
+ sal_Int32 length = selected.getLength();
+ if (aSelection.nEndPos == length)
+ autoClose = true;
+ else
+ {
+ selected = selected.copy(aSelection.nEndPos);
+ if (selected.trim().isEmpty())
+ autoClose = true;
+ }
+ }
+ }
+ else
+ autoClose = true;
+ }
+
+ if ( !pEditView->PostKeyEvent(rKEvt) )
+ {
+ SmViewShell *pView = GetView();
+ if ( pView && !pView->KeyInput(rKEvt) )
+ {
+ // F1 (help) leads to the destruction of this
+ Flush();
+ if ( aModifyIdle.IsActive() )
+ aModifyIdle.Stop();
+ Window::KeyInput(rKEvt);
+ }
+ else
+ {
+ // SFX has maybe called a slot of the view and thus (because of a hack in SFX)
+ // set the focus to the view
+ SfxViewShell* pVShell = GetView();
+ if ( dynamic_cast<const SmViewShell *>(pVShell) &&
+ static_cast<SmViewShell*>(pVShell)->GetGraphicWindow().HasFocus() )
+ {
+ GrabFocus();
+ }
+ }
+ }
+ else
+ {
+ // have doc-shell modified only for formula input/change and not
+ // cursor travelling and such things...
+ SmDocShell *pDocShell = GetDoc();
+ EditEngine *pEditEngine = GetEditEngine();
+ if (pDocShell && pEditEngine)
+ pDocShell->SetModified(pEditEngine->IsModified());
+ aModifyIdle.Start();
+ }
+
+ // get the current char of the key event
+ sal_Unicode cCharCode = rKEvt.GetCharCode();
+ OUString sClose;
+
+ if (cCharCode == '{')
+ sClose = " }";
+ else if (cCharCode == '[')
+ sClose = " ]";
+ else if (cCharCode == '(')
+ sClose = " )";
+
+ // auto close the current character only when needed
+ if (!sClose.isEmpty() && autoClose)
+ {
+ pEditView->InsertText(sClose);
+ // position it at center of brackets
+ aSelection.nStartPos += 2;
+ aSelection.nEndPos = aSelection.nStartPos;
+ pEditView->SetSelection(aSelection);
+ }
+
+ InvalidateSlots();
+ }
+}
+
+void SmEditWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ if (!pEditView)
+ CreateEditView();
+ pEditView->Paint(rRect, &rRenderContext);
+}
+
+void SmEditWindow::CreateEditView()
+{
+ EditEngine *pEditEngine = GetEditEngine();
+
+ //! pEditEngine and pEditView may be 0.
+ //! For example when the program is used by the document-converter
+ if (pEditView || !pEditEngine)
+ return;
+
+ pEditView.reset(new EditView(pEditEngine, this));
+ pEditEngine->InsertView( pEditView.get() );
+
+ if (!pVScrollBar)
+ pVScrollBar = VclPtr<ScrollBar>::Create(this, WinBits(WB_VSCROLL));
+ if (!pHScrollBar)
+ pHScrollBar = VclPtr<ScrollBar>::Create(this, WinBits(WB_HSCROLL));
+ if (!pScrollBox)
+ pScrollBox = VclPtr<ScrollBarBox>::Create(this);
+ pVScrollBar->SetScrollHdl(LINK(this, SmEditWindow, ScrollHdl));
+ pHScrollBar->SetScrollHdl(LINK(this, SmEditWindow, ScrollHdl));
+ pVScrollBar->EnableDrag();
+ pHScrollBar->EnableDrag();
+
+ pEditView->SetOutputArea(AdjustScrollBars());
+
+ ESelection eSelection;
+
+ pEditView->SetSelection(eSelection);
+ PaintImmediately();
+ pEditView->ShowCursor();
+
+ pEditEngine->SetStatusEventHdl( LINK(this, SmEditWindow, EditStatusHdl) );
+ SetPointer(pEditView->GetPointer());
+
+ SetScrollBarRanges();
+}
+
+
+IMPL_LINK_NOARG( SmEditWindow, EditStatusHdl, EditStatus&, void )
+{
+ if (pEditView)
+ Resize();
+}
+
+IMPL_LINK( SmEditWindow, ScrollHdl, ScrollBar *, /*pScrollBar*/, void )
+{
+ OSL_ENSURE(pEditView, "EditView missing");
+ if (pEditView)
+ {
+ pEditView->SetVisArea(tools::Rectangle(Point(pHScrollBar->GetThumbPos(),
+ pVScrollBar->GetThumbPos()),
+ pEditView->GetVisArea().GetSize()));
+ pEditView->Invalidate();
+ }
+}
+
+tools::Rectangle SmEditWindow::AdjustScrollBars()
+{
+ const Size aOut( GetOutputSizePixel() );
+ tools::Rectangle aRect( Point(), aOut );
+
+ if (pVScrollBar && pHScrollBar && pScrollBox)
+ {
+ const long nTmp = GetSettings().GetStyleSettings().GetScrollBarSize();
+ Point aPt( aRect.TopRight() ); aPt.AdjustX( -(nTmp -1) );
+ pVScrollBar->SetPosSizePixel( aPt, Size(nTmp, aOut.Height() - nTmp));
+
+ aPt = aRect.BottomLeft(); aPt.AdjustY( -(nTmp - 1) );
+ pHScrollBar->SetPosSizePixel( aPt, Size(aOut.Width() - nTmp, nTmp));
+
+ aPt.setX( pHScrollBar->GetSizePixel().Width() );
+ aPt.setY( pVScrollBar->GetSizePixel().Height() );
+ pScrollBox->SetPosSizePixel(aPt, Size(nTmp, nTmp ));
+
+ aRect.SetRight( aPt.X() - 2 );
+ aRect.SetBottom( aPt.Y() - 2 );
+ }
+ return aRect;
+}
+
+void SmEditWindow::SetScrollBarRanges()
+{
+ // Extra method, not InitScrollBars, since it's also being used for EditEngine events
+ EditEngine *pEditEngine = GetEditEngine();
+ if (pVScrollBar && pHScrollBar && pEditEngine && pEditView)
+ {
+ long nTmp = pEditEngine->GetTextHeight();
+ pVScrollBar->SetRange(Range(0, nTmp));
+ pVScrollBar->SetThumbPos(pEditView->GetVisArea().Top());
+
+ nTmp = pEditEngine->GetPaperSize().Width();
+ pHScrollBar->SetRange(Range(0,nTmp));
+ pHScrollBar->SetThumbPos(pEditView->GetVisArea().Left());
+ }
+}
+
+void SmEditWindow::InitScrollBars()
+{
+ if (!(pVScrollBar && pHScrollBar && pScrollBox && pEditView))
+ return;
+
+ const Size aOut( pEditView->GetOutputArea().GetSize() );
+ pVScrollBar->SetVisibleSize(aOut.Height());
+ pVScrollBar->SetPageSize(aOut.Height() * 8 / 10);
+ pVScrollBar->SetLineSize(aOut.Height() * 2 / 10);
+
+ pHScrollBar->SetVisibleSize(aOut.Width());
+ pHScrollBar->SetPageSize(aOut.Width() * 8 / 10);
+ pHScrollBar->SetLineSize(SCROLL_LINE );
+
+ SetScrollBarRanges();
+
+ pVScrollBar->Show();
+ pHScrollBar->Show();
+ pScrollBox->Show();
+}
+
+
+OUString SmEditWindow::GetText() const
+{
+ OUString aText;
+ EditEngine *pEditEngine = const_cast< SmEditWindow* >(this)->GetEditEngine();
+ OSL_ENSURE( pEditEngine, "EditEngine missing" );
+ if (pEditEngine)
+ aText = pEditEngine->GetText();
+ return aText;
+}
+
+
+void SmEditWindow::SetText(const OUString& rText)
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ OSL_ENSURE( pEditEngine, "EditEngine missing" );
+ if (!pEditEngine || pEditEngine->IsModified())
+ return;
+
+ if (!pEditView)
+ CreateEditView();
+
+ ESelection eSelection = pEditView->GetSelection();
+
+ pEditEngine->SetText(rText);
+ pEditEngine->ClearModifyFlag();
+
+ // Restarting the timer here, prevents calling the handlers for other (currently inactive)
+ // math tasks
+ aModifyIdle.Start();
+
+ pEditView->SetSelection(eSelection);
+}
+
+
+void SmEditWindow::GetFocus()
+{
+ Window::GetFocus();
+
+ if (mxAccessible.is())
+ {
+ // Note: will implicitly send the AccessibleStateType::FOCUSED event
+ ::accessibility::AccessibleTextHelper *pHelper = mxAccessible->GetTextHelper();
+ if (pHelper)
+ pHelper->SetFocus();
+ }
+
+ if (!pEditView)
+ CreateEditView();
+ EditEngine *pEditEngine = GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetStatusEventHdl( LINK(this, SmEditWindow, EditStatusHdl) );
+
+ //Let SmViewShell know we got focus
+ if(GetView() && IsInlineEditEnabled())
+ GetView()->SetInsertIntoEditWindow(true);
+}
+
+
+void SmEditWindow::LoseFocus()
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetStatusEventHdl( Link<EditStatus&,void>() );
+
+ Window::LoseFocus();
+
+ if (mxAccessible.is())
+ {
+ // Note: will implicitly send the AccessibleStateType::FOCUSED event
+ ::accessibility::AccessibleTextHelper *pHelper = mxAccessible->GetTextHelper();
+ if (pHelper)
+ pHelper->SetFocus(false);
+ }
+}
+
+
+bool SmEditWindow::IsAllSelected() const
+{
+ bool bRes = false;
+ EditEngine *pEditEngine = const_cast<SmEditWindow *>(this)->GetEditEngine();
+ OSL_ENSURE( pEditView, "NULL pointer" );
+ OSL_ENSURE( pEditEngine, "NULL pointer" );
+ if (pEditEngine && pEditView)
+ {
+ ESelection eSelection( pEditView->GetSelection() );
+ sal_Int32 nParaCnt = pEditEngine->GetParagraphCount();
+ if (!(nParaCnt - 1))
+ {
+ sal_Int32 nTextLen = pEditEngine->GetText().getLength();
+ bRes = !eSelection.nStartPos && (eSelection.nEndPos == nTextLen - 1);
+ }
+ else
+ {
+ bRes = !eSelection.nStartPara && (eSelection.nEndPara == nParaCnt - 1);
+ }
+ }
+ return bRes;
+}
+
+void SmEditWindow::SelectAll()
+{
+ OSL_ENSURE( pEditView, "NULL pointer" );
+ if (pEditView)
+ {
+ // ALL as last two parameters refers to the end of the text
+ pEditView->SetSelection( ESelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL ) );
+ }
+}
+
+void SmEditWindow::MarkError(const Point &rPos)
+{
+ OSL_ENSURE( pEditView, "EditView missing" );
+ if (pEditView)
+ {
+ const sal_uInt16 nCol = sal::static_int_cast< sal_uInt16 >(rPos.X());
+ const sal_uInt16 nRow = sal::static_int_cast< sal_uInt16 >(rPos.Y() - 1);
+
+ pEditView->SetSelection(ESelection(nRow, nCol - 1, nRow, nCol));
+ GrabFocus();
+ }
+}
+
+// Makes selection to next <?> symbol
+void SmEditWindow::SelNextMark()
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ OSL_ENSURE( pEditView, "NULL pointer" );
+ OSL_ENSURE( pEditEngine, "NULL pointer" );
+ if (!pEditEngine || !pEditView)
+ return;
+
+ ESelection eSelection = pEditView->GetSelection();
+ sal_Int32 nPos = eSelection.nEndPos;
+ sal_Int32 nCounts = pEditEngine->GetParagraphCount();
+
+ while (eSelection.nEndPara < nCounts)
+ {
+ OUString aText = pEditEngine->GetText(eSelection.nEndPara);
+ nPos = aText.indexOf("<?>", nPos);
+ if (nPos != -1)
+ {
+ pEditView->SetSelection(ESelection(
+ eSelection.nEndPara, nPos, eSelection.nEndPara, nPos + 3));
+ break;
+ }
+
+ nPos = 0;
+ eSelection.nEndPara++;
+ }
+}
+
+void SmEditWindow::SelPrevMark()
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ OSL_ENSURE( pEditEngine, "NULL pointer" );
+ OSL_ENSURE( pEditView, "NULL pointer" );
+ if (!(pEditEngine && pEditView))
+ return;
+
+ ESelection eSelection = pEditView->GetSelection();
+ sal_Int32 nPara = eSelection.nStartPara;
+ sal_Int32 nMax = eSelection.nStartPos;
+ OUString aText(pEditEngine->GetText(nPara));
+ const OUString aMark("<?>");
+ sal_Int32 nPos;
+
+ while ( (nPos = aText.lastIndexOf(aMark, nMax)) < 0 )
+ {
+ if (--nPara < 0)
+ return;
+ aText = pEditEngine->GetText(nPara);
+ nMax = aText.getLength();
+ }
+ pEditView->SetSelection(ESelection(nPara, nPos, nPara, nPos + 3));
+}
+
+bool SmEditWindow::HasMark(const OUString& rText)
+ // returns true iff 'rText' contains a mark
+{
+ return rText.indexOf("<?>") != -1;
+}
+
+void SmEditWindow::MouseMove(const MouseEvent &rEvt)
+{
+ if (pEditView)
+ pEditView->MouseMove(rEvt);
+}
+
+sal_Int8 SmEditWindow::AcceptDrop( const AcceptDropEvent& /*rEvt*/ )
+{
+ return DND_ACTION_NONE;
+}
+
+sal_Int8 SmEditWindow::ExecuteDrop( const ExecuteDropEvent& /*rEvt*/ )
+{
+ return DND_ACTION_NONE;
+}
+
+ESelection SmEditWindow::GetSelection() const
+{
+ // pointer may be 0 when reloading a document and the old view
+ // was already destroyed
+ //(OSL_ENSURE( pEditView, "NULL pointer" );
+ ESelection eSel;
+ if (pEditView)
+ eSel = pEditView->GetSelection();
+ return eSel;
+}
+
+void SmEditWindow::SetSelection(const ESelection &rSel)
+{
+ OSL_ENSURE( pEditView, "NULL pointer" );
+ if (pEditView)
+ pEditView->SetSelection(rSel);
+ InvalidateSlots();
+}
+
+bool SmEditWindow::IsEmpty() const
+{
+ EditEngine *pEditEngine = const_cast<SmEditWindow *>(this)->GetEditEngine();
+ bool bEmpty = ( pEditEngine && pEditEngine->GetTextLen() == 0 );
+ return bEmpty;
+}
+
+bool SmEditWindow::IsSelected() const
+{
+ return pEditView && pEditView->HasSelection();
+}
+
+
+void SmEditWindow::UpdateStatus( bool bSetDocModified )
+{
+ SmModule *pMod = SM_MOD();
+ if (pMod && pMod->GetConfig()->IsAutoRedraw())
+ Flush();
+ if ( bSetDocModified )
+ GetDoc()->SetModified();
+}
+
+void SmEditWindow::Cut()
+{
+ OSL_ENSURE( pEditView, "EditView missing" );
+ if (pEditView)
+ {
+ pEditView->Cut();
+ UpdateStatus(true);
+ }
+}
+
+void SmEditWindow::Copy()
+{
+ OSL_ENSURE( pEditView, "EditView missing" );
+ if (pEditView)
+ pEditView->Copy();
+}
+
+void SmEditWindow::Paste()
+{
+ OSL_ENSURE( pEditView, "EditView missing" );
+ if (pEditView)
+ {
+ pEditView->Paste();
+ UpdateStatus(true);
+ }
+}
+
+void SmEditWindow::Delete()
+{
+ OSL_ENSURE( pEditView, "EditView missing" );
+ if (pEditView)
+ {
+ pEditView->DeleteSelected();
+ UpdateStatus(true);
+ }
+}
+
+void SmEditWindow::InsertText(const OUString& rText)
+{
+ OSL_ENSURE( pEditView, "EditView missing" );
+ if (!pEditView)
+ return;
+
+ // Note: Insertion of a space in front of commands is done here and
+ // in SmEditWindow::InsertCommand.
+ ESelection aSelection = pEditView->GetSelection();
+ OUString aCurrentFormula = pEditView->GetEditEngine()->GetText();
+ sal_Int32 nStartIndex = 0;
+
+ // get the start position (when we get a multi line formula)
+ for (sal_Int32 nParaPos = 0; nParaPos < aSelection.nStartPara; nParaPos++)
+ nStartIndex = aCurrentFormula.indexOf("\n", nStartIndex) + 1;
+
+ nStartIndex += aSelection.nStartPos;
+
+ // TODO: unify this function with the InsertCommand: The do the same thing for different
+ // callers
+ OUString string(rText);
+
+ OUString selected(pEditView->GetSelected());
+ // if we have text selected, use it in the first placeholder
+ if (!selected.isEmpty())
+ string = string.replaceFirst("<?>", selected);
+
+ // put a space before a new command if not in the beginning of a line
+ if (aSelection.nStartPos > 0 && aCurrentFormula[nStartIndex - 1] != ' ')
+ string = " " + string;
+
+ /*
+ fdo#65588 - Elements Dock: Scrollbar moves into input window
+ This change "solves" the visual problem. But I don't think so
+ this is the best solution.
+ */
+ pVScrollBar->Hide();
+ pHScrollBar->Hide();
+ pEditView->InsertText(string);
+ AdjustScrollBars();
+ pVScrollBar->Show();
+ pHScrollBar->Show();
+
+ // Remember start of the selection and move the cursor there afterwards.
+ aSelection.nEndPara = aSelection.nStartPara;
+ if (HasMark(string))
+ {
+ aSelection.nEndPos = aSelection.nStartPos;
+ pEditView->SetSelection(aSelection);
+ SelNextMark();
+ }
+ else
+ { // set selection after inserted text
+ aSelection.nEndPos = aSelection.nStartPos + string.getLength();
+ aSelection.nStartPos = aSelection.nEndPos;
+ pEditView->SetSelection(aSelection);
+ }
+
+ aModifyIdle.Start();
+ StartCursorMove();
+ GrabFocus();
+}
+
+void SmEditWindow::Flush()
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ if (pEditEngine && pEditEngine->IsModified())
+ {
+ pEditEngine->ClearModifyFlag();
+ SmViewShell *pViewSh = rCmdBox.GetView();
+ if (pViewSh)
+ {
+ std::unique_ptr<SfxStringItem> pTextToFlush = std::make_unique<SfxStringItem>(SID_TEXT, GetText());
+ pViewSh->GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_TEXT, SfxCallMode::RECORD,
+ { pTextToFlush.get() });
+ }
+ }
+ if (aCursorMoveIdle.IsActive())
+ {
+ aCursorMoveIdle.Stop();
+ CursorMoveTimerHdl(&aCursorMoveIdle);
+ }
+}
+
+void SmEditWindow::DeleteEditView()
+{
+ if (pEditView)
+ {
+ std::unique_ptr<EditEngine> xEditEngine(pEditView->GetEditEngine());
+ if (xEditEngine)
+ {
+ xEditEngine->SetStatusEventHdl( Link<EditStatus&,void>() );
+ xEditEngine->RemoveView( pEditView.get() );
+ }
+ pEditView.reset();
+ }
+}
+
+uno::Reference< XAccessible > SmEditWindow::CreateAccessible()
+{
+ if (!mxAccessible.is())
+ {
+ mxAccessible.set(new SmEditAccessible( this ));
+ mxAccessible->Init();
+ }
+ return uno::Reference< XAccessible >(mxAccessible.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/eqnolefilehdr.cxx b/starmath/source/eqnolefilehdr.cxx
new file mode 100644
index 000000000..f211f1aaa
--- /dev/null
+++ b/starmath/source/eqnolefilehdr.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 "eqnolefilehdr.hxx"
+#include <sot/storage.hxx>
+
+
+bool GetMathTypeVersion( SotStorage* pStor, sal_uInt8 &nVersion )
+{
+ sal_uInt8 nVer = 0;
+ bool bSuccess = false;
+
+
+ // code snippet copied from MathType::Parse
+
+ tools::SvRef<SotStorageStream> xSrc = pStor->OpenSotStream(
+ "Equation Native",
+ StreamMode::STD_READ);
+ if ( (!xSrc.is()) || (ERRCODE_NONE != xSrc->GetError()))
+ return bSuccess;
+ SotStorageStream *pS = xSrc.get();
+ pS->SetEndian( SvStreamEndian::LITTLE );
+
+ EQNOLEFILEHDR aHdr;
+ aHdr.Read(pS);
+ pS->ReadUChar( nVer );
+
+ if (!pS->GetError())
+ {
+ nVersion = nVer;
+ bSuccess = true;
+ }
+ return bSuccess;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/eqnolefilehdr.hxx b/starmath/source/eqnolefilehdr.hxx
new file mode 100644
index 000000000..29f93cd00
--- /dev/null
+++ b/starmath/source/eqnolefilehdr.hxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_EQNOLEFILEHDR_HXX
+#define INCLUDED_STARMATH_SOURCE_EQNOLEFILEHDR_HXX
+
+#include <sal/types.h>
+#include <tools/stream.hxx>
+
+class SotStorage;
+
+#define EQNOLEFILEHDR_SIZE 28
+
+class EQNOLEFILEHDR
+{
+public:
+ EQNOLEFILEHDR() : nCBHdr(0),nVersion(0),
+ nCf(0),nCBObject(0),nReserved1(0),nReserved2(0),
+ nReserved3(0), nReserved4(0) {}
+ explicit EQNOLEFILEHDR(sal_uInt32 nLenMTEF) : nCBHdr(0x1c),nVersion(0x20000),
+ nCf(0xc1c6),nCBObject(nLenMTEF),nReserved1(0),nReserved2(0x0014F690),
+ nReserved3(0x0014EBB4), nReserved4(0) {}
+
+ sal_uInt16 nCBHdr; // length of header, sizeof(EQNOLEFILEHDR) = 28
+ sal_uInt32 nVersion; // hiword = 2, loword = 0
+ sal_uInt16 nCf; // clipboard format ("MathType EF")
+ sal_uInt32 nCBObject; // length of MTEF data following this header
+ sal_uInt32 nReserved1; // not used
+ sal_uInt32 nReserved2; // not used
+ sal_uInt32 nReserved3; // not used
+ sal_uInt32 nReserved4; // not used
+
+ void Read(SvStream* pS)
+ {
+ pS->ReadUInt16( nCBHdr );
+ pS->ReadUInt32( nVersion );
+ pS->ReadUInt16( nCf );
+ pS->ReadUInt32( nCBObject );
+ pS->ReadUInt32( nReserved1 );
+ pS->ReadUInt32( nReserved2 );
+ pS->ReadUInt32( nReserved3 );
+ pS->ReadUInt32( nReserved4 );
+ }
+ void Write(SvStream* pS)
+ {
+ pS->WriteUInt16( nCBHdr );
+ pS->WriteUInt32( nVersion );
+ pS->WriteUInt16( nCf );
+ pS->WriteUInt32( nCBObject );
+ pS->WriteUInt32( nReserved1 );
+ pS->WriteUInt32( nReserved2 );
+ pS->WriteUInt32( nReserved3 );
+ pS->WriteUInt32( nReserved4 );
+ }
+};
+
+bool GetMathTypeVersion( SotStorage* pStor, sal_uInt8 &nVersion );
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/format.cxx b/starmath/source/format.cxx
new file mode 100644
index 000000000..a4bf960fd
--- /dev/null
+++ b/starmath/source/format.cxx
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <format.hxx>
+
+
+SmFormat::SmFormat()
+: aBaseSize(0, SmPtsTo100th_mm(12))
+{
+ eHorAlign = SmHorAlign::Center;
+ nGreekCharStyle = 0;
+ bIsTextmode = bScaleNormalBrackets = false;
+
+ vSize[SIZ_TEXT] = 100;
+ vSize[SIZ_INDEX] = 60;
+ vSize[SIZ_FUNCTION] =
+ vSize[SIZ_OPERATOR] = 100;
+ vSize[SIZ_LIMITS] = 60;
+
+ vDist[DIS_HORIZONTAL] = 10;
+ vDist[DIS_VERTICAL] = 5;
+ vDist[DIS_ROOT] = 0;
+ vDist[DIS_SUPERSCRIPT] =
+ vDist[DIS_SUBSCRIPT] = 20;
+ vDist[DIS_NUMERATOR] =
+ vDist[DIS_DENOMINATOR] = 0;
+ vDist[DIS_FRACTION] = 10;
+ vDist[DIS_STROKEWIDTH] = 5;
+ vDist[DIS_UPPERLIMIT] =
+ vDist[DIS_LOWERLIMIT] = 0;
+ vDist[DIS_BRACKETSIZE] =
+ vDist[DIS_BRACKETSPACE] = 5;
+ vDist[DIS_MATRIXROW] = 3;
+ vDist[DIS_MATRIXCOL] = 30;
+ vDist[DIS_ORNAMENTSIZE] =
+ vDist[DIS_ORNAMENTSPACE] = 0;
+ vDist[DIS_OPERATORSIZE] = 50;
+ vDist[DIS_OPERATORSPACE] = 20;
+ vDist[DIS_LEFTSPACE] =
+ vDist[DIS_RIGHTSPACE] = 100;
+ vDist[DIS_TOPSPACE] =
+ vDist[DIS_BOTTOMSPACE] =
+ vDist[DIS_NORMALBRACKETSIZE] = 0;
+
+ vFont[FNT_VARIABLE] =
+ vFont[FNT_FUNCTION] =
+ vFont[FNT_NUMBER] =
+ vFont[FNT_TEXT] =
+ vFont[FNT_SERIF] = SmFace(FNTNAME_TIMES, aBaseSize);
+ vFont[FNT_SANS] = SmFace(FNTNAME_HELV, aBaseSize);
+ vFont[FNT_FIXED] = SmFace(FNTNAME_COUR, aBaseSize);
+ vFont[FNT_MATH] = SmFace(FNTNAME_MATH, aBaseSize);
+
+ vFont[FNT_MATH].SetCharSet( RTL_TEXTENCODING_UNICODE );
+
+ vFont[FNT_VARIABLE].SetItalic(ITALIC_NORMAL);
+ vFont[FNT_FUNCTION].SetItalic(ITALIC_NONE);
+ vFont[FNT_NUMBER] .SetItalic(ITALIC_NONE);
+ vFont[FNT_TEXT] .SetItalic(ITALIC_NONE);
+ vFont[FNT_SERIF] .SetItalic(ITALIC_NONE);
+ vFont[FNT_SANS] .SetItalic(ITALIC_NONE);
+ vFont[FNT_FIXED] .SetItalic(ITALIC_NONE);
+
+ for ( sal_uInt16 i = FNT_BEGIN; i <= FNT_END; i++ )
+ {
+ SmFace &rFace = vFont[i];
+ rFace.SetTransparent( true );
+ rFace.SetAlignment( ALIGN_BASELINE );
+ rFace.SetColor( COL_AUTO );
+ bDefaultFont[i] = false;
+ }
+}
+
+
+void SmFormat::SetFont(sal_uInt16 nIdent, const SmFace &rFont, bool bDefault )
+{
+ vFont[nIdent] = rFont;
+ vFont[nIdent].SetTransparent( true );
+ vFont[nIdent].SetAlignment( ALIGN_BASELINE );
+
+ bDefaultFont[nIdent] = bDefault;
+}
+
+SmFormat & SmFormat::operator = (const SmFormat &rFormat)
+{
+ SetBaseSize(rFormat.GetBaseSize());
+ SetHorAlign(rFormat.GetHorAlign());
+ SetTextmode(rFormat.IsTextmode());
+ SetGreekCharStyle(rFormat.GetGreekCharStyle());
+ SetScaleNormalBrackets(rFormat.IsScaleNormalBrackets());
+
+ sal_uInt16 i;
+ for (i = FNT_BEGIN; i <= FNT_END; i++)
+ {
+ SetFont(i, rFormat.GetFont(i));
+ SetDefaultFont(i, rFormat.IsDefaultFont(i));
+ }
+ for (i = SIZ_BEGIN; i <= SIZ_END; i++)
+ SetRelSize(i, rFormat.GetRelSize(i));
+ for (i = DIS_BEGIN; i <= DIS_END; i++)
+ SetDistance(i, rFormat.GetDistance(i));
+
+ return *this;
+}
+
+
+bool SmFormat::operator == (const SmFormat &rFormat) const
+{
+ bool bRes = aBaseSize == rFormat.aBaseSize &&
+ eHorAlign == rFormat.eHorAlign &&
+ nGreekCharStyle == rFormat.nGreekCharStyle &&
+ bIsTextmode == rFormat.bIsTextmode &&
+ bScaleNormalBrackets == rFormat.bScaleNormalBrackets;
+
+ sal_uInt16 i;
+ for (i = 0; i <= SIZ_END && bRes; ++i)
+ {
+ if (vSize[i] != rFormat.vSize[i])
+ bRes = false;
+ }
+ for (i = 0; i <= DIS_END && bRes; ++i)
+ {
+ if (vDist[i] != rFormat.vDist[i])
+ bRes = false;
+ }
+ for (i = 0; i <= FNT_END && bRes; ++i)
+ {
+ if (vFont[i] != rFormat.vFont[i] ||
+ bDefaultFont[i] != rFormat.bDefaultFont[i])
+ bRes = false;
+ }
+
+ return bRes;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathmlattr.cxx b/starmath/source/mathmlattr.cxx
new file mode 100644
index 000000000..5e3f09df9
--- /dev/null
+++ b/starmath/source/mathmlattr.cxx
@@ -0,0 +1,145 @@
+/* -*- 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 "mathmlattr.hxx"
+
+#include <unordered_map>
+
+static sal_Int32 ParseMathMLUnsignedNumber(const OUString &rStr, Fraction& rUN)
+{
+ auto nLen = rStr.getLength();
+ sal_Int32 nDecimalPoint = -1;
+ sal_Int32 nIdx;
+ for (nIdx = 0; nIdx < nLen; nIdx++)
+ {
+ auto cD = rStr[nIdx];
+ if (cD == u'.')
+ {
+ if (nDecimalPoint >= 0)
+ return -1;
+ nDecimalPoint = nIdx;
+ continue;
+ }
+ if (cD < u'0' || u'9' < cD)
+ break;
+ }
+ if (nIdx == 0 || (nIdx == 1 && nDecimalPoint == 0))
+ return -1;
+
+ rUN = Fraction(rStr.copy(0, nIdx).toDouble());
+
+ return nIdx;
+}
+
+static sal_Int32 ParseMathMLNumber(const OUString &rStr, Fraction& rN)
+{
+ if (rStr.isEmpty())
+ return -1;
+ bool bNegative = (rStr[0] == '-');
+ sal_Int32 nOffset = bNegative ? 1 : 0;
+ auto nIdx = ParseMathMLUnsignedNumber(rStr.copy(nOffset), rN);
+ if (nIdx <= 0 || !rN.IsValid())
+ return -1;
+ if (bNegative)
+ rN *= -1;
+ return nOffset + nIdx;
+}
+
+sal_Int32 ParseMathMLAttributeLengthValue(const OUString &rStr, MathMLAttributeLengthValue& rV)
+{
+ auto nIdx = ParseMathMLNumber(rStr, rV.aNumber);
+ if (nIdx <= 0)
+ return -1;
+ OUString sRest = rStr.copy(nIdx);
+ if (sRest.isEmpty())
+ {
+ rV.eUnit = MathMLLengthUnit::None;
+ return nIdx;
+ }
+ if (sRest.startsWith("em"))
+ {
+ rV.eUnit = MathMLLengthUnit::Em;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("ex"))
+ {
+ rV.eUnit = MathMLLengthUnit::Ex;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("px"))
+ {
+ rV.eUnit = MathMLLengthUnit::Px;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("in"))
+ {
+ rV.eUnit = MathMLLengthUnit::In;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("cm"))
+ {
+ rV.eUnit = MathMLLengthUnit::Cm;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("mm"))
+ {
+ rV.eUnit = MathMLLengthUnit::Mm;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("pt"))
+ {
+ rV.eUnit = MathMLLengthUnit::Pt;
+ return nIdx + 2;
+ }
+ if (sRest.startsWith("pc"))
+ {
+ rV.eUnit = MathMLLengthUnit::Pc;
+ return nIdx + 2;
+ }
+ if (sRest[0] == u'%')
+ {
+ rV.eUnit = MathMLLengthUnit::Percent;
+ return nIdx + 2;
+ }
+ return nIdx;
+}
+
+bool GetMathMLMathvariantValue(const OUString &rStr, MathMLMathvariantValue& rV)
+{
+ static const std::unordered_map<OUString, MathMLMathvariantValue> aMap{
+ {"normal", MathMLMathvariantValue::Normal},
+ {"bold", MathMLMathvariantValue::Bold},
+ {"italic", MathMLMathvariantValue::Italic},
+ {"bold-italic", MathMLMathvariantValue::BoldItalic},
+ {"double-struck", MathMLMathvariantValue::DoubleStruck},
+ {"bold-fraktur", MathMLMathvariantValue::BoldFraktur},
+ {"script", MathMLMathvariantValue::Script},
+ {"bold-script", MathMLMathvariantValue::BoldScript},
+ {"fraktur", MathMLMathvariantValue::Fraktur},
+ {"sans-serif", MathMLMathvariantValue::SansSerif},
+ {"bold-sans-serif", MathMLMathvariantValue::BoldSansSerif},
+ {"sans-serif-italic", MathMLMathvariantValue::SansSerifItalic},
+ {"sans-serif-bold-italic", MathMLMathvariantValue::SansSerifBoldItalic},
+ {"monospace", MathMLMathvariantValue::Monospace},
+ {"initial", MathMLMathvariantValue::Initial},
+ {"tailed", MathMLMathvariantValue::Tailed},
+ {"looped", MathMLMathvariantValue::Looped},
+ {"stretched", MathMLMathvariantValue::Stretched}
+ };
+
+ auto it = aMap.find(rStr);
+ if (it != aMap.end())
+ {
+ rV = it->second;
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/mathmlattr.hxx b/starmath/source/mathmlattr.hxx
new file mode 100644
index 000000000..232e080e6
--- /dev/null
+++ b/starmath/source/mathmlattr.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_MATHMLATTR_HXX
+#define INCLUDED_STARMATH_SOURCE_MATHMLATTR_HXX
+
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <tools/fract.hxx>
+
+// MathML 3: 2.1.5.1 Syntax notation used in the MathML specification
+// <https://www.w3.org/TR/MathML/chapter2.html#id.2.1.5.1>
+// MathML 2: 2.4.4.2 Attributes with units
+// <https://www.w3.org/TR/MathML2/chapter2.html#fund.attval>
+// MathML 3: 2.1.5.2 Length Valued Attributes
+// <https://www.w3.org/TR/MathML/chapter2.html#fund.units>
+
+enum class MathMLLengthUnit {
+ None,
+ Em,
+ Ex,
+ Px,
+ In,
+ Cm,
+ Mm,
+ Pt,
+ Pc,
+ Percent
+};
+
+struct MathMLAttributeLengthValue
+{
+ Fraction aNumber;
+ MathMLLengthUnit eUnit;
+ MathMLAttributeLengthValue()
+ : eUnit(MathMLLengthUnit::None)
+ {
+ }
+};
+
+sal_Int32 ParseMathMLAttributeLengthValue(const OUString &rStr, MathMLAttributeLengthValue& rV);
+
+
+// MathML 3: 3.2.2 Mathematics style attributes common to token elements
+// <https://www.w3.org/TR/MathML3/chapter3.html#presm.commatt>
+
+enum class MathMLMathvariantValue {
+ Normal,
+ Bold,
+ Italic,
+ BoldItalic,
+ DoubleStruck,
+ BoldFraktur,
+ Script,
+ BoldScript,
+ Fraktur,
+ SansSerif,
+ BoldSansSerif,
+ SansSerifItalic,
+ SansSerifBoldItalic,
+ Monospace,
+ Initial,
+ Tailed,
+ Looped,
+ Stretched
+};
+
+bool GetMathMLMathvariantValue(const OUString &rStr, MathMLMathvariantValue& rV);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/mathmlexport.cxx b/starmath/source/mathmlexport.cxx
new file mode 100644
index 000000000..c619f22f4
--- /dev/null
+++ b/starmath/source/mathmlexport.cxx
@@ -0,0 +1,1526 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/*
+ Warning: The SvXMLElementExport helper class creates the beginning and
+ closing tags of xml elements in its constructor and destructor, so there's
+ hidden stuff going on, on occasion the ordering of these classes declarations
+ may be significant
+*/
+
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/uno/Any.h>
+
+#include <rtl/math.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <osl/diagnose.h>
+#include <unotools/saveopt.hxx>
+#include <sot/storage.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <unotools/streamwrap.hxx>
+#include <sax/tools/converter.hxx>
+#include <xmloff/xmlnmspe.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/nmspmap.hxx>
+#include <xmloff/attrlist.hxx>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <stack>
+
+#include "mathmlexport.hxx"
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <unomodel.hxx>
+#include <document.hxx>
+#include <utility.hxx>
+#include "cfgitem.hxx"
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+namespace {
+
+bool IsInPrivateUseArea( sal_Unicode cChar ) { return 0xE000 <= cChar && cChar <= 0xF8FF; }
+
+sal_Unicode ConvertMathToMathML( sal_Unicode cChar )
+{
+ sal_Unicode cRes = cChar;
+ if (IsInPrivateUseArea( cChar ))
+ {
+ SAL_WARN("starmath", "Error: private use area characters should no longer be in use!" );
+ cRes = u'@'; // just some character that should easily be notice as odd in the context
+ }
+ return cRes;
+}
+
+}
+
+bool SmXMLExportWrapper::Export(SfxMedium &rMedium)
+{
+ bool bRet=true;
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+
+ //Get model
+ uno::Reference< lang::XComponent > xModelComp = xModel;
+
+ bool bEmbedded = false;
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+
+ SmDocShell *pDocShell = pModel ?
+ static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
+ if ( pDocShell &&
+ SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode() )
+ bEmbedded = true;
+
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+ if (!bEmbedded)
+ {
+ if (pDocShell /*&& pDocShell->GetMedium()*/)
+ {
+ OSL_ENSURE( pDocShell->GetMedium() == &rMedium,
+ "different SfxMedium found" );
+
+ SfxItemSet* pSet = rMedium.GetItemSet();
+ if (pSet)
+ {
+ const SfxUnoAnyItem* pItem = static_cast<const SfxUnoAnyItem*>(
+ pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) );
+ if (pItem)
+ pItem->GetValue() >>= xStatusIndicator;
+ }
+ }
+
+ // set progress range and start status indicator
+ if (xStatusIndicator.is())
+ {
+ sal_Int32 nProgressRange = bFlat ? 1 : 3;
+ xStatusIndicator->start(SmResId(STR_STATSTR_WRITING),
+ nProgressRange);
+ }
+ }
+
+
+ // create XPropertySet with three properties for status indicator
+ comphelper::PropertyMapEntry aInfoMap[] =
+ {
+ { OUString("UsePrettyPrinting"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("BaseURI"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString(), 0, css::uno::Type(), 0, 0 }
+ };
+ uno::Reference< beans::XPropertySet > xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(
+ new comphelper::PropertySetInfo( aInfoMap ) ) );
+
+ SvtSaveOptions aSaveOpt;
+ bool bUsePrettyPrinting( bFlat || aSaveOpt.IsPrettyPrinting() );
+ xInfoSet->setPropertyValue( "UsePrettyPrinting", Any(bUsePrettyPrinting) );
+
+ // Set base URI
+ OUString sPropName( "BaseURI" );
+ xInfoSet->setPropertyValue( sPropName, makeAny( rMedium.GetBaseURL( true ) ) );
+
+ sal_Int32 nSteps=0;
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+ if (!bFlat) //Storage (Package) of Stream
+ {
+ uno::Reference < embed::XStorage > xStg = rMedium.GetOutputStorage();
+ bool bOASIS = ( SotStorage::GetVersion( xStg ) > SOFFICE_FILEFORMAT_60 );
+
+ // TODO/LATER: handle the case of embedded links gracefully
+ if ( bEmbedded ) //&& !pStg->IsRoot() )
+ {
+ OUString aName;
+ if ( rMedium.GetItemSet() )
+ {
+ const SfxStringItem* pDocHierarchItem = static_cast<const SfxStringItem*>(
+ rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) );
+ if ( pDocHierarchItem )
+ aName = pDocHierarchItem->GetValue();
+ }
+
+ if ( !aName.isEmpty() )
+ {
+ sPropName = "StreamRelPath";
+ xInfoSet->setPropertyValue( sPropName, makeAny( aName ) );
+ }
+ }
+
+ if ( !bEmbedded )
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(
+ xStg, xModelComp, "meta.xml", xContext, xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaExporter"
+ : "com.sun.star.comp.Math.XMLMetaExporter"));
+ }
+ if ( bRet )
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(
+ xStg, xModelComp, "content.xml", xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLContentExporter");
+ }
+
+ if ( bRet )
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(
+ xStg, xModelComp, "settings.xml", xContext, xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
+ : "com.sun.star.comp.Math.XMLSettingsExporter") );
+ }
+ }
+ else
+ {
+ SvStream *pStream = rMedium.GetOutStream();
+ uno::Reference<io::XOutputStream> xOut(
+ new utl::OOutputStreamWrapper(*pStream) );
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(
+ xOut, xModelComp, xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLContentExporter");
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+
+ return bRet;
+}
+
+
+/// export through an XML exporter component (output stream version)
+bool SmXMLExportWrapper::WriteThroughComponent(
+ const Reference<io::XOutputStream>& xOutputStream,
+ const Reference<XComponent>& xComponent,
+ Reference<uno::XComponentContext> const & rxContext,
+ Reference<beans::XPropertySet> const & rPropSet,
+ const char* pComponentName )
+{
+ OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
+ OSL_ENSURE(xComponent.is(), "Need component!");
+ OSL_ENSURE(nullptr != pComponentName, "Need component name!");
+
+ // get component
+ Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext );
+
+ // connect XML writer to output stream
+ xSaxWriter->setOutputStream( xOutputStream );
+
+ // prepare arguments (prepend doc handler to given arguments)
+ Sequence<Any> aArgs( 2 );
+ aArgs[0] <<= xSaxWriter;
+ aArgs[1] <<= rPropSet;
+
+ // get filter component
+ Reference< document::XExporter > xExporter(
+ rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(OUString::createFromAscii(pComponentName), aArgs, rxContext),
+ UNO_QUERY);
+ OSL_ENSURE( xExporter.is(),
+ "can't instantiate export filter component" );
+ if ( !xExporter.is() )
+ return false;
+
+
+ // connect model and filter
+ xExporter->setSourceDocument( xComponent );
+
+ // filter!
+ Reference < XFilter > xFilter( xExporter, UNO_QUERY );
+ uno::Sequence< PropertyValue > aProps(0);
+ xFilter->filter( aProps );
+
+ auto pFilter = comphelper::getUnoTunnelImplementation<SmXMLExport>(xFilter);
+ return pFilter == nullptr || pFilter->GetSuccess();
+}
+
+
+/// export through an XML exporter component (storage version)
+bool SmXMLExportWrapper::WriteThroughComponent(
+ const Reference < embed::XStorage >& xStorage,
+ const Reference<XComponent>& xComponent,
+ const char* pStreamName,
+ Reference<uno::XComponentContext> const & rxContext,
+ Reference<beans::XPropertySet> const & rPropSet,
+ const char* pComponentName
+ )
+{
+ OSL_ENSURE(xStorage.is(), "Need storage!");
+ OSL_ENSURE(nullptr != pStreamName, "Need stream name!");
+
+ // open stream
+ Reference < io::XStream > xStream;
+ OUString sStreamName = OUString::createFromAscii(pStreamName);
+ try
+ {
+ xStream = xStorage->openStreamElement( sStreamName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ }
+ catch ( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("starmath", "Can't create output stream in package" );
+ return false;
+ }
+
+ uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
+ xSet->setPropertyValue( "MediaType", Any(OUString( "text/xml" )) );
+
+ // all streams must be encrypted in encrypted document
+ xSet->setPropertyValue( "UseCommonStoragePasswordEncryption", Any(true) );
+
+ // set Base URL
+ if ( rPropSet.is() )
+ {
+ rPropSet->setPropertyValue( "StreamName", makeAny( sStreamName ) );
+ }
+
+ // write the stuff
+ bool bRet = WriteThroughComponent( xStream->getOutputStream(), xComponent, rxContext,
+ rPropSet, pComponentName );
+
+ return bRet;
+}
+
+SmXMLExport::SmXMLExport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLExportFlags nExportFlags)
+ : SvXMLExport(util::MeasureUnit::INCH, rContext, implementationName, XML_MATH,
+ nExportFlags)
+ , pTree(nullptr)
+ , bSuccess(false)
+{
+}
+
+sal_Int64 SAL_CALL SmXMLExport::getSomething(
+ const uno::Sequence< sal_Int8 >& rId )
+{
+ if ( isUnoTunnelId<SmXMLExport>(rId) )
+ return sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_uIntPtr >(this));
+
+ return SvXMLExport::getSomething( rId );
+}
+
+namespace
+{
+ class theSmXMLExportUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSmXMLExportUnoTunnelId> {};
+}
+
+const uno::Sequence< sal_Int8 > & SmXMLExport::getUnoTunnelId() throw()
+{
+ return theSmXMLExportUnoTunnelId::get().getSeq();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLSettingsExporter", SvXMLExportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLContentExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::CONTENT));
+}
+
+ErrCode SmXMLExport::exportDoc(enum XMLTokenEnum eClass)
+{
+ if ( !(getExportFlags() & SvXMLExportFlags::CONTENT) )
+ {
+ SvXMLExport::exportDoc( eClass );
+ }
+ else
+ {
+ uno::Reference <frame::XModel> xModel = GetModel();
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+
+ if (pModel)
+ {
+ SmDocShell *pDocShell =
+ static_cast<SmDocShell*>(pModel->GetObjectShell());
+ pTree = pDocShell->GetFormulaTree();
+ aText = pDocShell->GetText();
+ }
+
+ GetDocHandler()->startDocument();
+
+ addChaffWhenEncryptedStorage();
+
+ /*Add xmlns line*/
+ SvXMLAttributeList &rList = GetAttrList();
+
+ // make use of a default namespace
+ ResetNamespaceMap(); // Math doesn't need namespaces from xmloff, since it now uses default namespaces (because that is common with current MathML usage in the web)
+ GetNamespaceMap_().Add( OUString(), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH );
+
+ rList.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH),
+ GetNamespaceMap().GetNameByKey( XML_NAMESPACE_MATH));
+
+ //I think we need something like ImplExportEntities();
+ ExportContent_();
+ GetDocHandler()->endDocument();
+ }
+
+ bSuccess=true;
+ return ERRCODE_NONE;
+}
+
+void SmXMLExport::ExportContent_()
+{
+ uno::Reference <frame::XModel> xModel = GetModel();
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+ SmDocShell *pDocShell = pModel ?
+ static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
+ OSL_ENSURE( pDocShell, "doc shell missing" );
+
+ if (pDocShell && !pDocShell->GetFormat().IsTextmode())
+ {
+ // If the Math equation is not in text mode, we attach a display="block"
+ // attribute on the <math> root. We don't do anything if it is in
+ // text mode, the default display="inline" value will be used.
+ AddAttribute(XML_NAMESPACE_MATH, XML_DISPLAY, XML_BLOCK);
+ }
+ SvXMLElementExport aEquation(*this, XML_NAMESPACE_MATH, XML_MATH, true, true);
+ std::unique_ptr<SvXMLElementExport> pSemantics;
+
+ if (!aText.isEmpty())
+ {
+ pSemantics.reset( new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_SEMANTICS, true, true) );
+ }
+
+ ExportNodes(pTree, 0);
+
+ if (aText.isEmpty())
+ return;
+
+ // Convert symbol names
+ if (pDocShell)
+ {
+ SmParser &rParser = pDocShell->GetParser();
+ bool bVal = rParser.IsExportSymbolNames();
+ rParser.SetExportSymbolNames( true );
+ auto pTmpTree = rParser.Parse( aText );
+ aText = rParser.GetText();
+ pTmpTree.reset();
+ rParser.SetExportSymbolNames( bVal );
+ }
+
+ AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING,
+ OUString("StarMath 5.0"));
+ SvXMLElementExport aAnnotation(*this, XML_NAMESPACE_MATH,
+ XML_ANNOTATION, true, false);
+ GetDocHandler()->characters( aText );
+}
+
+void SmXMLExport::GetViewSettings( Sequence < PropertyValue >& aProps)
+{
+ uno::Reference <frame::XModel> xModel = GetModel();
+ if ( !xModel.is() )
+ return;
+
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+
+ if ( !pModel )
+ return;
+
+ SmDocShell *pDocShell =
+ static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if ( !pDocShell )
+ return;
+
+ aProps.realloc( 4 );
+ PropertyValue *pValue = aProps.getArray();
+ sal_Int32 nIndex = 0;
+
+ tools::Rectangle aRect( pDocShell->GetVisArea() );
+
+ pValue[nIndex].Name = "ViewAreaTop";
+ pValue[nIndex++].Value <<= aRect.Top();
+
+ pValue[nIndex].Name = "ViewAreaLeft";
+ pValue[nIndex++].Value <<= aRect.Left();
+
+ pValue[nIndex].Name = "ViewAreaWidth";
+ pValue[nIndex++].Value <<= aRect.GetWidth();
+
+ pValue[nIndex].Name = "ViewAreaHeight";
+ pValue[nIndex++].Value <<= aRect.GetHeight();
+}
+
+void SmXMLExport::GetConfigurationSettings( Sequence < PropertyValue > & rProps)
+{
+ Reference < XPropertySet > xProps ( GetModel(), UNO_QUERY );
+ if ( !xProps.is() )
+ return;
+
+ Reference< XPropertySetInfo > xPropertySetInfo = xProps->getPropertySetInfo();
+ if (!xPropertySetInfo.is())
+ return;
+
+ Sequence< Property > aProps = xPropertySetInfo->getProperties();
+ const sal_Int32 nCount = aProps.getLength();
+ if (!nCount)
+ return;
+
+ rProps.realloc(nCount);
+ SmMathConfig* pConfig = SM_MOD()->GetConfig();
+ const bool bUsedSymbolsOnly = pConfig && pConfig->IsSaveOnlyUsedSymbols();
+
+ std::transform(aProps.begin(), aProps.end(), rProps.begin(),
+ [bUsedSymbolsOnly, &xProps](Property& prop) {
+ PropertyValue aRet;
+ if (prop.Name != "Formula" && prop.Name != "BasicLibraries"
+ && prop.Name != "DialogLibraries"
+ && prop.Name != "RuntimeUID")
+ {
+ aRet.Name = prop.Name;
+ OUString aActualName(prop.Name);
+ // handle 'save used symbols only'
+ if (bUsedSymbolsOnly && prop.Name == "Symbols")
+ aActualName = "UserDefinedSymbolsInUse";
+ aRet.Value = xProps->getPropertyValue(aActualName);
+ }
+ return aRet;
+ });
+}
+
+void SmXMLExport::ExportLine(const SmNode *pNode, int nLevel)
+{
+ ExportExpression(pNode, nLevel);
+}
+
+void SmXMLExport::ExportBinaryHorizontal(const SmNode *pNode, int nLevel)
+{
+ TG nGroup = pNode->GetToken().nGroup;
+
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
+
+ // Unfold the binary tree structure as long as the nodes are SmBinHorNode
+ // with the same nGroup. This will reduce the number of nested <mrow>
+ // elements e.g. we only need three <mrow> levels to export
+
+ // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
+ // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
+
+ // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
+ ::std::stack< const SmNode* > s;
+ s.push(pNode);
+ while (!s.empty())
+ {
+ const SmNode *node = s.top();
+ s.pop();
+ if (node->GetType() != SmNodeType::BinHor || node->GetToken().nGroup != nGroup)
+ {
+ ExportNodes(node, nLevel+1);
+ continue;
+ }
+ const SmBinHorNode* binNode = static_cast<const SmBinHorNode*>(node);
+ s.push(binNode->RightOperand());
+ s.push(binNode->Symbol());
+ s.push(binNode->LeftOperand());
+ }
+}
+
+void SmXMLExport::ExportUnaryHorizontal(const SmNode *pNode, int nLevel)
+{
+ ExportExpression(pNode, nLevel);
+}
+
+void SmXMLExport::ExportExpression(const SmNode *pNode, int nLevel,
+ bool bNoMrowContainer /*=false*/)
+{
+ std::unique_ptr<SvXMLElementExport> pRow;
+ size_t nSize = pNode->GetNumSubNodes();
+
+ // #i115443: nodes of type expression always need to be grouped with mrow statement
+ if (!bNoMrowContainer &&
+ (nSize > 1 || pNode->GetType() == SmNodeType::Expression))
+ pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true));
+
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (const SmNode *pTemp = pNode->GetSubNode(i))
+ ExportNodes(pTemp, nLevel+1);
+ }
+}
+
+void SmXMLExport::ExportBinaryVertical(const SmNode *pNode, int nLevel)
+{
+ assert(pNode->GetNumSubNodes() == 3);
+ const SmNode *pNum = pNode->GetSubNode(0);
+ const SmNode *pDenom = pNode->GetSubNode(2);
+ if (pNum->GetType() == SmNodeType::Align && pNum->GetToken().eType != TALIGNC)
+ {
+ // A left or right alignment is specified on the numerator:
+ // attach the corresponding numalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_NUMALIGN,
+ pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
+ }
+ if (pDenom->GetType() == SmNodeType::Align && pDenom->GetToken().eType != TALIGNC)
+ {
+ // A left or right alignment is specified on the denominator:
+ // attach the corresponding denomalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_DENOMALIGN,
+ pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
+ }
+ SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
+ ExportNodes(pNum, nLevel);
+ ExportNodes(pDenom, nLevel);
+}
+
+void SmXMLExport::ExportBinaryDiagonal(const SmNode *pNode, int nLevel)
+{
+ assert(pNode->GetNumSubNodes() == 3);
+
+ if (pNode->GetToken().eType == TWIDESLASH)
+ {
+ // wideslash
+ // export the node as <mfrac bevelled="true">
+ AddAttribute(XML_NAMESPACE_MATH, XML_BEVELLED, XML_TRUE);
+ SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC,
+ true, true);
+ ExportNodes(pNode->GetSubNode(0), nLevel);
+ ExportNodes(pNode->GetSubNode(1), nLevel);
+ }
+ else
+ {
+ // widebslash
+ // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
+
+ ExportNodes(pNode->GetSubNode(0), nLevel);
+
+ { // Scoping for <mo> creation
+ SvXMLElementExport aMo(*this, XML_NAMESPACE_MATH, XML_MO,
+ true, true);
+ sal_Unicode const nArse[2] = {MS_BACKSLASH,0x00};
+ GetDocHandler()->characters(nArse);
+ }
+
+ ExportNodes(pNode->GetSubNode(1), nLevel);
+ }
+}
+
+void SmXMLExport::ExportTable(const SmNode *pNode, int nLevel)
+{
+ std::unique_ptr<SvXMLElementExport> pTable;
+
+ size_t nSize = pNode->GetNumSubNodes();
+
+ //If the list ends in newline then the last entry has
+ //no subnodes, the newline is superfluous so we just drop
+ //the last node, inclusion would create a bad MathML
+ //table
+ if (nSize >= 1)
+ {
+ const SmNode *pLine = pNode->GetSubNode(nSize-1);
+ if (pLine->GetType() == SmNodeType::Line && pLine->GetNumSubNodes() == 1 &&
+ pLine->GetSubNode(0) != nullptr &&
+ pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
+ --nSize;
+ }
+
+ // try to avoid creating a mtable element when the formula consists only
+ // of a single output line
+ if (nLevel || (nSize >1))
+ pTable.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true));
+
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (const SmNode *pTemp = pNode->GetSubNode(i))
+ {
+ std::unique_ptr<SvXMLElementExport> pRow;
+ std::unique_ptr<SvXMLElementExport> pCell;
+ if (pTable)
+ {
+ pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true));
+ SmTokenType eAlign = TALIGNC;
+ if (pTemp->GetType() == SmNodeType::Align)
+ {
+ // For Binom() and Stack() constructions, the SmNodeType::Align nodes
+ // are direct children.
+ // binom{alignl ...}{alignr ...} and
+ // stack{alignl ... ## alignr ... ## ...}
+ eAlign = pTemp->GetToken().eType;
+ }
+ else if (pTemp->GetType() == SmNodeType::Line &&
+ pTemp->GetNumSubNodes() == 1 &&
+ pTemp->GetSubNode(0) &&
+ pTemp->GetSubNode(0)->GetType() == SmNodeType::Align)
+ {
+ // For the Table() construction, the SmNodeType::Align node is a child
+ // of an SmNodeType::Line node.
+ // alignl ... newline alignr ... newline ...
+ eAlign = pTemp->GetSubNode(0)->GetToken().eType;
+ }
+ if (eAlign != TALIGNC)
+ {
+ // If a left or right alignment is specified on this line,
+ // attach the corresponding columnalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
+ eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
+ }
+ pCell.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true));
+ }
+ ExportNodes(pTemp, nLevel+1);
+ }
+ }
+}
+
+void SmXMLExport::ExportMath(const SmNode *pNode)
+{
+ const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
+ std::unique_ptr<SvXMLElementExport> pMath;
+
+ if (pNode->GetType() == SmNodeType::Math || pNode->GetType() == SmNodeType::GlyphSpecial)
+ {
+ // Export SmNodeType::Math and SmNodeType::GlyphSpecial symbols as <mo> elements
+ pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false));
+ }
+ else if (pNode->GetType() == SmNodeType::Special)
+ {
+ bool bIsItalic = IsItalic(pNode->GetFont());
+ if (!bIsItalic)
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
+ pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
+ }
+ else
+ {
+ // Export SmNodeType::MathIdent and SmNodeType::Place symbols as <mi> elements:
+ // - These math symbols should not be drawn slanted. Hence we should
+ // attach a mathvariant="normal" attribute to single-char <mi> elements
+ // that are not mathematical alphanumeric symbol. For simplicity and to
+ // work around browser limitations, we always attach such an attribute.
+ // - The MathML specification suggests to use empty <mi> elements as
+ // placeholders but they won't be visible in most MathML rendering
+ // engines so let's use an empty square for SmNodeType::Place instead.
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
+ pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
+ }
+ sal_Unicode nArse[2];
+ nArse[0] = pTemp->GetText()[0];
+ sal_Unicode cTmp = ConvertMathToMathML( nArse[0] );
+ if (cTmp != 0)
+ nArse[0] = cTmp;
+ OSL_ENSURE(nArse[0] != 0xffff,"Non existent symbol");
+ nArse[1] = 0;
+ GetDocHandler()->characters(nArse);
+}
+
+void SmXMLExport::ExportText(const SmNode *pNode)
+{
+ std::unique_ptr<SvXMLElementExport> pText;
+ const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
+ switch (pNode->GetToken().eType)
+ {
+ default:
+ case TIDENT:
+ {
+ //Note that we change the fontstyle to italic for strings that
+ //are italic and longer than a single character.
+ bool bIsItalic = IsItalic( pTemp->GetFont() );
+ if ((pTemp->GetText().getLength() > 1) && bIsItalic)
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_ITALIC);
+ else if ((pTemp->GetText().getLength() == 1) && !bIsItalic)
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
+ pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
+ break;
+ }
+ case TNUMBER:
+ pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false));
+ break;
+ case TTEXT:
+ pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false));
+ break;
+ }
+ GetDocHandler()->characters(pTemp->GetText());
+}
+
+void SmXMLExport::ExportBlank(const SmNode *pNode)
+{
+ const SmBlankNode *pTemp = static_cast<const SmBlankNode *>(pNode);
+ //!! exports an <mspace> element. Note that for example "~_~" is allowed in
+ //!! Math (so it has no sense at all) but must not result in an empty
+ //!! <msub> tag in MathML !!
+
+ if (pTemp->GetBlankNum() != 0)
+ {
+ // Attach a width attribute. We choose the (somewhat arbitrary) values
+ // ".5em" for a small gap '`' and "2em" for a large gap '~'.
+ // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
+ OUStringBuffer sStrBuf;
+ ::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
+ sStrBuf.append("em");
+ AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.getStr());
+ }
+
+ SvXMLElementExport aTextExport(*this, XML_NAMESPACE_MATH, XML_MSPACE,
+ true, false);
+
+ GetDocHandler()->characters( OUString() );
+}
+
+void SmXMLExport::ExportSubSupScript(const SmNode *pNode, int nLevel)
+{
+ const SmNode *pSub = nullptr;
+ const SmNode *pSup = nullptr;
+ const SmNode *pCSub = nullptr;
+ const SmNode *pCSup = nullptr;
+ const SmNode *pLSub = nullptr;
+ const SmNode *pLSup = nullptr;
+ std::unique_ptr<SvXMLElementExport> pThing2;
+
+ //if we have prescripts at all then we must use the tensor notation
+
+ //This is one of those excellent locations where scope is vital to
+ //arrange the construction and destruction of the element helper
+ //classes correctly
+ pLSub = pNode->GetSubNode(LSUB+1);
+ pLSup = pNode->GetSubNode(LSUP+1);
+ if (pLSub || pLSup)
+ {
+ SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH,
+ XML_MMULTISCRIPTS, true, true);
+
+
+ if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1))
+ && nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MUNDEROVER, true, true));
+ }
+ else if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MUNDER, true, true));
+ }
+ else if (nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MOVER, true, true));
+ }
+
+ ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
+
+ if (pCSub)
+ ExportNodes(pCSub, nLevel+1);
+ if (pCSup)
+ ExportNodes(pCSup, nLevel+1);
+ pThing2.reset();
+
+ pSub = pNode->GetSubNode(RSUB+1);
+ pSup = pNode->GetSubNode(RSUP+1);
+ if (pSub || pSup)
+ {
+ if (pSub)
+ ExportNodes(pSub, nLevel+1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
+ }
+ if (pSup)
+ ExportNodes(pSup, nLevel+1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
+ }
+ }
+
+ //Separator element between suffix and prefix sub/sup pairs
+ {
+ SvXMLElementExport aPrescripts(*this, XML_NAMESPACE_MATH,
+ XML_MPRESCRIPTS, true, true);
+ }
+
+ if (pLSub)
+ ExportNodes(pLSub, nLevel+1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
+ true, true);
+
+ }
+ if (pLSup)
+ ExportNodes(pLSup, nLevel+1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
+ true, true);
+
+ }
+ }
+ else
+ {
+ std::unique_ptr<SvXMLElementExport> pThing;
+ if (nullptr != (pSub = pNode->GetSubNode(RSUB+1)) &&
+ nullptr != (pSup = pNode->GetSubNode(RSUP+1)))
+ {
+ pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MSUBSUP, true, true));
+ }
+ else if (nullptr != (pSub = pNode->GetSubNode(RSUB+1)))
+ {
+ pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUB,
+ true, true));
+ }
+ else if (nullptr != (pSup = pNode->GetSubNode(RSUP+1)))
+ {
+ pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUP,
+ true, true));
+ }
+
+ if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1))
+ && nullptr != (pCSup=pNode->GetSubNode(CSUP+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MUNDEROVER, true, true));
+ }
+ else if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MUNDER, true, true));
+ }
+ else if (nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MOVER, true, true));
+ }
+ ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
+
+ if (pCSub)
+ ExportNodes(pCSub, nLevel+1);
+ if (pCSup)
+ ExportNodes(pCSup, nLevel+1);
+ pThing2.reset();
+
+ if (pSub)
+ ExportNodes(pSub, nLevel+1);
+ if (pSup)
+ ExportNodes(pSup, nLevel+1);
+ pThing.reset();
+ }
+}
+
+void SmXMLExport::ExportBrace(const SmNode *pNode, int nLevel)
+{
+ const SmNode *pTemp;
+ const SmNode *pLeft=pNode->GetSubNode(0);
+ const SmNode *pRight=pNode->GetSubNode(2);
+
+ // This used to generate <mfenced> or <mrow>+<mo> elements according to
+ // the stretchiness of fences. The MathML recommendation defines an
+ // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
+ // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
+ // To simplify our code and avoid issues with mfenced implementations in
+ // MathML rendering engines, we now always generate <mrow>+<mo> elements.
+ // See #fdo 66282.
+
+ // <mrow>
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
+ true, true);
+
+ // <mo fence="true"> opening-fence </mo>
+ if (pLeft && (pLeft->GetToken().eType != TNONE))
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
+ if (pNode->GetScaleMode() == SmScaleMode::Height)
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ else
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
+ ExportNodes(pLeft, nLevel+1);
+ }
+
+ if (nullptr != (pTemp = pNode->GetSubNode(1)))
+ {
+ // <mrow>
+ SvXMLElementExport aRowExport(*this, XML_NAMESPACE_MATH, XML_MROW,
+ true, true);
+ ExportNodes(pTemp, nLevel+1);
+ // </mrow>
+ }
+
+ // <mo fence="true"> closing-fence </mo>
+ if (pRight && (pRight->GetToken().eType != TNONE))
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
+ if (pNode->GetScaleMode() == SmScaleMode::Height)
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ else
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
+ ExportNodes(pRight, nLevel+1);
+ }
+
+ // </mrow>
+}
+
+void SmXMLExport::ExportRoot(const SmNode *pNode, int nLevel)
+{
+ if (pNode->GetSubNode(0))
+ {
+ SvXMLElementExport aRoot(*this, XML_NAMESPACE_MATH, XML_MROOT, true,
+ true);
+ ExportNodes(pNode->GetSubNode(2), nLevel+1);
+ ExportNodes(pNode->GetSubNode(0), nLevel+1);
+ }
+ else
+ {
+ SvXMLElementExport aSqrt(*this, XML_NAMESPACE_MATH, XML_MSQRT, true,
+ true);
+ ExportNodes(pNode->GetSubNode(2), nLevel+1);
+ }
+}
+
+void SmXMLExport::ExportOperator(const SmNode *pNode, int nLevel)
+{
+ /*we need to either use content or font and size attributes
+ *here*/
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
+ true, true);
+ ExportNodes(pNode->GetSubNode(0), nLevel+1);
+ ExportNodes(pNode->GetSubNode(1), nLevel+1);
+}
+
+void SmXMLExport::ExportAttributes(const SmNode *pNode, int nLevel)
+{
+ std::unique_ptr<SvXMLElementExport> pElement;
+
+ if (pNode->GetToken().eType == TUNDERLINE)
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_ACCENTUNDER,
+ XML_TRUE);
+ pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER,
+ true, true));
+ }
+ else if (pNode->GetToken().eType == TOVERSTRIKE)
+ {
+ // export as <menclose notation="horizontalstrike">
+ AddAttribute(XML_NAMESPACE_MATH, XML_NOTATION, XML_HORIZONTALSTRIKE);
+ pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MENCLOSE, true, true));
+ }
+ else
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_ACCENT,
+ XML_TRUE);
+ pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER,
+ true, true));
+ }
+
+ ExportNodes(pNode->GetSubNode(1), nLevel+1);
+ switch (pNode->GetToken().eType)
+ {
+ case TOVERLINE:
+ {
+ //proper entity support required
+ SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
+ true, true);
+ sal_Unicode const nArse[2] = {0xAF,0x00};
+ GetDocHandler()->characters(nArse);
+ }
+ break;
+ case TUNDERLINE:
+ {
+ //proper entity support required
+ SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
+ true, true);
+ sal_Unicode const nArse[2] = {0x0332,0x00};
+ GetDocHandler()->characters(nArse);
+ }
+ break;
+ case TOVERSTRIKE:
+ break;
+ case TWIDETILDE:
+ case TWIDEHAT:
+ case TWIDEVEC:
+ case TWIDEHARPOON:
+ {
+ // make these wide accents stretchy
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ ExportNodes(pNode->GetSubNode(0), nLevel+1);
+ }
+ break;
+ default:
+ ExportNodes(pNode->GetSubNode(0), nLevel+1);
+ break;
+ }
+}
+
+static bool lcl_HasEffectOnMathvariant( const SmTokenType eType )
+{
+ return eType == TBOLD || eType == TNBOLD ||
+ eType == TITALIC || eType == TNITALIC ||
+ eType == TSANS || eType == TSERIF || eType == TFIXED;
+}
+
+void SmXMLExport::ExportFont(const SmNode *pNode, int nLevel)
+{
+
+ // gather the mathvariant attribute relevant data from all
+ // successively following SmFontNodes...
+
+ int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
+ int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
+ int nSansSerifFixed = -1;
+ SmTokenType eNodeType = TUNKNOWN;
+ for (;;)
+ {
+ eNodeType = pNode->GetToken().eType;
+ if (!lcl_HasEffectOnMathvariant(eNodeType))
+ break;
+ switch (eNodeType)
+ {
+ case TBOLD : nBold = 1; break;
+ case TNBOLD : nBold = 0; break;
+ case TITALIC : nItalic = 1; break;
+ case TNITALIC : nItalic = 0; break;
+ case TSANS : nSansSerifFixed = 0; break;
+ case TSERIF : nSansSerifFixed = 1; break;
+ case TFIXED : nSansSerifFixed = 2; break;
+ default:
+ SAL_WARN("starmath", "unexpected case");
+ }
+ // According to the parser every node that is to be evaluated here
+ // has a single non-zero subnode at index 1!! Thus we only need to check
+ // that single node for follow-up nodes that have an effect on the attribute.
+ if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1) &&
+ lcl_HasEffectOnMathvariant( pNode->GetSubNode(1)->GetToken().eType))
+ {
+ pNode = pNode->GetSubNode(1);
+ }
+ else
+ break;
+ }
+
+ switch (pNode->GetToken().eType)
+ {
+ case TPHANTOM:
+ // No attribute needed. An <mphantom> element will be used below.
+ break;
+ case TBLACK:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLACK);
+ break;
+ case TWHITE:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_WHITE);
+ break;
+ case TRED:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_RED);
+ break;
+ case TGREEN:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GREEN);
+ break;
+ case TBLUE:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLUE);
+ break;
+ case TCYAN:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
+ break;
+ case TMAGENTA:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
+ break;
+ case TYELLOW:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_YELLOW);
+ break;
+ case TSILVER:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_SILVER);
+ break;
+ case TGRAY:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GRAY);
+ break;
+ case TMAROON:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_MAROON);
+ break;
+ case TOLIVE:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_OLIVE);
+ break;
+ case TLIME:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_LIME);
+ break;
+ case TAQUA:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
+ break;
+ case TTEAL:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_TEAL);
+ break;
+ case TNAVY:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_NAVY);
+ break;
+ case TFUCHSIA:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
+ break;
+ case TPURPLE:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_PURPLE);
+ break;
+ case TSIZE:
+ {
+ const SmFontNode *pFontNode = static_cast<const SmFontNode *>(pNode);
+ const Fraction &aFrac = pFontNode->GetSizeParameter();
+
+ OUStringBuffer sStrBuf;
+ switch(pFontNode->GetSizeType())
+ {
+ case FontSizeType::MULTIPLY:
+ ::sax::Converter::convertDouble(sStrBuf,
+ static_cast<double>(aFrac*Fraction(100.00)));
+ sStrBuf.append('%');
+ break;
+ case FontSizeType::DIVIDE:
+ ::sax::Converter::convertDouble(sStrBuf,
+ static_cast<double>(Fraction(100.00)/aFrac));
+ sStrBuf.append('%');
+ break;
+ case FontSizeType::ABSOLUT:
+ ::sax::Converter::convertDouble(sStrBuf,
+ static_cast<double>(aFrac));
+ sStrBuf.append(
+ GetXMLToken(XML_UNIT_PT));
+ break;
+ default:
+ {
+ //The problem here is that the wheels fall off because
+ //font size is stored in 100th's of a mm not pts, and
+ //rounding errors take their toll on the original
+ //value specified in points.
+
+ //Must fix StarMath to retain the original pt values
+ Fraction aTemp = Sm100th_mmToPts(pFontNode->GetFont().GetFontSize().Height());
+
+ if (pFontNode->GetSizeType() == FontSizeType::MINUS)
+ aTemp-=aFrac;
+ else
+ aTemp+=aFrac;
+
+ double mytest = static_cast<double>(aTemp);
+
+ mytest = ::rtl::math::round(mytest,1);
+ ::sax::Converter::convertDouble(sStrBuf,mytest);
+ sStrBuf.append(GetXMLToken(XML_UNIT_PT));
+ }
+ break;
+ }
+
+ OUString sStr(sStrBuf.makeStringAndClear());
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHSIZE, sStr);
+ }
+ break;
+ case TBOLD:
+ case TITALIC:
+ case TNBOLD:
+ case TNITALIC:
+ case TFIXED:
+ case TSANS:
+ case TSERIF:
+ {
+ // nBold: -1 = yet undefined; 0 = false; 1 = true;
+ // nItalic: -1 = yet undefined; 0 = false; 1 = true;
+ // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
+ const char *pText = "normal";
+ if (nSansSerifFixed == -1 || nSansSerifFixed == 1)
+ {
+ pText = "normal";
+ if (nBold == 1 && nItalic != 1)
+ pText = "bold";
+ else if (nBold != 1 && nItalic == 1)
+ pText = "italic";
+ else if (nBold == 1 && nItalic == 1)
+ pText = "bold-italic";
+ }
+ else if (nSansSerifFixed == 0)
+ {
+ pText = "sans-serif";
+ if (nBold == 1 && nItalic != 1)
+ pText = "bold-sans-serif";
+ else if (nBold != 1 && nItalic == 1)
+ pText = "sans-serif-italic";
+ else if (nBold == 1 && nItalic == 1)
+ pText = "sans-serif-bold-italic";
+ }
+ else if (nSansSerifFixed == 2)
+ pText = "monospace"; // no modifiers allowed for monospace ...
+ else
+ {
+ SAL_WARN("starmath", "unexpected case");
+ }
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, OUString::createFromAscii( pText ));
+ }
+ break;
+ default:
+ break;
+
+ }
+ {
+ // Wrap everything in an <mphantom> or <mstyle> element. These elements
+ // are mrow-like, so ExportExpression doesn't need to add an explicit
+ // <mrow> element. See #fdo 66283.
+ SvXMLElementExport aElement(*this, XML_NAMESPACE_MATH,
+ pNode->GetToken().eType == TPHANTOM ? XML_MPHANTOM : XML_MSTYLE,
+ true, true);
+ ExportExpression(pNode, nLevel, true);
+ }
+}
+
+
+void SmXMLExport::ExportVerticalBrace(const SmVerticalBraceNode *pNode, int nLevel)
+{
+ // "[body] overbrace [script]"
+
+ // Position body, overbrace and script vertically. First place the overbrace
+ // OVER the body and then the script OVER this expression.
+
+ // [script]
+ // --[overbrace]--
+ // XXXXXX[body]XXXXXXX
+
+ // Similarly for the underbrace construction.
+
+ XMLTokenEnum which;
+
+ switch (pNode->GetToken().eType)
+ {
+ case TOVERBRACE:
+ default:
+ which = XML_MOVER;
+ break;
+ case TUNDERBRACE:
+ which = XML_MUNDER;
+ break;
+ }
+
+ SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH,which, true, true);
+ {//Scoping
+ // using accents will draw the over-/underbraces too close to the base
+ // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
+ // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
+ SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH,which, true, true);
+ ExportNodes(pNode->Body(), nLevel);
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ ExportNodes(pNode->Brace(), nLevel);
+ }
+ ExportNodes(pNode->Script(), nLevel);
+}
+
+void SmXMLExport::ExportMatrix(const SmNode *pNode, int nLevel)
+{
+ SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
+ const SmMatrixNode *pMatrix = static_cast<const SmMatrixNode *>(pNode);
+ size_t i = 0;
+ for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
+ {
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
+ for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
+ {
+ if (const SmNode *pTemp = pNode->GetSubNode(i++))
+ {
+ if (pTemp->GetType() == SmNodeType::Align &&
+ pTemp->GetToken().eType != TALIGNC)
+ {
+ // A left or right alignment is specified on this cell,
+ // attach the corresponding columnalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
+ pTemp->GetToken().eType == TALIGNL ?
+ XML_LEFT : XML_RIGHT);
+ }
+ SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
+ ExportNodes(pTemp, nLevel+1);
+ }
+ }
+ }
+}
+
+void SmXMLExport::ExportNodes(const SmNode *pNode, int nLevel)
+{
+ if (!pNode)
+ return;
+ switch(pNode->GetType())
+ {
+ case SmNodeType::Table:
+ ExportTable(pNode, nLevel);
+ break;
+ case SmNodeType::Align:
+ case SmNodeType::Bracebody:
+ case SmNodeType::Expression:
+ ExportExpression(pNode, nLevel);
+ break;
+ case SmNodeType::Line:
+ ExportLine(pNode, nLevel);
+ break;
+ case SmNodeType::Text:
+ ExportText(pNode);
+ break;
+ case SmNodeType::GlyphSpecial:
+ case SmNodeType::Math:
+ {
+ sal_Unicode cTmp = 0;
+ const SmTextNode *pTemp = static_cast< const SmTextNode * >(pNode);
+ if (!pTemp->GetText().isEmpty())
+ cTmp = ConvertMathToMathML( pTemp->GetText()[0] );
+ if (cTmp == 0)
+ {
+ // no conversion to MathML implemented -> export it as text
+ // thus at least it will not vanish into nothing
+ ExportText(pNode);
+ }
+ else
+ {
+ switch (pNode->GetToken().eType)
+ {
+ case TINTD:
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ break;
+ default:
+ break;
+ }
+ //To fully handle generic MathML we need to implement the full
+ //operator dictionary, we will generate MathML with explicit
+ //stretchiness for now.
+ sal_Int16 nLength = GetAttrList().getLength();
+ bool bAddStretch=true;
+ for ( sal_Int16 i = 0; i < nLength; i++ )
+ {
+ OUString sLocalName;
+ sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
+ GetAttrList().getNameByIndex(i), &sLocalName );
+
+ if ( ( XML_NAMESPACE_MATH == nPrefix ) &&
+ IsXMLToken(sLocalName, XML_STRETCHY) )
+ {
+ bAddStretch = false;
+ break;
+ }
+ }
+ if (bAddStretch)
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
+ }
+ ExportMath(pNode);
+ }
+ }
+ break;
+ case SmNodeType::Special: //SmNodeType::Special requires some sort of Entity preservation in the XML engine.
+ case SmNodeType::MathIdent:
+ case SmNodeType::Place:
+ ExportMath(pNode);
+ break;
+ case SmNodeType::BinHor:
+ ExportBinaryHorizontal(pNode, nLevel);
+ break;
+ case SmNodeType::UnHor:
+ ExportUnaryHorizontal(pNode, nLevel);
+ break;
+ case SmNodeType::Brace:
+ ExportBrace(pNode, nLevel);
+ break;
+ case SmNodeType::BinVer:
+ ExportBinaryVertical(pNode, nLevel);
+ break;
+ case SmNodeType::BinDiagonal:
+ ExportBinaryDiagonal(pNode, nLevel);
+ break;
+ case SmNodeType::SubSup:
+ ExportSubSupScript(pNode, nLevel);
+ break;
+ case SmNodeType::Root:
+ ExportRoot(pNode, nLevel);
+ break;
+ case SmNodeType::Oper:
+ ExportOperator(pNode, nLevel);
+ break;
+ case SmNodeType::Attribut:
+ ExportAttributes(pNode, nLevel);
+ break;
+ case SmNodeType::Font:
+ ExportFont(pNode, nLevel);
+ break;
+ case SmNodeType::VerticalBrace:
+ ExportVerticalBrace(static_cast<const SmVerticalBraceNode *>(pNode), nLevel);
+ break;
+ case SmNodeType::Matrix:
+ ExportMatrix(pNode, nLevel);
+ break;
+ case SmNodeType::Blank:
+ ExportBlank(pNode);
+ break;
+ default:
+ SAL_WARN("starmath", "Warning: failed to export a node?");
+ break;
+
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathmlexport.hxx b/starmath/source/mathmlexport.hxx
new file mode 100644
index 000000000..376365842
--- /dev/null
+++ b/starmath/source/mathmlexport.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_MATHMLEXPORT_HXX
+#define INCLUDED_STARMATH_SOURCE_MATHMLEXPORT_HXX
+
+#include <xmloff/xmlexp.hxx>
+#include <xmloff/xmltoken.hxx>
+
+class SfxMedium;
+class SmNode;
+class SmVerticalBraceNode;
+namespace com::sun::star {
+ namespace io {
+ class XOutputStream; }
+ namespace beans {
+ class XPropertySet; }
+}
+
+
+class SmXMLExportWrapper
+{
+ css::uno::Reference<css::frame::XModel> xModel;
+ bool bFlat; //set true for export to flat .mml, set false for
+ //export to a .sxm (or whatever) package
+public:
+ explicit SmXMLExportWrapper(css::uno::Reference<css::frame::XModel> const &rRef)
+ : xModel(rRef), bFlat(true) {}
+
+ bool Export(SfxMedium &rMedium);
+ void SetFlat(bool bIn) {bFlat = bIn;}
+
+ static bool WriteThroughComponent(
+ const css::uno::Reference< css::io::XOutputStream >& xOutputStream,
+ const css::uno::Reference< css::lang::XComponent >& xComponent,
+ css::uno::Reference< css::uno::XComponentContext > const & rxContext,
+ css::uno::Reference< css::beans::XPropertySet > const & rPropSet,
+ const char* pComponentName );
+
+ static bool WriteThroughComponent(
+ const css::uno::Reference< css::embed::XStorage >& xStor,
+ const css::uno::Reference< css::lang::XComponent >& xComponent,
+ const char* pStreamName,
+ css::uno::Reference< css::uno::XComponentContext > const & rxContext,
+ css::uno::Reference< css::beans::XPropertySet > const & rPropSet,
+ const char* pComponentName );
+};
+
+
+class SmXMLExport final : public SvXMLExport
+{
+ const SmNode * pTree;
+ OUString aText;
+ bool bSuccess;
+
+ void ExportNodes(const SmNode *pNode, int nLevel);
+ void ExportTable(const SmNode *pNode, int nLevel);
+ void ExportLine(const SmNode *pNode, int nLevel);
+ void ExportExpression(const SmNode *pNode, int nLevel,
+ bool bNoMrowContainer = false);
+ void ExportText(const SmNode *pNode);
+ void ExportMath(const SmNode *pNode);
+ void ExportBinaryHorizontal(const SmNode *pNode, int nLevel);
+ void ExportUnaryHorizontal(const SmNode *pNode, int nLevel);
+ void ExportBrace(const SmNode *pNode, int nLevel);
+ void ExportBinaryVertical(const SmNode *pNode, int nLevel);
+ void ExportBinaryDiagonal(const SmNode *pNode, int nLevel);
+ void ExportSubSupScript(const SmNode *pNode, int nLevel);
+ void ExportRoot(const SmNode *pNode, int nLevel);
+ void ExportOperator(const SmNode *pNode, int nLevel);
+ void ExportAttributes(const SmNode *pNode, int nLevel);
+ void ExportFont(const SmNode *pNode, int nLevel);
+ void ExportVerticalBrace(const SmVerticalBraceNode *pNode, int nLevel);
+ void ExportMatrix(const SmNode *pNode, int nLevel);
+ void ExportBlank(const SmNode *pNode);
+
+public:
+ SmXMLExport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLExportFlags nExportFlags);
+
+ // XUnoTunnel
+ sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rId ) override;
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId() throw();
+
+ void ExportAutoStyles_() override {}
+ void ExportMasterStyles_() override {}
+ void ExportContent_() override;
+ ErrCode exportDoc(enum ::xmloff::token::XMLTokenEnum eClass = ::xmloff::token::XML_TOKEN_INVALID) override;
+
+ virtual void GetViewSettings(css::uno::Sequence<css::beans::PropertyValue>& aProps) override;
+ virtual void GetConfigurationSettings(css::uno::Sequence<css::beans::PropertyValue>& aProps) override;
+
+ bool GetSuccess() const {return bSuccess;}
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathmlimport.cxx b/starmath/source/mathmlimport.cxx
new file mode 100644
index 000000000..6ae00afbb
--- /dev/null
+++ b/starmath/source/mathmlimport.cxx
@@ -0,0 +1,2744 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/*todo: Change characters and tcharacters to accumulate the characters together
+into one string, xml parser hands them to us line by line rather than all in
+one go*/
+
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/FastParser.hpp>
+#include <com/sun/star/xml/sax/XFastParser.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+#include <comphelper/fileformat.h>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <rtl/character.hxx>
+#include <sal/log.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/sfxmodelfactory.hxx>
+#include <osl/diagnose.h>
+#include <sot/storage.hxx>
+#include <svtools/sfxecode.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/streamwrap.hxx>
+#include <sax/tools/converter.hxx>
+#include <xmloff/DocumentSettingsContext.hxx>
+#include <xmloff/xmlnmspe.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/nmspmap.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/xmlmetai.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <tools/diagnose_ex.h>
+
+#include <memory>
+
+#include "mathmlattr.hxx"
+#include "mathmlimport.hxx"
+#include <document.hxx>
+#include <smdll.hxx>
+#include <unomodel.hxx>
+#include <utility.hxx>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+namespace {
+
+std::unique_ptr<SmNode> popOrZero(SmNodeStack& rStack)
+{
+ if (rStack.empty())
+ return nullptr;
+ auto pTmp = std::move(rStack.front());
+ rStack.pop_front();
+ return pTmp;
+}
+
+}
+
+ErrCode SmXMLImportWrapper::Import(SfxMedium &rMedium)
+{
+ ErrCode nError = ERRCODE_SFX_DOLOADFAILED;
+
+ uno::Reference<uno::XComponentContext> xContext( comphelper::getProcessComponentContext() );
+
+ //Make a model component from our SmModel
+ uno::Reference< lang::XComponent > xModelComp = xModel;
+ OSL_ENSURE( xModelComp.is(), "XMLReader::Read: got no model" );
+
+ // try to get an XStatusIndicator from the Medium
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+
+ bool bEmbedded = false;
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+
+ SmDocShell *pDocShell = pModel ?
+ static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
+ if (pDocShell)
+ {
+ OSL_ENSURE( pDocShell->GetMedium() == &rMedium,
+ "different SfxMedium found" );
+
+ SfxItemSet* pSet = rMedium.GetItemSet();
+ if (pSet)
+ {
+ const SfxUnoAnyItem* pItem = static_cast<const SfxUnoAnyItem*>(
+ pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) );
+ if (pItem)
+ pItem->GetValue() >>= xStatusIndicator;
+ }
+
+ if ( SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode() )
+ bEmbedded = true;
+ }
+
+ comphelper::PropertyMapEntry aInfoMap[] =
+ {
+ { OUString("PrivateData"), 0,
+ cppu::UnoType<XInterface>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BaseURI"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString(), 0, css::uno::Type(), 0, 0 }
+ };
+ uno::Reference< beans::XPropertySet > xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(
+ new comphelper::PropertySetInfo( aInfoMap ) ) );
+
+ // Set base URI
+ OUString const baseURI(rMedium.GetBaseURL());
+ // needed for relative URLs; but it's OK to import e.g. MathML from the
+ // clipboard without one
+ SAL_INFO_IF(baseURI.isEmpty(), "starmath", "SmXMLImportWrapper: no base URL");
+ xInfoSet->setPropertyValue("BaseURI", makeAny(baseURI));
+
+ sal_Int32 nSteps=3;
+ if ( !(rMedium.IsStorage()))
+ nSteps = 1;
+
+ sal_Int32 nProgressRange(nSteps);
+ if (xStatusIndicator.is())
+ {
+ xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange);
+ }
+
+ nSteps=0;
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ if ( rMedium.IsStorage())
+ {
+ // TODO/LATER: handle the case of embedded links gracefully
+ if ( bEmbedded ) // && !rMedium.GetStorage()->IsRoot() )
+ {
+ OUString aName( "dummyObjName" );
+ if ( rMedium.GetItemSet() )
+ {
+ const SfxStringItem* pDocHierarchItem = static_cast<const SfxStringItem*>(
+ rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) );
+ if ( pDocHierarchItem )
+ aName = pDocHierarchItem->GetValue();
+ }
+
+ if ( !aName.isEmpty() )
+ {
+ xInfoSet->setPropertyValue("StreamRelPath", makeAny(aName));
+ }
+ }
+
+ bool bOASIS = ( SotStorage::GetVersion( rMedium.GetStorage() ) > SOFFICE_FILEFORMAT_60 );
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ auto nWarn = ReadThroughComponent(
+ rMedium.GetStorage(), xModelComp, "meta.xml",
+ xContext, xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaImporter"
+ : "com.sun.star.comp.Math.XMLMetaImporter") );
+
+ if ( nWarn != ERRCODE_IO_BROKENPACKAGE )
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ nWarn = ReadThroughComponent(
+ rMedium.GetStorage(), xModelComp, "settings.xml",
+ xContext, xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsImporter"
+ : "com.sun.star.comp.Math.XMLSettingsImporter" ) );
+
+ if ( nWarn != ERRCODE_IO_BROKENPACKAGE )
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ nError = ReadThroughComponent(
+ rMedium.GetStorage(), xModelComp, "content.xml",
+ xContext, xInfoSet, "com.sun.star.comp.Math.XMLImporter" );
+ }
+ else
+ nError = ERRCODE_IO_BROKENPACKAGE;
+ }
+ else
+ nError = ERRCODE_IO_BROKENPACKAGE;
+ }
+ else
+ {
+ Reference<io::XInputStream> xInputStream =
+ new utl::OInputStreamWrapper(rMedium.GetInStream());
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ nError = ReadThroughComponent( xInputStream, xModelComp,
+ xContext, xInfoSet, "com.sun.star.comp.Math.XMLImporter", false );
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ return nError;
+}
+
+
+/// read a component (file + filter version)
+ErrCode SmXMLImportWrapper::ReadThroughComponent(
+ const Reference<io::XInputStream>& xInputStream,
+ const Reference<XComponent>& xModelComponent,
+ Reference<uno::XComponentContext> const & rxContext,
+ Reference<beans::XPropertySet> const & rPropSet,
+ const char* pFilterName,
+ bool bEncrypted )
+{
+ ErrCode nError = ERRCODE_SFX_DOLOADFAILED;
+ OSL_ENSURE(xInputStream.is(), "input stream missing");
+ OSL_ENSURE(xModelComponent.is(), "document missing");
+ OSL_ENSURE(rxContext.is(), "factory missing");
+ OSL_ENSURE(nullptr != pFilterName,"I need a service name for the component!");
+
+ // prepare ParserInputSrouce
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ // get parser
+ Reference< xml::sax::XFastParser > xParser = xml::sax::FastParser::create(rxContext);
+
+ Sequence<Any> aArgs( 1 );
+ aArgs[0] <<= rPropSet;
+
+ // get filter
+ Reference< xml::sax::XFastDocumentHandler > xFilter(
+ rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ OUString::createFromAscii(pFilterName), aArgs, rxContext),
+ UNO_QUERY );
+ OSL_ENSURE( xFilter.is(), "Can't instantiate filter component." );
+ if ( !xFilter.is() )
+ return nError;
+
+ // connect parser and filter
+ xParser->setFastDocumentHandler( xFilter );
+
+ // connect model and filter
+ Reference < XImporter > xImporter( xFilter, UNO_QUERY );
+ xImporter->setTargetDocument( xModelComponent );
+
+ uno::Reference< xml::sax::XFastParser > xFastParser = dynamic_cast<
+ xml::sax::XFastParser* >( xFilter.get() );
+
+ // finally, parser the stream
+ try
+ {
+ if( xFastParser.is() )
+ xFastParser->parseStream( aParserInput );
+ else
+ xParser->parseStream( aParserInput );
+
+ auto pFilter = comphelper::getUnoTunnelImplementation<SmXMLImport>(xFilter);
+ if ( pFilter && pFilter->GetSuccess() )
+ nError = ERRCODE_NONE;
+ }
+ catch (const xml::sax::SAXParseException& r)
+ {
+ // sax parser sends wrapped exceptions,
+ // try to find the original one
+ xml::sax::SAXException aSaxEx = *static_cast<const xml::sax::SAXException*>(&r);
+ bool bTryChild = true;
+
+ while( bTryChild )
+ {
+ xml::sax::SAXException aTmp;
+ if ( aSaxEx.WrappedException >>= aTmp )
+ aSaxEx = aTmp;
+ else
+ bTryChild = false;
+ }
+
+ packages::zip::ZipIOException aBrokenPackage;
+ if ( aSaxEx.WrappedException >>= aBrokenPackage )
+ return ERRCODE_IO_BROKENPACKAGE;
+
+ if ( bEncrypted )
+ nError = ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch (const xml::sax::SAXException& r)
+ {
+ packages::zip::ZipIOException aBrokenPackage;
+ if ( r.WrappedException >>= aBrokenPackage )
+ return ERRCODE_IO_BROKENPACKAGE;
+
+ if ( bEncrypted )
+ nError = ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch (const packages::zip::ZipIOException&)
+ {
+ nError = ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch (const io::IOException&)
+ {
+ }
+ catch (const std::range_error&)
+ {
+ }
+
+ return nError;
+}
+
+
+ErrCode SmXMLImportWrapper::ReadThroughComponent(
+ const uno::Reference< embed::XStorage >& xStorage,
+ const Reference<XComponent>& xModelComponent,
+ const char* pStreamName,
+ Reference<uno::XComponentContext> const & rxContext,
+ Reference<beans::XPropertySet> const & rPropSet,
+ const char* pFilterName )
+{
+ OSL_ENSURE(xStorage.is(), "Need storage!");
+ OSL_ENSURE(nullptr != pStreamName, "Please, please, give me a name!");
+
+ // open stream (and set parser input)
+ OUString sStreamName = OUString::createFromAscii(pStreamName);
+
+ // get input stream
+ try
+ {
+ uno::Reference < io::XStream > xEventsStream = xStorage->openStreamElement( sStreamName, embed::ElementModes::READ );
+
+ // determine if stream is encrypted or not
+ uno::Reference < beans::XPropertySet > xProps( xEventsStream, uno::UNO_QUERY );
+ Any aAny = xProps->getPropertyValue( "Encrypted" );
+ bool bEncrypted = false;
+ if ( aAny.getValueType() == cppu::UnoType<bool>::get() )
+ aAny >>= bEncrypted;
+
+ // set Base URL
+ if ( rPropSet.is() )
+ {
+ rPropSet->setPropertyValue( "StreamName", makeAny( sStreamName ) );
+ }
+
+
+ Reference < io::XInputStream > xStream = xEventsStream->getInputStream();
+ return ReadThroughComponent( xStream, xModelComponent, rxContext, rPropSet, pFilterName, bEncrypted );
+ }
+ catch ( packages::WrongPasswordException& )
+ {
+ return ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch( packages::zip::ZipIOException& )
+ {
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ return ERRCODE_SFX_DOLOADFAILED;
+}
+
+
+SmXMLImport::SmXMLImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLImportFlags nImportFlags)
+: SvXMLImport(rContext, implementationName, nImportFlags),
+ bSuccess(false),
+ nParseDepth(0)
+{
+}
+
+namespace
+{
+ class theSmXMLImportUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSmXMLImportUnoTunnelId> {};
+}
+
+const uno::Sequence< sal_Int8 > & SmXMLImport::getUnoTunnelId() throw()
+{
+ return theSmXMLImportUnoTunnelId::get().getSeq();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_XMLImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(
+ new SmXMLImport(pCtx, "com.sun.star.comp.Math.XMLImporter", SvXMLImportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_XMLOasisMetaImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SmXMLImport(pCtx, "com.sun.star.comp.Math.XMLOasisMetaImporter",
+ SvXMLImportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_XMLOasisSettingsImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SmXMLImport(pCtx, "com.sun.star.comp.Math.XMLOasisSettingsImporter",
+ SvXMLImportFlags::SETTINGS));
+}
+
+sal_Int64 SAL_CALL SmXMLImport::getSomething(
+ const uno::Sequence< sal_Int8 >&rId )
+{
+ if ( isUnoTunnelId<SmXMLImport>(rId) )
+ return sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_uIntPtr >(this));
+
+ return SvXMLImport::getSomething( rId );
+}
+
+void SmXMLImport::endDocument()
+{
+ //Set the resulted tree into the SmDocShell where it belongs
+ std::unique_ptr<SmNode> pTree = popOrZero(aNodeStack);
+ if (pTree && pTree->GetType() == SmNodeType::Table)
+ {
+ uno::Reference <frame::XModel> xModel = GetModel();
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+
+ if (pModel)
+ {
+ SmDocShell *pDocShell =
+ static_cast<SmDocShell*>(pModel->GetObjectShell());
+ auto pTreeTmp = pTree.get();
+ pDocShell->SetFormulaTree(static_cast<SmTableNode *>(pTree.release()));
+ if (aText.isEmpty()) //If we picked up no annotation text
+ {
+ OUStringBuffer aStrBuf;
+ // Get text from imported formula
+ pTreeTmp->CreateTextFromNode(aStrBuf);
+ aStrBuf.stripEnd(' ');
+ aText = aStrBuf.makeStringAndClear();
+ }
+
+ // Convert symbol names
+ SmParser &rParser = pDocShell->GetParser();
+ bool bVal = rParser.IsImportSymbolNames();
+ rParser.SetImportSymbolNames( true );
+ auto pTmpTree = rParser.Parse( aText );
+ aText = rParser.GetText();
+ pTmpTree.reset();
+ rParser.SetImportSymbolNames( bVal );
+
+ pDocShell->SetText( aText );
+ }
+ OSL_ENSURE(pModel,"So there *was* a UNO problem after all");
+
+ bSuccess = true;
+ }
+
+ SvXMLImport::endDocument();
+}
+
+namespace {
+
+class SmXMLImportContext: public SvXMLImportContext
+{
+public:
+ SmXMLImportContext( SmXMLImport &rImport)
+ : SvXMLImportContext(rImport)
+ {
+ GetSmImport().IncParseDepth();
+ }
+
+ virtual ~SmXMLImportContext() override
+ {
+ GetSmImport().DecParseDepth();
+ }
+
+ SmXMLImport& GetSmImport()
+ {
+ return static_cast<SmXMLImport&>(GetImport());
+ }
+
+ virtual void TCharacters(const OUString & /*rChars*/);
+ virtual void SAL_CALL characters(const OUString &rChars) override;
+ virtual uno::Reference< xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL startFastElement(sal_Int32 /*nElement*/, const css::uno::Reference<css::xml::sax::XFastAttributeList>& /*rAttrList*/) override
+ {
+ if (GetSmImport().TooDeep())
+ throw std::range_error("too deep");
+ }
+};
+
+}
+
+void SmXMLImportContext::TCharacters(const OUString & /*rChars*/)
+{
+}
+
+void SmXMLImportContext::characters(const OUString &rChars)
+{
+ /*
+ Whitespace occurring within the content of token elements is "trimmed"
+ from the ends (i.e. all whitespace at the beginning and end of the
+ content is removed), and "collapsed" internally (i.e. each sequence of
+ 1 or more whitespace characters is replaced with one blank character).
+ */
+ //collapsing not done yet!
+ const OUString &rChars2 = rChars.trim();
+ if (!rChars2.isEmpty())
+ TCharacters(rChars2/*.collapse()*/);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SmXMLImportContext::createFastChildContext(
+ sal_Int32 /*nElement*/, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ return nullptr;
+}
+
+namespace {
+
+struct SmXMLContext_Helper
+{
+ sal_Int8 nIsBold;
+ sal_Int8 nIsItalic;
+ double nFontSize;
+ OUString sFontFamily;
+ OUString sColor;
+
+ SmXMLImportContext & rContext;
+
+ explicit SmXMLContext_Helper(SmXMLImportContext &rImport)
+ : nIsBold( -1 )
+ , nIsItalic( -1 )
+ , nFontSize( 0.0 )
+ , rContext( rImport )
+ {}
+
+ bool IsFontNodeNeeded() const;
+ void RetrieveAttrs(const uno::Reference< xml::sax::XFastAttributeList > &xAttrList );
+ void ApplyAttrs();
+};
+
+}
+
+bool SmXMLContext_Helper::IsFontNodeNeeded() const
+{
+ return nIsBold != -1 ||
+ nIsItalic != -1 ||
+ nFontSize != 0.0 ||
+ !sFontFamily.isEmpty() ||
+ !sColor.isEmpty();
+}
+
+void SmXMLContext_Helper::RetrieveAttrs(const uno::Reference<
+ xml::sax::XFastAttributeList > & xAttrList )
+{
+ bool bMvFound = false;
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ OUString sValue = aIter.toString();
+ // sometimes they have namespace, sometimes not?
+ switch(aIter.getToken() & TOKEN_MASK)
+ {
+ case XML_FONTWEIGHT:
+ nIsBold = sal_Int8(sValue == GetXMLToken(XML_BOLD));
+ break;
+ case XML_FONTSTYLE:
+ nIsItalic = sal_Int8(sValue == GetXMLToken(XML_ITALIC));
+ break;
+ case XML_FONTSIZE:
+ case XML_MATHSIZE:
+ ::sax::Converter::convertDouble(nFontSize, sValue);
+ rContext.GetSmImport().GetMM100UnitConverter().
+ SetXMLMeasureUnit(util::MeasureUnit::POINT);
+ if (-1 == sValue.indexOf(GetXMLToken(XML_UNIT_PT)))
+ {
+ if (-1 == sValue.indexOf('%'))
+ nFontSize=0.0;
+ else
+ {
+ rContext.GetSmImport().GetMM100UnitConverter().
+ SetXMLMeasureUnit(util::MeasureUnit::PERCENT);
+ }
+ }
+ break;
+ case XML_FONTFAMILY:
+ sFontFamily = sValue;
+ break;
+ case XML_COLOR:
+ sColor = sValue;
+ break;
+ case XML_MATHCOLOR:
+ sColor = sValue;
+ break;
+ case XML_MATHVARIANT:
+ bMvFound = true;
+ break;
+ default:
+ SAL_WARN("starmath", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString());
+ break;
+ }
+ }
+
+ if (bMvFound)
+ {
+ // Ignore deprecated attributes fontfamily, fontweight, and fontstyle
+ // in favor of mathvariant, as specified in
+ // <https://www.w3.org/TR/MathML3/chapter3.html#presm.deprecatt>.
+ sFontFamily.clear();
+ nIsBold = -1;
+ nIsItalic = -1;
+ }
+}
+
+void SmXMLContext_Helper::ApplyAttrs()
+{
+ SmNodeStack &rNodeStack = rContext.GetSmImport().GetNodeStack();
+
+ if (!IsFontNodeNeeded())
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.nLevel = 5;
+
+ if (nIsBold != -1)
+ {
+ if (nIsBold)
+ aToken.eType = TBOLD;
+ else
+ aToken.eType = TNBOLD;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ if (nIsItalic != -1)
+ {
+ if (nIsItalic)
+ aToken.eType = TITALIC;
+ else
+ aToken.eType = TNITALIC;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr,popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ if (nFontSize != 0.0)
+ {
+ aToken.eType = TSIZE;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+
+ if (util::MeasureUnit::PERCENT == rContext.GetSmImport()
+ .GetMM100UnitConverter().GetXMLMeasureUnit())
+ {
+ if (nFontSize < 100.00)
+ pFontNode->SetSizeParameter(Fraction(100.00/nFontSize),
+ FontSizeType::DIVIDE);
+ else
+ pFontNode->SetSizeParameter(Fraction(nFontSize/100.00),
+ FontSizeType::MULTIPLY);
+ }
+ else
+ pFontNode->SetSizeParameter(Fraction(nFontSize),FontSizeType::ABSOLUT);
+
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ if (!sFontFamily.isEmpty())
+ {
+ if (sFontFamily.equalsIgnoreAsciiCase(GetXMLToken(XML_FIXED)))
+ aToken.eType = TFIXED;
+ else if (sFontFamily.equalsIgnoreAsciiCase("sans"))
+ aToken.eType = TSANS;
+ else if (sFontFamily.equalsIgnoreAsciiCase("serif"))
+ aToken.eType = TSERIF;
+ else //Just give up, we need to extend our font mechanism to be
+ //more general
+ return;
+
+ aToken.aText = sFontFamily;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ if (sColor.isEmpty())
+ return;
+
+ //Again we can only handle a small set of colours in
+ //StarMath for now.
+ const SvXMLTokenMap& rTokenMap =
+ rContext.GetSmImport().GetColorTokenMap();
+ sal_uInt16 tok = rTokenMap.Get(XML_NAMESPACE_MATH, sColor);
+ if (tok != XML_TOK_UNKNOWN)
+ {
+ aToken.eType = static_cast<SmTokenType>(tok);
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+}
+
+namespace {
+
+class SmXMLTokenAttrHelper
+{
+ SmXMLImportContext& mrContext;
+ MathMLMathvariantValue meMv;
+ bool mbMvFound;
+
+public:
+ SmXMLTokenAttrHelper(SmXMLImportContext& rContext)
+ : mrContext(rContext)
+ , meMv(MathMLMathvariantValue::Normal)
+ , mbMvFound(false)
+ {}
+
+ void RetrieveAttrs(const uno::Reference<xml::sax::XFastAttributeList>& xAttrList);
+ void ApplyAttrs(MathMLMathvariantValue eDefaultMv);
+};
+
+}
+
+void SmXMLTokenAttrHelper::RetrieveAttrs(const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ OUString sValue = aIter.toString();
+ switch(aIter.getToken())
+ {
+ case XML_MATHVARIANT:
+ if (!GetMathMLMathvariantValue(sValue, meMv))
+ SAL_WARN("starmath", "failed to recognize mathvariant: " << sValue);
+ mbMvFound = true;
+ break;
+ default:
+ SAL_WARN("starmath", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString());
+ break;
+ }
+ }
+}
+
+void SmXMLTokenAttrHelper::ApplyAttrs(MathMLMathvariantValue eDefaultMv)
+{
+ assert( eDefaultMv == MathMLMathvariantValue::Normal ||
+ eDefaultMv == MathMLMathvariantValue::Italic );
+
+ std::vector<SmTokenType> vVariant;
+ MathMLMathvariantValue eMv = mbMvFound ? meMv : eDefaultMv;
+ switch(eMv)
+ {
+ case MathMLMathvariantValue::Normal:
+ vVariant.push_back(TNITALIC);
+ break;
+ case MathMLMathvariantValue::Bold:
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::Italic:
+ // nothing to do
+ break;
+ case MathMLMathvariantValue::BoldItalic:
+ vVariant.push_back(TITALIC);
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::DoubleStruck:
+ // TODO
+ break;
+ case MathMLMathvariantValue::BoldFraktur:
+ // TODO: Fraktur
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::Script:
+ // TODO
+ break;
+ case MathMLMathvariantValue::BoldScript:
+ // TODO: Script
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::Fraktur:
+ // TODO
+ break;
+ case MathMLMathvariantValue::SansSerif:
+ vVariant.push_back(TSANS);
+ break;
+ case MathMLMathvariantValue::BoldSansSerif:
+ vVariant.push_back(TSANS);
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::SansSerifItalic:
+ vVariant.push_back(TITALIC);
+ vVariant.push_back(TSANS);
+ break;
+ case MathMLMathvariantValue::SansSerifBoldItalic:
+ vVariant.push_back(TITALIC);
+ vVariant.push_back(TBOLD);
+ vVariant.push_back(TSANS);
+ break;
+ case MathMLMathvariantValue::Monospace:
+ vVariant.push_back(TFIXED);
+ break;
+ case MathMLMathvariantValue::Initial:
+ case MathMLMathvariantValue::Tailed:
+ case MathMLMathvariantValue::Looped:
+ case MathMLMathvariantValue::Stretched:
+ // TODO
+ break;
+ }
+ if (vVariant.empty())
+ return;
+ SmNodeStack &rNodeStack = mrContext.GetSmImport().GetNodeStack();
+ for (auto eType : vVariant)
+ {
+ SmToken aToken;
+ aToken.eType = eType;
+ aToken.cMathChar = '\0';
+ aToken.nLevel = 5;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+}
+
+namespace {
+
+class SmXMLDocContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLDocContext_Impl( SmXMLImport &rImport)
+ : SmXMLImportContext(rImport) {}
+
+ virtual uno::Reference< xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+
+/*avert the gaze from the originator*/
+class SmXMLRowContext_Impl : public SmXMLDocContext_Impl
+{
+protected:
+ size_t nElementCount;
+
+public:
+ SmXMLRowContext_Impl(SmXMLImport &rImport)
+ : SmXMLDocContext_Impl(rImport)
+ , nElementCount(GetSmImport().GetNodeStack().size())
+ {
+ }
+
+ virtual uno::Reference< xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ uno::Reference< xml::sax::XFastContextHandler > StrictCreateChildContext(sal_Int32 nElement);
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class SmXMLEncloseContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ // TODO/LATER: convert <menclose notation="horizontalstrike"> into
+ // "overstrike{}" and extend the Math syntax to support more notations
+ SmXMLEncloseContext_Impl(SmXMLImport &rImport)
+ : SmXMLRowContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLEncloseContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <menclose> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement( nElement );
+}
+
+namespace {
+
+class SmXMLFracContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ // TODO/LATER: convert <mfrac bevelled="true"> into "wideslash{}{}"
+ SmXMLFracContext_Impl(SmXMLImport &rImport)
+ : SmXMLRowContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+
+class SmXMLSqrtContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLSqrtContext_Impl(SmXMLImport &rImport)
+ : SmXMLRowContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+
+class SmXMLRootContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLRootContext_Impl(SmXMLImport &rImport)
+ : SmXMLRowContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+
+class SmXMLStyleContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ SmXMLContext_Helper aStyleHelper;
+
+public:
+ /*Right now the style tag is completely ignored*/
+ SmXMLStyleContext_Impl(SmXMLImport &rImport) : SmXMLRowContext_Impl(rImport),
+ aStyleHelper(*this) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ void SAL_CALL startFastElement(sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList > &xAttrList ) override;
+};
+
+}
+
+void SmXMLStyleContext_Impl::startFastElement( sal_Int32 /*nElement*/, const uno::Reference<
+ xml::sax::XFastAttributeList > & xAttrList )
+{
+ aStyleHelper.RetrieveAttrs(xAttrList);
+}
+
+
+void SmXMLStyleContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <mstyle> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+ if (rNodeStack.size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+ aStyleHelper.ApplyAttrs();
+}
+
+namespace {
+
+class SmXMLPaddedContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ /*Right now the style tag is completely ignored*/
+ SmXMLPaddedContext_Impl(SmXMLImport &rImport)
+ : SmXMLRowContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLPaddedContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <mpadded> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+}
+
+namespace {
+
+class SmXMLPhantomContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ /*Right now the style tag is completely ignored*/
+ SmXMLPhantomContext_Impl(SmXMLImport &rImport)
+ : SmXMLRowContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLPhantomContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <mphantom> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.nLevel = 5;
+ aToken.eType = TPHANTOM;
+
+ std::unique_ptr<SmFontNode> pPhantom(new SmFontNode(aToken));
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+ pPhantom->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pPhantom));
+}
+
+namespace {
+
+class SmXMLFencedContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ sal_Unicode cBegin;
+ sal_Unicode cEnd;
+
+public:
+ SmXMLFencedContext_Impl(SmXMLImport &rImport)
+ : SmXMLRowContext_Impl(rImport),
+ cBegin('('), cEnd(')') {}
+
+ void SAL_CALL startFastElement(sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLFencedContext_Impl::startFastElement(sal_Int32 /*nElement*/, const uno::Reference<
+ xml::sax::XFastAttributeList > & xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ OUString sValue = aIter.toString();
+ switch(aIter.getToken())
+ {
+ //temp, starmath cannot handle multichar brackets (I think)
+ case XML_OPEN:
+ cBegin = sValue[0];
+ break;
+ case XML_CLOSE:
+ cEnd = sValue[0];
+ break;
+ default:
+ SAL_WARN("starmath", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString());
+ /*Go to superclass*/
+ break;
+ }
+ }
+}
+
+
+void SmXMLFencedContext_Impl::endFastElement(sal_Int32 /*nElement*/)
+{
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.aText = ",";
+ aToken.nLevel = 5;
+
+ aToken.eType = TLPARENT;
+ aToken.cMathChar = cBegin;
+ std::unique_ptr<SmStructureNode> pSNode(new SmBraceNode(aToken));
+ std::unique_ptr<SmNode> pLeft(new SmMathSymbolNode(aToken));
+
+ aToken.cMathChar = cEnd;
+ aToken.eType = TRPARENT;
+ std::unique_ptr<SmNode> pRight(new SmMathSymbolNode(aToken));
+
+ SmNodeArray aRelationArray;
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+
+ aToken.cMathChar = '\0';
+ aToken.eType = TIDENT;
+
+ auto i = rNodeStack.size() - nElementCount;
+ if (rNodeStack.size() - nElementCount > 1)
+ i += rNodeStack.size() - 1 - nElementCount;
+ aRelationArray.resize(i);
+ while (rNodeStack.size() > nElementCount)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ aRelationArray[--i] = pNode.release();
+ if (i > 1 && rNodeStack.size() > 1)
+ aRelationArray[--i] = new SmGlyphSpecialNode(aToken);
+ }
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pBody(new SmExpressionNode(aDummy));
+ pBody->SetSubNodes(std::move(aRelationArray));
+
+
+ pSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ pSNode->SetScaleMode(SmScaleMode::Height);
+ GetSmImport().GetNodeStack().push_front(std::move(pSNode));
+}
+
+namespace {
+
+class SmXMLErrorContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLErrorContext_Impl(SmXMLImport &rImport)
+ : SmXMLRowContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLErrorContext_Impl::endFastElement(sal_Int32 /*nElement*/)
+{
+ /*Right now the error tag is completely ignored, what
+ can I do with it in starmath, ?, maybe we need a
+ report window ourselves, do a test for validity of
+ the xml input, use mirrors, and then generate
+ the markup inside the merror with a big red colour
+ of something. For now just throw them all away.
+ */
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+ while (rNodeStack.size() > nElementCount)
+ {
+ rNodeStack.pop_front();
+ }
+}
+
+namespace {
+
+class SmXMLNumberContext_Impl : public SmXMLImportContext
+{
+protected:
+ SmToken aToken;
+
+public:
+ SmXMLNumberContext_Impl(SmXMLImport &rImport)
+ : SmXMLImportContext(rImport)
+ {
+ aToken.cMathChar = '\0';
+ aToken.nLevel = 5;
+ aToken.eType = TNUMBER;
+ }
+
+ virtual void TCharacters(const OUString &rChars) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLNumberContext_Impl::TCharacters(const OUString &rChars)
+{
+ aToken.aText = rChars;
+}
+
+void SmXMLNumberContext_Impl::endFastElement(sal_Int32 )
+{
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken,FNT_NUMBER));
+}
+
+namespace {
+
+class SmXMLAnnotationContext_Impl : public SmXMLImportContext
+{
+ bool bIsStarMath;
+
+public:
+ SmXMLAnnotationContext_Impl(SmXMLImport &rImport)
+ : SmXMLImportContext(rImport), bIsStarMath(false) {}
+
+ void SAL_CALL characters(const OUString &rChars) override;
+
+ void SAL_CALL startFastElement(sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList > & xAttrList ) override;
+};
+
+}
+
+void SmXMLAnnotationContext_Impl::startFastElement(sal_Int32 /*nElement*/, const uno::Reference<
+ xml::sax::XFastAttributeList > & xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ OUString sValue = aIter.toString();
+ // sometimes they have namespace, sometimes not?
+ switch(aIter.getToken() & TOKEN_MASK)
+ {
+ case XML_ENCODING:
+ bIsStarMath= sValue == "StarMath 5.0";
+ break;
+ default:
+ SAL_WARN("starmath", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString());
+ break;
+ }
+ }
+}
+
+void SmXMLAnnotationContext_Impl::characters(const OUString &rChars)
+{
+ if (bIsStarMath)
+ GetSmImport().SetText( GetSmImport().GetText() + rChars );
+}
+
+namespace {
+
+class SmXMLTextContext_Impl : public SmXMLImportContext
+{
+protected:
+ SmToken aToken;
+
+public:
+ SmXMLTextContext_Impl(SmXMLImport &rImport)
+ : SmXMLImportContext(rImport)
+ {
+ aToken.cMathChar = '\0';
+ aToken.nLevel = 5;
+ aToken.eType = TTEXT;
+ }
+
+ virtual void TCharacters(const OUString &rChars) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLTextContext_Impl::TCharacters(const OUString &rChars)
+{
+ aToken.aText = rChars;
+}
+
+void SmXMLTextContext_Impl::endFastElement(sal_Int32 )
+{
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken,FNT_TEXT));
+}
+
+namespace {
+
+class SmXMLStringContext_Impl : public SmXMLImportContext
+{
+protected:
+ SmToken aToken;
+
+public:
+ SmXMLStringContext_Impl(SmXMLImport &rImport)
+ : SmXMLImportContext(rImport)
+ {
+ aToken.cMathChar = '\0';
+ aToken.nLevel = 5;
+ aToken.eType = TTEXT;
+ }
+
+ virtual void TCharacters(const OUString &rChars) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLStringContext_Impl::TCharacters(const OUString &rChars)
+{
+ /*
+ The content of <ms> elements should be rendered with visible "escaping" of
+ certain characters in the content, including at least "double quote"
+ itself, and preferably whitespace other than individual blanks. The intent
+ is for the viewer to see that the expression is a string literal, and to
+ see exactly which characters form its content. For example, <ms>double
+ quote is "</ms> might be rendered as "double quote is \"".
+
+ Obviously this isn't fully done here.
+ */
+ aToken.aText = "\"" + rChars + "\"";
+}
+
+void SmXMLStringContext_Impl::endFastElement(sal_Int32 )
+{
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken,FNT_FIXED));
+}
+
+namespace {
+
+class SmXMLIdentifierContext_Impl : public SmXMLImportContext
+{
+ SmXMLTokenAttrHelper maTokenAttrHelper;
+ SmXMLContext_Helper aStyleHelper;
+ SmToken aToken;
+
+public:
+ SmXMLIdentifierContext_Impl(SmXMLImport &rImport)
+ : SmXMLImportContext(rImport)
+ , maTokenAttrHelper(*this)
+ , aStyleHelper(*this)
+ {
+ aToken.cMathChar = '\0';
+ aToken.nLevel = 5;
+ aToken.eType = TIDENT;
+ }
+
+ void TCharacters(const OUString &rChars) override;
+ void SAL_CALL startFastElement(sal_Int32 /*nElement*/, const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) override
+ {
+ maTokenAttrHelper.RetrieveAttrs(xAttrList);
+ aStyleHelper.RetrieveAttrs(xAttrList);
+ };
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLIdentifierContext_Impl::endFastElement(sal_Int32 )
+{
+ std::unique_ptr<SmTextNode> pNode;
+ //we will handle identifier italic/normal here instead of with a standalone
+ //font node
+ if (((aStyleHelper.nIsItalic == -1) && (aToken.aText.getLength() > 1))
+ || ((aStyleHelper.nIsItalic == 0) && (aToken.aText.getLength() == 1)))
+ {
+ pNode.reset(new SmTextNode(aToken,FNT_FUNCTION));
+ pNode->GetFont().SetItalic(ITALIC_NONE);
+ aStyleHelper.nIsItalic = -1;
+ }
+ else
+ pNode.reset(new SmTextNode(aToken,FNT_VARIABLE));
+ if (aStyleHelper.nIsItalic != -1)
+ {
+ if (aStyleHelper.nIsItalic)
+ pNode->GetFont().SetItalic(ITALIC_NORMAL);
+ else
+ pNode->GetFont().SetItalic(ITALIC_NONE);
+ aStyleHelper.nIsItalic = -1;
+ }
+ GetSmImport().GetNodeStack().push_front(std::move(pNode));
+ aStyleHelper.ApplyAttrs();
+
+ maTokenAttrHelper.ApplyAttrs( (aToken.aText.getLength() == 1)
+ ? MathMLMathvariantValue::Italic
+ : MathMLMathvariantValue::Normal );
+}
+
+void SmXMLIdentifierContext_Impl::TCharacters(const OUString &rChars)
+{
+ aToken.aText = rChars;
+}
+
+namespace {
+
+class SmXMLOperatorContext_Impl : public SmXMLImportContext
+{
+ SmXMLTokenAttrHelper maTokenAttrHelper;
+ bool bIsStretchy;
+ SmToken aToken;
+
+public:
+ SmXMLOperatorContext_Impl(SmXMLImport &rImport)
+ : SmXMLImportContext(rImport)
+ , maTokenAttrHelper(*this)
+ , bIsStretchy(false)
+ {
+ aToken.eType = TSPECIAL;
+ aToken.nLevel = 5;
+ }
+
+ void TCharacters(const OUString &rChars) override;
+ void SAL_CALL startFastElement(sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList > &xAttrList ) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLOperatorContext_Impl::TCharacters(const OUString &rChars)
+{
+ aToken.cMathChar = rChars[0];
+}
+
+void SmXMLOperatorContext_Impl::endFastElement(sal_Int32 )
+{
+ std::unique_ptr<SmMathSymbolNode> pNode(new SmMathSymbolNode(aToken));
+ //For stretchy scaling the scaling must be retrieved from this node
+ //and applied to the expression itself so as to get the expression
+ //to scale the operator to the height of the expression itself
+ if (bIsStretchy)
+ pNode->SetScaleMode(SmScaleMode::Height);
+ GetSmImport().GetNodeStack().push_front(std::move(pNode));
+
+ // TODO: apply to non-alphabetic characters too
+ if (rtl::isAsciiAlpha(aToken.cMathChar))
+ maTokenAttrHelper.ApplyAttrs(MathMLMathvariantValue::Normal);
+}
+
+
+void SmXMLOperatorContext_Impl::startFastElement(sal_Int32 /*nElement*/, const uno::Reference<
+ xml::sax::XFastAttributeList > & xAttrList )
+{
+ maTokenAttrHelper.RetrieveAttrs(xAttrList);
+
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ OUString sValue = aIter.toString();
+ switch(aIter.getToken())
+ {
+ case XML_STRETCHY:
+ bIsStretchy = sValue == GetXMLToken(XML_TRUE);
+ break;
+ default:
+ SAL_WARN("starmath", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString());
+ break;
+ }
+ }
+}
+
+namespace {
+
+class SmXMLSpaceContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLSpaceContext_Impl(SmXMLImport &rImport)
+ : SmXMLImportContext(rImport) {}
+
+ void SAL_CALL startFastElement(sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+bool lcl_CountBlanks(const MathMLAttributeLengthValue &rLV,
+ sal_Int32 *pWide, sal_Int32 *pNarrow)
+{
+ assert(pWide);
+ assert(pNarrow);
+ if (rLV.aNumber.GetNumerator() == 0)
+ {
+ *pWide = *pNarrow = 0;
+ return true;
+ }
+ // TODO: honor other units than em
+ if (rLV.eUnit != MathMLLengthUnit::Em)
+ return false;
+ if (rLV.aNumber.GetNumerator() < 0)
+ return false;
+ const Fraction aTwo(2, 1);
+ auto aWide = rLV.aNumber / aTwo;
+ auto nWide = static_cast<sal_Int32>(static_cast<long>(aWide));
+ if (nWide < 0)
+ return false;
+ const Fraction aPointFive(1, 2);
+ auto aNarrow = (rLV.aNumber - Fraction(nWide, 1) * aTwo) / aPointFive;
+ auto nNarrow = static_cast<sal_Int32>(static_cast<long>(aNarrow));
+ if (nNarrow < 0)
+ return false;
+ *pWide = nWide;
+ *pNarrow = nNarrow;
+ return true;
+}
+
+}
+
+void SmXMLSpaceContext_Impl::startFastElement(sal_Int32 /*nElement*/,
+ const uno::Reference<xml::sax::XFastAttributeList > & xAttrList )
+{
+ // There is no syntax in Math to specify blank nodes of arbitrary size yet.
+ MathMLAttributeLengthValue aLV;
+ sal_Int32 nWide = 0, nNarrow = 0;
+
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ OUString sValue = aIter.toString();
+ switch (aIter.getToken())
+ {
+ case XML_WIDTH:
+ if ( ParseMathMLAttributeLengthValue(sValue.trim(), aLV) <= 0 ||
+ !lcl_CountBlanks(aLV, &nWide, &nNarrow) )
+ SAL_WARN("starmath", "ignore mspace's width: " << sValue);
+ break;
+ default:
+ SAL_WARN("starmath", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString());
+ break;
+ }
+ }
+ SmToken aToken;
+ aToken.eType = TBLANK;
+ aToken.cMathChar = '\0';
+ aToken.nGroup = TG::Blank;
+ aToken.nLevel = 5;
+ std::unique_ptr<SmBlankNode> pBlank(new SmBlankNode(aToken));
+ if (nWide > 0)
+ pBlank->IncreaseBy(aToken, nWide);
+ if (nNarrow > 0)
+ {
+ aToken.eType = TSBLANK;
+ pBlank->IncreaseBy(aToken, nNarrow);
+ }
+ GetSmImport().GetNodeStack().push_front(std::move(pBlank));
+}
+
+namespace {
+
+class SmXMLSubContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ void GenericEndElement(SmTokenType eType,SmSubSup aSubSup);
+
+public:
+ SmXMLSubContext_Impl(SmXMLImport &rImport)
+ : SmXMLRowContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 ) override
+ {
+ GenericEndElement(TRSUB,RSUB);
+ }
+};
+
+}
+
+void SmXMLSubContext_Impl::GenericEndElement(SmTokenType eType, SmSubSup eSubSup)
+{
+ /*The <msub> element requires exactly 2 arguments.*/
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE( bNodeCheck, "Sub has not two arguments" );
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.eType = eType;
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+
+ // initialize subnodes array
+ SmNodeArray aSubNodes;
+ aSubNodes.resize(1 + SUBSUP_NUM_ENTRIES);
+ for (size_t i = 1; i < aSubNodes.size(); i++)
+ aSubNodes[i] = nullptr;
+
+ aSubNodes[eSubSup+1] = popOrZero(rNodeStack).release();
+ aSubNodes[0] = popOrZero(rNodeStack).release();
+ pNode->SetSubNodes(std::move(aSubNodes));
+ rNodeStack.push_front(std::move(pNode));
+}
+
+namespace {
+
+class SmXMLSupContext_Impl : public SmXMLSubContext_Impl
+{
+public:
+ SmXMLSupContext_Impl(SmXMLImport &rImport)
+ : SmXMLSubContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 ) override
+ {
+ GenericEndElement(TRSUP,RSUP);
+ }
+};
+
+
+class SmXMLSubSupContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ void GenericEndElement(SmTokenType eType, SmSubSup aSub,SmSubSup aSup);
+
+public:
+ SmXMLSubSupContext_Impl(SmXMLImport &rImport)
+ : SmXMLRowContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 ) override
+ {
+ GenericEndElement(TRSUB,RSUB,RSUP);
+ }
+};
+
+}
+
+void SmXMLSubSupContext_Impl::GenericEndElement(SmTokenType eType,
+ SmSubSup aSub,SmSubSup aSup)
+{
+ /*The <msub> element requires exactly 3 arguments.*/
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 3;
+ OSL_ENSURE( bNodeCheck, "SubSup has not three arguments" );
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.eType = eType;
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+
+ // initialize subnodes array
+ SmNodeArray aSubNodes;
+ aSubNodes.resize(1 + SUBSUP_NUM_ENTRIES);
+ for (size_t i = 1; i < aSubNodes.size(); i++)
+ aSubNodes[i] = nullptr;
+
+ aSubNodes[aSup+1] = popOrZero(rNodeStack).release();
+ aSubNodes[aSub+1] = popOrZero(rNodeStack).release();
+ aSubNodes[0] = popOrZero(rNodeStack).release();
+ pNode->SetSubNodes(std::move(aSubNodes));
+ rNodeStack.push_front(std::move(pNode));
+}
+
+namespace {
+
+class SmXMLUnderContext_Impl : public SmXMLSubContext_Impl
+{
+protected:
+ sal_Int16 nAttrCount;
+
+public:
+ SmXMLUnderContext_Impl(SmXMLImport &rImport)
+ : SmXMLSubContext_Impl(rImport)
+ , nAttrCount( 0 )
+ {}
+
+ void SAL_CALL startFastElement(sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList > &xAttrList ) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ void HandleAccent();
+};
+
+}
+
+void SmXMLUnderContext_Impl::startFastElement(sal_Int32 /*nElement*/, const uno::Reference<
+ xml::sax::XFastAttributeList > & xAttrList )
+{
+ sax_fastparser::FastAttributeList& rAttribList =
+ sax_fastparser::castToFastAttributeList( xAttrList );
+ nAttrCount = rAttribList.getFastAttributeTokens().size();
+}
+
+void SmXMLUnderContext_Impl::HandleAccent()
+{
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE( bNodeCheck, "Sub has not two arguments" );
+ if (!bNodeCheck)
+ return;
+
+ /*Just one special case for the underline thing*/
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+ std::unique_ptr<SmNode> pTest = popOrZero(rNodeStack);
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.eType = TUNDERLINE;
+
+ std::unique_ptr<SmNode> pFirst;
+ std::unique_ptr<SmStructureNode> pNode(new SmAttributNode(aToken));
+ if ((pTest->GetToken().cMathChar & 0x0FFF) == 0x0332)
+ {
+ pFirst.reset(new SmRectangleNode(aToken));
+ }
+ else
+ pFirst = std::move(pTest);
+
+ std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
+ pNode->SetSubNodes(std::move(pFirst), std::move(pSecond));
+ pNode->SetScaleMode(SmScaleMode::Width);
+ rNodeStack.push_front(std::move(pNode));
+}
+
+
+void SmXMLUnderContext_Impl::endFastElement(sal_Int32 )
+{
+ if (!nAttrCount)
+ GenericEndElement(TCSUB,CSUB);
+ else
+ HandleAccent();
+}
+
+namespace {
+
+class SmXMLOverContext_Impl : public SmXMLSubContext_Impl
+{
+protected:
+ sal_Int16 nAttrCount;
+
+public:
+ SmXMLOverContext_Impl(SmXMLImport &rImport)
+ : SmXMLSubContext_Impl(rImport), nAttrCount(0) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ void SAL_CALL startFastElement(sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList > &xAttrList ) override;
+ void HandleAccent();
+};
+
+}
+
+void SmXMLOverContext_Impl::startFastElement(sal_Int32 /*nElement*/, const uno::Reference<
+ xml::sax::XFastAttributeList > & xAttrList )
+{
+ sax_fastparser::FastAttributeList& rAttribList =
+ sax_fastparser::castToFastAttributeList( xAttrList );
+ nAttrCount = rAttribList.getFastAttributeTokens().size();
+}
+
+
+void SmXMLOverContext_Impl::endFastElement(sal_Int32 )
+{
+ if (!nAttrCount)
+ GenericEndElement(TCSUP,CSUP);
+ else
+ HandleAccent();
+}
+
+
+void SmXMLOverContext_Impl::HandleAccent()
+{
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE (bNodeCheck, "Sub has not two arguments");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.eType = TACUTE;
+
+ std::unique_ptr<SmAttributNode> pNode(new SmAttributNode(aToken));
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+
+ std::unique_ptr<SmNode> pFirst = popOrZero(rNodeStack);
+ std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
+ pNode->SetSubNodes(std::move(pFirst), std::move(pSecond));
+ pNode->SetScaleMode(SmScaleMode::Width);
+ rNodeStack.push_front(std::move(pNode));
+
+}
+
+namespace {
+
+class SmXMLUnderOverContext_Impl : public SmXMLSubSupContext_Impl
+{
+public:
+ SmXMLUnderOverContext_Impl(SmXMLImport &rImport)
+ : SmXMLSubSupContext_Impl(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 ) override
+ {
+ GenericEndElement(TCSUB,CSUB,CSUP);
+ }
+};
+
+
+class SmXMLMultiScriptsContext_Impl : public SmXMLSubSupContext_Impl
+{
+ bool bHasPrescripts;
+
+ void ProcessSubSupPairs(bool bIsPrescript);
+
+public:
+ SmXMLMultiScriptsContext_Impl(SmXMLImport &rImport) :
+ SmXMLSubSupContext_Impl(rImport),
+ bHasPrescripts(false) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ virtual uno::Reference< xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+
+class SmXMLNoneContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLNoneContext_Impl(SmXMLImport &rImport)
+ : SmXMLImportContext(rImport) {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+void SmXMLNoneContext_Impl::endFastElement(sal_Int32 )
+{
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.aText.clear();
+ aToken.nLevel = 5;
+ aToken.eType = TIDENT;
+ GetSmImport().GetNodeStack().push_front(
+ std::make_unique<SmTextNode>(aToken,FNT_VARIABLE));
+}
+
+namespace {
+
+class SmXMLPrescriptsContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLPrescriptsContext_Impl(SmXMLImport &rImport)
+ : SmXMLImportContext(rImport) {}
+};
+
+
+class SmXMLTableRowContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLTableRowContext_Impl(SmXMLImport &rImport) :
+ SmXMLRowContext_Impl(rImport)
+ {}
+
+ virtual uno::Reference< xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+
+class SmXMLTableContext_Impl : public SmXMLTableRowContext_Impl
+{
+public:
+ SmXMLTableContext_Impl(SmXMLImport &rImport) :
+ SmXMLTableRowContext_Impl(rImport)
+ {}
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ virtual uno::Reference< xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+
+class SmXMLTableCellContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLTableCellContext_Impl(SmXMLImport &rImport) :
+ SmXMLRowContext_Impl(rImport)
+ {}
+};
+
+
+class SmXMLAlignGroupContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLAlignGroupContext_Impl(SmXMLImport &rImport) :
+ SmXMLRowContext_Impl(rImport)
+ {}
+
+ /*Don't do anything with alignment for now*/
+ void SAL_CALL endFastElement(sal_Int32 ) override
+ {
+ }
+};
+
+
+class SmXMLActionContext_Impl : public SmXMLRowContext_Impl
+{
+ size_t mnSelection; // 1-based
+
+public:
+ SmXMLActionContext_Impl(SmXMLImport &rImport) :
+ SmXMLRowContext_Impl(rImport)
+ , mnSelection(1)
+ {}
+
+ void SAL_CALL startFastElement(sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList> &xAttrList) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+
+// NB: virtually inherit so we can multiply inherit properly
+// in SmXMLFlatDocContext_Impl
+class SmXMLOfficeContext_Impl : public virtual SvXMLImportContext
+{
+public:
+ SmXMLOfficeContext_Impl( SmXMLImport &rImport )
+ : SvXMLImportContext(rImport) {}
+
+ virtual void SAL_CALL characters( const OUString& /*aChars*/ ) override {}
+
+ virtual void SAL_CALL startFastElement( sal_Int32 /*nElement*/,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) override {}
+
+ virtual void SAL_CALL endFastElement( sal_Int32 /*nElement*/ ) override {}
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SmXMLOfficeContext_Impl::createFastChildContext(sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > &/*xAttrList*/)
+{
+ if ( nElement == XML_ELEMENT(OFFICE, XML_META) )
+ {
+ SAL_WARN("starmath", "XML_TOK_DOC_META: should not have come here, maybe document is invalid?");
+ }
+ else if ( nElement == XML_ELEMENT(OFFICE, XML_SETTINGS) )
+ {
+ return new XMLDocumentSettingsContext( GetImport() );
+ }
+ return nullptr;
+}
+
+namespace {
+
+// context for flat file xml format
+class SmXMLFlatDocContext_Impl
+ : public SmXMLOfficeContext_Impl, public SvXMLMetaDocumentContext
+{
+public:
+ SmXMLFlatDocContext_Impl( SmXMLImport& i_rImport,
+ const uno::Reference<document::XDocumentProperties>& i_xDocProps);
+
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+}
+
+SmXMLFlatDocContext_Impl::SmXMLFlatDocContext_Impl( SmXMLImport& i_rImport,
+ const uno::Reference<document::XDocumentProperties>& i_xDocProps) :
+ SvXMLImportContext(i_rImport),
+ SmXMLOfficeContext_Impl(i_rImport),
+ SvXMLMetaDocumentContext(i_rImport, i_xDocProps)
+{
+}
+
+void SAL_CALL SmXMLFlatDocContext_Impl::startFastElement( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLMetaDocumentContext::startFastElement(nElement, xAttrList);
+}
+
+void SAL_CALL SmXMLFlatDocContext_Impl::endFastElement( sal_Int32 nElement )
+{
+ SvXMLMetaDocumentContext::endFastElement(nElement);
+}
+
+void SAL_CALL SmXMLFlatDocContext_Impl::characters( const OUString& rChars )
+{
+ SvXMLMetaDocumentContext::characters(rChars);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SmXMLFlatDocContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ // behave like meta base class iff we encounter office:meta
+ if ( nElement == XML_ELEMENT(OFFICE, XML_META) )
+ {
+ return SvXMLMetaDocumentContext::createFastChildContext(
+ nElement, xAttrList );
+ }
+ else
+ {
+ return SmXMLOfficeContext_Impl::createFastChildContext(
+ nElement, xAttrList );
+ }
+}
+
+static const SvXMLTokenMapEntry aColorTokenMap[] =
+{
+ { XML_NAMESPACE_MATH, XML_BLACK, TBLACK},
+ { XML_NAMESPACE_MATH, XML_WHITE, TWHITE},
+ { XML_NAMESPACE_MATH, XML_RED, TRED},
+ { XML_NAMESPACE_MATH, XML_GREEN, TGREEN},
+ { XML_NAMESPACE_MATH, XML_BLUE, TBLUE},
+ { XML_NAMESPACE_MATH, XML_AQUA, TAQUA},
+ { XML_NAMESPACE_MATH, XML_FUCHSIA, TFUCHSIA},
+ { XML_NAMESPACE_MATH, XML_YELLOW, TYELLOW},
+ { XML_NAMESPACE_MATH, XML_NAVY, TNAVY},
+ { XML_NAMESPACE_MATH, XML_TEAL, TTEAL},
+ { XML_NAMESPACE_MATH, XML_MAROON, TMAROON},
+ { XML_NAMESPACE_MATH, XML_PURPLE, TPURPLE},
+ { XML_NAMESPACE_MATH, XML_OLIVE, TOLIVE},
+ { XML_NAMESPACE_MATH, XML_GRAY, TGRAY},
+ { XML_NAMESPACE_MATH, XML_SILVER, TSILVER},
+ { XML_NAMESPACE_MATH, XML_LIME, TLIME},
+ XML_TOKEN_MAP_END
+};
+
+
+const SvXMLTokenMap& SmXMLImport::GetColorTokenMap()
+{
+ if (!pColorTokenMap)
+ pColorTokenMap.reset(new SvXMLTokenMap(aColorTokenMap));
+ return *pColorTokenMap;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SmXMLDocContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
+{
+ uno::Reference< xml::sax::XFastContextHandler > xContext;
+
+ switch(nElement)
+ {
+ //Consider semantics a dummy except for any starmath annotations
+ case XML_ELEMENT(MATH, XML_SEMANTICS):
+ xContext = new SmXMLRowContext_Impl(GetSmImport());
+ break;
+ /*General Layout Schemata*/
+ case XML_ELEMENT(MATH, XML_MROW):
+ xContext = new SmXMLRowContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MENCLOSE):
+ xContext = new SmXMLEncloseContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MFRAC):
+ xContext = new SmXMLFracContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSQRT):
+ xContext = new SmXMLSqrtContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MROOT):
+ xContext = new SmXMLRootContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSTYLE):
+ xContext = new SmXMLStyleContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MERROR):
+ xContext = new SmXMLErrorContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MPADDED):
+ xContext = new SmXMLPaddedContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MPHANTOM):
+ xContext = new SmXMLPhantomContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MFENCED):
+ xContext = new SmXMLFencedContext_Impl(GetSmImport());
+ break;
+ /*Script and Limit Schemata*/
+ case XML_ELEMENT(MATH, XML_MSUB):
+ xContext = new SmXMLSubContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSUP):
+ xContext = new SmXMLSupContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSUBSUP):
+ xContext = new SmXMLSubSupContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MUNDER):
+ xContext = new SmXMLUnderContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MOVER):
+ xContext = new SmXMLOverContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MUNDEROVER):
+ xContext = new SmXMLUnderOverContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MMULTISCRIPTS):
+ xContext = new SmXMLMultiScriptsContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MTABLE):
+ xContext = new SmXMLTableContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MACTION):
+ xContext = new SmXMLActionContext_Impl(GetSmImport());
+ break;
+ default:
+ /*Basically there's an implicit mrow around certain bare
+ *elements, use a RowContext to see if this is one of
+ *those ones*/
+ rtl::Reference<SmXMLRowContext_Impl> aTempContext(new SmXMLRowContext_Impl(GetSmImport()));
+
+ xContext = aTempContext->StrictCreateChildContext(nElement);
+ break;
+ }
+ return xContext;
+}
+
+void SmXMLDocContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+
+ std::unique_ptr<SmNode> pContextNode = popOrZero(rNodeStack);
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pSNode(new SmLineNode(aDummy));
+ pSNode->SetSubNodes(std::move(pContextNode), nullptr);
+ rNodeStack.push_front(std::move(pSNode));
+
+ SmNodeArray LineArray;
+ auto n = rNodeStack.size();
+ LineArray.resize(n);
+ for (size_t j = 0; j < n; j++)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ LineArray[n - (j + 1)] = pNode.release();
+ }
+ std::unique_ptr<SmStructureNode> pSNode2(new SmTableNode(aDummy));
+ pSNode2->SetSubNodes(std::move(LineArray));
+ rNodeStack.push_front(std::move(pSNode2));
+}
+
+void SmXMLFracContext_Impl::endFastElement(sal_Int32 )
+{
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+ const bool bNodeCheck = rNodeStack.size() - nElementCount == 2;
+ OSL_ENSURE( bNodeCheck, "Fraction (mfrac) tag is missing component" );
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.eType = TOVER;
+ std::unique_ptr<SmStructureNode> pSNode(new SmBinVerNode(aToken));
+ std::unique_ptr<SmNode> pOper(new SmRectangleNode(aToken));
+ std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
+ std::unique_ptr<SmNode> pFirst = popOrZero(rNodeStack);
+ pSNode->SetSubNodes(std::move(pFirst), std::move(pOper), std::move(pSecond));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+void SmXMLRootContext_Impl::endFastElement(sal_Int32 )
+{
+ /*The <mroot> element requires exactly 2 arguments.*/
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE( bNodeCheck, "Root tag is missing component");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = MS_SQRT; //Temporary: alert, based on StarSymbol font
+ aToken.eType = TNROOT;
+ std::unique_ptr<SmStructureNode> pSNode(new SmRootNode(aToken));
+ std::unique_ptr<SmNode> pOper(new SmRootSymbolNode(aToken));
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+ std::unique_ptr<SmNode> pIndex = popOrZero(rNodeStack);
+ std::unique_ptr<SmNode> pBase = popOrZero(rNodeStack);
+ pSNode->SetSubNodes(std::move(pIndex), std::move(pOper), std::move(pBase));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+void SmXMLSqrtContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <msqrt> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+
+ SmToken aToken;
+ aToken.cMathChar = MS_SQRT; //Temporary: alert, based on StarSymbol font
+ aToken.eType = TSQRT;
+ std::unique_ptr<SmStructureNode> pSNode(new SmRootNode(aToken));
+ std::unique_ptr<SmNode> pOper(new SmRootSymbolNode(aToken));
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+ pSNode->SetSubNodes(nullptr, std::move(pOper), popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+void SmXMLRowContext_Impl::endFastElement(sal_Int32 )
+{
+ SmNodeArray aRelationArray;
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+
+ if (rNodeStack.size() > nElementCount)
+ {
+ auto nSize = rNodeStack.size() - nElementCount;
+
+ aRelationArray.resize(nSize);
+ for (auto j=nSize;j > 0;j--)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ aRelationArray[j-1] = pNode.release();
+ }
+
+ //If the first or last element is an operator with stretchyness
+ //set then we must create a brace node here from those elements,
+ //removing the stretchness from the operators and applying it to
+ //ourselves, and creating the appropriate dummy StarMath none bracket
+ //to balance the arrangement
+ if (((aRelationArray[0]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[0]->GetType() == SmNodeType::Math))
+ || ((aRelationArray[nSize-1]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[nSize-1]->GetType() == SmNodeType::Math)))
+ {
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.nLevel = 5;
+
+ int nLeft=0,nRight=0;
+ if ((aRelationArray[0]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[0]->GetType() == SmNodeType::Math))
+ {
+ aToken = aRelationArray[0]->GetToken();
+ nLeft=1;
+ }
+ else
+ aToken.cMathChar = '\0';
+
+ aToken.eType = TLPARENT;
+ std::unique_ptr<SmNode> pLeft(new SmMathSymbolNode(aToken));
+
+ if ((aRelationArray[nSize-1]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[nSize-1]->GetType() == SmNodeType::Math))
+ {
+ aToken = aRelationArray[nSize-1]->GetToken();
+ nRight=1;
+ }
+ else
+ aToken.cMathChar = '\0';
+
+ aToken.eType = TRPARENT;
+ std::unique_ptr<SmNode> pRight(new SmMathSymbolNode(aToken));
+
+ SmNodeArray aRelationArray2;
+
+ //!! nSize-nLeft-nRight may be < 0 !!
+ int nRelArrSize = nSize-nLeft-nRight;
+ if (nRelArrSize > 0)
+ {
+ aRelationArray2.resize(nRelArrSize);
+ for (int i=0;i < nRelArrSize;i++)
+ {
+ aRelationArray2[i] = aRelationArray[i+nLeft];
+ aRelationArray[i+nLeft] = nullptr;
+ }
+ }
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pSNode(new SmBraceNode(aToken));
+ std::unique_ptr<SmStructureNode> pBody(new SmExpressionNode(aDummy));
+ pBody->SetSubNodes(std::move(aRelationArray2));
+
+ pSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ pSNode->SetScaleMode(SmScaleMode::Height);
+ rNodeStack.push_front(std::move(pSNode));
+
+ for (auto a : aRelationArray)
+ delete a;
+
+ return;
+ }
+ }
+ else
+ {
+ // The elements msqrt, mstyle, merror, menclose, mpadded, mphantom, mtd, and math
+ // treat their content as a single inferred mrow in case their content is empty.
+ // Here an empty group {} is used to catch those cases and transform them without error
+ // to StarMath.
+ aRelationArray.resize(2);
+ SmToken aToken;
+ aToken.cMathChar = MS_LBRACE;
+ aToken.nLevel = 5;
+ aToken.eType = TLGROUP;
+ aToken.aText = "{";
+ aRelationArray[0] = new SmLineNode(aToken);
+
+ aToken.cMathChar = MS_RBRACE;
+ aToken.nLevel = 0;
+ aToken.eType = TRGROUP;
+ aToken.aText = "}";
+ aRelationArray[1] = new SmLineNode(aToken);
+ }
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pSNode(new SmExpressionNode(aDummy));
+ pSNode->SetSubNodes(std::move(aRelationArray));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SmXMLRowContext_Impl::StrictCreateChildContext(
+ sal_Int32 nElement)
+{
+ uno::Reference< xml::sax::XFastContextHandler > pContext;
+
+ switch(nElement)
+ {
+ /*Note that these should accept malignmark subelements, but do not*/
+ case XML_ELEMENT(MATH, XML_MN):
+ pContext = new SmXMLNumberContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MI):
+ pContext = new SmXMLIdentifierContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MO):
+ pContext = new SmXMLOperatorContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MTEXT):
+ pContext = new SmXMLTextContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSPACE):
+ pContext = new SmXMLSpaceContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MS):
+ pContext = new SmXMLStringContext_Impl(GetSmImport());
+ break;
+
+ /*Note: The maligngroup should only be seen when the row
+ * (or descendants) are in a table*/
+ case XML_ELEMENT(MATH, XML_MALIGNGROUP):
+ pContext = new SmXMLAlignGroupContext_Impl(GetSmImport());
+ break;
+
+ case XML_ELEMENT(MATH, XML_ANNOTATION):
+ pContext = new SmXMLAnnotationContext_Impl(GetSmImport());
+ break;
+
+ default:
+ break;
+ }
+ return pContext;
+}
+
+
+uno::Reference< xml::sax::XFastContextHandler > SmXMLRowContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference< xml::sax::XFastContextHandler > xContext = StrictCreateChildContext(nElement);
+
+ if (!xContext)
+ {
+ //Hmm, unrecognized for this level, check to see if its
+ //an element that can have an implicit schema around it
+ xContext = SmXMLDocContext_Impl::createFastChildContext(nElement, xAttrList);
+ }
+ return xContext;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SmXMLMultiScriptsContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference< xml::sax::XFastContextHandler > xContext;
+
+ switch(nElement)
+ {
+ case XML_ELEMENT(MATH, XML_MPRESCRIPTS):
+ bHasPrescripts = true;
+ ProcessSubSupPairs(false);
+ xContext = new SmXMLPrescriptsContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_NONE):
+ xContext = new SmXMLNoneContext_Impl(GetSmImport());
+ break;
+ default:
+ xContext = SmXMLRowContext_Impl::createFastChildContext(nElement,xAttrList);
+ break;
+ }
+ return xContext;
+}
+
+void SmXMLMultiScriptsContext_Impl::ProcessSubSupPairs(bool bIsPrescript)
+{
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+
+ if (rNodeStack.size() <= nElementCount)
+ return;
+
+ auto nCount = rNodeStack.size() - nElementCount - 1;
+ if (nCount == 0)
+ return;
+
+ if (nCount % 2 == 0)
+ {
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.eType = bIsPrescript ? TLSUB : TRSUB;
+
+ SmNodeStack aReverseStack;
+ for (size_t i = 0; i < nCount + 1; i++)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ aReverseStack.push_front(std::move(pNode));
+ }
+
+ SmSubSup eSub = bIsPrescript ? LSUB : RSUB;
+ SmSubSup eSup = bIsPrescript ? LSUP : RSUP;
+
+ for (size_t i = 0; i < nCount; i += 2)
+ {
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
+
+ // initialize subnodes array
+ SmNodeArray aSubNodes(1 + SUBSUP_NUM_ENTRIES);
+
+ /*On each loop the base and its sub sup pair becomes the
+ base for the next loop to which the next sub sup pair is
+ attached, i.e. wheels within wheels*/
+ aSubNodes[0] = popOrZero(aReverseStack).release();
+
+ std::unique_ptr<SmNode> pScriptNode = popOrZero(aReverseStack);
+
+ if (pScriptNode && ((pScriptNode->GetToken().eType != TIDENT) ||
+ (!pScriptNode->GetToken().aText.isEmpty())))
+ aSubNodes[eSub+1] = pScriptNode.release();
+ pScriptNode = popOrZero(aReverseStack);
+ if (pScriptNode && ((pScriptNode->GetToken().eType != TIDENT) ||
+ (!pScriptNode->GetToken().aText.isEmpty())))
+ aSubNodes[eSup+1] = pScriptNode.release();
+
+ pNode->SetSubNodes(std::move(aSubNodes));
+ aReverseStack.push_front(std::move(pNode));
+ }
+ assert(!aReverseStack.empty());
+ auto pNode = std::move(aReverseStack.front());
+ aReverseStack.pop_front();
+ rNodeStack.push_front(std::move(pNode));
+ }
+ else
+ {
+ // Ignore odd number of elements.
+ for (size_t i = 0; i < nCount; i++)
+ {
+ rNodeStack.pop_front();
+ }
+ }
+}
+
+
+void SmXMLTableContext_Impl::endFastElement(sal_Int32 )
+{
+ SmNodeArray aExpressionArray;
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+ SmNodeStack aReverseStack;
+ aExpressionArray.resize(rNodeStack.size()-nElementCount);
+
+ size_t nRows = rNodeStack.size()-nElementCount;
+ size_t nCols = 0;
+
+ for (size_t i = nRows; i > 0; --i)
+ {
+ SmNode* pArray = rNodeStack.front().release();
+ rNodeStack.pop_front();
+ if (pArray->GetNumSubNodes() == 0)
+ {
+ //This is a little tricky, it is possible that there was
+ //be elements that were not inside a <mtd> pair, in which
+ //case they will not be in a row, i.e. they will not have
+ //SubNodes, so we have to wait until here before we can
+ //resolve the situation. Implicit surrounding tags are
+ //surprisingly difficult to get right within this
+ //architecture
+
+ SmNodeArray aRelationArray;
+ aRelationArray.resize(1);
+ aRelationArray[0] = pArray;
+ SmToken aDummy;
+ SmExpressionNode* pExprNode = new SmExpressionNode(aDummy);
+ pExprNode->SetSubNodes(std::move(aRelationArray));
+ pArray = pExprNode;
+ }
+
+ nCols = std::max(nCols, pArray->GetNumSubNodes());
+ aReverseStack.push_front(std::unique_ptr<SmNode>(pArray));
+ }
+ if (nCols > SAL_MAX_UINT16)
+ throw std::range_error("column limit");
+ if (nRows > SAL_MAX_UINT16)
+ throw std::range_error("row limit");
+ aExpressionArray.resize(nCols*nRows);
+ size_t j=0;
+ for (auto & elem : aReverseStack)
+ {
+ std::unique_ptr<SmStructureNode> xArray(static_cast<SmStructureNode*>(elem.release()));
+ for (size_t i = 0; i < xArray->GetNumSubNodes(); ++i)
+ aExpressionArray[j++] = xArray->GetSubNode(i);
+ xArray->ClearSubNodes();
+ }
+ aReverseStack.clear();
+
+ SmToken aToken;
+ aToken.cMathChar = '\0';
+ aToken.eType = TMATRIX;
+ std::unique_ptr<SmMatrixNode> pSNode(new SmMatrixNode(aToken));
+ pSNode->SetSubNodes(std::move(aExpressionArray));
+ pSNode->SetRowCol(nRows, nCols);
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SmXMLTableRowContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference< xml::sax::XFastContextHandler > xContext;
+
+ switch(nElement)
+ {
+ case XML_ELEMENT(MATH, XML_MTD):
+ xContext = new SmXMLTableCellContext_Impl(GetSmImport());
+ break;
+ default:
+ xContext = SmXMLRowContext_Impl::createFastChildContext(nElement,xAttrList);
+ break;
+ }
+ return xContext;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SmXMLTableContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference< xml::sax::XFastContextHandler > xContext;
+
+ switch(nElement)
+ {
+ case XML_ELEMENT(MATH, XML_MTR):
+ xContext = new SmXMLTableRowContext_Impl(GetSmImport());
+ break;
+ default:
+ xContext = SmXMLTableRowContext_Impl::createFastChildContext(nElement, xAttrList);
+ break;
+ }
+ return xContext;
+}
+
+void SmXMLMultiScriptsContext_Impl::endFastElement(sal_Int32 )
+{
+ ProcessSubSupPairs(bHasPrescripts);
+}
+
+void SmXMLActionContext_Impl::startFastElement(sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList> & xAttrList)
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ OUString sValue = aIter.toString();
+ switch(aIter.getToken())
+ {
+ case XML_SELECTION:
+ {
+ sal_uInt32 n = sValue.toUInt32();
+ if (n > 0) mnSelection = static_cast<size_t>(n);
+ }
+ break;
+ default:
+ SAL_WARN("starmath", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString());
+ break;
+ }
+ }
+}
+
+void SmXMLActionContext_Impl::endFastElement(sal_Int32 )
+{
+ SmNodeStack &rNodeStack = GetSmImport().GetNodeStack();
+ auto nSize = rNodeStack.size();
+ if (nSize <= nElementCount) {
+ // not compliant to maction's specification, e.g., no subexpressions
+ return;
+ }
+ assert(mnSelection > 0);
+ if (nSize < nElementCount + mnSelection) {
+ // No selected subexpression exists, which is a MathML error;
+ // fallback to selecting the first
+ mnSelection = 1;
+ }
+ assert(nSize >= nElementCount + mnSelection);
+ for (auto i=nSize-(nElementCount+mnSelection); i > 0; i--)
+ {
+ rNodeStack.pop_front();
+ }
+ auto pSelected = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ for (auto i=rNodeStack.size()-nElementCount; i > 0; i--)
+ {
+ rNodeStack.pop_front();
+ }
+ rNodeStack.push_front(std::move(pSelected));
+}
+
+SvXMLImportContext *SmXMLImport::CreateFastContext(sal_Int32 nElement,
+ const uno::Reference <xml::sax::XFastAttributeList> & /*xAttrList*/)
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT ):
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT_META ):
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetModel(), uno::UNO_QUERY_THROW);
+ pContext = ( (nElement & TOKEN_MASK) == XML_DOCUMENT_META )
+ ? new SvXMLMetaDocumentContext( *this,
+ xDPS->getDocumentProperties() )
+ // flat OpenDocument file format -- this has not been tested...
+ : new SmXMLFlatDocContext_Impl( *this,
+ xDPS->getDocumentProperties() );
+ }
+ break;
+ default:
+ if (IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE))
+ pContext = new SmXMLOfficeContext_Impl(*this);
+ else
+ pContext = new SmXMLDocContext_Impl(*this);
+ }
+ return pContext;
+}
+
+SmXMLImport::~SmXMLImport() throw ()
+{
+ cleanup();
+}
+
+void SmXMLImport::SetViewSettings(const Sequence<PropertyValue>& aViewProps)
+{
+ uno::Reference <frame::XModel> xModel = GetModel();
+ if ( !xModel.is() )
+ return;
+
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+
+ if ( !pModel )
+ return;
+
+ SmDocShell *pDocShell =
+ static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if ( !pDocShell )
+ return;
+
+ tools::Rectangle aRect( pDocShell->GetVisArea() );
+
+ long nTmp = 0;
+
+ for (const PropertyValue& rValue : aViewProps)
+ {
+ if (rValue.Name == "ViewAreaTop" )
+ {
+ rValue.Value >>= nTmp;
+ aRect.SaturatingSetY(nTmp);
+ }
+ else if (rValue.Name == "ViewAreaLeft" )
+ {
+ rValue.Value >>= nTmp;
+ aRect.SaturatingSetX(nTmp);
+ }
+ else if (rValue.Name == "ViewAreaWidth" )
+ {
+ rValue.Value >>= nTmp;
+ Size aSize( aRect.GetSize() );
+ aSize.setWidth( nTmp );
+ aRect.SaturatingSetSize(aSize);
+ }
+ else if (rValue.Name == "ViewAreaHeight" )
+ {
+ rValue.Value >>= nTmp;
+ Size aSize( aRect.GetSize() );
+ aSize.setHeight( nTmp );
+ aRect.SaturatingSetSize(aSize);
+ }
+ }
+
+ pDocShell->SetVisArea ( aRect );
+}
+
+void SmXMLImport::SetConfigurationSettings(const Sequence<PropertyValue>& aConfProps)
+{
+ uno::Reference < XPropertySet > xProps ( GetModel(), UNO_QUERY );
+ if ( !xProps.is() )
+ return;
+
+ Reference < XPropertySetInfo > xInfo ( xProps->getPropertySetInfo() );
+ if (!xInfo.is() )
+ return;
+
+ const OUString sFormula ( "Formula" );
+ const OUString sBasicLibraries ( "BasicLibraries" );
+ const OUString sDialogLibraries ( "DialogLibraries" );
+ for ( const PropertyValue& rValue : aConfProps )
+ {
+ if (rValue.Name != sFormula &&
+ rValue.Name != sBasicLibraries &&
+ rValue.Name != sDialogLibraries)
+ {
+ try
+ {
+ if ( xInfo->hasPropertyByName( rValue.Name ) )
+ xProps->setPropertyValue( rValue.Name, rValue.Value );
+ }
+ catch (const beans::PropertyVetoException &)
+ {
+ // dealing with read-only properties here. Nothing to do...
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("starmath");
+ }
+ }
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportMML(SvStream &rStream)
+{
+ SmGlobals::ensure();
+
+ SfxObjectShellLock xDocSh(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT));
+ xDocSh->DoInitNew();
+ uno::Reference<frame::XModel> xModel(xDocSh->GetModel());
+
+ uno::Reference<beans::XPropertySet> xInfoSet;
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+ uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream));
+
+ //SetLoading hack because the document properties will be re-initted
+ //by the xml filter and during the init, while it's considered uninitialized,
+ //setting a property will inform the document it's modified, which attempts
+ //to update the properties, which throws cause the properties are uninitialized
+ xDocSh->SetLoading(SfxLoadedFlags::NONE);
+
+ ErrCode nRet = ERRCODE_SFX_DOLOADFAILED;
+
+ try
+ {
+ nRet = SmXMLImportWrapper::ReadThroughComponent(xStream, xModel, xContext, xInfoSet, "com.sun.star.comp.Math.XMLImporter", false);
+ }
+ catch (...)
+ {
+ }
+
+ xDocSh->SetLoading(SfxLoadedFlags::ALL);
+
+ xDocSh->DoClose();
+
+ return nRet != ERRCODE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathmlimport.hxx b/starmath/source/mathmlimport.hxx
new file mode 100644
index 000000000..555ea0d2c
--- /dev/null
+++ b/starmath/source/mathmlimport.hxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_MATHMLIMPORT_HXX
+#define INCLUDED_STARMATH_SOURCE_MATHMLIMPORT_HXX
+
+#include <xmloff/xmlimp.hxx>
+#include <vcl/errcode.hxx>
+
+#include <deque>
+#include <memory>
+
+class SmNode;
+class SfxMedium;
+namespace com::sun::star {
+ namespace beans {
+ class XPropertySet; }
+}
+
+
+typedef std::deque<std::unique_ptr<SmNode>> SmNodeStack;
+
+class SmXMLImportWrapper
+{
+ css::uno::Reference<css::frame::XModel> xModel;
+
+public:
+ explicit SmXMLImportWrapper(css::uno::Reference<css::frame::XModel> const &rRef)
+ : xModel(rRef) {}
+
+ ErrCode Import(SfxMedium &rMedium);
+
+ static ErrCode ReadThroughComponent(
+ const css::uno::Reference< css::io::XInputStream >& xInputStream,
+ const css::uno::Reference< css::lang::XComponent >& xModelComponent,
+ css::uno::Reference< css::uno::XComponentContext > const & rxContext,
+ css::uno::Reference< css::beans::XPropertySet > const & rPropSet,
+ const char* pFilterName,
+ bool bEncrypted );
+
+ static ErrCode ReadThroughComponent(
+ const css::uno::Reference< css::embed::XStorage >& xStorage,
+ const css::uno::Reference< css::lang::XComponent >& xModelComponent,
+ const char* pStreamName,
+ css::uno::Reference< css::uno::XComponentContext > const & rxContext,
+ css::uno::Reference< css::beans::XPropertySet > const & rPropSet,
+ const char* pFilterName );
+};
+
+
+class SmXMLImport : public SvXMLImport
+{
+ std::unique_ptr<SvXMLTokenMap> pColorTokenMap;
+
+ SmNodeStack aNodeStack;
+ bool bSuccess;
+ int nParseDepth;
+ OUString aText;
+
+public:
+ SmXMLImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLImportFlags nImportFlags);
+ virtual ~SmXMLImport() throw () override;
+
+ // XUnoTunnel
+ sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rId ) override;
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId() throw();
+
+ void SAL_CALL endDocument() override;
+
+ SvXMLImportContext *CreateFastContext( sal_Int32 nElement,
+ const css::uno::Reference<
+ css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ const SvXMLTokenMap &GetColorTokenMap();
+
+ SmNodeStack & GetNodeStack() { return aNodeStack; }
+
+ bool GetSuccess() const { return bSuccess; }
+ [[nodiscard]] const OUString& GetText() const { return aText; }
+ void SetText(const OUString &rStr) { aText = rStr; }
+
+ virtual void SetViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& aViewProps) override;
+ virtual void SetConfigurationSettings(const css::uno::Sequence<css::beans::PropertyValue>& aViewProps) override;
+
+ void IncParseDepth() { ++nParseDepth; }
+ bool TooDeep() const { return nParseDepth >= 2048; }
+ void DecParseDepth() { --nParseDepth; }
+};
+
+
+enum SmXMLPresScriptEmptyElemTokenMap
+{
+ XML_TOK_MPRESCRIPTS,
+ XML_TOK_NONE
+};
+
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathtype.cxx b/starmath/source/mathtype.cxx
new file mode 100644
index 000000000..13297eb7f
--- /dev/null
+++ b/starmath/source/mathtype.cxx
@@ -0,0 +1,3345 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "mathtype.hxx"
+
+#include <filter/msfilter/classids.hxx>
+#include <osl/diagnose.h>
+#include <sfx2/docfile.hxx>
+#include <sot/storage.hxx>
+#include <sal/log.hxx>
+
+#include "eqnolefilehdr.hxx"
+#include <node.hxx>
+
+void MathType::Init()
+{
+ //These are the default MathType sizes
+ aSizeTable.push_back(12);
+ aSizeTable.push_back(8);
+ aSizeTable.push_back(6);
+ aSizeTable.push_back(24);
+ aSizeTable.push_back(10);
+ aSizeTable.push_back(12);
+ aSizeTable.push_back(12);
+
+ /*
+ These are the default MathType italic/bold settings If mathtype is changed
+ from its defaults, there is nothing we can do, as this information is not
+ stored in the document
+ */
+ MathTypeFont aFont;
+ for(sal_uInt8 i=1;i<=11;i++)
+ {
+ aFont.nTface = i+128;
+ switch (i)
+ {
+ default:
+ aFont.nStyle=0;
+ break;
+ case 3:
+ case 4:
+ aFont.nStyle=1;
+ break;
+ case 7:
+ aFont.nStyle=2;
+ break;
+ }
+ aUserStyles.insert(aFont);
+ }
+}
+
+
+/*ToDo replace with table rather than switch, returns
+ sal_True in the case that the char is just a char, and
+ sal_False if the character is an operator which must not be
+ placed inside the quote sequence designed to protect
+ against being parsed as a keyword
+
+ General solution required to force starmath to handle
+ unicode math chars the way it handles its own math
+ chars rather than handle them as text as it will do
+ for the default case below, i.e. incorrect spacing
+ between math symbols and ordinary text e.g. 1=2 rather
+ than 1 = 2
+ */
+bool MathType::LookupChar(sal_Unicode nChar,OUStringBuffer &rRet,sal_uInt8 nVersion,
+ sal_uInt8 nTypeFace)
+{
+ bool bRet=false;
+ const char *pC = nullptr;
+ switch(nChar)
+ {
+ case 0x0000:
+ pC = " none ";
+ break;
+ case 0x00ac:
+ pC = " neg ";
+ break;
+ case 0x00b1:
+ pC = " +- ";
+ break;
+ case '(':
+ pC = " \\( ";
+ break;
+ case ')':
+ pC = " \\) ";
+ break;
+ case '[':
+ pC = " \\[ ";
+ break;
+ case ']':
+ pC = " \\] ";
+ break;
+ case '.':
+ pC = " \".\" ";
+ break;
+ case 0xae:
+ if ((nVersion < 3) && (nTypeFace == 0x86))
+ pC = " rightarrow ";
+ else
+ {
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ }
+ break;
+ case 0x00fb:
+ if ((nVersion < 3) && (nTypeFace == 0x81))
+ nChar = 0xDF;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'a':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x3b1;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'b':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x3b2;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'l':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x3bb;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'n':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x3bd;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'r':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x3c1;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'D':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x394;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 0xa9:
+ if ((nVersion < 3) && (nTypeFace == 0x82))
+ nChar = '\'';
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 0x00f1:
+ if ((nVersion < 3) && (nTypeFace == 0x86))
+ pC = " \\rangle ";
+ else
+ {
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ }
+ break;
+ case 0x00a3:
+ if ((nVersion < 3) && (nTypeFace == 0x86))
+ pC = " <= ";
+ else
+ {
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ }
+ break;
+ case 0x00de:
+ if ((nVersion < 3) && (nTypeFace == 0x86))
+ pC = " drarrow ";
+ else
+ {
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ }
+ break;
+ case 0x0057:
+ if ((nVersion < 3) && (nTypeFace == 0x85))
+ pC = " %OMEGA ";
+ else
+ {
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ }
+ break;
+ case 0x007b:
+ pC = " lbrace ";
+ break;
+ case 0x007c:
+ pC = " \\lline ";
+ break;
+ case 0x007d:
+ pC = " rbrace ";
+ break;
+ case 0x007e:
+ pC = " \"~\" ";
+ break;
+ case 0x2224:
+ pC = " ndivides ";
+ break;
+ case 0x2225:
+ pC = " parallel ";
+ break;
+ case 0x00d7:
+ if (nVersion < 3)
+ pC = " cdot ";
+ else
+ pC = " times ";
+ break;
+ case 0x00f7:
+ pC = " div ";
+ break;
+ case 0x019b:
+ pC = " lambdabar ";
+ break;
+ case 0x2026:
+ pC = " dotslow ";
+ break;
+ case 0x2022:
+ pC = " cdot ";
+ break;
+ case 0x2102:
+ pC = " setC ";
+ break;
+ case 0x210f:
+ pC = " hbar ";
+ break;
+ case 0x2111:
+ pC = " Im ";
+ break;
+ case 0x2115:
+ pC = " setN ";
+ break;
+ case 0x2118:
+ pC = " wp ";
+ break;
+ case 0x211a:
+ pC = " setQ ";
+ break;
+ case 0x211c:
+ pC = " Re ";
+ break;
+ case 0x211d:
+ pC = " setR ";
+ break;
+ case 0x2124:
+ pC = " setZ ";
+ break;
+ case 0x2135:
+ pC = " aleph ";
+ break;
+ case 0x2190:
+ pC = " leftarrow ";
+ break;
+ case 0x2191:
+ pC = " uparrow ";
+ break;
+ case 0x2192:
+ pC = " rightarrow ";
+ break;
+ case 0x0362:
+ pC = " widevec ";
+ break;
+ case 0x2193:
+ pC = " downarrow ";
+ break;
+ case 0x21d0:
+ pC = " dlarrow ";
+ break;
+ case 0x21d2:
+ pC = " drarrow ";
+ break;
+ case 0x21d4:
+ pC = " dlrarrow ";
+ break;
+ case 0x2200:
+ pC = " forall ";
+ break;
+ case 0x2202:
+ pC = " partial ";
+ break;
+ case 0x2203:
+ pC = " exists ";
+ break;
+ case 0x2204:
+ pC = " notexists ";
+ break;
+ case 0x2205:
+ pC = " emptyset ";
+ break;
+ case 0x2207:
+ pC = " nabla ";
+ break;
+ case 0x2112:
+ pC = " laplace ";
+ break;
+ case 0x2208: // in
+ case 0x2209: // notin
+ rRet.append(" func ").append(OUStringChar(nChar)).append(" ");
+ break;
+ case 0x220d: // owns
+ rRet.append(u" func \u220b ");
+ break;
+ case 0x220f:
+ pC = " prod ";
+ break;
+ case 0x2210:
+ pC = " coprod ";
+ break;
+ case 0x2211:
+ pC = " sum ";
+ break;
+ case 0x2212:
+ pC = " - ";
+ break;
+ case 0x2213:
+ pC = " -+ ";
+ break;
+ case 0x2217:
+ pC = " * ";
+ break;
+ case 0x2218:
+ pC = " circ ";
+ break;
+ case 0x221d:
+ pC = " prop ";
+ break;
+ case 0x221e:
+ pC = " infinity ";
+ break;
+ case 0x2227:
+ pC = " and ";
+ break;
+ case 0x2228:
+ pC = " or ";
+ break;
+ case 0x2229:
+ pC = " intersection ";
+ break;
+ case 0x222a:
+ pC = " union ";
+ break;
+ case 0x222b:
+ pC = " int ";
+ break;
+ case 0x222c:
+ pC = " iint ";
+ break;
+ case 0x222d:
+ pC = " iiint ";
+ break;
+ case 0x222e:
+ pC = " lint ";
+ break;
+ case 0x222f:
+ pC = " llint ";
+ break;
+ case 0x2230:
+ pC = " lllint ";
+ break;
+ case 0x2245:
+ pC = " simeq ";
+ break;
+ case 0x2248:
+ pC = " approx ";
+ break;
+ case 0x2260:
+ pC = " <> ";
+ break;
+ case 0x2261:
+ pC = " equiv ";
+ break;
+ case 0x2264:
+ pC = " <= ";
+ break;
+ case 0x2265:
+ pC = " >= ";
+ break;
+
+ case 0x227A:
+ pC = " prec ";
+ break;
+ case 0x227B:
+ pC = " succ ";
+ break;
+ case 0x227C:
+ pC = " preccurlyeq ";
+ break;
+ case 0x227D:
+ pC = " succcurlyeq ";
+ break;
+ case 0x227E:
+ pC = " precsim ";
+ break;
+ case 0x227F:
+ pC = " succsim ";
+ break;
+ case 0x2280:
+ pC = " nprec ";
+ break;
+ case 0x2281:
+ pC = " nsucc ";
+ break;
+
+ case 0x2282: // subset
+ case 0x2283: // supset
+ case 0x2284: // nsubset
+ case 0x2285: // nsupset
+ case 0x2286: // subseteq
+ case 0x2287: // supseteq
+ case 0x2288: // nsubseteq
+ case 0x2289: // nsupseteq
+ case 0x22b2: // NORMAL SUBGROUP OF
+ case 0x22b3: // CONTAINS AS NORMAL SUBGROUP
+ rRet.append(" func ").append(OUStringChar(nChar)).append(" ");
+ break;
+ case 0x22a5:
+ pC = " ortho ";
+ break;
+ case 0x22c5:
+ pC = " cdot ";
+ break;
+ case 0x22ee:
+ pC = " dotsvert ";
+ break;
+ case 0x22ef:
+ pC = " dotsaxis ";
+ break;
+ case 0x22f0:
+ pC = " dotsup ";
+ break;
+ case 0x22f1:
+ pC = " dotsdown ";
+ break;
+ case MS_LANGLE:
+ case MS_LMATHANGLE:
+ pC = " langle ";
+ break;
+ case MS_RANGLE:
+ case MS_RMATHANGLE:
+ pC = " rangle ";
+ break;
+ case 0x301a:
+ pC = " ldbracket ";
+ break;
+ case 0x301b:
+ pC = " rdbracket ";
+ break;
+ case 0xe083:
+ rRet.append("+");
+ bRet=true;
+ break;
+ case '^':
+ case 0xe091:
+ pC = " widehat ";
+ break;
+ case 0xe096:
+ pC = " widetilde ";
+ break;
+ case 0xe098:
+ pC = " widevec ";
+ break;
+ case 0xE421:
+ pC = " geslant ";
+ break;
+ case 0xE425:
+ pC = " leslant ";
+ break;
+ case 0xeb01: //no space
+ case 0xeb08: //normal space
+ bRet=true;
+ break;
+ case 0xef04: //tiny space
+ case 0xef05: //tiny space
+ case 0xeb02: //small space
+ case 0xeb04: //medium space
+ rRet.append("`");
+ break;
+ case 0xeb05: //large space
+ rRet.append("~");
+ break;
+ case 0x3a9:
+ pC = " %OMEGA ";
+ break;
+ default:
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ }
+ if (pC)
+ rRet.appendAscii(pC);
+ return bRet;
+}
+
+void MathTypeFont::AppendStyleToText(OUString &rRet)
+{
+ const char *pC = nullptr;
+ switch (nStyle)
+ {
+ default:
+ case 0:
+ break;
+ case 1:
+ pC = " ital ";
+ break;
+ case 2:
+ pC = " bold ";
+ break;
+ case 3:
+ pC = " bold italic";
+ break;
+ }
+ if (pC)
+ rRet += OUString::createFromAscii( pC );
+}
+
+void MathType::TypeFaceToString(OUString &rTxt,sal_uInt8 nFace)
+{
+ MathTypeFont aFont(nFace);
+ MathTypeFontSet::iterator aItr = aUserStyles.find(aFont);
+ if (aItr != aUserStyles.end())
+ aFont.nStyle = aItr->nStyle;
+ aFont.AppendStyleToText(rTxt);
+}
+
+bool MathType::Parse(SotStorage *pStor)
+{
+ tools::SvRef<SotStorageStream> xSrc = pStor->OpenSotStream(
+ "Equation Native",
+ StreamMode::STD_READ);
+ if ( (!xSrc.is()) || (ERRCODE_NONE != xSrc->GetError()))
+ return false;
+ return Parse(xSrc.get());
+}
+
+bool MathType::Parse(SvStream* pStream)
+{
+ pS = pStream;
+ pS->SetEndian( SvStreamEndian::LITTLE );
+
+ EQNOLEFILEHDR aHdr;
+ aHdr.Read(pS);
+ sal_uInt8 nProdVersion;
+ sal_uInt8 nProdSubVersion;
+ sal_uInt8 nPlatform;
+ sal_uInt8 nProduct;
+ pS->ReadUChar( nVersion );
+ pS->ReadUChar( nPlatform );
+ pS->ReadUChar( nProduct );
+ pS->ReadUChar( nProdVersion );
+ pS->ReadUChar( nProdSubVersion );
+
+ if (nVersion > 3) // allow only supported versions of MathType to be parsed
+ return false;
+
+ bool bRet = HandleRecords(0);
+ //little crude hack to close occasionally open expressions
+ //a sophisticated system to determine what expressions are
+ //opened is required, but this is as much work as rewriting
+ //starmaths internals.
+ rRet.append("{}");
+
+ return bRet;
+}
+
+static void lcl_PrependDummyTerm(OUStringBuffer &rRet, sal_Int32 &rTextStart)
+{
+ if ((rTextStart < rRet.getLength()) &&
+ (rRet[rTextStart] == '=') &&
+ ((rTextStart == 0) || (rRet[ rTextStart-1 ] == '{'))
+ )
+ {
+ rRet.insert(rTextStart, " {}");
+ rTextStart+=3;
+ }
+}
+
+static void lcl_AppendDummyTerm(OUStringBuffer &rRet)
+{
+ bool bOk=false;
+ for(int nI=rRet.getLength()-1;nI >= 0; nI--)
+ {
+ sal_Int32 nIdx = sal::static_int_cast< sal_Int32 >(nI);
+ sal_Unicode nChar = rRet[nIdx];
+ if (nChar == ' ')
+ continue;
+ if (rRet[nIdx] != '{')
+ bOk=true;
+ break;
+ }
+ if (!bOk) //No term, use dummy
+ rRet.append(" {}");
+}
+
+void MathType::HandleNudge()
+{
+ sal_uInt8 nXNudge;
+ pS->ReadUChar( nXNudge );
+ sal_uInt8 nYNudge;
+ pS->ReadUChar( nYNudge );
+ if (nXNudge == 128 && nYNudge == 128)
+ {
+ sal_uInt16 nXLongNudge;
+ sal_uInt16 nYLongNudge;
+ pS->ReadUInt16( nXLongNudge );
+ pS->ReadUInt16( nYLongNudge );
+ }
+}
+
+/* Fabulously complicated as many tokens have to be reordered and generally
+ * moved around from mathtypes paradigm to starmaths. */
+bool MathType::HandleRecords(int nLevel, sal_uInt8 nSelector,
+ sal_uInt8 nVariation, int nMatrixRows, int nMatrixCols)
+{
+ //depth-protect
+ if (nLevel > 1024)
+ return false;
+
+ sal_uInt8 nTag,nRecord;
+ sal_uInt8 nTabType,nTabStops;
+ sal_uInt16 nTabOffset;
+ int i, newline=0;
+ bool bSilent=false;
+ int nPart=0;
+ OUString sPush,sMainTerm;
+ int nSetSize=0,nSetAlign=0;
+ int nCurRow=0,nCurCol=0;
+ bool bOpenString=false;
+ sal_Int32 nTextStart = 0;
+ sal_Int32 nSubSupStartPos = 0;
+ sal_Int32 nLastTemplateBracket=-1;
+ bool bRet = true;
+
+ do
+ {
+ nTag = 0;
+ pS->ReadUChar( nTag );
+ nRecord = nTag&0x0F;
+
+ /*MathType strings can of course include words which
+ *are StarMath keywords, the simplest solution is
+ to escape strings of greater than len 1 with double
+ quotes to avoid scanning the TokenTable for matches
+
+ Unfortunately it may turn out that the string gets
+ split during the handling of a character emblishment
+ so this special case must be handled in the
+ character handler case 2:
+ */
+ if ((nRecord == CHAR) && (!bOpenString))
+ {
+ bOpenString=true;
+ nTextStart = rRet.getLength();
+ }
+ else if ((nRecord != CHAR) && bOpenString)
+ {
+ bOpenString=false;
+ if ((rRet.getLength() - nTextStart) > 1)
+ {
+ OUString aStr;
+ TypeFaceToString(aStr,nTypeFace);
+ aStr += "\"";
+ rRet.insert(nTextStart,aStr);
+ rRet.append("\"");
+ }
+ else if (nRecord == END && !rRet.isEmpty())
+ {
+ sal_Unicode cChar = 0;
+ sal_Int32 nI = rRet.getLength()-1;
+ while (nI)
+ {
+ cChar = rRet[nI];
+ if (cChar != ' ')
+ break;
+ --nI;
+ }
+ if ((cChar == '=') || (cChar == '+') || (cChar == '-'))
+ rRet.append("{}");
+ }
+ }
+
+ switch(nRecord)
+ {
+ case LINE:
+ {
+ if (xfLMOVE(nTag))
+ HandleNudge();
+
+ if (newline>0)
+ rRet.append("\nnewline\n");
+ if (!(xfNULL(nTag)))
+ {
+ switch (nSelector)
+ {
+ case tmANGLE:
+ if (nVariation==0)
+ rRet.append(" langle ");
+ else if (nVariation==1)
+ rRet.append(" \\langle ");
+ break;
+ case tmPAREN:
+ if (nVariation==0)
+ rRet.append(" left (");
+ else if (nVariation==1)
+ rRet.append("\\(");
+ break;
+ case tmBRACE:
+ if ((nVariation==0) || (nVariation==1))
+ rRet.append(" left lbrace ");
+ else
+ rRet.append(" left none ");
+ break;
+ case tmBRACK:
+ if (nVariation==0)
+ rRet.append(" left [");
+ else if (nVariation==1)
+ rRet.append("\\[");
+ break;
+ case tmLBLB:
+ case tmLBRP:
+ rRet.append(" \\[");
+ break;
+ case tmBAR:
+ if (nVariation==0)
+ rRet.append(" lline ");
+ else if (nVariation==1)
+ rRet.append(" \\lline ");
+ break;
+ case tmDBAR:
+ if (nVariation==0)
+ rRet.append(" ldline ");
+ else if (nVariation==1)
+ rRet.append(" \\ldline ");
+ break;
+ case tmFLOOR:
+ if (nVariation == 0 || nVariation & 0x01) // tvFENCE_L
+ rRet.append(" left lfloor ");
+ else
+ rRet.append(" left none ");
+ break;
+ case tmCEILING:
+ if (nVariation==0)
+ rRet.append(" lceil ");
+ else if (nVariation==1)
+ rRet.append(" \\lceil ");
+ break;
+ case tmRBRB:
+ case tmRBLB:
+ rRet.append(" \\]");
+ break;
+ case tmLPRB:
+ rRet.append(" \\(");
+ break;
+ case tmROOT:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" sqrt");
+ else
+ {
+ rRet.append(" nroot");
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ rRet.append(" {");
+ break;
+ case tmFRACT:
+ if (nPart == 0)
+ rRet.append(" { ");
+
+
+ if (nPart == 1)
+ rRet.append(" over ");
+ rRet.append(" {");
+ break;
+ case tmSCRIPT:
+ nSubSupStartPos = rRet.getLength();
+ if ((nVariation == 0) ||
+ ((nVariation == 2) && (nPart==1)))
+ {
+ lcl_AppendDummyTerm(rRet);
+ rRet.append(" rSup");
+ }
+ else if ((nVariation == 1) ||
+ ((nVariation == 2) && (nPart==0)))
+ {
+ lcl_AppendDummyTerm(rRet);
+ rRet.append(" rSub");
+ }
+ rRet.append(" {");
+ break;
+ case tmUBAR:
+ if (nVariation == 0)
+ rRet.append(" {underline ");
+ else if (nVariation == 1)
+ rRet.append(" {underline underline ");
+ rRet.append(" {");
+ break;
+ case tmOBAR:
+ if (nVariation == 0)
+ rRet.append(" {overline ");
+ else if (nVariation == 1)
+ rRet.append(" {overline overline ");
+ rRet.append(" {");
+ break;
+ case tmLARROW:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" widevec ");//left arrow above
+ else if (nVariation == 1)
+ rRet.append(" widevec ");//left arrow below
+ rRet.append(" {");
+ }
+ break;
+ case tmRARROW:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" widevec ");//right arrow above
+ else if (nVariation == 1)
+ rRet.append(" widevec ");//right arrow below
+ rRet.append(" {");
+ }
+ break;
+ case tmBARROW:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" widevec ");//double arrow above
+ else if (nVariation == 1)
+ rRet.append(" widevec ");//double arrow below
+ rRet.append(" {");
+ }
+ break;
+ case tmSINT:
+ if (nPart == 0)
+ {
+ if ((nVariation == 3) || (nVariation == 4))
+ rRet.append(" lInt");
+ else
+ rRet.append(" Int");
+ if ( (nVariation != 0) && (nVariation != 3))
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if (((nVariation == 1) ||
+ (nVariation == 4)) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 2) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 2) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmDINT:
+ if (nPart == 0)
+ {
+ if ((nVariation == 2) || (nVariation == 3))
+ rRet.append(" llInt");
+ else
+ rRet.append(" iInt");
+ if ( (nVariation != 0) && (nVariation != 2))
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if (((nVariation == 1) ||
+ (nVariation == 3)) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmTINT:
+ if (nPart == 0)
+ {
+ if ((nVariation == 2) || (nVariation == 3))
+ rRet.append(" lllInt");
+ else
+ rRet.append(" iiInt");
+ if ( (nVariation != 0) && (nVariation != 2))
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if (((nVariation == 1) ||
+ (nVariation == 3)) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmSSINT:
+ if (nPart == 0)
+ {
+ if (nVariation == 2)
+ rRet.append(" lInt");
+ else
+ rRet.append(" Int");
+ sPush = rRet.makeStringAndClear();
+ }
+ if (((nVariation == 1) ||
+ (nVariation == 2)) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 0) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmDSINT:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" llInt");
+ else
+ rRet.append(" iInt");
+ sPush = rRet.makeStringAndClear();
+ }
+ if (nPart==1)
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmTSINT:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" lllInt");
+ else
+ rRet.append(" iiInt");
+ sPush = rRet.makeStringAndClear();
+ }
+ if (nPart==1)
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmUHBRACE:
+ case tmLHBRACE:
+ rRet.append(" {");
+ break;
+ case tmSUM:
+ if (nPart == 0)
+ {
+ rRet.append(" Sum");
+ if (nVariation != 2)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmISUM:
+ if (nPart == 0)
+ {
+ rRet.append(" Sum");
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmPROD:
+ if (nPart == 0)
+ {
+ rRet.append(" Prod");
+ if (nVariation != 2)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmIPROD:
+ if (nPart == 0)
+ {
+ rRet.append(" Prod");
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmCOPROD:
+ if (nPart == 0)
+ {
+ rRet.append(" coProd");
+ if (nVariation != 2)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmICOPROD:
+ if (nPart == 0)
+ {
+ rRet.append(" coProd");
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmUNION:
+ if (nPart == 0)
+ {
+ rRet.append(" union"); //union
+ if (nVariation != 2)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmIUNION:
+ if (nPart == 0)
+ {
+ rRet.append(" union"); //union
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmINTER:
+ if (nPart == 0)
+ {
+ rRet.append(" intersect"); //intersect
+ if (nVariation != 2)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmIINTER:
+ if (nPart == 0)
+ {
+ rRet.append(" intersect"); //intersect
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmLIM:
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 2) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 2) && (nPart==2))
+ rRet.append(" cSup");
+ rRet.append(" {");
+ break;
+ case tmLDIV:
+ if (nVariation == 0)
+ {
+ if (nPart == 0)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ rRet.append(" {");
+ if (nVariation == 0)
+ {
+ if (nPart == 1)
+ rRet.append("alignr ");
+ }
+ if (nPart == 0)
+ rRet.append("\\lline ");
+ if (nVariation == 1)
+ rRet.append("overline ");
+ break;
+ case tmSLFRACT:
+ rRet.append(" {");
+ break;
+ case tmINTOP:
+ if (nPart == 0)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==0))
+ rRet.append(" rSup");
+ else if ((nVariation == 2) && (nPart==1))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==0))
+ rRet.append(" rSub");
+ else if ((nVariation == 2) && (nPart==0))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmSUMOP:
+ if (nPart == 0)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==0))
+ rRet.append(" cSup");
+ else if ((nVariation == 2) && (nPart==1))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==0))
+ rRet.append(" cSub");
+ else if ((nVariation == 2) && (nPart==0))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmLSCRIPT:
+ if (nPart == 0)
+ rRet.append("\"\"");
+ if ((nVariation == 0)
+ || ((nVariation == 2) && (nPart==1)))
+ rRet.append(" lSup");
+ else if ((nVariation == 1)
+ || ((nVariation == 2) && (nPart==0)))
+ rRet.append(" lSub");
+ rRet.append(" {");
+ break;
+ case tmDIRAC:
+ if (nVariation==0)
+ {
+ if (nPart == 0)
+ rRet.append(" langle ");
+ }
+ else if (nVariation==1)
+ {
+ rRet.append(" \\langle ");
+ newline--;
+ }
+ else if (nVariation==2)
+ {
+ rRet.append(" \\lline ");
+ newline--;
+ }
+ break;
+ case tmUARROW:
+ if (nVariation == 0)
+ rRet.append(" widevec ");//left below
+ else if (nVariation == 1)
+ rRet.append(" widevec ");//right below
+ else if (nVariation == 2)
+ rRet.append(" widevec ");//double headed below
+ rRet.append(" {");
+ break;
+ case tmOARROW:
+ if (nVariation == 0)
+ rRet.append(" widevec ");//left above
+ else if (nVariation == 1)
+ rRet.append(" widevec ");//right above
+ else if (nVariation == 2)
+ rRet.append(" widevec ");//double headed above
+ rRet.append(" {");
+ break;
+ default:
+ break;
+ }
+ sal_Int16 nOldCurSize=nCurSize;
+ sal_Int32 nSizeStartPos = rRet.getLength();
+ HandleSize( nLSize, nDSize, nSetSize );
+ bRet = HandleRecords( nLevel+1 );
+ while (nSetSize)
+ {
+ bool bOk=false;
+ sal_Int32 nI = rRet.lastIndexOf('{');
+ if (nI != -1)
+ {
+ for(nI=nI+1;nI<rRet.getLength();nI++)
+ if (rRet[nI] != ' ')
+ {
+ bOk=true;
+ break;
+ }
+ }
+ else
+ bOk=true;
+
+ if (bOk)
+ rRet.append("} ");
+ else if (rRet.getLength() > nSizeStartPos)
+ rRet = rRet.truncate(nSizeStartPos);
+ nSetSize--;
+ nCurSize=nOldCurSize;
+ }
+
+
+ HandleMatrixSeparator(nMatrixRows,nMatrixCols,
+ nCurCol,nCurRow);
+
+ switch (nSelector)
+ {
+ case tmANGLE:
+ if (nVariation==0)
+ rRet.append(" rangle ");
+ else if (nVariation==2)
+ rRet.append(" \\rangle ");
+ break;
+ case tmPAREN:
+ if (nVariation==0)
+ rRet.append(" right )");
+ else if (nVariation==2)
+ rRet.append("\\)");
+ break;
+ case tmBRACE:
+ if ((nVariation==0) || (nVariation==2))
+ rRet.append(" right rbrace ");
+ else
+ rRet.append(" right none ");
+ break;
+ case tmBRACK:
+ if (nVariation==0)
+ rRet.append(" right ]");
+ else if (nVariation==2)
+ rRet.append("\\]");
+ break;
+ case tmBAR:
+ if (nVariation==0)
+ rRet.append(" rline ");
+ else if (nVariation==2)
+ rRet.append(" \\rline ");
+ break;
+ case tmDBAR:
+ if (nVariation==0)
+ rRet.append(" rdline ");
+ else if (nVariation==2)
+ rRet.append(" \\rdline ");
+ break;
+ case tmFLOOR:
+ if (nVariation == 0 || nVariation & 0x02) // tvFENCE_R
+ rRet.append(" right rfloor ");
+ else
+ rRet.append(" right none ");
+ break;
+ case tmCEILING:
+ if (nVariation==0)
+ rRet.append(" rceil ");
+ else if (nVariation==2)
+ rRet.append(" \\rceil ");
+ break;
+ case tmLBLB:
+ case tmRBLB:
+ rRet.append("\\[");
+ break;
+ case tmRBRB:
+ case tmLPRB:
+ rRet.append("\\]");
+ break;
+ case tmROOT:
+ rRet.append("} ");
+ if (nVariation == 1)
+ {
+ if (nPart == 0)
+ {
+ newline--;
+ sMainTerm = rRet.makeStringAndClear();
+ }
+ else if (nPart == 1)
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ }
+ }
+ else
+ {
+ if (nPart == 0)
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmLBRP:
+ rRet.append("\\)");
+ break;
+ case tmFRACT:
+ rRet.append("} ");
+ if (nPart == 0)
+ newline--;
+ else
+ rRet.append("} ");
+ nPart++;
+ break;
+ case tmSCRIPT:
+ {
+ if ((nPart == 0) &&
+ ((nVariation == 2) || (nVariation == 1)))
+ newline--;
+
+ bool bOk=false;
+ sal_Int32 nI = rRet.lastIndexOf('{');
+ if (nI != -1)
+ {
+ for(nI=nI+1;nI<rRet.getLength();nI++)
+ if (rRet[nI] != ' ')
+ {
+ bOk=true;
+ break;
+ }
+ }
+ else
+ bOk=true;
+
+ if (bOk)
+ rRet.append("} ");
+ else if (rRet.getLength() > nSubSupStartPos)
+ rRet = rRet.truncate(nSubSupStartPos);
+ nPart++;
+ }
+ break;
+ case tmLSCRIPT:
+ if ((nPart == 0) &&
+ ((nVariation == 2) || (nVariation == 1)))
+ newline--;
+ rRet.append("} ");
+ nPart++;
+ break;
+ case tmUARROW:
+ case tmOARROW:
+ rRet.append("} ");
+ break;
+ case tmUBAR:
+ case tmOBAR:
+ rRet.append("}} ");
+ break;
+ case tmLARROW:
+ case tmRARROW:
+ case tmBARROW:
+ if (nPart == 0)
+ {
+ newline--;
+ rRet.append("} ");
+ }
+ nPart++;
+ break;
+ case tmUHBRACE:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ newline--;
+ rRet.append("overbrace");
+ }
+ nPart++;
+ break;
+ case tmLHBRACE:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ newline--;
+ rRet.append("underbrace");
+ }
+ nPart++;
+ break;
+ case tmLIM:
+ if (nPart==0)
+ newline--;
+ else if ((nPart==1) &&
+ ((nVariation == 2) || (nVariation == 1)))
+ newline--;
+ rRet.append("} ");
+ nPart++;
+ break;
+ case tmLDIV:
+ rRet.append("} ");
+ if (nVariation == 0)
+ {
+ if (nPart == 0)
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ }
+ else if (nPart == 1)
+ {
+ rRet.insert(0, sPush);
+ rRet.append(" over ").append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ }
+ }
+ if (nPart == 0)
+ newline--;
+ nPart++;
+ break;
+ case tmSLFRACT:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ newline--;
+ switch (nVariation)
+ {
+ case 1:
+ rRet.append("slash");
+ break;
+ default:
+ rRet.append("wideslash");
+ break;
+ }
+ }
+ nPart++;
+ break;
+ case tmSUM:
+ case tmISUM:
+ case tmPROD:
+ case tmIPROD:
+ case tmCOPROD:
+ case tmICOPROD:
+ case tmUNION:
+ case tmIUNION:
+ case tmINTER:
+ case tmIINTER:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ if (nVariation != 2)
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ }
+ newline--;
+ }
+ else if ((nPart == 1) && (nVariation == 0))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ else if ((nPart == 1) && (nVariation == 1))
+ newline--;
+ else if ((nPart == 2) && (nVariation == 1))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmSINT:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ if ((nVariation != 0) && (nVariation != 3))
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ }
+ newline--;
+ }
+ else if ((nPart == 1) &&
+ ((nVariation == 1) || (nVariation==4)))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ else if ((nPart == 1) && (nVariation == 2))
+ newline--;
+ else if ((nPart == 2) && (nVariation == 2))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmDINT:
+ case tmTINT:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ if ((nVariation != 0) && (nVariation != 2))
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ }
+ newline--;
+ }
+ else if ((nPart == 1) &&
+ ((nVariation == 1) || (nVariation==3)))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmSSINT:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ newline--;
+ }
+ else if ((nPart == 1) &&
+ ((nVariation == 1) || (nVariation==2)))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ else if ((nPart == 1) && (nVariation == 0))
+ newline--;
+ else if ((nPart == 2) && (nVariation == 0))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmDSINT:
+ case tmTSINT:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ newline--;
+ }
+ else if (nPart == 1)
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmINTOP:
+ case tmSUMOP:
+ rRet.append("} ");
+
+ if ((nPart == 0) &&
+ ((nVariation == 0) || (nVariation == 1)))
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ newline--;
+ }
+ else if ((nPart == 0) && (nVariation == 2))
+ newline--;
+ else if ((nPart == 1) && (nVariation == 2))
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ newline--;
+ }
+ else if ((nPart == 2) || ((nPart == 1) &&
+ (nVariation == 0 || nVariation == 1)))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ }
+ nPart++;
+ break;
+ case tmDIRAC:
+ if (nVariation==0)
+ {
+ if (nPart == 0)
+ {
+ newline--; //there is another term to arrive
+ rRet.append(" mline ");
+ }
+ else
+ rRet.append(" rangle ");
+ }
+ else if (nVariation==1)
+ rRet.append(" \\lline ");
+ else if (nVariation==2)
+ rRet.append(" \\rangle ");
+ nPart++;
+ break;
+ default:
+ break;
+ }
+ bSilent = true; //Skip the optional brackets and/or
+ //symbols that follow some of these
+ //records. Foo Data.
+
+ /*In matrices and piles we cannot separate equation
+ *lines with the newline keyword*/
+ if (nMatrixCols==0)
+ newline++;
+ }
+ }
+ break;
+ case CHAR:
+ if (xfLMOVE(nTag))
+ HandleNudge();
+ bRet = HandleChar( nTextStart, nSetSize, nLevel, nTag, nSelector, nVariation, bSilent );
+ break;
+ case TMPL:
+ if (xfLMOVE(nTag))
+ HandleNudge();
+ bRet = HandleTemplate( nLevel, nSelector, nVariation, nLastTemplateBracket );
+ break;
+ case PILE:
+ if (xfLMOVE(nTag))
+ HandleNudge();
+ bRet = HandlePile( nSetAlign, nLevel, nSelector, nVariation );
+ HandleMatrixSeparator( nMatrixRows, nMatrixCols, nCurCol, nCurRow );
+ break;
+ case MATRIX:
+ if (xfLMOVE(nTag))
+ HandleNudge();
+ bRet = HandleMatrix( nLevel, nSelector, nVariation );
+ HandleMatrixSeparator( nMatrixRows, nMatrixCols, nCurCol, nCurRow );
+ break;
+ case EMBEL:
+ if (xfLMOVE(nTag))
+ HandleNudge();
+ HandleEmblishments();
+ break;
+ case RULER:
+ pS->ReadUChar( nTabStops );
+ for (i=0;i<nTabStops;i++)
+ {
+ pS->ReadUChar( nTabType );
+ pS->ReadUInt16( nTabOffset );
+ }
+ SAL_WARN("starmath", "Not seen in the wild Equation Ruler Field");
+ break;
+ case FONT:
+ {
+ MathTypeFont aFont;
+ pS->ReadUChar( aFont.nTface );
+ /*
+ The typeface number is the negative (which makes it
+ positive) of the typeface value (unbiased) that appears in
+ CHAR records that might follow a given FONT record
+ */
+ aFont.nTface = 128-aFont.nTface;
+ pS->ReadUChar( aFont.nStyle );
+ aUserStyles.insert(aFont);
+ // read font name
+ while(true)
+ {
+ char nChar8(0);
+ pS->ReadChar( nChar8 );
+ if (nChar8 == 0)
+ break;
+ }
+ }
+ break;
+ case SIZE:
+ HandleSetSize();
+ break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ nLSize=nRecord-10;
+ break;
+ case END:
+ default:
+ break;
+ }
+ }
+ while (nRecord != END && !pS->eof());
+ while (nSetSize)
+ {
+ rRet.append("}");
+ nSetSize--;
+ }
+ return bRet;
+}
+
+/*Simply determine if we are at the end of a record or the end of a line,
+ *with fiddly logic to see if we are in a matrix or a pile or neither
+
+ Note we cannot tell until after the event that this is the last entry
+ of a pile, so we must strip the last separator of a pile after this
+ is detected in the PILE handler
+ */
+void MathType::HandleMatrixSeparator(int nMatrixRows,int nMatrixCols,
+ int &rCurCol,int &rCurRow)
+{
+ if (nMatrixRows==0)
+ return;
+
+ if (rCurCol == nMatrixCols-1)
+ {
+ if (rCurRow != nMatrixRows-1)
+ rRet.append(" {} ##\n");
+ if (nMatrixRows!=-1)
+ {
+ rCurCol=0;
+ rCurRow++;
+ }
+ }
+ else
+ {
+ rRet.append(" {} # ");
+ if (nMatrixRows!=-1)
+ rCurCol++;
+ else
+ rRet.append("\n");
+ }
+}
+
+/* set the alignment of the following term, but starmath currently
+ * cannot handle vertical alignment */
+void MathType::HandleAlign(sal_uInt8 nHorAlign, int &rSetAlign)
+{
+ switch(nHorAlign)
+ {
+ case 1:
+ default:
+ rRet.append("alignl {");
+ break;
+ case 2:
+ rRet.append("alignc {");
+ break;
+ case 3:
+ rRet.append("alignr {");
+ break;
+ }
+ rSetAlign++;
+}
+
+/* set size of text, complexity due to overuse of signedness as a flag
+ * indicator by mathtype file format*/
+bool MathType::HandleSize(sal_Int16 nLstSize,sal_Int16 nDefSize, int &rSetSize)
+{
+ const sal_Int16 nDefaultSize = 12;
+ bool bRet=false;
+ if (nLstSize < 0)
+ {
+ if ((-nLstSize/32 != nDefaultSize) && (-nLstSize/32 != nCurSize))
+ {
+ if (rSetSize)
+ {
+ rSetSize--;
+ rRet.append("}");
+ bRet=true;
+ }
+ if (-nLstSize/32 != nLastSize)
+ {
+ nLastSize = nCurSize;
+ rRet.append(" size ");
+ rRet.append(OUString::number(-nLstSize/32));
+ rRet.append("{");
+ bRet=true;
+ rSetSize++;
+ }
+ nCurSize = -nLstSize/32;
+ }
+ }
+ else
+ {
+ /*sizetable should theoretically be filled with the default sizes
+ *of the various font groupings matching starmaths equivalents
+ in aTypeFaces, and a test would be done to see if the new font
+ size would be the same as what starmath would have chosen for
+ itself anyway in which case the size setting could be ignored*/
+ nLstSize = aSizeTable.at(nLstSize);
+ nLstSize = nLstSize + nDefSize;
+ if (nLstSize != nCurSize)
+ {
+ if (rSetSize)
+ {
+ rSetSize--;
+ rRet.append("}");
+ bRet=true;
+ }
+ if (nLstSize != nLastSize)
+ {
+ nLastSize = nCurSize;
+ rRet.append(" size ");
+ rRet.append(OUString::number(nLstSize));
+ rRet.append("{");
+ bRet=true;
+ rSetSize++;
+ }
+ nCurSize = nLstSize;
+ }
+ }
+ return bRet;
+}
+
+bool MathType::ConvertFromStarMath( SfxMedium& rMedium )
+{
+ if (!pTree)
+ return false;
+
+ SvStream *pStream = rMedium.GetOutStream();
+ if ( pStream )
+ {
+ tools::SvRef<SotStorage> pStor = new SotStorage( pStream, false );
+
+ SvGlobalName aGName(MSO_EQUATION3_CLASSID);
+ pStor->SetClass( aGName, SotClipboardFormatId::NONE, "Microsoft Equation 3.0");
+
+ static sal_uInt8 const aCompObj[] = {
+ 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0xCE, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46, 0x17, 0x00, 0x00, 0x00,
+ 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66,
+ 0x74, 0x20, 0x45, 0x71, 0x75, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x30, 0x00, 0x0C,
+ 0x00, 0x00, 0x00, 0x44, 0x53, 0x20, 0x45, 0x71,
+ 0x75, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x0B,
+ 0x00, 0x00, 0x00, 0x45, 0x71, 0x75, 0x61, 0x74,
+ 0x69, 0x6F, 0x6E, 0x2E, 0x33, 0x00, 0xF4, 0x39,
+ 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ tools::SvRef<SotStorageStream> xStor( pStor->OpenSotStream("\1CompObj"));
+ xStor->WriteBytes(aCompObj, sizeof(aCompObj));
+
+ static sal_uInt8 const aOle[] = {
+ 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ tools::SvRef<SotStorageStream> xStor2( pStor->OpenSotStream("\1Ole"));
+ xStor2->WriteBytes(aOle, sizeof(aOle));
+ xStor.clear();
+ xStor2.clear();
+
+ tools::SvRef<SotStorageStream> xSrc = pStor->OpenSotStream("Equation Native");
+ if ( (!xSrc.is()) || (ERRCODE_NONE != xSrc->GetError()))
+ return false;
+
+ pS = xSrc.get();
+ pS->SetEndian( SvStreamEndian::LITTLE );
+
+ pS->SeekRel(EQNOLEFILEHDR_SIZE); //Skip 28byte Header and fill it in later
+ pS->WriteUChar( 0x03 );
+ pS->WriteUChar( 0x01 );
+ pS->WriteUChar( 0x01 );
+ pS->WriteUChar( 0x03 );
+ pS->WriteUChar( 0x00 );
+ sal_uInt32 nSize = pS->Tell();
+ nPendingAttributes=0;
+
+ HandleNodes(pTree, 0);
+ pS->WriteUChar( END );
+
+ nSize = pS->Tell()-nSize;
+ pS->Seek(0);
+ EQNOLEFILEHDR aHdr(nSize+4+1);
+ aHdr.Write(pS);
+
+ pStor->Commit();
+ }
+
+ return true;
+}
+
+
+void MathType::HandleNodes(SmNode *pNode,int nLevel)
+{
+ switch(pNode->GetType())
+ {
+ case SmNodeType::Attribut:
+ HandleAttributes(pNode,nLevel);
+ break;
+ case SmNodeType::Text:
+ HandleText(pNode);
+ break;
+ case SmNodeType::VerticalBrace:
+ HandleVerticalBrace(pNode,nLevel);
+ break;
+ case SmNodeType::Brace:
+ HandleBrace(pNode,nLevel);
+ break;
+ case SmNodeType::Oper:
+ HandleOperator(pNode,nLevel);
+ break;
+ case SmNodeType::BinVer:
+ HandleFractions(pNode,nLevel);
+ break;
+ case SmNodeType::Root:
+ HandleRoot(pNode,nLevel);
+ break;
+ case SmNodeType::Special:
+ {
+ SmTextNode *pText = static_cast<SmTextNode *>(pNode);
+ //if the token str and the result text are the same then this
+ //is to be seen as text, else assume it's a mathchar
+ if (pText->GetText() == pText->GetToken().aText)
+ HandleText(pText);
+ else
+ HandleMath(pText);
+ }
+ break;
+ case SmNodeType::Math:
+ case SmNodeType::MathIdent:
+ HandleMath(pNode);
+ break;
+ case SmNodeType::SubSup:
+ HandleSubSupScript(pNode,nLevel);
+ break;
+ case SmNodeType::Expression:
+ {
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pNode->GetSubNode(i))
+ HandleNodes(pTemp,nLevel+1);
+ }
+ break;
+ }
+ case SmNodeType::Table:
+ //Root Node, PILE equivalent, i.e. vertical stack
+ HandleTable(pNode,nLevel);
+ break;
+ case SmNodeType::Matrix:
+ HandleSmMatrix(static_cast<SmMatrixNode *>(pNode),nLevel);
+ break;
+ case SmNodeType::Line:
+ {
+ pS->WriteUChar( 0x0a );
+ pS->WriteUChar( LINE );
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pNode->GetSubNode(i))
+ HandleNodes(pTemp,nLevel+1);
+ }
+ pS->WriteUChar( END );
+ break;
+ }
+ case SmNodeType::Align:
+ HandleMAlign(pNode,nLevel);
+ break;
+ case SmNodeType::Blank:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x98 );
+ if (pNode->GetToken().eType == TSBLANK)
+ pS->WriteUInt16( 0xEB04 );
+ else
+ pS->WriteUInt16( 0xEB05 );
+ break;
+ default:
+ {
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pNode->GetSubNode(i))
+ HandleNodes(pTemp,nLevel+1);
+ }
+ break;
+ }
+ }
+}
+
+
+int MathType::StartTemplate(sal_uInt16 nSelector,sal_uInt16 nVariation)
+{
+ int nOldPending=nPendingAttributes;
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( nSelector ); //selector
+ pS->WriteUChar( nVariation ); //variation
+ pS->WriteUChar( 0x00 ); //options
+ pS->WriteUChar( LINE );
+ //there's just no way we can now handle any character
+ //attributes (from mathtypes perspective) centered
+ //over an expression but above template attribute
+ //such as widevec and similar constructs
+ //we have to drop them
+ nPendingAttributes=0;
+ return nOldPending;
+}
+
+void MathType::EndTemplate(int nOldPendingAttributes)
+{
+ pS->WriteUChar( END ); //end line
+ pS->WriteUChar( END ); //end template
+ nPendingAttributes=nOldPendingAttributes;
+}
+
+
+void MathType::HandleSmMatrix(SmMatrixNode *pMatrix,int nLevel)
+{
+ pS->WriteUChar( MATRIX );
+ pS->WriteUChar( 0x00 ); //vAlign ?
+ pS->WriteUChar( 0x00 ); //h_just
+ pS->WriteUChar( 0x00 ); //v_just
+ pS->WriteUChar( pMatrix->GetNumRows() ); //v_just
+ pS->WriteUChar( pMatrix->GetNumCols() ); //v_just
+ int nBytes=(pMatrix->GetNumRows()+1)*2/8;
+ if (((pMatrix->GetNumRows()+1)*2)%8)
+ nBytes++;
+ for (int j = 0; j < nBytes; j++)
+ pS->WriteUChar( 0x00 ); //row_parts
+ nBytes=(pMatrix->GetNumCols()+1)*2/8;
+ if (((pMatrix->GetNumCols()+1)*2)%8)
+ nBytes++;
+ for (int k = 0; k < nBytes; k++)
+ pS->WriteUChar( 0x00 ); //col_parts
+ size_t nSize = pMatrix->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pMatrix->GetSubNode(i))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //end line
+ }
+ }
+ pS->WriteUChar( END );
+}
+
+
+//Root Node, PILE equivalent, i.e. vertical stack
+void MathType::HandleTable(SmNode *pNode,int nLevel)
+{
+ size_t nSize = pNode->GetNumSubNodes();
+ //The root of the starmath is a table, if
+ //we convert this them each iteration of
+ //conversion from starmath to mathtype will
+ //add an extra unnecessary level to the
+ //mathtype output stack which would grow
+ //without bound in a multi step conversion
+
+ if (nLevel == 0)
+ pS->WriteUChar( 0x0A ); //initial size
+
+ if ( nLevel || (nSize >1))
+ {
+ pS->WriteUChar( PILE );
+ pS->WriteUChar( nHAlign ); //vAlign ?
+ pS->WriteUChar( 0x01 ); //hAlign
+ }
+
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pNode->GetSubNode(i))
+ {
+ pS->WriteUChar( LINE );
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END );
+ }
+ }
+ if (nLevel || (nSize>1))
+ pS->WriteUChar( END );
+}
+
+
+void MathType::HandleRoot(SmNode *pNode,int nLevel)
+{
+ SmNode *pTemp;
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( 0x0D ); //selector
+ if (pNode->GetSubNode(0))
+ pS->WriteUChar( 0x01 ); //variation
+ else
+ pS->WriteUChar( 0x00 ); //variation
+ pS->WriteUChar( 0x00 ); //options
+
+ if (nullptr != (pTemp = pNode->GetSubNode(2)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END );
+ }
+
+ if (nullptr != (pTemp = pNode->GetSubNode(0)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END );
+ }
+ else
+ pS->WriteUChar( LINE|0x10 ); //dummy line
+
+
+ pS->WriteUChar( END );
+}
+
+sal_uInt8 MathType::HandleCScript(SmNode *pNode,SmNode *pContent,int nLevel,
+ sal_uInt64 *pPos,bool bTest)
+{
+ sal_uInt8 nVariation2=0xff;
+
+ if (bTest && pNode->GetSubNode(CSUP+1))
+ {
+ nVariation2=0;
+ if (pNode->GetSubNode(CSUB+1))
+ nVariation2=2;
+ }
+ else if (pNode->GetSubNode(CSUB+1))
+ nVariation2=1;
+
+ if (nVariation2!=0xff)
+ {
+ if (pPos)
+ *pPos = pS->Tell();
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( 0x2B ); //selector
+ pS->WriteUChar( nVariation2 );
+ pS->WriteUChar( 0x00 ); //options
+
+ if (pContent)
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pContent,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+
+ pS->WriteUChar( 0x0B );
+
+ SmNode *pTemp;
+ if (nullptr != (pTemp = pNode->GetSubNode(CSUB+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ if (bTest && nullptr != (pTemp = pNode->GetSubNode(CSUP+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ }
+ return nVariation2;
+}
+
+
+/*
+ Sub and Sup scripts and another problem area, StarMath
+ can have all possible options used at the same time, whereas
+ Mathtype cannot. The ordering of the nodes for each system
+ is quite different as well leading to some complexity
+ */
+void MathType::HandleSubSupScript(SmNode *pNode,int nLevel)
+{
+ sal_uInt8 nVariation=0xff;
+ if (pNode->GetSubNode(LSUP+1))
+ {
+ nVariation=0;
+ if (pNode->GetSubNode(LSUB+1))
+ nVariation=2;
+ }
+ else if ( nullptr != pNode->GetSubNode(LSUB+1) )
+ nVariation=1;
+
+ SmNode *pTemp;
+ if (nVariation!=0xff)
+ {
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( 0x2c ); //selector
+ pS->WriteUChar( nVariation );
+ pS->WriteUChar( 0x00 ); //options
+ pS->WriteUChar( 0x0B );
+
+ if (nullptr != (pTemp = pNode->GetSubNode(LSUB+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ if (nullptr != (pTemp = pNode->GetSubNode(LSUP+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ pS->WriteUChar( END );
+ nVariation=0xff;
+ }
+
+
+ sal_uInt8 nVariation2=HandleCScript(pNode,nullptr,nLevel);
+
+ if (nullptr != (pTemp = pNode->GetSubNode(0)))
+ {
+ HandleNodes(pTemp,nLevel+1);
+ }
+
+ if (nVariation2 != 0xff)
+ pS->WriteUChar( END );
+
+ if (nullptr != (pNode->GetSubNode(RSUP+1)))
+ {
+ nVariation=0;
+ if (pNode->GetSubNode(RSUB+1))
+ nVariation=2;
+ }
+ else if (nullptr != pNode->GetSubNode(RSUB+1))
+ nVariation=1;
+
+ if (nVariation!=0xff)
+ {
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( 0x0F ); //selector
+ pS->WriteUChar( nVariation );
+ pS->WriteUChar( 0x00 ); //options
+ pS->WriteUChar( 0x0B );
+
+ if (nullptr != (pTemp = pNode->GetSubNode(RSUB+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ if (nullptr != (pTemp = pNode->GetSubNode(RSUP+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ pS->WriteUChar( END ); //line
+ }
+
+ //After subscript mathtype will keep the size of
+ //normal text at the subscript size, sigh.
+ pS->WriteUChar( 0x0A );
+}
+
+
+void MathType::HandleFractions(SmNode *pNode,int nLevel)
+{
+ SmNode *pTemp;
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( 0x0E ); //selector
+ pS->WriteUChar( 0x00 ); //variation
+ pS->WriteUChar( 0x00 ); //options
+
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( LINE ); //line
+ if (nullptr != (pTemp = pNode->GetSubNode(0)))
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END );
+
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( LINE ); //line
+ if (nullptr != (pTemp = pNode->GetSubNode(2)))
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END );
+
+ pS->WriteUChar( END );
+}
+
+
+void MathType::HandleBrace(SmNode *pNode,int nLevel)
+{
+ SmNode *pTemp;
+ SmNode *pLeft=pNode->GetSubNode(0);
+ SmNode *pRight=pNode->GetSubNode(2);
+
+ pS->WriteUChar( TMPL ); //Template
+ bIsReInterpBrace=false;
+ sal_uInt8 nBSpec=0x10;
+ auto nLoc = pS->Tell();
+ if (pLeft)
+ {
+ switch (pLeft->GetToken().eType)
+ {
+ case TLANGLE:
+ pS->WriteUChar( tmANGLE ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ break;
+ case TLBRACE:
+ pS->WriteUChar( tmBRACE ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ nBSpec+=3;
+ break;
+ case TLBRACKET:
+ pS->WriteUChar( tmBRACK ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ nBSpec+=3;
+ break;
+ case TLFLOOR:
+ pS->WriteUChar( tmFLOOR ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ break;
+ case TLLINE:
+ pS->WriteUChar( tmBAR ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ nBSpec+=3;
+ break;
+ case TLDLINE:
+ pS->WriteUChar( tmDBAR ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ break;
+ default:
+ pS->WriteUChar( tmPAREN ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ nBSpec+=3;
+ break;
+ }
+ }
+
+ if (nullptr != (pTemp = pNode->GetSubNode(1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //options
+ }
+ nSpec=nBSpec;
+ if (pLeft)
+ HandleNodes(pLeft,nLevel+1);
+ if (bIsReInterpBrace)
+ {
+ auto nLoc2 = pS->Tell();
+ pS->Seek(nLoc);
+ pS->WriteUChar( 0x2D );
+ pS->Seek(nLoc2);
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x96 );
+ pS->WriteUInt16( 0xEC07 );
+ bIsReInterpBrace=false;
+ }
+ if (pRight)
+ HandleNodes(pRight,nLevel+1);
+ nSpec=0x0;
+ pS->WriteUChar( END );
+}
+
+
+void MathType::HandleVerticalBrace(SmNode *pNode,int nLevel)
+{
+ SmNode *pTemp;
+ pS->WriteUChar( TMPL ); //Template
+ if (pNode->GetToken().eType == TUNDERBRACE)
+ pS->WriteUChar( tmLHBRACE ); //selector
+ else
+ pS->WriteUChar( tmUHBRACE ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+
+ if (nullptr != (pTemp = pNode->GetSubNode(0)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //options
+ }
+
+ if (nullptr != (pTemp = pNode->GetSubNode(2)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //options
+ }
+ pS->WriteUChar( END );
+}
+
+void MathType::HandleOperator(SmNode *pNode,int nLevel)
+{
+ if (HandleLim(pNode,nLevel))
+ return;
+
+ sal_uInt64 nPos;
+ sal_uInt8 nVariation;
+
+ switch (pNode->GetToken().eType)
+ {
+ case TIINT:
+ case TIIINT:
+ case TLINT:
+ case TLLINT:
+ case TLLLINT:
+ nVariation=HandleCScript(pNode->GetSubNode(0),
+ pNode->GetSubNode(1),nLevel,&nPos,false);
+ break;
+ default:
+ nVariation=HandleCScript(pNode->GetSubNode(0),
+ pNode->GetSubNode(1),nLevel,&nPos);
+ break;
+ }
+
+ sal_uInt8 nOldVariation=nVariation;
+ sal_uInt8 nIntVariation=nVariation;
+
+ sal_uInt64 nPos2=0;
+ if (nVariation != 0xff)
+ {
+ nPos2 = pS->Tell();
+ pS->Seek(nPos);
+ if (nVariation == 2)
+ {
+ nIntVariation=0;
+ nVariation = 1;
+ }
+ else if (nVariation == 0)
+ nVariation = 1;
+ else if (nVariation == 1)
+ nVariation = 0;
+ }
+ else
+ {
+ nVariation = 2;
+ nIntVariation=0;
+ }
+ pS->WriteUChar( TMPL );
+ switch(pNode->GetToken().eType)
+ {
+ case TINT:
+ case TINTD:
+ if (nOldVariation != 0xff)
+ pS->WriteUChar( 0x18 ); //selector
+ else
+ pS->WriteUChar( 0x15 ); //selector
+ pS->WriteUChar( nIntVariation ); //variation
+ break;
+ case TIINT:
+ if (nOldVariation != 0xff)
+ {
+ pS->WriteUChar( 0x19 );
+ pS->WriteUChar( 0x01 );
+ }
+ else
+ {
+ pS->WriteUChar( 0x16 );
+ pS->WriteUChar( 0x00 );
+ }
+ break;
+ case TIIINT:
+ if (nOldVariation != 0xff)
+ {
+ pS->WriteUChar( 0x1a );
+ pS->WriteUChar( 0x01 );
+ }
+ else
+ {
+ pS->WriteUChar( 0x17 );
+ pS->WriteUChar( 0x00 );
+ }
+ break;
+ case TLINT:
+ if (nOldVariation != 0xff)
+ {
+ pS->WriteUChar( 0x18 );
+ pS->WriteUChar( 0x02 );
+ }
+ else
+ {
+ pS->WriteUChar( 0x15 );
+ pS->WriteUChar( 0x03 );
+ }
+ break;
+ case TLLINT:
+ if (nOldVariation != 0xff)
+ {
+ pS->WriteUChar( 0x19 );
+ pS->WriteUChar( 0x00 );
+ }
+ else
+ {
+ pS->WriteUChar( 0x16 );
+ pS->WriteUChar( 0x02 );
+ }
+ break;
+ case TLLLINT:
+ if (nOldVariation != 0xff)
+ {
+ pS->WriteUChar( 0x1a );
+ pS->WriteUChar( 0x00 );
+ }
+ else
+ {
+ pS->WriteUChar( 0x17 );
+ pS->WriteUChar( 0x02 );
+ }
+ break;
+ case TSUM:
+ default:
+ pS->WriteUChar( 0x1d );
+ pS->WriteUChar( nVariation );
+ break;
+ case TPROD:
+ pS->WriteUChar( 0x1f );
+ pS->WriteUChar( nVariation );
+ break;
+ case TCOPROD:
+ pS->WriteUChar( 0x21 );
+ pS->WriteUChar( nVariation );
+ break;
+ }
+ pS->WriteUChar( 0 ); //options
+
+ if (nPos2)
+ pS->Seek(nPos2);
+ else
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pNode->GetSubNode(1),nLevel+1);
+ pS->WriteUChar( END ); //line
+ pS->WriteUChar( LINE|0x10 );
+ pS->WriteUChar( LINE|0x10 );
+ }
+
+ pS->WriteUChar( 0x0D );
+ switch(pNode->GetToken().eType)
+ {
+ case TSUM:
+ default:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x2211 );
+ break;
+ case TPROD:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x220F );
+ break;
+ case TCOPROD:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x8B );
+ pS->WriteUInt16( 0x2210 );
+ break;
+ case TIIINT:
+ case TLLLINT:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x222B );
+ [[fallthrough]];
+ case TIINT:
+ case TLLINT:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x222B );
+ [[fallthrough]];
+ case TINT:
+ case TINTD:
+ case TLINT:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x222B );
+ break;
+ }
+ pS->WriteUChar( END );
+ pS->WriteUChar( 0x0A );
+}
+
+
+bool MathType::HandlePile(int &rSetAlign, int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariation)
+{
+ sal_uInt8 nVAlign;
+ pS->ReadUChar( nHAlign );
+ pS->ReadUChar( nVAlign );
+
+ HandleAlign(nHAlign, rSetAlign);
+
+ rRet.append(" stack {\n");
+ bool bRet = HandleRecords( nLevel+1, nSelector, nVariation, -1, -1 );
+ int nRemoveFrom = rRet.getLength() >= 3 ? rRet.getLength() - 3 : 0;
+ rRet.remove(nRemoveFrom, 2);
+ rRet.append("} ");
+
+ while (rSetAlign)
+ {
+ rRet.append("} ");
+ rSetAlign--;
+ }
+ return bRet;
+}
+
+bool MathType::HandleMatrix(int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariation)
+{
+ sal_uInt8 nH_just,nV_just,nRows,nCols,nVAlign;
+ pS->ReadUChar( nVAlign );
+ pS->ReadUChar( nH_just );
+ pS->ReadUChar( nV_just );
+ pS->ReadUChar( nRows );
+ pS->ReadUChar( nCols );
+ int nBytes = ((nRows+1)*2)/8;
+ if (((nRows+1)*2)%8)
+ nBytes++;
+ pS->SeekRel(nBytes);
+ nBytes = ((nCols+1)*2)/8;
+ if (((nCols+1)*2)%8)
+ nBytes++;
+ pS->SeekRel(nBytes);
+ rRet.append(" matrix {\n");
+ bool bRet = HandleRecords( nLevel+1, nSelector, nVariation, nRows, nCols );
+
+ sal_Int32 nI = rRet.lastIndexOf('#');
+ if (nI > 0)
+ if (rRet[nI-1] != '#') //missing column
+ rRet.append("{}");
+
+ rRet.append("\n} ");
+ return bRet;
+}
+
+bool MathType::HandleTemplate(int nLevel, sal_uInt8 &rSelector,
+ sal_uInt8 &rVariation, sal_Int32 &rLastTemplateBracket)
+{
+ sal_uInt8 nOption; //This appears utterly unused
+ pS->ReadUChar( rSelector );
+ pS->ReadUChar( rVariation );
+ pS->ReadUChar( nOption );
+ OSL_ENSURE(rSelector < 48,"Selector out of range");
+ if ((rSelector >= 21) && (rSelector <=26))
+ {
+ OSL_ENSURE(nOption < 2,"Option out of range");
+ }
+ else if (rSelector <= 12)
+ {
+ OSL_ENSURE(nOption < 3,"Option out of range");
+ }
+
+ //For the (broken) case where one subscript template ends, and there is
+ //another one after it, mathtype handles it as if the second one was
+ //inside the first one and renders it as sub of sub
+ bool bRemove=false;
+ if ( (rSelector == 0xf) && (rLastTemplateBracket != -1) )
+ {
+ bRemove=true;
+ for (sal_Int32 nI = rLastTemplateBracket+1; nI < rRet.getLength(); nI++ )
+ if (rRet[nI] != ' ')
+ {
+ bRemove=false;
+ break;
+ }
+ }
+
+ //suborderlist
+ bool bRet = HandleRecords( nLevel+1, rSelector, rVariation );
+
+ if (bRemove)
+ {
+ if (rLastTemplateBracket < rRet.getLength())
+ rRet.remove(rLastTemplateBracket, 1);
+ rRet.append("} ");
+ rLastTemplateBracket = -1;
+ }
+ if (rSelector == 0xf)
+ rLastTemplateBracket = rRet.lastIndexOf('}');
+ else
+ rLastTemplateBracket = -1;
+
+ rSelector = sal::static_int_cast< sal_uInt8 >(-1);
+ return bRet;
+}
+
+void MathType::HandleEmblishments()
+{
+ sal_uInt8 nEmbel;
+ do
+ {
+ pS->ReadUChar( nEmbel );
+ if (!pS->good())
+ break;
+ switch (nEmbel)
+ {
+ case 0x02:
+ rRet.append(" dot ");
+ break;
+ case 0x03:
+ rRet.append(" ddot ");
+ break;
+ case 0x04:
+ rRet.append(" dddot ");
+ break;
+ case 0x05:
+ if (!nPostSup)
+ {
+ sPost.append(" sup {}");
+ nPostSup = sPost.getLength();
+ }
+ sPost.insert(nPostSup-1," ' ");
+ nPostSup += 3;
+ break;
+ case 0x06:
+ if (!nPostSup)
+ {
+ sPost.append(" sup {}");
+ nPostSup = sPost.getLength();
+ }
+ sPost.insert(nPostSup-1," '' ");
+ nPostSup += 4;
+ break;
+ case 0x07:
+ if (!nPostlSup)
+ {
+ sPost.append(" lsup {}");
+ nPostlSup = sPost.getLength();
+ }
+ sPost.insert(nPostlSup-1," ' ");
+ nPostlSup += 3;
+ break;
+ case 0x08:
+ rRet.append(" tilde ");
+ break;
+ case 0x09:
+ rRet.append(" hat ");
+ break;
+ case 0x0b:
+ rRet.append(" vec ");
+ break;
+ case 0x10:
+ rRet.append(" overstrike ");
+ break;
+ case 0x11:
+ rRet.append(" bar ");
+ break;
+ case 0x12:
+ if (!nPostSup)
+ {
+ sPost.append(" sup {}");
+ nPostSup = sPost.getLength();
+ }
+ sPost.insert(nPostSup-1," ''' ");
+ nPostSup += 5;
+ break;
+ case 0x14:
+ rRet.append(" breve ");
+ break;
+ default:
+ OSL_ENSURE(nEmbel < 21,"Embel out of range");
+ break;
+ }
+ if (nVersion < 3)
+ break;
+ }while (nEmbel);
+}
+
+void MathType::HandleSetSize()
+{
+ sal_uInt8 nTemp;
+ pS->ReadUChar( nTemp );
+ switch (nTemp)
+ {
+ case 101:
+ pS->ReadInt16( nLSize );
+ nLSize = -nLSize;
+ break;
+ case 100:
+ pS->ReadUChar( nTemp );
+ nLSize = nTemp;
+ pS->ReadInt16( nDSize );
+ break;
+ default:
+ nLSize = nTemp;
+ pS->ReadUChar( nTemp );
+ nDSize = nTemp-128;
+ break;
+ }
+}
+
+bool MathType::HandleChar(sal_Int32 &rTextStart, int &rSetSize, int nLevel,
+ sal_uInt8 nTag, sal_uInt8 nSelector, sal_uInt8 nVariation, bool bSilent)
+{
+ sal_Unicode nChar(0);
+ bool bRet = true;
+
+ if (xfAUTO(nTag))
+ {
+ //This is a candidate for function recognition, whatever
+ //that is!
+ }
+
+ sal_uInt8 nOldTypeFace = nTypeFace;
+ pS->ReadUChar( nTypeFace );
+ if (nVersion < 3)
+ {
+ sal_uInt8 nChar8(0);
+ pS->ReadUChar( nChar8 );
+ nChar = nChar8;
+ }
+ else
+ pS->ReadUtf16( nChar );
+
+ /*
+ bad character, old mathtype < 3 has these
+ */
+ if (nChar < 0x20)
+ return bRet;
+
+ if (xfEMBELL(nTag))
+ {
+ //A bit tricky, the character emblishments for
+ //mathtype can all be listed after each other, in
+ //starmath some must go before the character and some
+ //must go after. In addition some of the emblishments
+ //may repeated and in starmath some of these groups
+ //must be gathered together. sPost is the portion that
+ //follows the char and nPostSup and nPostlSup are the
+ //indexes at which this class of emblishment is
+ //collated together
+ sPost = "";
+ nPostSup = nPostlSup = 0;
+ int nOriglen=rRet.getLength()-rTextStart;
+ rRet.append(" {"); // #i24340# make what would be "vec {A}_n" become "{vec {A}}_n"
+ if ((!bSilent) && (nOriglen > 1))
+ rRet.append("\"");
+ bRet = HandleRecords( nLevel+1, nSelector, nVariation );
+ if (!bSilent)
+ {
+ if (nOriglen > 1)
+ {
+ OUString aStr;
+ TypeFaceToString(aStr,nOldTypeFace);
+ aStr += "\"";
+ rRet.insert(std::min(rTextStart, rRet.getLength()), aStr);
+
+ aStr.clear();
+ TypeFaceToString(aStr,nTypeFace);
+ rRet.append(aStr).append("{");
+ }
+ else
+ rRet.append(" {");
+ rTextStart = rRet.getLength();
+ }
+ }
+
+ if (!bSilent)
+ {
+ sal_Int32 nOldLen = rRet.getLength();
+ if (
+ HandleSize(nLSize,nDSize,rSetSize) ||
+ (nOldTypeFace != nTypeFace)
+ )
+ {
+ if ((nOldLen - rTextStart) > 1)
+ {
+ rRet.insert(nOldLen, "\"");
+ OUString aStr;
+ TypeFaceToString(aStr,nOldTypeFace);
+ aStr += "\"";
+ rRet.insert(rTextStart,aStr);
+ }
+ rTextStart = rRet.getLength();
+ }
+ nOldLen = rRet.getLength();
+ if (!LookupChar(nChar,rRet,nVersion,nTypeFace))
+ {
+ if (nOldLen - rTextStart > 1)
+ {
+ rRet.insert(nOldLen, "\"");
+ OUString aStr;
+ TypeFaceToString(aStr,nOldTypeFace);
+ aStr += "\"";
+ rRet.insert(rTextStart, aStr);
+ }
+ rTextStart = rRet.getLength();
+ }
+ lcl_PrependDummyTerm(rRet, rTextStart);
+ }
+
+ if ((xfEMBELL(nTag)) && (!bSilent))
+ {
+ rRet.append("}}").append(sPost); // #i24340# make what would be "vec {A}_n" become "{vec {A}}_n"
+ rTextStart = rRet.getLength();
+ }
+ return bRet;
+}
+
+bool MathType::HandleLim(SmNode *pNode,int nLevel)
+{
+ bool bRet=false;
+ //Special case for the "lim" option in StarMath
+ if ((pNode->GetToken().eType == TLIM)
+ || (pNode->GetToken().eType == TLIMSUP)
+ || (pNode->GetToken().eType == TLIMINF)
+ )
+ {
+ if (pNode->GetSubNode(1))
+ {
+ sal_uInt8 nVariation2=HandleCScript(pNode->GetSubNode(0),nullptr,
+ nLevel);
+
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( LINE ); //line
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'l' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'i' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'm' );
+
+ if (pNode->GetToken().eType == TLIMSUP)
+ {
+ pS->WriteUChar( CHAR ); //some space
+ pS->WriteUChar( 0x98 );
+ pS->WriteUInt16( 0xEB04 );
+
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 's' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'u' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'p' );
+ }
+ else if (pNode->GetToken().eType == TLIMINF)
+ {
+ pS->WriteUChar( CHAR ); //some space
+ pS->WriteUChar( 0x98 );
+ pS->WriteUInt16( 0xEB04 );
+
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'i' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'n' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'f' );
+ }
+
+
+ pS->WriteUChar( CHAR ); //some space
+ pS->WriteUChar( 0x98 );
+ pS->WriteUInt16( 0xEB04 );
+
+ if (nVariation2 != 0xff)
+ {
+ pS->WriteUChar( END );
+ pS->WriteUChar( END );
+ }
+ HandleNodes(pNode->GetSubNode(1),nLevel+1);
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void MathType::HandleMAlign(SmNode *pNode,int nLevel)
+{
+ sal_uInt8 nPushedHAlign=nHAlign;
+ switch(pNode->GetToken().eType)
+ {
+ case TALIGNC:
+ nHAlign=2;
+ break;
+ case TALIGNR:
+ nHAlign=3;
+ break;
+ default:
+ nHAlign=1;
+ break;
+ }
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pNode->GetSubNode(i))
+ HandleNodes(pTemp,nLevel+1);
+ }
+ nHAlign=nPushedHAlign;
+}
+
+void MathType::HandleMath(SmNode *pNode)
+{
+ if (pNode->GetToken().eType == TMLINE)
+ {
+ pS->WriteUChar( END );
+ pS->WriteUChar( LINE );
+ bIsReInterpBrace=true;
+ return;
+ }
+ SmMathSymbolNode *pTemp = static_cast<SmMathSymbolNode *>(pNode);
+ for(sal_Int32 i=0;i<pTemp->GetText().getLength();i++)
+ {
+ sal_Unicode nArse = SmTextNode::ConvertSymbolToUnicode(pTemp->GetText()[i]);
+ if ((nArse == 0x2224) || (nArse == 0x2288) || (nArse == 0x2285) ||
+ (nArse == 0x2289))
+ {
+ pS->WriteUChar( CHAR|0x20 );
+ }
+ else if (nPendingAttributes &&
+ (i == ((pTemp->GetText().getLength()+1)/2)-1))
+ {
+ pS->WriteUChar( 0x22 );
+ }
+ else
+ pS->WriteUChar( CHAR ); //char without formula recognition
+ //The typeface seems to be MTEXTRA for unicode characters,
+ //though how to determine when mathtype chooses one over
+ //the other is unknown. This should do the trick
+ //nevertheless.
+ sal_uInt8 nBias;
+ if ( (nArse == 0x2213) || (nArse == 0x2218) ||
+ (nArse == 0x210F) || (
+ (nArse >= 0x22EE) && (nArse <= 0x22FF)
+ ))
+ {
+ nBias = 0xB; //typeface
+ }
+ else if ((nArse == 0x2F) || (nArse == 0x2225))
+ nBias = 0x2; //typeface
+ else if ((nArse > 0x2000) || (nArse == 0x00D7))
+ nBias = 0x6; //typeface
+ else if (nArse == 0x3d1)
+ nBias = 0x4;
+ else if ((nArse > 0xFF) && ((nArse < 0x393) || (nArse > 0x3c9)))
+ nBias = 0xB; //typeface
+ else
+ nBias = 0x3; //typeface
+
+ pS->WriteUChar( nSpec+nBias+128 ); //typeface
+
+ if (nArse == 0x2224)
+ {
+ pS->WriteUInt16( 0x7C );
+ pS->WriteUChar( EMBEL );
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( END ); //end embel
+ pS->WriteUChar( END ); //end embel
+ }
+ else if (nArse == 0x2225)
+ pS->WriteUInt16( 0xEC09 );
+ else if (nArse == 0xE421)
+ pS->WriteUInt16( 0x2265 );
+ else if (nArse == 0x230A)
+ pS->WriteUInt16( 0xF8F0 );
+ else if (nArse == 0x230B)
+ pS->WriteUInt16( 0xF8FB );
+ else if (nArse == 0xE425)
+ pS->WriteUInt16( 0x2264 );
+ else if (nArse == 0x226A)
+ {
+ pS->WriteUInt16( 0x3C );
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x98 );
+ pS->WriteUInt16( 0xEB01 );
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x3c );
+ }
+ else if (nArse == 0x2288)
+ {
+ pS->WriteUInt16( 0x2286 );
+ pS->WriteUChar( EMBEL );
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( END ); //end embel
+ pS->WriteUChar( END ); //end embel
+ }
+ else if (nArse == 0x2289)
+ {
+ pS->WriteUInt16( 0x2287 );
+ pS->WriteUChar( EMBEL );
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( END ); //end embel
+ pS->WriteUChar( END ); //end embel
+ }
+ else if (nArse == 0x2285)
+ {
+ pS->WriteUInt16( 0x2283 );
+ pS->WriteUChar( EMBEL );
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( END ); //end embel
+ pS->WriteUChar( END ); //end embel
+ }
+ else
+ pS->WriteUInt16( nArse );
+ }
+ nPendingAttributes = 0;
+}
+
+void MathType::HandleAttributes(SmNode *pNode,int nLevel)
+{
+ int nOldPending = 0;
+ SmNode *pTemp = nullptr;
+ SmTextNode *pIsText = nullptr;
+
+ if (nullptr != (pTemp = pNode->GetSubNode(0)))
+ {
+ pIsText = static_cast<SmTextNode *>(pNode->GetSubNode(1));
+
+ switch (pTemp->GetToken().eType)
+ {
+ case TWIDEVEC:
+ //there's just no way we can now handle any character
+ //attributes (from mathtypes perspective) centered
+ //over an expression but above template attributes
+ //such as widevec and similar constructs
+ //we have to drop them
+ nOldPending = StartTemplate(0x2f,0x01);
+ break;
+ case TCHECK: //Not Exportable
+ case TACUTE: //Not Exportable
+ case TGRAVE: //Not Exportable
+ case TCIRCLE: //Not Exportable
+ case TWIDEHARPOON: //Not Exportable
+ case TWIDETILDE: //Not Exportable
+ case TWIDEHAT: //Not Exportable
+ break;
+ case TUNDERLINE:
+ nOldPending = StartTemplate(0x10);
+ break;
+ case TOVERLINE: //If the next node is not text
+ //or text with more than one char
+ if ((pIsText->GetToken().eType != TTEXT) ||
+ (pIsText->GetText().getLength() > 1))
+ nOldPending = StartTemplate(0x11);
+ break;
+ default:
+ nPendingAttributes++;
+ break;
+ }
+ }
+
+ if (pIsText)
+ HandleNodes(pIsText,nLevel+1);
+
+ switch (pTemp->GetToken().eType)
+ {
+ case TWIDEVEC:
+ case TUNDERLINE:
+ EndTemplate(nOldPending);
+ break;
+ case TOVERLINE:
+ if ((pIsText->GetToken().eType != TTEXT) ||
+ (pIsText->GetText().getLength() > 1))
+ EndTemplate(nOldPending);
+ break;
+ default:
+ break;
+ }
+
+ //if there was no suitable place to put the attribute,
+ //then we have to just give up on it
+ if (nPendingAttributes)
+ nPendingAttributes--;
+ else
+ {
+ if ((nInsertion != 0) && nullptr != (pTemp = pNode->GetSubNode(0)))
+ {
+ auto nPos = pS->Tell();
+ nInsertion--;
+ pS->Seek(nInsertion);
+ switch(pTemp->GetToken().eType)
+ {
+ case TACUTE: //Not Exportable
+ case TGRAVE: //Not Exportable
+ case TCIRCLE: //Not Exportable
+ break;
+ case TCDOT:
+ pS->WriteUChar( 2 );
+ break;
+ case TDDOT:
+ pS->WriteUChar( 3 );
+ break;
+ case TDDDOT:
+ pS->WriteUChar( 4 );
+ break;
+ case TTILDE:
+ pS->WriteUChar( 8 );
+ break;
+ case THAT:
+ pS->WriteUChar( 9 );
+ break;
+ case TVEC:
+ pS->WriteUChar( 11 );
+ break;
+ case TOVERSTRIKE:
+ pS->WriteUChar( 16 );
+ break;
+ case TOVERLINE:
+ if ((pIsText->GetToken().eType == TTEXT) &&
+ (pIsText->GetText().getLength() == 1))
+ pS->WriteUChar( 17 );
+ break;
+ case TBREVE:
+ pS->WriteUChar( 20 );
+ break;
+ case TWIDEVEC:
+ case TWIDEHARPOON:
+ case TUNDERLINE:
+ case TWIDETILDE:
+ case TWIDEHAT:
+ break;
+ case TBAR:
+ pS->WriteUChar( 17 );
+ break;
+ default:
+ pS->WriteUChar( 2 );
+ break;
+ }
+ pS->Seek(nPos);
+ }
+ }
+}
+
+void MathType::HandleText(SmNode *pNode)
+{
+ SmTextNode *pTemp = static_cast<SmTextNode *>(pNode);
+ for(sal_Int32 i=0;i<pTemp->GetText().getLength();i++)
+ {
+ if (nPendingAttributes &&
+ (i == ((pTemp->GetText().getLength()+1)/2)-1))
+ {
+ pS->WriteUChar( 0x22 ); //char, with attributes right
+ //after the character
+ }
+ else
+ pS->WriteUChar( CHAR );
+
+ sal_uInt8 nFace = 0x1;
+ if (pNode->GetFont().GetItalic() == ITALIC_NORMAL)
+ nFace = 0x3;
+ else if (pNode->GetFont().GetWeight() == WEIGHT_BOLD)
+ nFace = 0x7;
+ pS->WriteUChar( nFace+128 ); //typeface
+ sal_uInt16 nChar = pTemp->GetText()[i];
+ pS->WriteUInt16( SmTextNode::ConvertSymbolToUnicode(nChar) );
+
+ //Mathtype can only have these sort of character
+ //attributes on a single character, starmath can put them
+ //anywhere, when the entity involved is a text run this is
+ //a large effort to place the character attribute on the
+ //central mathtype character so that it does pretty much
+ //what the user probably has in mind. The attributes
+ //filled in here are dummy ones which are replaced in the
+ //ATTRIBUT handler if a suitable location for the
+ //attributes was found here. Unfortunately it is
+ //possible for starmath to place character attributes on
+ //entities which cannot occur in mathtype e.g. a Summation
+ //symbol so these attributes may be lost
+ if (nPendingAttributes &&
+ (i == ((pTemp->GetText().getLength()+1)/2)-1))
+ {
+ pS->WriteUChar( EMBEL );
+ while (nPendingAttributes)
+ {
+ pS->WriteUChar( 2 );
+ //wedge the attributes in here and clear
+ //the pending stack
+ nPendingAttributes--;
+ }
+ nInsertion=pS->Tell();
+ pS->WriteUChar( END ); //end embel
+ pS->WriteUChar( END ); //end embel
+ }
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportMathType(SvStream &rStream)
+{
+ OUStringBuffer sText;
+ MathType aEquation(sText);
+ bool bRet = false;
+ try
+ {
+ bRet = aEquation.Parse(&rStream);
+ }
+ catch (const std::out_of_range&)
+ {
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathtype.hxx b/starmath/source/mathtype.hxx
new file mode 100644
index 000000000..1e3a744d7
--- /dev/null
+++ b/starmath/source/mathtype.hxx
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_MATHTYPE_HXX
+#define INCLUDED_STARMATH_SOURCE_MATHTYPE_HXX
+
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <set>
+#include <vector>
+
+class SfxMedium;
+class SmMatrixNode;
+class SmNode;
+class SotStorage;
+class SvStream;
+
+class MathTypeFont
+{
+public:
+ sal_uInt8 nTface;
+ sal_uInt8 nStyle;
+ MathTypeFont() : nTface(0),nStyle(0) {}
+ explicit MathTypeFont(sal_uInt8 nFace) : nTface(nFace),nStyle(0) {}
+ void AppendStyleToText(OUString &rS);
+};
+
+struct LessMathTypeFont
+{
+ bool operator() (const MathTypeFont &rValue1,
+ const MathTypeFont &rValue2) const
+ {
+ return rValue1.nTface < rValue2.nTface;
+ }
+};
+
+typedef ::std::set< MathTypeFont, LessMathTypeFont > MathTypeFontSet;
+
+class MathType
+{
+public:
+ explicit MathType(OUStringBuffer &rIn)
+ : nVersion(0)
+ , pS(nullptr)
+ , rRet(rIn)
+ , pTree(nullptr)
+ , nHAlign(0)
+ , nPendingAttributes(0)
+ , nInsertion(0)
+ , nLSize(0)
+ , nDSize(0)
+ , nCurSize(0)
+ , nLastSize(0)
+ , nSpec(0)
+ , bIsReInterpBrace(false)
+ , nPostSup(0)
+ , nPostlSup(0)
+ , nTypeFace(0)
+ {
+ Init();
+ }
+
+ MathType(OUStringBuffer &rIn,SmNode *pIn)
+ : nVersion(0)
+ , pS(nullptr)
+ , rRet(rIn)
+ , pTree(pIn)
+ , nHAlign(2)
+ , nPendingAttributes(0)
+ , nInsertion(0)
+ , nLSize(0)
+ , nDSize(0)
+ , nCurSize(0)
+ , nLastSize(0)
+ , nSpec(0)
+ , bIsReInterpBrace(false)
+ , nPostSup(0)
+ , nPostlSup(0)
+ , nTypeFace(0)
+ {
+ Init();
+ }
+
+ bool Parse(SotStorage* pStor);
+ bool Parse(SvStream* pStream);
+ bool ConvertFromStarMath( SfxMedium& rMedium );
+
+private:
+/*Ver 2 Header*/
+ sal_uInt8 nVersion;
+
+ SvStream* pS;
+
+ void Init();
+
+ bool HandleRecords(int nLevel, sal_uInt8 nSelector =0xFF,
+ sal_uInt8 nVariation =0xFF, int nRows =0, int nCols =0);
+ bool HandleSize(sal_Int16 nLSize, sal_Int16 nDSize, int &rSetSize);
+ void HandleAlign(sal_uInt8 nHAlign, int &rSetAlign);
+ bool HandlePile(int &rSetAlign, int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariation);
+ bool HandleMatrix(int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariarion);
+ void HandleMatrixSeparator(int nMatrixRows, int nMatrixCols, int &rCurCol, int &rCurRow);
+ bool HandleTemplate(int nLevel, sal_uInt8 &rSelector, sal_uInt8 &rVariation,
+ sal_Int32 &rLastTemplateBracket);
+ void HandleEmblishments();
+ void HandleSetSize();
+ bool HandleChar(sal_Int32 &rTextStart, int &rSetSize, int nLevel,
+ sal_uInt8 nTag, sal_uInt8 nSelector, sal_uInt8 nVariation, bool bSilent);
+ void HandleNudge();
+
+ static int xfLMOVE(sal_uInt8 nTest) {return nTest&0x80;}
+ static int xfAUTO(sal_uInt8 nTest) {return nTest&0x10;}
+ static int xfEMBELL(sal_uInt8 nTest) {return nTest&0x20;}
+ static int xfNULL(sal_uInt8 nTest) {return nTest&0x10;}
+
+ void HandleNodes(SmNode *pNode,int nLevel);
+ int StartTemplate(sal_uInt16 nSelector,sal_uInt16 nVariation=0);
+ void EndTemplate(int nOldPendingAttributes);
+ void HandleSmMatrix(SmMatrixNode *pMatrix,int nLevel);
+ void HandleTable(SmNode *pNode,int nLevel);
+ void HandleRoot(SmNode *pNode,int nLevel);
+ void HandleSubSupScript(SmNode *pNode,int nLevel);
+ sal_uInt8 HandleCScript(SmNode *pNode,SmNode *pContent,int nLevel,
+ sal_uInt64 *pPos=nullptr,bool bTest=true);
+ void HandleFractions(SmNode *pNode,int nLevel);
+ void HandleBrace(SmNode *pNode,int nLevel);
+ void HandleVerticalBrace(SmNode *pNode,int nLevel);
+ void HandleOperator(SmNode *pNode,int nLevel);
+ bool HandleLim(SmNode *pNode,int nLevel);
+ void HandleMAlign(SmNode *pNode,int nLevel);
+ void HandleMath(SmNode *pNode);
+ void HandleText(SmNode *pNode);
+ void HandleAttributes(SmNode *pNode,int nLevel);
+ void TypeFaceToString(OUString &rRet,sal_uInt8 nFace);
+
+ OUStringBuffer &rRet;
+ SmNode *pTree;
+
+ sal_uInt8 nHAlign;
+
+ int nPendingAttributes;
+ sal_uInt64 nInsertion;
+
+ std::vector<sal_Int16> aSizeTable;
+ sal_Int16 nLSize;
+ sal_Int16 nDSize;
+ sal_Int16 nCurSize;
+ sal_Int16 nLastSize;
+ sal_uInt8 nSpec;
+ bool bIsReInterpBrace;
+ OUStringBuffer sPost;
+ sal_Int32 nPostSup;
+ sal_Int32 nPostlSup;
+ sal_uInt8 nTypeFace;
+ MathTypeFontSet aUserStyles;
+
+ enum MTOKENS {END,LINE,CHAR,TMPL,PILE,MATRIX,EMBEL,RULER,FONT,SIZE};
+ enum MTEMPLATES
+ {
+ tmANGLE,tmPAREN,tmBRACE,tmBRACK,tmBAR,tmDBAR,tmFLOOR,tmCEILING,
+ tmLBLB,tmRBRB,tmRBLB,tmLBRP,tmLPRB,tmROOT,tmFRACT,tmSCRIPT,tmUBAR,
+ tmOBAR,tmLARROW,tmRARROW,tmBARROW,tmSINT,tmDINT,tmTINT,tmSSINT,
+ tmDSINT,tmTSINT,tmUHBRACE,tmLHBRACE,tmSUM,tmISUM,tmPROD,tmIPROD,
+ tmCOPROD,tmICOPROD,tmUNION,tmIUNION,tmINTER,tmIINTER,tmLIM,tmLDIV,
+ tmSLFRACT,tmINTOP,tmSUMOP,tmLSCRIPT,tmDIRAC,tmUARROW,tmOARROW,
+ tmOARC
+ };
+public:
+ static bool LookupChar(sal_Unicode nChar,OUStringBuffer &rRet,
+ sal_uInt8 nVersion,sal_uInt8 nTypeFace=0);
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/node.cxx b/starmath/source/node.cxx
new file mode 100644
index 000000000..526b2e3ee
--- /dev/null
+++ b/starmath/source/node.cxx
@@ -0,0 +1,2940 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <node.hxx>
+#include <parse.hxx>
+#include <rect.hxx>
+#include <symbol.hxx>
+#include <smmod.hxx>
+#include "mathtype.hxx"
+#include "tmpdevice.hxx"
+#include <visitors.hxx>
+
+#include <comphelper/string.hxx>
+#include <tools/color.hxx>
+#include <tools/fract.hxx>
+#include <tools/gen.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/outdev.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <rtl/math.hxx>
+
+#include <cassert>
+#include <math.h>
+#include <memory>
+#include <float.h>
+#include <vector>
+
+namespace {
+
+template<typename F>
+void ForEachNonNull(SmNode *pNode, F && f)
+{
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ SmNode *pSubNode = pNode->GetSubNode(i);
+ if (pSubNode != nullptr)
+ f(pSubNode);
+ }
+}
+
+}
+
+SmNode::SmNode(SmNodeType eNodeType, const SmToken &rNodeToken)
+ : maNodeToken( rNodeToken )
+ , meType( eNodeType )
+ , meScaleMode( SmScaleMode::None )
+ , meRectHorAlign( RectHorAlign::Left )
+ , mnFlags( FontChangeMask::None )
+ , mnAttributes( FontAttribute::None )
+ , mbIsPhantom( false )
+ , mbIsSelected( false )
+ , mnAccIndex( -1 )
+ , mpParentNode( nullptr )
+{
+}
+
+SmNode::~SmNode()
+{
+}
+
+const SmNode * SmNode::GetLeftMost() const
+ // returns leftmost node of current subtree.
+ //! (this assumes the one with index 0 is always the leftmost subnode
+ //! for the current node).
+{
+ const SmNode *pNode = GetNumSubNodes() > 0 ?
+ GetSubNode(0) : nullptr;
+
+ return pNode ? pNode->GetLeftMost() : this;
+}
+
+
+void SmNode::SetPhantom(bool bIsPhantomP)
+{
+ if (! (Flags() & FontChangeMask::Phantom))
+ mbIsPhantom = bIsPhantomP;
+
+ bool b = mbIsPhantom;
+ ForEachNonNull(this, [b](SmNode *pNode){pNode->SetPhantom(b);});
+}
+
+
+void SmNode::SetColor(const Color& rColor)
+{
+ if (! (Flags() & FontChangeMask::Color))
+ GetFont().SetColor(rColor);
+
+ ForEachNonNull(this, [&rColor](SmNode *pNode){pNode->SetColor(rColor);});
+}
+
+
+void SmNode::SetAttribut(FontAttribute nAttrib)
+{
+ if (
+ (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
+ (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
+ )
+ {
+ mnAttributes |= nAttrib;
+ }
+
+ ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->SetAttribut(nAttrib);});
+}
+
+
+void SmNode::ClearAttribut(FontAttribute nAttrib)
+{
+ if (
+ (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
+ (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
+ )
+ {
+ mnAttributes &= ~nAttrib;
+ }
+
+ ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->ClearAttribut(nAttrib);});
+}
+
+
+void SmNode::SetFont(const SmFace &rFace)
+{
+ if (!(Flags() & FontChangeMask::Face))
+ GetFont() = rFace;
+
+ ForEachNonNull(this, [&rFace](SmNode *pNode){pNode->SetFont(rFace);});
+}
+
+
+void SmNode::SetFontSize(const Fraction &rSize, FontSizeType nType)
+ //! 'rSize' is in units of pts
+{
+ Size aFntSize;
+
+ if (!(Flags() & FontChangeMask::Size))
+ {
+ Fraction aVal (SmPtsTo100th_mm(rSize.GetNumerator()),
+ rSize.GetDenominator());
+ long nHeight = static_cast<long>(aVal);
+
+ aFntSize = GetFont().GetFontSize();
+ aFntSize.setWidth( 0 );
+ switch(nType)
+ {
+ case FontSizeType::ABSOLUT:
+ aFntSize.setHeight( nHeight );
+ break;
+
+ case FontSizeType::PLUS:
+ aFntSize.AdjustHeight(nHeight );
+ break;
+
+ case FontSizeType::MINUS:
+ aFntSize.AdjustHeight( -nHeight );
+ break;
+
+ case FontSizeType::MULTIPLY:
+ aFntSize.setHeight( static_cast<long>(Fraction(aFntSize.Height()) * rSize) );
+ break;
+
+ case FontSizeType::DIVIDE:
+ if (rSize != Fraction(0))
+ aFntSize.setHeight( static_cast<long>(Fraction(aFntSize.Height()) / rSize) );
+ break;
+ default:
+ break;
+ }
+
+ // check the requested size against maximum value
+ static int const nMaxVal = SmPtsTo100th_mm(128);
+ if (aFntSize.Height() > nMaxVal)
+ aFntSize.setHeight( nMaxVal );
+
+ GetFont().SetSize(aFntSize);
+ }
+
+ ForEachNonNull(this, [&rSize, &nType](SmNode *pNode){pNode->SetFontSize(rSize, nType);});
+}
+
+
+void SmNode::SetSize(const Fraction &rSize)
+{
+ GetFont() *= rSize;
+
+ ForEachNonNull(this, [&rSize](SmNode *pNode){pNode->SetSize(rSize);});
+}
+
+
+void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree )
+{
+ meRectHorAlign = eHorAlign;
+
+ if (bApplyToSubTree)
+ ForEachNonNull(this, [eHorAlign](SmNode *pNode){pNode->SetRectHorAlign(eHorAlign);});
+}
+
+
+void SmNode::PrepareAttributes()
+{
+ GetFont().SetWeight((Attributes() & FontAttribute::Bold) ? WEIGHT_BOLD : WEIGHT_NORMAL);
+ GetFont().SetItalic((Attributes() & FontAttribute::Italic) ? ITALIC_NORMAL : ITALIC_NONE);
+}
+
+
+void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ if (nDepth > 1024)
+ throw std::range_error("parser depth limit");
+
+ mbIsPhantom = false;
+ mnFlags = FontChangeMask::None;
+ mnAttributes = FontAttribute::None;
+
+ switch (rFormat.GetHorAlign())
+ { case SmHorAlign::Left: meRectHorAlign = RectHorAlign::Left; break;
+ case SmHorAlign::Center: meRectHorAlign = RectHorAlign::Center; break;
+ case SmHorAlign::Right: meRectHorAlign = RectHorAlign::Right; break;
+ }
+
+ GetFont() = rFormat.GetFont(FNT_MATH);
+ OSL_ENSURE( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
+ "unexpected CharSet" );
+ GetFont().SetWeight(WEIGHT_NORMAL);
+ GetFont().SetItalic(ITALIC_NONE);
+
+ ForEachNonNull(this, [&rFormat, &rDocShell, nDepth](SmNode *pNode){pNode->Prepare(rFormat, rDocShell, nDepth + 1);});
+}
+
+void SmNode::Move(const Point& rPosition)
+{
+ if (rPosition.X() == 0 && rPosition.Y() == 0)
+ return;
+
+ SmRect::Move(rPosition);
+
+ ForEachNonNull(this, [&rPosition](SmNode *pNode){pNode->Move(rPosition);});
+}
+
+void SmNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ auto nSize = GetNumSubNodes();
+ if (nSize > 1)
+ rText.append("{");
+ ForEachNonNull(this, [&rText](SmNode *pNode){pNode->CreateTextFromNode(rText);});
+ if (nSize > 1)
+ {
+ rText.stripEnd(' ');
+ rText.append("} ");
+ }
+}
+
+void SmNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong /*nWidth*/)
+{
+}
+
+
+void SmNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong /*nHeight*/)
+{
+}
+
+
+const SmNode * SmNode::FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const
+ // returns (first) ** visible ** (sub)node with the tokens text at
+ // position 'nRow', 'nCol'.
+ //! (there should be exactly one such node if any)
+{
+ if ( IsVisible()
+ && nRow == GetToken().nRow
+ && nCol >= GetToken().nCol && nCol < GetToken().nCol + GetToken().aText.getLength())
+ return this;
+ else
+ {
+ size_t nNumSubNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNumSubNodes; ++i)
+ {
+ const SmNode *pNode = GetSubNode(i);
+
+ if (!pNode)
+ continue;
+
+ const SmNode *pResult = pNode->FindTokenAt(nRow, nCol);
+ if (pResult)
+ return pResult;
+ }
+ }
+
+ return nullptr;
+}
+
+
+const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const
+{
+ long nDist = LONG_MAX;
+ const SmNode *pResult = nullptr;
+
+ if (IsVisible())
+ pResult = this;
+ else
+ {
+ size_t nNumSubNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNumSubNodes; ++i)
+ {
+ const SmNode *pNode = GetSubNode(i);
+
+ if (!pNode)
+ continue;
+
+ const SmNode *pFound = pNode->FindRectClosestTo(rPoint);
+ if (pFound)
+ {
+ long nTmp = pFound->OrientedDist(rPoint);
+ if (nTmp < nDist)
+ {
+ nDist = nTmp;
+ pResult = pFound;
+
+ // quit immediately if 'rPoint' is inside the *should not
+ // overlap with other rectangles* part.
+ // This (partly) serves for getting the attributes in eg
+ // "bar overstrike a".
+ // ('nDist < 0' is used as *quick shot* to avoid evaluation of
+ // the following expression, where the result is already determined)
+ if (nDist < 0 && pFound->IsInsideRect(rPoint))
+ break;
+ }
+ }
+ }
+ }
+
+ return pResult;
+}
+
+const SmNode * SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx) const
+{
+ const SmNode *pResult = nullptr;
+
+ sal_Int32 nIdx = GetAccessibleIndex();
+ OUStringBuffer aTxt;
+ if (nIdx >= 0)
+ GetAccessibleText( aTxt ); // get text if used in following 'if' statement
+
+ if (nIdx >= 0
+ && nIdx <= nAccIdx && nAccIdx < nIdx + aTxt.getLength())
+ pResult = this;
+ else
+ {
+ size_t nNumSubNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNumSubNodes; ++i)
+ {
+ const SmNode *pNode = GetSubNode(i);
+ if (!pNode)
+ continue;
+
+ pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx);
+ if (pResult)
+ return pResult;
+ }
+ }
+
+ return pResult;
+}
+
+
+SmStructureNode::~SmStructureNode()
+{
+ ForEachNonNull(this, std::default_delete<SmNode>());
+}
+
+
+void SmStructureNode::ClearSubNodes()
+{
+ maSubNodes.clear();
+}
+
+void SmStructureNode::SetSubNodes(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
+{
+ size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst.release();
+ if (pSecond)
+ maSubNodes[1] = pSecond.release();
+ if (pThird)
+ maSubNodes[2] = pThird.release();
+
+ ClaimPaternity();
+}
+
+void SmStructureNode::SetSubNodes(SmNodeArray&& rNodeArray)
+{
+ maSubNodes = std::move(rNodeArray);
+ ClaimPaternity();
+}
+
+bool SmStructureNode::IsVisible() const
+{
+ return false;
+}
+
+size_t SmStructureNode::GetNumSubNodes() const
+{
+ return maSubNodes.size();
+}
+
+SmNode* SmStructureNode::GetSubNode(size_t nIndex)
+{
+ return maSubNodes[nIndex];
+}
+
+void SmStructureNode::GetAccessibleText( OUStringBuffer &rText ) const
+{
+ ForEachNonNull(const_cast<SmStructureNode *>(this),
+ [&rText](SmNode *pNode)
+ {
+ if (pNode->IsVisible())
+ pNode->SetAccessibleIndex(rText.getLength());
+ pNode->GetAccessibleText( rText );
+ });
+}
+
+void SmStructureNode::ClaimPaternity()
+{
+ ForEachNonNull(this, [this](SmNode *pNode){pNode->SetParent(this);});
+}
+
+bool SmVisibleNode::IsVisible() const
+{
+ return true;
+}
+
+size_t SmVisibleNode::GetNumSubNodes() const
+{
+ return 0;
+}
+
+SmNode * SmVisibleNode::GetSubNode(size_t /*nIndex*/)
+{
+ return nullptr;
+}
+
+void SmGraphicNode::GetAccessibleText( OUStringBuffer &rText ) const
+{
+ rText.append(GetToken().aText);
+}
+
+void SmExpressionNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ size_t nSize = GetNumSubNodes();
+ if (nSize > 1)
+ rText.append("{");
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ SmNode *pNode = GetSubNode(i);
+ if (pNode)
+ {
+ pNode->CreateTextFromNode(rText);
+ //Just a bit of foo to make unary +asd -asd +-asd -+asd look nice
+ if (pNode->GetType() == SmNodeType::Math)
+ if ((nSize != 2) || rText.isEmpty() ||
+ (rText[rText.getLength() - 1] != '+' && rText[rText.getLength() - 1] != '-') )
+ rText.append(" ");
+ }
+ }
+
+ if (nSize > 1)
+ {
+ rText.stripEnd(' ');
+ rText.append("} ");
+ }
+}
+
+void SmTableNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // arranges all subnodes in one column
+{
+ SmNode *pNode;
+ size_t nSize = GetNumSubNodes();
+
+ // make distance depend on font size
+ long nDist = +(rFormat.GetDistance(DIS_VERTICAL)
+ * GetFont().GetFontSize().Height()) / 100;
+
+ if (nSize < 1)
+ return;
+
+ // arrange subnodes and get maximum width of them
+ long nMaxWidth = 0,
+ nTmp;
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ { pNode->Arrange(rDev, rFormat);
+ if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth)
+ nMaxWidth = nTmp;
+ }
+ }
+
+ Point aPos;
+ SmRect::operator = (SmRect(nMaxWidth, 1));
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ { const SmRect &rNodeRect = pNode->GetRect();
+ const SmNode *pCoNode = pNode->GetLeftMost();
+ RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
+
+ aPos = rNodeRect.AlignTo(*this, RectPos::Bottom,
+ eHorAlign, RectVerAlign::Baseline);
+ if (i)
+ aPos.AdjustY(nDist );
+ pNode->MoveTo(aPos);
+ ExtendBy(rNodeRect, nSize > 1 ? RectCopyMBL::None : RectCopyMBL::Arg);
+ }
+ }
+ // #i972#
+ if (HasBaseline())
+ mnFormulaBaseline = GetBaseline();
+ else
+ {
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect aRect(aTmpDev, &rFormat, "a", GetFont().GetBorderWidth());
+ mnFormulaBaseline = GetAlignM();
+ // move from middle position by constant - distance
+ // between middle and baseline for single letter
+ mnFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM();
+ }
+}
+
+const SmNode * SmTableNode::GetLeftMost() const
+{
+ return this;
+}
+
+
+long SmTableNode::GetFormulaBaseline() const
+{
+ return mnFormulaBaseline;
+}
+
+
+/**************************************************************************/
+
+
+void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
+ // to the rest of the formula compared to the 'FNT_MATH' font.
+ GetFont() = rFormat.GetFont(FNT_VARIABLE);
+ Flags() |= FontChangeMask::Face;
+}
+
+
+/**************************************************************************/
+
+
+void SmLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // arranges all subnodes in one row with some extra space between
+{
+ SmNode *pNode;
+ size_t nSize = GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ pNode->Arrange(rDev, rFormat);
+ }
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ if (nSize < 1)
+ {
+ // provide an empty rectangle with alignment parameters for the "current"
+ // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
+ // same sub-/supscript positions.)
+ //! be sure to use a character that has explicitly defined HiAttribut
+ //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
+ //! 'vec {a}'.
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, "a",
+ GetFont().GetBorderWidth()));
+ // make sure that the rectangle occupies (almost) no space
+ SetWidth(1);
+ SetItalicSpaces(0, 0);
+ return;
+ }
+
+ // make distance depend on font size
+ long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetFontSize().Height()) / 100;
+ if (!IsUseExtraSpaces())
+ nDist = 0;
+
+ Point aPos;
+ // copy the first node into LineNode and extend by the others
+ if (nullptr != (pNode = GetSubNode(0)))
+ SmRect::operator = (pNode->GetRect());
+
+ for (size_t i = 1; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ {
+ aPos = pNode->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+
+ // add horizontal space to the left for each but the first sub node
+ aPos.AdjustX(nDist );
+
+ pNode->MoveTo(aPos);
+ ExtendBy( *pNode, RectCopyMBL::Xor );
+ }
+ }
+}
+
+
+/**************************************************************************/
+
+
+void SmExpressionNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
+{
+ SmLineNode::Arrange(rDev, rFormat);
+
+ // copy alignment of leftmost subnode if any
+ const SmNode *pNode = GetLeftMost();
+ if (pNode)
+ SetRectHorAlign(pNode->GetRectHorAlign(), false);
+}
+
+
+/**************************************************************************/
+
+
+void SmUnHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ bool bIsPostfix = GetToken().eType == TFACT;
+
+ SmNode *pNode0 = GetSubNode(0),
+ *pNode1 = GetSubNode(1);
+ SmNode *pOper = bIsPostfix ? pNode1 : pNode0,
+ *pBody = bIsPostfix ? pNode0 : pNode1;
+ assert(pOper);
+ assert(pBody);
+
+ pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
+ pOper->Arrange(rDev, rFormat);
+ pBody->Arrange(rDev, rFormat);
+
+ long nDist = (pOper->GetRect().GetWidth() * rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
+
+ SmRect::operator = (*pNode0);
+
+ Point aPos = pNode1->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustX(nDist );
+ pNode1->MoveTo(aPos);
+ ExtendBy(*pNode1, RectCopyMBL::Xor);
+}
+
+
+/**************************************************************************/
+
+namespace {
+
+void lcl_GetHeightVerOffset(const SmRect &rRect,
+ long &rHeight, long &rVerOffset)
+ // calculate height and vertical offset of root sign suitable for 'rRect'
+{
+ rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2;
+ rHeight = rRect.GetHeight() - rVerOffset;
+
+ OSL_ENSURE(rHeight >= 0, "Sm : Ooops...");
+ OSL_ENSURE(rVerOffset >= 0, "Sm : Ooops...");
+}
+
+
+Point lcl_GetExtraPos(const SmRect &rRootSymbol,
+ const SmRect &rExtra)
+{
+ const Size &rSymSize = rRootSymbol.GetSize();
+
+ Point aPos = rRootSymbol.GetTopLeft()
+ + Point((rSymSize.Width() * 70) / 100,
+ (rSymSize.Height() * 52) / 100);
+
+ // from this calculate topleft edge of 'rExtra'
+ aPos.AdjustX( -(rExtra.GetWidth() + rExtra.GetItalicRightSpace()) );
+ aPos.AdjustY( -(rExtra.GetHeight()) );
+ // if there's enough space move a bit less to the right
+ // examples: "nroot i a", "nroot j a"
+ // (it looks better if we don't use italic-spaces here)
+ long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100;
+ if (aPos.X() > nX)
+ aPos.setX( nX );
+
+ return aPos;
+}
+
+}
+
+void SmRootNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ //! pExtra needs to have the smaller index than pRootSym in order to
+ //! not to get the root symbol but the pExtra when clicking on it in the
+ //! GraphicWindow. (That is because of the simplicity of the algorithm
+ //! that finds the node corresponding to a mouseclick in the window.)
+ SmNode *pExtra = GetSubNode(0),
+ *pRootSym = GetSubNode(1),
+ *pBody = GetSubNode(2);
+ assert(pRootSym);
+ assert(pBody);
+
+ pBody->Arrange(rDev, rFormat);
+
+ long nHeight,
+ nVerOffset;
+ lcl_GetHeightVerOffset(*pBody, nHeight, nVerOffset);
+ nHeight += rFormat.GetDistance(DIS_ROOT)
+ * GetFont().GetFontSize().Height() / 100;
+
+ // font specialist advised to change the width first
+ pRootSym->AdaptToY(rDev, nHeight);
+ pRootSym->AdaptToX(rDev, pBody->GetItalicWidth());
+
+ pRootSym->Arrange(rDev, rFormat);
+
+ Point aPos = pRootSym->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, RectVerAlign::Baseline);
+ //! override calculated vertical position
+ aPos.setY( pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom() );
+ aPos.AdjustY( -nVerOffset );
+ pRootSym->MoveTo(aPos);
+
+ if (pExtra)
+ { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100));
+ pExtra->Arrange(rDev, rFormat);
+
+ aPos = lcl_GetExtraPos(*pRootSym, *pExtra);
+ pExtra->MoveTo(aPos);
+ }
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pRootSym, RectCopyMBL::This);
+ if (pExtra)
+ ExtendBy(*pExtra, RectCopyMBL::This, true);
+}
+
+
+void SmRootNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ SmNode *pExtra = GetSubNode(0);
+ if (pExtra)
+ {
+ rText.append("nroot ");
+ pExtra->CreateTextFromNode(rText);
+ }
+ else
+ rText.append("sqrt ");
+
+ if (!pExtra && GetSubNode(2)->GetNumSubNodes() > 1)
+ rText.append("{ ");
+
+ GetSubNode(2)->CreateTextFromNode(rText);
+
+ if (!pExtra && GetSubNode(2)->GetNumSubNodes() > 1)
+ rText.append("} ");
+}
+
+/**************************************************************************/
+
+
+void SmBinHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pLeft = LeftOperand(),
+ *pOper = Symbol(),
+ *pRight = RightOperand();
+ assert(pLeft);
+ assert(pOper);
+ assert(pRight);
+
+ pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
+
+ pLeft ->Arrange(rDev, rFormat);
+ pOper ->Arrange(rDev, rFormat);
+ pRight->Arrange(rDev, rFormat);
+
+ const SmRect &rOpRect = pOper->GetRect();
+
+ long nDist = (rOpRect.GetWidth() *
+ rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
+
+ SmRect::operator = (*pLeft);
+
+ Point aPos;
+ aPos = pOper->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustX(nDist );
+ pOper->MoveTo(aPos);
+ ExtendBy(*pOper, RectCopyMBL::Xor);
+
+ aPos = pRight->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustX(nDist );
+
+ pRight->MoveTo(aPos);
+ ExtendBy(*pRight, RectCopyMBL::Xor);
+}
+
+
+/**************************************************************************/
+
+
+void SmBinVerNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pNum = GetSubNode(0),
+ *pLine = GetSubNode(1),
+ *pDenom = GetSubNode(2);
+ assert(pNum);
+ assert(pLine);
+ assert(pDenom);
+
+ bool bIsTextmode = rFormat.IsTextmode();
+ if (bIsTextmode)
+ {
+ Fraction aFraction(rFormat.GetRelSize(SIZ_INDEX), 100);
+ pNum ->SetSize(aFraction);
+ pLine ->SetSize(aFraction);
+ pDenom->SetSize(aFraction);
+ }
+
+ pNum ->Arrange(rDev, rFormat);
+ pDenom->Arrange(rDev, rFormat);
+
+ long nFontHeight = GetFont().GetFontSize().Height(),
+ nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100,
+ nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100,
+ nWidth = std::max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()),
+ nNumDist = bIsTextmode ? 0 :
+ nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100,
+ nDenomDist = bIsTextmode ? 0 :
+ nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100;
+
+ // font specialist advised to change the width first
+ pLine->AdaptToY(rDev, nThick);
+ pLine->AdaptToX(rDev, nWidth + 2 * nExtLen);
+ pLine->Arrange(rDev, rFormat);
+
+ // get horizontal alignment for numerator
+ const SmNode *pLM = pNum->GetLeftMost();
+ RectHorAlign eHorAlign = pLM->GetRectHorAlign();
+
+ // move numerator to its position
+ Point aPos = pNum->AlignTo(*pLine, RectPos::Top, eHorAlign, RectVerAlign::Baseline);
+ aPos.AdjustY( -nNumDist );
+ pNum->MoveTo(aPos);
+
+ // get horizontal alignment for denominator
+ pLM = pDenom->GetLeftMost();
+ eHorAlign = pLM->GetRectHorAlign();
+
+ // move denominator to its position
+ aPos = pDenom->AlignTo(*pLine, RectPos::Bottom, eHorAlign, RectVerAlign::Baseline);
+ aPos.AdjustY(nDenomDist );
+ pDenom->MoveTo(aPos);
+
+ SmRect::operator = (*pNum);
+ ExtendBy(*pDenom, RectCopyMBL::None).ExtendBy(*pLine, RectCopyMBL::None, pLine->GetCenterY());
+}
+
+void SmBinVerNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ SmNode *pNum = GetSubNode(0),
+ *pDenom = GetSubNode(2);
+ pNum->CreateTextFromNode(rText);
+ rText.append("over ");
+ pDenom->CreateTextFromNode(rText);
+}
+
+const SmNode * SmBinVerNode::GetLeftMost() const
+{
+ return this;
+}
+
+
+namespace {
+
+/// @return value of the determinant formed by the two points
+double Det(const Point &rHeading1, const Point &rHeading2)
+{
+ return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X();
+}
+
+
+/// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
+/// and has the direction vector 'rHeading2'
+bool IsPointInLine(const Point &rPoint1,
+ const Point &rPoint2, const Point &rHeading2)
+{
+ assert(rHeading2 != Point());
+
+ bool bRes = false;
+ static const double eps = 5.0 * DBL_EPSILON;
+
+ double fLambda;
+ if (labs(rHeading2.X()) > labs(rHeading2.Y()))
+ {
+ fLambda = (rPoint1.X() - rPoint2.X()) / static_cast<double>(rHeading2.X());
+ bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps;
+ }
+ else
+ {
+ fLambda = (rPoint1.Y() - rPoint2.Y()) / static_cast<double>(rHeading2.Y());
+ bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps;
+ }
+
+ return bRes;
+}
+
+
+sal_uInt16 GetLineIntersectionPoint(Point &rResult,
+ const Point& rPoint1, const Point &rHeading1,
+ const Point& rPoint2, const Point &rHeading2)
+{
+ assert(rHeading1 != Point());
+ assert(rHeading2 != Point());
+
+ sal_uInt16 nRes = 1;
+ static const double eps = 5.0 * DBL_EPSILON;
+
+ // are the direction vectors linearly dependent?
+ double fDet = Det(rHeading1, rHeading2);
+ if (fabs(fDet) < eps)
+ {
+ nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0;
+ rResult = nRes ? rPoint1 : Point();
+ }
+ else
+ {
+ // here we do not pay attention to the computational accuracy
+ // (that would be more complicated and is not really worth it in this case)
+ double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X()
+ - (rPoint1.X() - rPoint2.X()) * rHeading2.Y())
+ / fDet;
+ rResult = Point(rPoint1.X() + static_cast<long>(fLambda * rHeading1.X()),
+ rPoint1.Y() + static_cast<long>(fLambda * rHeading1.Y()));
+ }
+
+ return nRes;
+}
+
+}
+
+
+/// @return position and size of the diagonal line
+/// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
+void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize,
+ const Point &rDiagPoint, double fAngleDeg) const
+
+{
+ static const double fPi = 3.1415926535897932384626433;
+ double fAngleRad = fAngleDeg / 180.0 * fPi;
+ long nRectLeft = GetItalicLeft(),
+ nRectRight = GetItalicRight(),
+ nRectTop = GetTop(),
+ nRectBottom = GetBottom();
+ Point aRightHdg (100, 0),
+ aDownHdg (0, 100),
+ aDiagHdg ( static_cast<long>(100.0 * cos(fAngleRad)),
+ static_cast<long>(-100.0 * sin(fAngleRad)) );
+
+ long nLeft, nRight, nTop, nBottom; // margins of the rectangle for the diagonal
+ Point aPoint;
+ if (IsAscending())
+ {
+ // determine top right corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the top border?
+ if (aPoint.X() <= nRectRight)
+ {
+ nRight = aPoint.X();
+ nTop = nRectTop;
+ }
+ else
+ {
+ // there has to be a point of intersection with the right border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectRight, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nRight = nRectRight;
+ nTop = aPoint.Y();
+ }
+
+ // determine bottom left corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectBottom), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the bottom border?
+ if (aPoint.X() >= nRectLeft)
+ {
+ nLeft = aPoint.X();
+ nBottom = nRectBottom;
+ }
+ else
+ {
+ // there has to be a point of intersection with the left border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nLeft = nRectLeft;
+ nBottom = aPoint.Y();
+ }
+ }
+ else
+ {
+ // determine top left corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the top border?
+ if (aPoint.X() >= nRectLeft)
+ {
+ nLeft = aPoint.X();
+ nTop = nRectTop;
+ }
+ else
+ {
+ // there has to be a point of intersection with the left border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nLeft = nRectLeft;
+ nTop = aPoint.Y();
+ }
+
+ // determine bottom right corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectBottom), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the bottom border?
+ if (aPoint.X() <= nRectRight)
+ {
+ nRight = aPoint.X();
+ nBottom = nRectBottom;
+ }
+ else
+ {
+ // there has to be a point of intersection with the right border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectRight, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nRight = nRectRight;
+ nBottom = aPoint.Y();
+ }
+ }
+
+ rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1);
+ rPos.setX( nLeft );
+ rPos.setY( nTop );
+}
+
+
+void SmBinDiagonalNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ // Both arguments have to get into the SubNodes before the Operator so that clicking
+ // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
+ SmNode *pLeft = GetSubNode(0),
+ *pRight = GetSubNode(1),
+ *pLine = GetSubNode(2);
+ assert(pLeft);
+ assert(pRight);
+ assert(pLine && pLine->GetType() == SmNodeType::PolyLine);
+
+ SmPolyLineNode *pOper = static_cast<SmPolyLineNode *>(pLine);
+ assert(pOper);
+
+ //! some routines being called extract some info from the OutputDevice's
+ //! font (eg the space to be used for borders OR the font name(!!)).
+ //! Thus the font should reflect the needs and has to be set!
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ pLeft->Arrange(aTmpDev, rFormat);
+ pRight->Arrange(aTmpDev, rFormat);
+
+ // determine implicitly the values (incl. the margin) of the diagonal line
+ pOper->Arrange(aTmpDev, rFormat);
+
+ long nDelta = pOper->GetWidth() * 8 / 10;
+
+ // determine TopLeft position from the right argument
+ Point aPos;
+ aPos.setX( pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace() );
+ if (IsAscending())
+ aPos.setY( pLeft->GetBottom() + nDelta );
+ else
+ aPos.setY( pLeft->GetTop() - nDelta - pRight->GetHeight() );
+
+ pRight->MoveTo(aPos);
+
+ // determine new baseline
+ long nTmpBaseline = IsAscending() ? (pLeft->GetBottom() + pRight->GetTop()) / 2
+ : (pLeft->GetTop() + pRight->GetBottom()) / 2;
+ Point aLogCenter ((pLeft->GetItalicRight() + pRight->GetItalicLeft()) / 2,
+ nTmpBaseline);
+
+ SmRect::operator = (*pLeft);
+ ExtendBy(*pRight, RectCopyMBL::None);
+
+
+ // determine position and size of diagonal line
+ Size aTmpSize;
+ GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0);
+
+ // font specialist advised to change the width first
+ pOper->AdaptToY(aTmpDev, aTmpSize.Height());
+ pOper->AdaptToX(aTmpDev, aTmpSize.Width());
+ // and make it active
+ pOper->Arrange(aTmpDev, rFormat);
+
+ pOper->MoveTo(aPos);
+
+ ExtendBy(*pOper, RectCopyMBL::None, nTmpBaseline);
+}
+
+
+/**************************************************************************/
+
+
+void SmSubSupNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES,
+ "Sm: wrong number of subnodes");
+
+ SmNode *pBody = GetBody();
+ assert(pBody);
+
+ long nOrigHeight = pBody->GetFont().GetFontSize().Height();
+
+ pBody->Arrange(rDev, rFormat);
+
+ const SmRect &rBodyRect = pBody->GetRect();
+ SmRect::operator = (rBodyRect);
+
+ // line that separates sub- and supscript rectangles
+ long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4);
+
+ Point aPos;
+ long nDelta, nDist;
+
+ // iterate over all possible sub-/supscripts
+ SmRect aTmpRect (rBodyRect);
+ for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++)
+ {
+ SmSubSup eSubSup = static_cast<SmSubSup>(i);
+ SmNode *pSubSup = GetSubSup(eSubSup);
+
+ if (!pSubSup)
+ continue;
+
+ // switch position of limits if we are in textmode
+ if (rFormat.IsTextmode() && (GetToken().nGroup & TG::Limit))
+ switch (eSubSup)
+ { case CSUB: eSubSup = RSUB; break;
+ case CSUP: eSubSup = RSUP; break;
+ default:
+ break;
+ }
+
+ // prevent sub-/supscripts from diminishing in size
+ // (as would be in "a_{1_{2_{3_4}}}")
+ if (GetFont().GetFontSize().Height() > rFormat.GetBaseSize().Height() / 3)
+ {
+ sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ?
+ SIZ_LIMITS : SIZ_INDEX;
+ Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 );
+ pSubSup->SetSize(aFraction);
+ }
+
+ pSubSup->Arrange(rDev, rFormat);
+
+ bool bIsTextmode = rFormat.IsTextmode();
+ nDist = 0;
+
+ //! be sure that CSUB, CSUP are handled before the other cases!
+ switch (eSubSup)
+ { case RSUB :
+ case LSUB :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_SUBSCRIPT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(aTmpRect,
+ eSubSup == LSUB ? RectPos::Left : RectPos::Right,
+ RectHorAlign::Center, RectVerAlign::Bottom);
+ aPos.AdjustY(nDist );
+ nDelta = nDelimLine - aPos.Y();
+ if (nDelta > 0)
+ aPos.AdjustY(nDelta );
+ break;
+ case RSUP :
+ case LSUP :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(aTmpRect,
+ eSubSup == LSUP ? RectPos::Left : RectPos::Right,
+ RectHorAlign::Center, RectVerAlign::Top);
+ aPos.AdjustY( -nDist );
+ nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine;
+ if (nDelta > 0)
+ aPos.AdjustY( -nDelta );
+ break;
+ case CSUB :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_LOWERLIMIT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Bottom,
+ RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY(nDist );
+ break;
+ case CSUP :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_UPPERLIMIT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Top,
+ RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY( -nDist );
+ break;
+ }
+
+ pSubSup->MoveTo(aPos);
+ ExtendBy(*pSubSup, RectCopyMBL::This, true);
+
+ // update rectangle to which RSUB, RSUP, LSUB, LSUP
+ // will be aligned to
+ if (eSubSup == CSUB || eSubSup == CSUP)
+ aTmpRect = *this;
+ }
+}
+
+void SmSubSupNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ SmNode *pNode;
+ GetSubNode(0)->CreateTextFromNode(rText);
+
+ if (nullptr != (pNode = GetSubNode(LSUB+1)))
+ {
+ rText.append("lsub ");
+ pNode->CreateTextFromNode(rText);
+ }
+ if (nullptr != (pNode = GetSubNode(LSUP+1)))
+ {
+ rText.append("lsup ");
+ pNode->CreateTextFromNode(rText);
+ }
+ if (nullptr != (pNode = GetSubNode(CSUB+1)))
+ {
+ rText.append("csub ");
+ pNode->CreateTextFromNode(rText);
+ }
+ if (nullptr != (pNode = GetSubNode(CSUP+1)))
+ {
+ rText.append("csup ");
+ pNode->CreateTextFromNode(rText);
+ }
+ if (nullptr != (pNode = GetSubNode(RSUB+1)))
+ {
+ rText.stripEnd(' ');
+ rText.append("_");
+ pNode->CreateTextFromNode(rText);
+ }
+ if (nullptr != (pNode = GetSubNode(RSUP+1)))
+ {
+ rText.stripEnd(' ');
+ rText.append("^");
+ pNode->CreateTextFromNode(rText);
+ }
+}
+
+
+/**************************************************************************/
+
+void SmBraceNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ if (GetScaleMode() == SmScaleMode::Height)
+ rText.append("left ");
+ {
+ OUStringBuffer aStrBuf;
+ OpeningBrace()->CreateTextFromNode(aStrBuf);
+ OUString aStr = aStrBuf.makeStringAndClear();
+ aStr = comphelper::string::strip(aStr, ' ');
+ aStr = comphelper::string::stripStart(aStr, '\\');
+ if (!aStr.isEmpty())
+ {
+ if (aStr == "divides")
+ rText.append("lline");
+ else if (aStr == "parallel")
+ rText.append("ldline");
+ else if (aStr == "<")
+ rText.append("langle");
+ else
+ rText.append(aStr);
+ rText.append(" ");
+ }
+ else
+ rText.append("none ");
+ }
+ Body()->CreateTextFromNode(rText);
+ if (GetScaleMode() == SmScaleMode::Height)
+ rText.append("right ");
+ {
+ OUStringBuffer aStrBuf;
+ ClosingBrace()->CreateTextFromNode(aStrBuf);
+ OUString aStr = aStrBuf.makeStringAndClear();
+ aStr = comphelper::string::strip(aStr, ' ');
+ aStr = comphelper::string::stripStart(aStr, '\\');
+ if (!aStr.isEmpty())
+ {
+ if (aStr == "divides")
+ rText.append("rline");
+ else if (aStr == "parallel")
+ rText.append("rdline");
+ else if (aStr == ">")
+ rText.append("rangle");
+ else
+ rText.append(aStr);
+ rText.append(" ");
+ }
+ else
+ rText.append("none ");
+ }
+ rText.append(" ");
+}
+
+void SmBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pLeft = OpeningBrace(),
+ *pBody = Body(),
+ *pRight = ClosingBrace();
+ assert(pLeft);
+ assert(pBody);
+ assert(pRight);
+
+ pBody->Arrange(rDev, rFormat);
+
+ bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(),
+ bScale = pBody->GetHeight() > 0 &&
+ (GetScaleMode() == SmScaleMode::Height || bIsScaleNormal),
+ bIsABS = GetToken().eType == TABS;
+
+ long nFaceHeight = GetFont().GetFontSize().Height();
+
+ // determine oversize in %
+ sal_uInt16 nPerc = 0;
+ if (!bIsABS && bScale)
+ { // in case of oversize braces...
+ sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
+ DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
+ nPerc = rFormat.GetDistance(nIndex);
+ }
+
+ // determine the height for the braces
+ long nBraceHeight;
+ if (bScale)
+ {
+ nBraceHeight = pBody->GetType() == SmNodeType::Bracebody ?
+ static_cast<SmBracebodyNode *>(pBody)->GetBodyHeight()
+ : pBody->GetHeight();
+ nBraceHeight += 2 * (nBraceHeight * nPerc / 100);
+ }
+ else
+ nBraceHeight = nFaceHeight;
+
+ // distance to the argument
+ nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE);
+ long nDist = nFaceHeight * nPerc / 100;
+
+ // if wanted, scale the braces to the wanted size
+ if (bScale)
+ {
+ Size aTmpSize (pLeft->GetFont().GetFontSize());
+ OSL_ENSURE(pRight->GetFont().GetFontSize() == aTmpSize,
+ "Sm : different font sizes");
+ aTmpSize.setWidth( std::min(nBraceHeight * 60 / 100,
+ rFormat.GetBaseSize().Height() * 3 / 2) );
+ // correction factor since change from StarMath to OpenSymbol font
+ // because of the different font width in the FontMetric
+ aTmpSize.setWidth( aTmpSize.Width() * 182 );
+ aTmpSize.setWidth( aTmpSize.Width() / 267 );
+
+ sal_Unicode cChar = pLeft->GetToken().cMathChar;
+ if (cChar != MS_LINE && cChar != MS_DLINE &&
+ cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
+ pLeft ->GetFont().SetSize(aTmpSize);
+
+ cChar = pRight->GetToken().cMathChar;
+ if (cChar != MS_LINE && cChar != MS_DLINE &&
+ cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
+ pRight->GetFont().SetSize(aTmpSize);
+
+ pLeft ->AdaptToY(rDev, nBraceHeight);
+ pRight->AdaptToY(rDev, nBraceHeight);
+ }
+
+ pLeft ->Arrange(rDev, rFormat);
+ pRight->Arrange(rDev, rFormat);
+
+ // required in order to make "\(a\) - (a) - left ( a right )" look alright
+ RectVerAlign eVerAlign = bScale ? RectVerAlign::CenterY : RectVerAlign::Baseline;
+
+ Point aPos;
+ aPos = pLeft->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, eVerAlign);
+ aPos.AdjustX( -nDist );
+ pLeft->MoveTo(aPos);
+
+ aPos = pRight->AlignTo(*pBody, RectPos::Right, RectHorAlign::Center, eVerAlign);
+ aPos.AdjustX(nDist );
+ pRight->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pLeft, RectCopyMBL::This).ExtendBy(*pRight, RectCopyMBL::This);
+}
+
+
+/**************************************************************************/
+
+
+void SmBracebodyNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ size_t nNumSubNodes = GetNumSubNodes();
+ if (nNumSubNodes == 0)
+ return;
+
+ // arrange arguments
+ for (size_t i = 0; i < nNumSubNodes; i += 2)
+ GetSubNode(i)->Arrange(rDev, rFormat);
+
+ // build reference rectangle with necessary info for vertical alignment
+ SmRect aRefRect (*GetSubNode(0));
+ for (size_t i = 0; i < nNumSubNodes; i += 2)
+ {
+ SmRect aTmpRect (*GetSubNode(i));
+ Point aPos = aTmpRect.AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aTmpRect.MoveTo(aPos);
+ aRefRect.ExtendBy(aTmpRect, RectCopyMBL::Xor);
+ }
+
+ mnBodyHeight = aRefRect.GetHeight();
+
+ // scale separators to required height and arrange them
+ bool bScale = GetScaleMode() == SmScaleMode::Height || rFormat.IsScaleNormalBrackets();
+ long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetFontSize().Height();
+ sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
+ DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
+ sal_uInt16 nPerc = rFormat.GetDistance(nIndex);
+ if (bScale)
+ nHeight += 2 * (nHeight * nPerc / 100);
+ for (size_t i = 1; i < nNumSubNodes; i += 2)
+ {
+ SmNode *pNode = GetSubNode(i);
+ pNode->AdaptToY(rDev, nHeight);
+ pNode->Arrange(rDev, rFormat);
+ }
+
+ // horizontal distance between argument and brackets or separators
+ long nDist = GetFont().GetFontSize().Height()
+ * rFormat.GetDistance(DIS_BRACKETSPACE) / 100;
+
+ SmNode *pLeft = GetSubNode(0);
+ SmRect::operator = (*pLeft);
+ for (size_t i = 1; i < nNumSubNodes; ++i)
+ {
+ bool bIsSeparator = i % 2 != 0;
+ RectVerAlign eVerAlign = bIsSeparator ? RectVerAlign::CenterY : RectVerAlign::Baseline;
+
+ SmNode *pRight = GetSubNode(i);
+ Point aPosX = pRight->AlignTo(*pLeft, RectPos::Right, RectHorAlign::Center, eVerAlign),
+ aPosY = pRight->AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, eVerAlign);
+ aPosX.AdjustX(nDist );
+
+ pRight->MoveTo(Point(aPosX.X(), aPosY.Y()));
+ ExtendBy(*pRight, bIsSeparator ? RectCopyMBL::This : RectCopyMBL::Xor);
+
+ pLeft = pRight;
+ }
+}
+
+
+/**************************************************************************/
+
+
+void SmVerticalBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pBody = Body(),
+ *pBrace = Brace(),
+ *pScript = Script();
+ assert(pBody);
+ assert(pBrace);
+ assert(pScript);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ pBody->Arrange(aTmpDev, rFormat);
+
+ // size is the same as for limits for this part
+ pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) );
+ // braces are a bit taller than usually
+ pBrace ->SetSize( Fraction(3, 2) );
+
+ long nItalicWidth = pBody->GetItalicWidth();
+ if (nItalicWidth > 0)
+ pBrace->AdaptToX(aTmpDev, nItalicWidth);
+
+ pBrace ->Arrange(aTmpDev, rFormat);
+ pScript->Arrange(aTmpDev, rFormat);
+
+ // determine the relative position and the distances between each other
+ RectPos eRectPos;
+ long nFontHeight = pBody->GetFont().GetFontSize().Height();
+ long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE),
+ nDistScript = nFontHeight;
+ if (GetToken().eType == TOVERBRACE)
+ {
+ eRectPos = RectPos::Top;
+ nDistBody = - nDistBody;
+ nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT);
+ }
+ else // TUNDERBRACE
+ {
+ eRectPos = RectPos::Bottom;
+ nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT);
+ }
+ nDistBody /= 100;
+ nDistScript /= 100;
+
+ Point aPos = pBrace->AlignTo(*pBody, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY(nDistBody );
+ pBrace->MoveTo(aPos);
+
+ aPos = pScript->AlignTo(*pBrace, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY(nDistScript );
+ pScript->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pBrace, RectCopyMBL::This).ExtendBy(*pScript, RectCopyMBL::This);
+}
+
+
+/**************************************************************************/
+
+
+SmNode * SmOperNode::GetSymbol()
+{
+ SmNode *pNode = GetSubNode(0);
+ assert(pNode);
+
+ if (pNode->GetType() == SmNodeType::SubSup)
+ pNode = static_cast<SmSubSupNode *>(pNode)->GetBody();
+
+ OSL_ENSURE(pNode, "Sm: NULL pointer!");
+ return pNode;
+}
+
+
+long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol,
+ const SmFormat &rFormat) const
+ // returns the font height to be used for operator-symbol
+{
+ long nHeight = GetFont().GetFontSize().Height();
+
+ SmTokenType eTmpType = GetToken().eType;
+ if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP)
+ return nHeight;
+
+ if (!rFormat.IsTextmode())
+ {
+ // set minimum size ()
+ nHeight += (nHeight * 20) / 100;
+
+ nHeight += nHeight
+ * rFormat.GetDistance(DIS_OPERATORSIZE) / 100;
+ nHeight = nHeight * 686 / 845;
+ }
+
+ // correct user-defined symbols to match height of sum from used font
+ if (rSymbol.GetToken().eType == TSPECIAL)
+ nHeight = nHeight * 845 / 686;
+
+ return nHeight;
+}
+
+
+void SmOperNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pOper = GetSubNode(0);
+ SmNode *pBody = GetSubNode(1);
+
+ assert(pOper);
+ assert(pBody);
+
+ SmNode *pSymbol = GetSymbol();
+ pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat),
+ pSymbol->GetFont().GetFontSize().Height()));
+
+ pBody->Arrange(rDev, rFormat);
+ bool bDynamicallySized = false;
+ if (pSymbol->GetToken().eType == TINTD)
+ {
+ long nBodyHeight = pBody->GetHeight();
+ long nFontHeight = pSymbol->GetFont().GetFontSize().Height();
+ if (nFontHeight < nBodyHeight)
+ {
+ pSymbol->SetSize(Fraction(nBodyHeight, nFontHeight));
+ bDynamicallySized = true;
+ }
+ }
+ pOper->Arrange(rDev, rFormat);
+
+ long nOrigHeight = GetFont().GetFontSize().Height(),
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_OPERATORSPACE) / 100;
+
+ Point aPos = pOper->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, bDynamicallySized ? RectVerAlign::CenterY : RectVerAlign::Mid);
+ aPos.AdjustX( -nDist );
+ pOper->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pOper, RectCopyMBL::This);
+}
+
+
+/**************************************************************************/
+
+
+void SmAlignNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // set alignment within the entire subtree (including current node)
+{
+ assert(GetNumSubNodes() == 1);
+
+ SmNode *pNode = GetSubNode(0);
+ assert(pNode);
+
+ RectHorAlign eHorAlign = RectHorAlign::Center;
+ switch (GetToken().eType)
+ {
+ case TALIGNL: eHorAlign = RectHorAlign::Left; break;
+ case TALIGNC: eHorAlign = RectHorAlign::Center; break;
+ case TALIGNR: eHorAlign = RectHorAlign::Right; break;
+ default:
+ break;
+ }
+ SetRectHorAlign(eHorAlign);
+
+ pNode->Arrange(rDev, rFormat);
+
+ SmRect::operator = (pNode->GetRect());
+}
+
+
+/**************************************************************************/
+
+
+void SmAttributNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pAttr = Attribute(),
+ *pBody = Body();
+ assert(pBody);
+ assert(pAttr);
+
+ pBody->Arrange(rDev, rFormat);
+
+ if (GetScaleMode() == SmScaleMode::Width)
+ pAttr->AdaptToX(rDev, pBody->GetItalicWidth());
+ pAttr->Arrange(rDev, rFormat);
+
+ // get relative position of attribute
+ RectVerAlign eVerAlign;
+ long nDist = 0;
+ switch (GetToken().eType)
+ { case TUNDERLINE :
+ eVerAlign = RectVerAlign::AttributeLo;
+ break;
+ case TOVERSTRIKE :
+ eVerAlign = RectVerAlign::AttributeMid;
+ break;
+ default :
+ eVerAlign = RectVerAlign::AttributeHi;
+ if (pBody->GetType() == SmNodeType::Attribut)
+ nDist = GetFont().GetFontSize().Height()
+ * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100;
+ }
+ Point aPos = pAttr->AlignTo(*pBody, RectPos::Attribute, RectHorAlign::Center, eVerAlign);
+ aPos.AdjustY( -nDist );
+ pAttr->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pAttr, RectCopyMBL::This, true);
+}
+
+void SmFontNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ rText.append("{");
+ sal_Int32 nc,r,g,b;
+
+ switch (GetToken().eType)
+ {
+ case TBOLD:
+ rText.append("bold ");
+ break;
+ case TNBOLD:
+ rText.append("nbold ");
+ break;
+ case TITALIC:
+ rText.append("italic ");
+ break;
+ case TNITALIC:
+ rText.append("nitalic ");
+ break;
+ case TPHANTOM:
+ rText.append("phantom ");
+ break;
+ case TSIZE:
+ {
+ rText.append("size ");
+ switch (meSizeType)
+ {
+ case FontSizeType::PLUS:
+ rText.append("+");
+ break;
+ case FontSizeType::MINUS:
+ rText.append("-");
+ break;
+ case FontSizeType::MULTIPLY:
+ rText.append("*");
+ break;
+ case FontSizeType::DIVIDE:
+ rText.append("/");
+ break;
+ case FontSizeType::ABSOLUT:
+ default:
+ break;
+ }
+ rText.append(::rtl::math::doubleToUString(
+ static_cast<double>(maFontSize),
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true));
+ rText.append(" ");
+ }
+ break;
+ case TBLACK:
+ rText.append("color black ");
+ break;
+ case TWHITE:
+ rText.append("color white ");
+ break;
+ case TRED:
+ rText.append("color red ");
+ break;
+ case TGREEN:
+ rText.append("color green ");
+ break;
+ case TBLUE:
+ rText.append("color blue ");
+ break;
+ case TCYAN:
+ rText.append("color cyan ");
+ break;
+ case TMAGENTA:
+ rText.append("color magenta ");
+ break;
+ case TYELLOW:
+ rText.append("color yellow ");
+ break;
+ case TTEAL:
+ rText.append("color teal ");
+ break;
+ case TSILVER:
+ rText.append("color silver ");
+ break;
+ case TGRAY:
+ rText.append("color gray ");
+ break;
+ case TMAROON:
+ rText.append("color maroon ");
+ break;
+ case TPURPLE:
+ rText.append("color purple ");
+ break;
+ case TLIME:
+ rText.append("color lime ");
+ break;
+ case TOLIVE:
+ rText.append("color olive ");
+ break;
+ case TNAVY:
+ rText.append("color navy ");
+ break;
+ case TAQUA:
+ rText.append("color aqua ");
+ break;
+ case TFUCHSIA:
+ rText.append("color fuchsia ");
+ break;
+ case TRGB:
+ rText.append("color rgb ");
+ nc = GetToken().aText.toInt32();
+ b = nc % 256;
+ nc /= 256;
+ g = nc % 256;
+ nc /= 256;
+ r = nc % 256;
+ rText.append(r);
+ rText.append(" ");
+ rText.append(g);
+ rText.append(" ");
+ rText.append(b);
+ rText.append(" ");
+ break;
+ case TSANS:
+ rText.append("font sans ");
+ break;
+ case TSERIF:
+ rText.append("font serif ");
+ break;
+ case TFIXED:
+ rText.append("font fixed ");
+ break;
+ default:
+ break;
+ }
+ if (GetNumSubNodes() > 1)
+ GetSubNode(1)->CreateTextFromNode(rText);
+
+ rText.stripEnd(' ');
+ rText.append("} ");
+}
+
+void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ //! prepare subnodes first
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ int nFnt = -1;
+ switch (GetToken().eType)
+ {
+ case TFIXED: nFnt = FNT_FIXED; break;
+ case TSANS: nFnt = FNT_SANS; break;
+ case TSERIF: nFnt = FNT_SERIF; break;
+ default:
+ break;
+ }
+ if (nFnt != -1)
+ { GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) );
+ SetFont(GetFont());
+ }
+
+ //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
+ //! other font nodes (those with lower depth in the tree)
+ Flags() |= FontChangeMask::Face;
+}
+
+void SmFontNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pNode = GetSubNode(1);
+ assert(pNode);
+ sal_Int32 nc;
+ Color col_perso_rgb_color (0);
+
+ switch (GetToken().eType)
+ { case TSIZE :
+ pNode->SetFontSize(maFontSize, meSizeType);
+ break;
+ case TSANS :
+ case TSERIF :
+ case TFIXED :
+ pNode->SetFont(GetFont());
+ break;
+ case TUNKNOWN : break; // no assertion on "font <?> <?>"
+
+ case TPHANTOM : SetPhantom(true); break;
+ case TBOLD : SetAttribut(FontAttribute::Bold); break;
+ case TITALIC : SetAttribut(FontAttribute::Italic); break;
+ case TNBOLD : ClearAttribut(FontAttribute::Bold); break;
+ case TNITALIC : ClearAttribut(FontAttribute::Italic); break;
+
+ case TBLACK : SetColor(COL_BLACK); break;
+ case TWHITE : SetColor(COL_WHITE); break;
+ case TRED : SetColor(COL_LIGHTRED); break;
+ case TGREEN : SetColor(COL_GREEN); break;
+ case TBLUE : SetColor(COL_LIGHTBLUE); break;
+ case TCYAN : SetColor(COL_LIGHTCYAN); break; // as in Calc
+ case TMAGENTA : SetColor(COL_LIGHTMAGENTA); break; // as in Calc
+ case TYELLOW : SetColor(COL_YELLOW); break;
+ case TTEAL : SetColor(COL_CYAN); break;
+ case TSILVER : SetColor(COL_LIGHTGRAY); break;
+ case TGRAY : SetColor(COL_GRAY); break;
+ case TMAROON : SetColor(COL_RED); break;
+ case TPURPLE : SetColor(COL_MAGENTA); break;
+ case TLIME : SetColor(COL_LIGHTGREEN); break;
+ case TOLIVE : SetColor(COL_BROWN); break;
+ case TNAVY : SetColor(COL_BLUE); break;
+ case TAQUA : SetColor(COL_LIGHTCYAN); break;
+ case TFUCHSIA : SetColor(COL_LIGHTMAGENTA); break;
+ case TRGB :
+ nc = GetToken().aText.toInt32();
+ col_perso_rgb_color.SetBlue(nc % 256);
+ nc /= 256;
+ col_perso_rgb_color.SetGreen(nc % 256);
+ nc /= 256;
+ col_perso_rgb_color.SetRed(nc % 256);
+ SetColor(col_perso_rgb_color);
+ break;
+
+ default:
+ SAL_WARN("starmath", "unknown case");
+ }
+
+ pNode->Arrange(rDev, rFormat);
+
+ SmRect::operator = (pNode->GetRect());
+}
+
+
+void SmFontNode::SetSizeParameter(const Fraction& rValue, FontSizeType eType)
+{
+ meSizeType = eType;
+ maFontSize = rValue;
+}
+
+
+/**************************************************************************/
+
+
+SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken)
+ : SmGraphicNode(SmNodeType::PolyLine, rNodeToken)
+ , maPoly(2)
+ , maToSize()
+ , mnWidth(0)
+{
+}
+
+
+void SmPolyLineNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nNewWidth)
+{
+ maToSize.setWidth( nNewWidth );
+}
+
+
+void SmPolyLineNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nNewHeight)
+{
+ GetFont().FreezeBorderWidth();
+ maToSize.setHeight( nNewHeight );
+}
+
+
+void SmPolyLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ //! some routines being called extract some info from the OutputDevice's
+ //! font (eg the space to be used for borders OR the font name(!!)).
+ //! Thus the font should reflect the needs and has to be set!
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ long nBorderwidth = GetFont().GetBorderWidth();
+
+ // create polygon using both endpoints
+ assert(maPoly.GetSize() == 2);
+ Point aPointA, aPointB;
+ if (GetToken().eType == TWIDESLASH)
+ {
+ aPointA.setX( nBorderwidth );
+ aPointA.setY( maToSize.Height() - nBorderwidth );
+ aPointB.setX( maToSize.Width() - nBorderwidth );
+ aPointB.setY( nBorderwidth );
+ }
+ else
+ {
+ OSL_ENSURE(GetToken().eType == TWIDEBACKSLASH, "Sm : unexpected token");
+ aPointA.setX( nBorderwidth );
+ aPointA.setY( nBorderwidth );
+ aPointB.setX( maToSize.Width() - nBorderwidth );
+ aPointB.setY( maToSize.Height() - nBorderwidth );
+ }
+ maPoly.SetPoint(aPointA, 0);
+ maPoly.SetPoint(aPointB, 1);
+
+ long nThick = GetFont().GetFontSize().Height()
+ * rFormat.GetDistance(DIS_STROKEWIDTH) / 100;
+ mnWidth = nThick + 2 * nBorderwidth;
+
+ SmRect::operator = (SmRect(maToSize.Width(), maToSize.Height()));
+}
+
+
+/**************************************************************************/
+
+void SmRootSymbolNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
+{
+ mnBodyWidth = nWidth;
+}
+
+
+void SmRootSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
+{
+ // some additional length so that the horizontal
+ // bar will be positioned above the argument
+ SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10);
+}
+
+
+/**************************************************************************/
+
+
+void SmRectangleNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
+{
+ maToSize.setWidth( nWidth );
+}
+
+
+void SmRectangleNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nHeight)
+{
+ GetFont().FreezeBorderWidth();
+ maToSize.setHeight( nHeight );
+}
+
+
+void SmRectangleNode::Arrange(OutputDevice &rDev, const SmFormat &/*rFormat*/)
+{
+ long nFontHeight = GetFont().GetFontSize().Height();
+ long nWidth = maToSize.Width(),
+ nHeight = maToSize.Height();
+ if (nHeight == 0)
+ nHeight = nFontHeight / 30;
+ if (nWidth == 0)
+ nWidth = nFontHeight / 3;
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // add some borderspace
+ sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth();
+ nHeight += 2 * nTmpBorderWidth;
+
+ //! use this method in order to have 'SmRect::HasAlignInfo() == true'
+ //! and thus having the attribute-fences updated in 'SmRect::ExtendBy'
+ SmRect::operator = (SmRect(nWidth, nHeight));
+}
+
+
+/**************************************************************************/
+
+
+SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP )
+ : SmVisibleNode(eNodeType, rNodeToken)
+ , mnFontDesc(nFontDescP)
+ , mnSelectionStart(0)
+ , mnSelectionEnd(0)
+{
+}
+
+SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP )
+ : SmVisibleNode(SmNodeType::Text, rNodeToken)
+ , mnFontDesc(nFontDescP)
+ , mnSelectionStart(0)
+ , mnSelectionEnd(0)
+{
+}
+
+void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ // default setting for horizontal alignment of nodes with TTEXT
+ // content is as alignl (cannot be done in Arrange since it would
+ // override the settings made by an SmAlignNode before)
+ if (TTEXT == GetToken().eType)
+ SetRectHorAlign( RectHorAlign::Left );
+
+ maText = GetToken().aText;
+ GetFont() = rFormat.GetFont(GetFontDesc());
+
+ if (IsItalic( GetFont() ))
+ Attributes() |= FontAttribute::Italic;
+ if (IsBold( GetFont() ))
+ Attributes() |= FontAttribute::Bold;
+
+ // special handling for ':' where it is a token on its own and is likely
+ // to be used for mathematical notations. (E.g. a:b = 2:3)
+ // In that case it should not be displayed in italic.
+ if (GetToken().aText.getLength() == 1 && GetToken().aText[0] == ':')
+ Attributes() &= ~FontAttribute::Italic;
+};
+
+
+void SmTextNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ sal_uInt16 nSizeDesc = GetFontDesc() == FNT_FUNCTION ?
+ SIZ_FUNCTION : SIZ_TEXT;
+ GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, maText, GetFont().GetBorderWidth()));
+}
+
+void SmTextNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ bool bQuoted=false;
+ if (GetToken().eType == TTEXT)
+ {
+ rText.append("\"");
+ bQuoted=true;
+ }
+ else
+ {
+ SmParser aParseTest;
+ auto pTable = aParseTest.Parse(GetToken().aText);
+ assert(pTable->GetType() == SmNodeType::Table);
+ bQuoted=true;
+ if (pTable->GetNumSubNodes() == 1)
+ {
+ SmNode *pResult = pTable->GetSubNode(0);
+ if ( (pResult->GetType() == SmNodeType::Line) &&
+ (pResult->GetNumSubNodes() == 1) )
+ {
+ pResult = pResult->GetSubNode(0);
+ if (pResult->GetType() == SmNodeType::Text)
+ bQuoted=false;
+ }
+ }
+
+ if ((GetToken().eType == TIDENT) && (GetFontDesc() == FNT_FUNCTION))
+ {
+ //Search for existing functions and remove extraneous keyword
+ rText.append("func ");
+ }
+ else if (bQuoted)
+ rText.append("italic ");
+
+ if (bQuoted)
+ rText.append("\"");
+
+ }
+
+ rText.append(GetToken().aText);
+
+ if (bQuoted)
+ rText.append("\"");
+ rText.append(" ");
+}
+
+void SmTextNode::GetAccessibleText( OUStringBuffer &rText ) const
+{
+ rText.append(maText);
+}
+
+void SmTextNode::AdjustFontDesc()
+{
+ if (GetToken().eType == TTEXT)
+ mnFontDesc = FNT_TEXT;
+ else if(GetToken().eType == TFUNC)
+ mnFontDesc = FNT_FUNCTION;
+ else {
+ SmTokenType nTok;
+ const SmTokenTableEntry *pEntry = SmParser::GetTokenTableEntry( maText );
+ if (pEntry && pEntry->nGroup == TG::Function) {
+ nTok = pEntry->eType;
+ mnFontDesc = FNT_FUNCTION;
+ } else {
+ sal_Unicode firstChar = maText[0];
+ if( ('0' <= firstChar && firstChar <= '9') || firstChar == '.' || firstChar == ',') {
+ mnFontDesc = FNT_NUMBER;
+ nTok = TNUMBER;
+ } else if (maText.getLength() > 1) {
+ mnFontDesc = FNT_VARIABLE;
+ nTok = TIDENT;
+ } else {
+ mnFontDesc = FNT_VARIABLE;
+ nTok = TCHARACTER;
+ }
+ }
+ SmToken tok = GetToken();
+ tok.eType = nTok;
+ SetToken(tok);
+ }
+}
+
+sal_Unicode SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn)
+{
+ //Find the best match in accepted unicode for our private area symbols
+ static const sal_Unicode aStarMathPrivateToUnicode[] =
+ {
+ 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
+ 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
+ 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
+ 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
+ 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
+ 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
+ 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+ 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
+ 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
+ 0xE0DA, 0x2190, 0x2191, 0x2193
+ };
+ if ((nIn >= 0xE080) && (nIn <= 0xE0DD))
+ nIn = aStarMathPrivateToUnicode[nIn-0xE080];
+
+ //For whatever unicode glyph that equation editor doesn't ship with that
+ //we have a possible match we can munge it to.
+ switch (nIn)
+ {
+ case 0x2223:
+ nIn = '|';
+ break;
+ default:
+ break;
+ }
+
+ return nIn;
+}
+
+/**************************************************************************/
+
+void SmMatrixNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ rText.append("matrix {");
+ for (size_t i = 0; i < mnNumRows; ++i)
+ {
+ for (size_t j = 0; j < mnNumCols; ++j)
+ {
+ SmNode *pNode = GetSubNode(i * mnNumCols + j);
+ if (pNode)
+ pNode->CreateTextFromNode(rText);
+ if (j != mnNumCols - 1U)
+ rText.append("# ");
+ }
+ if (i != mnNumRows - 1U)
+ rText.append("## ");
+ }
+ rText.stripEnd(' ');
+ rText.append("} ");
+}
+
+void SmMatrixNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pNode;
+
+ // initialize array that is to hold the maximum widths of all
+ // elements (subnodes) in that column.
+ std::vector<long> aColWidth(mnNumCols);
+
+ // arrange subnodes and calculate the above arrays contents
+ size_t nNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNodes; ++i)
+ {
+ size_t nIdx = nNodes - 1 - i;
+ if (nullptr != (pNode = GetSubNode(nIdx)))
+ {
+ pNode->Arrange(rDev, rFormat);
+ int nCol = nIdx % mnNumCols;
+ aColWidth[nCol] = std::max(aColWidth[nCol], pNode->GetItalicWidth());
+ }
+ }
+
+ // norm distance from which the following two are calculated
+ const long nNormDist = 3 * GetFont().GetFontSize().Height();
+
+ // define horizontal and vertical minimal distances that separate
+ // the elements
+ long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100,
+ nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100;
+
+ // build array that holds the leftmost position for each column
+ std::vector<long> aColLeft(mnNumCols);
+ long nX = 0;
+ for (size_t j = 0; j < mnNumCols; ++j)
+ {
+ aColLeft[j] = nX;
+ nX += aColWidth[j] + nHorDist;
+ }
+
+ SmRect::operator = (SmRect());
+ for (size_t i = 0; i < mnNumRows; ++i)
+ {
+ Point aPos;
+ SmRect aLineRect;
+ for (size_t j = 0; j < mnNumCols; ++j)
+ {
+ SmNode *pTmpNode = GetSubNode(i * mnNumCols + j);
+ assert(pTmpNode);
+
+ const SmRect &rNodeRect = pTmpNode->GetRect();
+
+ // align all baselines in that row if possible
+ aPos = rNodeRect.AlignTo(aLineRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+
+ // get horizontal alignment
+ const SmNode *pCoNode = pTmpNode->GetLeftMost();
+ RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
+
+ // calculate horizontal position of element depending on column
+ // and horizontal alignment
+ switch (eHorAlign)
+ { case RectHorAlign::Left:
+ aPos.setX( aColLeft[j] );
+ break;
+ case RectHorAlign::Center:
+ aPos.setX( rNodeRect.GetLeft() + aColLeft[j]
+ + aColWidth[j] / 2
+ - rNodeRect.GetItalicCenterX() );
+ break;
+ case RectHorAlign::Right:
+ aPos.setX( aColLeft[j]
+ + aColWidth[j] - rNodeRect.GetItalicWidth() );
+ break;
+ default:
+ assert(false);
+ }
+
+ pTmpNode->MoveTo(aPos);
+ aLineRect.ExtendBy(rNodeRect, RectCopyMBL::Xor);
+ }
+
+ aPos = aLineRect.AlignTo(*this, RectPos::Bottom, RectHorAlign::Center, RectVerAlign::Baseline);
+ if (i > 0)
+ aPos.AdjustY(nVerDist );
+
+ // move 'aLineRect' and rectangles in that line to final position
+ Point aDelta(0, // since horizontal alignment is already done
+ aPos.Y() - aLineRect.GetTop());
+ aLineRect.Move(aDelta);
+ for (size_t j = 0; j < mnNumCols; ++j)
+ {
+ if (nullptr != (pNode = GetSubNode(i * mnNumCols + j)))
+ pNode->Move(aDelta);
+ }
+
+ ExtendBy(aLineRect, RectCopyMBL::None);
+ }
+}
+
+
+void SmMatrixNode::SetRowCol(sal_uInt16 nMatrixRows, sal_uInt16 nMatrixCols)
+{
+ mnNumRows = nMatrixRows;
+ mnNumCols = nMatrixCols;
+}
+
+
+const SmNode * SmMatrixNode::GetLeftMost() const
+{
+ return this;
+}
+
+
+/**************************************************************************/
+
+
+SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken)
+: SmSpecialNode(SmNodeType::Math, rNodeToken, FNT_MATH)
+{
+ sal_Unicode cChar = GetToken().cMathChar;
+ if (u'\0' != cChar)
+ SetText(OUString(cChar));
+}
+
+void SmMathSymbolNode::AdaptToX(OutputDevice &rDev, sal_uLong nWidth)
+{
+ // Since there is no function to do this, we try to approximate it:
+ Size aFntSize (GetFont().GetFontSize());
+
+ //! however the result is a bit better with 'nWidth' as initial font width
+ aFntSize.setWidth( nWidth );
+ GetFont().SetSize(aFntSize);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // get denominator of error factor for width
+ long nTmpBorderWidth = GetFont().GetBorderWidth();
+ long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetItalicWidth();
+
+ // scale fontwidth with this error factor
+ aFntSize.setWidth( aFntSize.Width() * nWidth );
+ aFntSize.setWidth( aFntSize.Width() / ( nDenom ? nDenom : 1) );
+
+ GetFont().SetSize(aFntSize);
+}
+
+void SmMathSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
+{
+ GetFont().FreezeBorderWidth();
+ Size aFntSize (GetFont().GetFontSize());
+
+ // Since we only want to scale the height, we might have
+ // to determine the font width in order to keep it
+ if (aFntSize.Width() == 0)
+ {
+ rDev.Push(PushFlags::FONT | PushFlags::MAPMODE);
+ rDev.SetFont(GetFont());
+ aFntSize.setWidth( rDev.GetFontMetric().GetFontSize().Width() );
+ rDev.Pop();
+ }
+ OSL_ENSURE(aFntSize.Width() != 0, "Sm: ");
+
+ //! however the result is a bit better with 'nHeight' as initial
+ //! font height
+ aFntSize.setHeight( nHeight );
+ GetFont().SetSize(aFntSize);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // get denominator of error factor for height
+ long nTmpBorderWidth = GetFont().GetBorderWidth();
+ long nDenom = 0;
+ if (!GetText().isEmpty())
+ nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetHeight();
+
+ // scale fontwidth with this error factor
+ aFntSize.setHeight( aFntSize.Height() * nHeight );
+ aFntSize.setHeight( aFntSize.Height() / ( nDenom ? nDenom : 1) );
+
+ GetFont().SetSize(aFntSize);
+}
+
+
+void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ GetFont() = rFormat.GetFont(GetFontDesc());
+ // use same font size as is used for variables
+ GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
+
+ OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL ||
+ GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
+ "wrong charset for character from StarMath/OpenSymbol font");
+
+ Flags() |= FontChangeMask::Face | FontChangeMask::Italic;
+};
+
+
+void SmMathSymbolNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ const OUString &rText = GetText();
+
+ if (rText.isEmpty() || rText[0] == '\0')
+ { SmRect::operator = (SmRect());
+ return;
+ }
+
+ PrepareAttributes();
+
+ GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
+}
+
+void SmMathSymbolNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ sal_Unicode cChar = GetToken().cMathChar;
+ if (cChar == MS_INT && GetScaleMode() == SmScaleMode::Height)
+ rText.append("intd ");
+ else
+ MathType::LookupChar(cChar, rText, 3);
+}
+
+void SmRectangleNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ switch (GetToken().eType)
+ {
+ case TUNDERLINE:
+ rText.append("underline ");
+ break;
+ case TOVERLINE:
+ rText.append("overline ");
+ break;
+ case TOVERSTRIKE:
+ rText.append("overstrike ");
+ break;
+ default:
+ break;
+ }
+}
+
+void SmAttributNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ SmNode *pNode;
+ assert(GetNumSubNodes() == 2);
+ rText.append("{");
+ sal_Unicode nLast=0;
+ if (nullptr != (pNode = Attribute()))
+ {
+ OUStringBuffer aStr;
+ pNode->CreateTextFromNode(aStr);
+ if (aStr.getLength() > 1)
+ rText.append(aStr);
+ else
+ {
+ nLast = aStr[0];
+ switch (nLast)
+ {
+ case MS_BAR: // MACRON
+ rText.append("overline ");
+ break;
+ case MS_DOT: // DOT ABOVE
+ rText.append("dot ");
+ break;
+ case 0x2dc: // SMALL TILDE
+ rText.append("widetilde ");
+ break;
+ case MS_DDOT: // DIAERESIS
+ rText.append("ddot ");
+ break;
+ case 0xE082:
+ break;
+ case 0xE09B:
+ case MS_DDDOT: // COMBINING THREE DOTS ABOVE
+ rText.append("dddot ");
+ break;
+ case MS_ACUTE: // ACUTE ACCENT
+ case MS_COMBACUTE: // COMBINING ACUTE ACCENT
+ rText.append("acute ");
+ break;
+ case MS_GRAVE: // GRAVE ACCENT
+ case MS_COMBGRAVE: // COMBINING GRAVE ACCENT
+ rText.append("grave ");
+ break;
+ case MS_CHECK: // CARON
+ case MS_COMBCHECK: // COMBINING CARON
+ rText.append("check ");
+ break;
+ case MS_BREVE: // BREVE
+ case MS_COMBBREVE: // COMBINING BREVE
+ rText.append("breve ");
+ break;
+ case MS_CIRCLE: // RING ABOVE
+ case MS_COMBCIRCLE: // COMBINING RING ABOVE
+ rText.append("circle ");
+ break;
+ case MS_RIGHTARROW: // RIGHTWARDS ARROW
+ case MS_VEC: // COMBINING RIGHT ARROW ABOVE
+ rText.append("vec ");
+ break;
+ case MS_HARPOON: // COMBINING RIGHT HARPOON ABOVE
+ rText.append("harpoon ");
+ break;
+ case MS_TILDE: // TILDE
+ case MS_COMBTILDE: // COMBINING TILDE
+ rText.append("tilde ");
+ break;
+ case MS_HAT: // CIRCUMFLEX ACCENT
+ case MS_COMBHAT: // COMBINING CIRCUMFLEX ACCENT
+ rText.append("hat ");
+ break;
+ case MS_COMBBAR: // COMBINING MACRON
+ rText.append("bar ");
+ break;
+ default:
+ rText.append(OUStringChar(nLast));
+ break;
+ }
+ }
+ }
+
+ if (nullptr != (pNode = Body()))
+ pNode->CreateTextFromNode(rText);
+
+ rText.stripEnd(' ');
+
+ if (nLast == 0xE082)
+ rText.append(" overbrace {}");
+
+ rText.append("} ");
+}
+
+/**************************************************************************/
+
+static bool lcl_IsFromGreekSymbolSet( const OUString &rTokenText )
+{
+ bool bRes = false;
+
+ // valid symbol name needs to have a '%' at pos 0 and at least an additional char
+ if (rTokenText.getLength() > 2 && rTokenText[0] == u'%')
+ {
+ OUString aName( rTokenText.copy(1) );
+ SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName( aName );
+ if (pSymbol && SmLocalizedSymbolData::GetExportSymbolSetName(pSymbol->GetSymbolSetName()) == "Greek")
+ bRes = true;
+ }
+
+ return bRes;
+}
+
+
+SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc)
+ : SmTextNode(eNodeType, rNodeToken, _nFontDesc)
+ , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
+{
+}
+
+
+SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken)
+ : SmTextNode(SmNodeType::Special, rNodeToken, FNT_MATH) // default Font isn't always correct!
+ , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
+{
+}
+
+
+void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ const SmSym *pSym;
+ SmModule *pp = SM_MOD();
+
+ OUString aName(GetToken().aText.copy(1));
+ if (nullptr != (pSym = pp->GetSymbolManager().GetSymbolByName( aName )))
+ {
+ sal_UCS4 cChar = pSym->GetCharacter();
+ OUString aTmp( &cChar, 1 );
+ SetText( aTmp );
+ GetFont() = pSym->GetFace();
+ }
+ else
+ {
+ SetText( GetToken().aText );
+ GetFont() = rFormat.GetFont(FNT_VARIABLE);
+ }
+ // use same font size as is used for variables
+ GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
+
+ // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
+ // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
+ // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
+
+ //! see also SmFontStyles::GetStyleName
+ if (IsItalic( GetFont() ))
+ SetAttribut(FontAttribute::Italic);
+ if (IsBold( GetFont() ))
+ SetAttribut(FontAttribute::Bold);
+
+ Flags() |= FontChangeMask::Face;
+
+ if (!mbIsFromGreekSymbolSet)
+ return;
+
+ OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
+ bool bItalic = false;
+ sal_Int16 nStyle = rFormat.GetGreekCharStyle();
+ OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
+ if (nStyle == 1)
+ bItalic = true;
+ else if (nStyle == 2)
+ {
+ const OUString& rTmp(GetText());
+ if (!rTmp.isEmpty())
+ {
+ static const sal_Unicode cUppercaseAlpha = 0x0391;
+ static const sal_Unicode cUppercaseOmega = 0x03A9;
+ sal_Unicode cChar = rTmp[0];
+ // uppercase letters should be straight and lowercase letters italic
+ bItalic = cUppercaseAlpha > cChar || cChar > cUppercaseOmega;
+ }
+ }
+
+ if (bItalic)
+ Attributes() |= FontAttribute::Italic;
+ else
+ Attributes() &= ~FontAttribute::Italic;
+};
+
+
+void SmSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
+}
+
+/**************************************************************************/
+
+
+void SmGlyphSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(),
+ GetFont().GetBorderWidth()).AsGlyphRect());
+}
+
+
+/**************************************************************************/
+
+
+void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ GetFont().SetColor(COL_GRAY);
+ Flags() |= FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Italic;
+};
+
+
+void SmPlaceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
+}
+
+
+/**************************************************************************/
+
+
+void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ GetFont().SetColor(COL_RED);
+ Flags() |= FontChangeMask::Phantom | FontChangeMask::Bold | FontChangeMask::Italic
+ | FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Size;
+}
+
+
+void SmErrorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ const OUString &rText = GetText();
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
+}
+
+/**************************************************************************/
+
+void SmBlankNode::IncreaseBy(const SmToken &rToken, sal_uInt32 nMultiplyBy)
+{
+ switch(rToken.eType)
+ {
+ case TBLANK: mnNum += (4 * nMultiplyBy); break;
+ case TSBLANK: mnNum += (1 * nMultiplyBy); break;
+ default:
+ break;
+ }
+}
+
+void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ // Here it need/should not be the StarMath font, so that for the character
+ // used in Arrange a normal (non-clipped) rectangle is generated
+ GetFont() = rFormat.GetFont(FNT_VARIABLE);
+
+ Flags() |= FontChangeMask::Face | FontChangeMask::Bold | FontChangeMask::Italic;
+}
+
+
+void SmBlankNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // make distance depend on the font height
+ // (so that it increases when scaling (e.g. size *2 {a ~ b})
+ long nDist = GetFont().GetFontSize().Height() / 10,
+ nSpace = mnNum * nDist;
+
+ // get a SmRect with Baseline and all the bells and whistles
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString(' '),
+ GetFont().GetBorderWidth()));
+
+ // and resize it to the requested size
+ SetItalicSpaces(0, 0);
+ SetWidth(nSpace);
+}
+
+void SmBlankNode::CreateTextFromNode(OUStringBuffer &rText)
+{
+ if (mnNum <= 0)
+ return;
+ sal_uInt16 nWide = mnNum / 4;
+ sal_uInt16 nNarrow = mnNum % 4;
+ for (sal_uInt16 i = 0; i < nWide; i++)
+ rText.append("~");
+ for (sal_uInt16 i = 0; i < nNarrow; i++)
+ rText.append("`");
+ rText.append(" ");
+}
+
+
+/**************************************************************************/
+//Implementation of all accept methods for SmVisitor
+
+void SmTableNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBraceNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmOperNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmAlignNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmAttributNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmFontNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmUnHorNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBinHorNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBinVerNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmSubSupNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmMatrixNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmPlaceNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmTextNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmSpecialNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBlankNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmErrorNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmLineNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmExpressionNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmRootNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmRectangleNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/ooxmlexport.cxx b/starmath/source/ooxmlexport.cxx
new file mode 100644
index 000000000..fff9cd7c5
--- /dev/null
+++ b/starmath/source/ooxmlexport.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/.
+ */
+
+#include "ooxmlexport.hxx"
+#include <node.hxx>
+
+#include <oox/token/tokens.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <oox/mathml/export.hxx>
+
+using namespace oox;
+using namespace oox::core;
+
+SmOoxmlExport::SmOoxmlExport(const SmNode *const pIn, OoxmlVersion const v,
+ drawingml::DocumentType const documentType)
+: SmWordExportBase( pIn )
+, version( v )
+, m_DocumentType(documentType)
+{
+}
+
+void SmOoxmlExport::ConvertFromStarMath( const ::sax_fastparser::FSHelperPtr& serializer, const sal_Int8 nAlign )
+{
+ if( m_pTree == nullptr )
+ return;
+ m_pSerializer = serializer;
+
+ //Formula alignment situations:
+ //
+ // 1)Inline(as before):
+ //
+ // <m:oMath>
+ // <m:r> ... </m:r>
+ // </m:oMath>
+ //
+ // 2)Aligned:
+ //
+ // <m:oMathPara>
+ // <m:oMathParaPr>
+ // <m:jc m:val="left|right|center">
+ // </m:oMathParaPr>
+ // <m:oMath>
+ // <m:r> ... </m:r>
+ // </m:oMath>
+ // </m:oMathPara>
+
+ if (nAlign != FormulaExportBase::eFormulaAlign::INLINE)
+ {
+ m_pSerializer->startElementNS(XML_m, XML_oMathPara,
+ FSNS(XML_xmlns, XML_m), "http://schemas.openxmlformats.org/officeDocument/2006/math");
+ m_pSerializer->startElementNS(XML_m, XML_oMathParaPr);
+ if (nAlign == FormulaExportBase::eFormulaAlign::CENTER)
+ m_pSerializer->singleElementNS(XML_m, XML_jc, FSNS(XML_m, XML_val), "center");
+ if (nAlign == FormulaExportBase::eFormulaAlign::GROUPEDCENTER)
+ m_pSerializer->singleElementNS(XML_m, XML_jc, FSNS(XML_m, XML_val), "center");
+ if (nAlign == FormulaExportBase::eFormulaAlign::LEFT)
+ m_pSerializer->singleElementNS(XML_m, XML_jc, FSNS(XML_m, XML_val), "left");
+ if (nAlign == FormulaExportBase::eFormulaAlign::RIGHT)
+ m_pSerializer->singleElementNS(XML_m, XML_jc, FSNS(XML_m, XML_val), "right");
+ m_pSerializer->endElementNS(XML_m, XML_oMathParaPr);
+ m_pSerializer->startElementNS(XML_m, XML_oMath);
+ HandleNode(m_pTree, 0);
+ m_pSerializer->endElementNS(XML_m, XML_oMath);
+ m_pSerializer->endElementNS(XML_m, XML_oMathPara);
+ }
+ else //else, inline as was before
+ {
+ m_pSerializer->startElementNS(XML_m, XML_oMath,
+ FSNS(XML_xmlns, XML_m), "http://schemas.openxmlformats.org/officeDocument/2006/math");
+ HandleNode( m_pTree, 0 );
+ m_pSerializer->endElementNS( XML_m, XML_oMath );
+ }
+}
+
+// NOTE: This is still work in progress and unfinished, but it already covers a good
+// part of the ooxml math stuff.
+
+void SmOoxmlExport::HandleVerticalStack( const SmNode* pNode, int nLevel )
+{
+ m_pSerializer->startElementNS(XML_m, XML_eqArr);
+ int size = pNode->GetNumSubNodes();
+ for( int i = 0;
+ i < size;
+ ++i )
+ {
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->GetSubNode( i ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ }
+ m_pSerializer->endElementNS( XML_m, XML_eqArr );
+}
+
+void SmOoxmlExport::HandleText( const SmNode* pNode, int /*nLevel*/)
+{
+ m_pSerializer->startElementNS(XML_m, XML_r);
+
+ if( pNode->GetToken().eType == TTEXT ) // literal text (in quotes)
+ {
+ m_pSerializer->startElementNS(XML_m, XML_rPr);
+ m_pSerializer->singleElementNS(XML_m, XML_lit);
+ m_pSerializer->singleElementNS(XML_m, XML_nor);
+ m_pSerializer->endElementNS( XML_m, XML_rPr );
+ }
+ if (drawingml::DOCUMENT_DOCX == m_DocumentType && ECMA_DIALECT == version)
+ { // HACK: MSOffice2007 does not import characters properly unless this font is explicitly given
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+ m_pSerializer->singleElementNS( XML_w, XML_rFonts, FSNS( XML_w, XML_ascii ), "Cambria Math",
+ FSNS( XML_w, XML_hAnsi ), "Cambria Math" );
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+ }
+ m_pSerializer->startElementNS(XML_m, XML_t, FSNS(XML_xml, XML_space), "preserve");
+ const SmTextNode* pTemp = static_cast<const SmTextNode* >(pNode);
+ SAL_INFO( "starmath.ooxml", "Text:" << pTemp->GetText());
+ OUStringBuffer buf(pTemp->GetText());
+ for(sal_Int32 i=0;i<pTemp->GetText().getLength();i++)
+ {
+#if 0
+ if ((nPendingAttributes) &&
+ (i == ((pTemp->GetText().getLength()+1)/2)-1))
+ {
+ *pS << sal_uInt8(0x22); //char, with attributes right
+ //after the character
+ }
+ else
+ *pS << sal_uInt8(CHAR);
+
+ sal_uInt8 nFace = 0x1;
+ if (pNode->GetFont().GetItalic() == ITALIC_NORMAL)
+ nFace = 0x3;
+ else if (pNode->GetFont().GetWeight() == WEIGHT_BOLD)
+ nFace = 0x7;
+ *pS << sal_uInt8(nFace+128); //typeface
+#endif
+ buf[i] = SmTextNode::ConvertSymbolToUnicode(buf[i]);
+#if 0
+ //Mathtype can only have these sort of character
+ //attributes on a single character, starmath can put them
+ //anywhere, when the entity involved is a text run this is
+ //a large effort to place the character attribute on the
+ //central mathtype character so that it does pretty much
+ //what the user probably has in mind. The attributes
+ //filled in here are dummy ones which are replaced in the
+ //ATTRIBUT handler if a suitable location for the
+ //attributes was found here. Unfortunately it is
+ //possible for starmath to place character attributes on
+ //entities which cannot occur in mathtype e.g. a Summation
+ //symbol so these attributes may be lost
+ if ((nPendingAttributes) &&
+ (i == ((pTemp->GetText().getLength()+1)/2)-1))
+ {
+ *pS << sal_uInt8(EMBEL);
+ while (nPendingAttributes)
+ {
+ *pS << sal_uInt8(2);
+ //wedge the attributes in here and clear
+ //the pending stack
+ nPendingAttributes--;
+ }
+ nInsertion=pS->Tell();
+ *pS << sal_uInt8(END); //end embel
+ *pS << sal_uInt8(END); //end embel
+ }
+#endif
+ }
+ m_pSerializer->writeEscaped(buf.makeStringAndClear());
+ m_pSerializer->endElementNS( XML_m, XML_t );
+ m_pSerializer->endElementNS( XML_m, XML_r );
+}
+
+void SmOoxmlExport::HandleFractions( const SmNode* pNode, int nLevel, const char* type )
+{
+ m_pSerializer->startElementNS(XML_m, XML_f);
+ if( type != nullptr )
+ {
+ m_pSerializer->startElementNS(XML_m, XML_fPr);
+ m_pSerializer->singleElementNS(XML_m, XML_type, FSNS(XML_m, XML_val), type);
+ m_pSerializer->endElementNS( XML_m, XML_fPr );
+ }
+ assert( pNode->GetNumSubNodes() == 3 );
+ m_pSerializer->startElementNS(XML_m, XML_num);
+ HandleNode( pNode->GetSubNode( 0 ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_num );
+ m_pSerializer->startElementNS(XML_m, XML_den);
+ HandleNode( pNode->GetSubNode( 2 ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_den );
+ m_pSerializer->endElementNS( XML_m, XML_f );
+}
+
+void SmOoxmlExport::HandleAttribute( const SmAttributNode* pNode, int nLevel )
+{
+ switch( pNode->Attribute()->GetToken().eType )
+ {
+ case TCHECK:
+ case TACUTE:
+ case TGRAVE:
+ case TBREVE:
+ case TCIRCLE:
+ case TVEC:
+ case TTILDE:
+ case THAT:
+ case TDOT:
+ case TDDOT:
+ case TDDDOT:
+ case TWIDETILDE:
+ case TWIDEHAT:
+ case TWIDEHARPOON:
+ case TWIDEVEC:
+ case TBAR:
+ {
+ m_pSerializer->startElementNS(XML_m, XML_acc);
+ m_pSerializer->startElementNS(XML_m, XML_accPr);
+ OString value = OUStringToOString(
+ OUString( pNode->Attribute()->GetToken().cMathChar ), RTL_TEXTENCODING_UTF8 );
+ m_pSerializer->singleElementNS(XML_m, XML_chr, FSNS(XML_m, XML_val), value);
+ m_pSerializer->endElementNS( XML_m, XML_accPr );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->Body(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_acc );
+ break;
+ }
+ case TOVERLINE:
+ case TUNDERLINE:
+ m_pSerializer->startElementNS(XML_m, XML_bar);
+ m_pSerializer->startElementNS(XML_m, XML_barPr);
+ m_pSerializer->singleElementNS( XML_m, XML_pos, FSNS( XML_m, XML_val ),
+ ( pNode->Attribute()->GetToken().eType == TUNDERLINE ) ? "bot" : "top" );
+ m_pSerializer->endElementNS( XML_m, XML_barPr );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->Body(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_bar );
+ break;
+ case TOVERSTRIKE:
+ m_pSerializer->startElementNS(XML_m, XML_borderBox);
+ m_pSerializer->startElementNS(XML_m, XML_borderBoxPr);
+ m_pSerializer->singleElementNS(XML_m, XML_hideTop, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->singleElementNS(XML_m, XML_hideBot, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->singleElementNS(XML_m, XML_hideLeft, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->singleElementNS(XML_m, XML_hideRight, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->singleElementNS(XML_m, XML_strikeH, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->endElementNS( XML_m, XML_borderBoxPr );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->Body(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_borderBox );
+ break;
+ default:
+ HandleAllSubNodes( pNode, nLevel );
+ break;
+ }
+}
+
+void SmOoxmlExport::HandleRoot( const SmRootNode* pNode, int nLevel )
+{
+ m_pSerializer->startElementNS(XML_m, XML_rad);
+ if( const SmNode* argument = pNode->Argument())
+ {
+ m_pSerializer->startElementNS(XML_m, XML_deg);
+ HandleNode( argument, nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_deg );
+ }
+ else
+ {
+ m_pSerializer->startElementNS(XML_m, XML_radPr);
+ m_pSerializer->singleElementNS(XML_m, XML_degHide, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->endElementNS( XML_m, XML_radPr );
+ m_pSerializer->singleElementNS(XML_m, XML_deg); // empty but present
+ }
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->Body(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_rad );
+}
+
+static OString mathSymbolToString( const SmNode* node )
+{
+ assert( node->GetType() == SmNodeType::Math || node->GetType() == SmNodeType::MathIdent );
+ const SmTextNode* txtnode = static_cast< const SmTextNode* >( node );
+ assert( txtnode->GetText().getLength() == 1 );
+ sal_Unicode chr = SmTextNode::ConvertSymbolToUnicode( txtnode->GetText()[0] );
+ return OUStringToOString( OUString( chr ), RTL_TEXTENCODING_UTF8 );
+}
+
+void SmOoxmlExport::HandleOperator( const SmOperNode* pNode, int nLevel )
+{
+ SAL_INFO( "starmath.ooxml", "Operator: " << int( pNode->GetToken().eType ));
+ switch( pNode->GetToken().eType )
+ {
+ case TINT:
+ case TINTD:
+ case TIINT:
+ case TIIINT:
+ case TLINT:
+ case TLLINT:
+ case TLLLINT:
+ case TPROD:
+ case TCOPROD:
+ case TSUM:
+ {
+ const SmSubSupNode* subsup = pNode->GetSubNode( 0 )->GetType() == SmNodeType::SubSup
+ ? static_cast< const SmSubSupNode* >( pNode->GetSubNode( 0 )) : nullptr;
+ const SmNode* operation = subsup != nullptr ? subsup->GetBody() : pNode->GetSubNode( 0 );
+ m_pSerializer->startElementNS(XML_m, XML_nary);
+ m_pSerializer->startElementNS(XML_m, XML_naryPr);
+ m_pSerializer->singleElementNS( XML_m, XML_chr,
+ FSNS( XML_m, XML_val ), mathSymbolToString(operation) );
+ if( subsup == nullptr || subsup->GetSubSup( CSUB ) == nullptr )
+ m_pSerializer->singleElementNS(XML_m, XML_subHide, FSNS(XML_m, XML_val), "1");
+ if( subsup == nullptr || subsup->GetSubSup( CSUP ) == nullptr )
+ m_pSerializer->singleElementNS(XML_m, XML_supHide, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->endElementNS( XML_m, XML_naryPr );
+ if( subsup == nullptr || subsup->GetSubSup( CSUB ) == nullptr )
+ m_pSerializer->singleElementNS(XML_m, XML_sub);
+ else
+ {
+ m_pSerializer->startElementNS(XML_m, XML_sub);
+ HandleNode( subsup->GetSubSup( CSUB ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sub );
+ }
+ if( subsup == nullptr || subsup->GetSubSup( CSUP ) == nullptr )
+ m_pSerializer->singleElementNS(XML_m, XML_sup);
+ else
+ {
+ m_pSerializer->startElementNS(XML_m, XML_sup);
+ HandleNode( subsup->GetSubSup( CSUP ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sup );
+ }
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->GetSubNode( 1 ), nLevel + 1 ); // body
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_nary );
+ break;
+ }
+ case TLIM:
+ m_pSerializer->startElementNS(XML_m, XML_func);
+ m_pSerializer->startElementNS(XML_m, XML_fName);
+ m_pSerializer->startElementNS(XML_m, XML_limLow);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->GetSymbol(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_lim);
+ if( const SmSubSupNode* subsup = pNode->GetSubNode( 0 )->GetType() == SmNodeType::SubSup
+ ? static_cast< const SmSubSupNode* >( pNode->GetSubNode( 0 )) : nullptr )
+ {
+ if( subsup->GetSubSup( CSUB ) != nullptr )
+ HandleNode( subsup->GetSubSup( CSUB ), nLevel + 1 );
+ }
+ m_pSerializer->endElementNS( XML_m, XML_lim );
+ m_pSerializer->endElementNS( XML_m, XML_limLow );
+ m_pSerializer->endElementNS( XML_m, XML_fName );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->GetSubNode( 1 ), nLevel + 1 ); // body
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_func );
+ break;
+ default:
+ SAL_WARN("starmath.ooxml", "Unhandled operation");
+ HandleAllSubNodes( pNode, nLevel );
+ break;
+ }
+}
+
+void SmOoxmlExport::HandleSubSupScriptInternal( const SmSubSupNode* pNode, int nLevel, int flags )
+{
+// docx supports only a certain combination of sub/super scripts, but LO can have any,
+// so try to merge it using several tags if necessary
+ if( flags == 0 ) // none
+ return;
+ if(( flags & ( 1 << RSUP | 1 << RSUB )) == ( 1 << RSUP | 1 << RSUB ))
+ { // m:sSubSup
+ m_pSerializer->startElementNS(XML_m, XML_sSubSup);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << RSUP | 1 << RSUB );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_sub);
+ HandleNode( pNode->GetSubSup( RSUB ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sub );
+ m_pSerializer->startElementNS(XML_m, XML_sup);
+ HandleNode( pNode->GetSubSup( RSUP ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sup );
+ m_pSerializer->endElementNS( XML_m, XML_sSubSup );
+ }
+ else if(( flags & ( 1 << RSUB )) == 1 << RSUB )
+ { // m:sSub
+ m_pSerializer->startElementNS(XML_m, XML_sSub);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << RSUB );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_sub);
+ HandleNode( pNode->GetSubSup( RSUB ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sub );
+ m_pSerializer->endElementNS( XML_m, XML_sSub );
+ }
+ else if(( flags & ( 1 << RSUP )) == 1 << RSUP )
+ { // m:sSup
+ m_pSerializer->startElementNS(XML_m, XML_sSup);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << RSUP );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_sup);
+ HandleNode( pNode->GetSubSup( RSUP ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sup );
+ m_pSerializer->endElementNS( XML_m, XML_sSup );
+ }
+ else if(( flags & ( 1 << LSUP | 1 << LSUB )) == ( 1 << LSUP | 1 << LSUB ))
+ { // m:sPre
+ m_pSerializer->startElementNS(XML_m, XML_sPre);
+ m_pSerializer->startElementNS(XML_m, XML_sub);
+ HandleNode( pNode->GetSubSup( LSUB ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sub );
+ m_pSerializer->startElementNS(XML_m, XML_sup);
+ HandleNode( pNode->GetSubSup( LSUP ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sup );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << LSUP | 1 << LSUB );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_sPre );
+ }
+ else if(( flags & ( 1 << CSUB )) == ( 1 << CSUB ))
+ { // m:limLow looks like a good element for central superscript
+ m_pSerializer->startElementNS(XML_m, XML_limLow);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << CSUB );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_lim);
+ HandleNode( pNode->GetSubSup( CSUB ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_lim );
+ m_pSerializer->endElementNS( XML_m, XML_limLow );
+ }
+ else if(( flags & ( 1 << CSUP )) == ( 1 << CSUP ))
+ { // m:limUpp looks like a good element for central superscript
+ m_pSerializer->startElementNS(XML_m, XML_limUpp);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << CSUP );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_lim);
+ HandleNode( pNode->GetSubSup( CSUP ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_lim );
+ m_pSerializer->endElementNS( XML_m, XML_limUpp );
+ }
+ else
+ {
+ SAL_WARN("starmath.ooxml", "Unhandled sub/sup combination");
+ // TODO do not do anything, this should be probably an assert()
+ // HandleAllSubNodes( pNode, nLevel );
+ }
+}
+
+void SmOoxmlExport::HandleMatrix( const SmMatrixNode* pNode, int nLevel )
+{
+ m_pSerializer->startElementNS(XML_m, XML_m);
+ for (size_t row = 0; row < pNode->GetNumRows(); ++row)
+ {
+ m_pSerializer->startElementNS(XML_m, XML_mr);
+ for (size_t col = 0; col < pNode->GetNumCols(); ++col)
+ {
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ if( const SmNode* node = pNode->GetSubNode( row * pNode->GetNumCols() + col ))
+ HandleNode( node, nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ }
+ m_pSerializer->endElementNS( XML_m, XML_mr );
+ }
+ m_pSerializer->endElementNS( XML_m, XML_m );
+}
+
+void SmOoxmlExport::HandleBrace( const SmBraceNode* pNode, int nLevel )
+{
+ m_pSerializer->startElementNS(XML_m, XML_d);
+ m_pSerializer->startElementNS(XML_m, XML_dPr);
+
+ //check if the node has an opening brace
+ if( TNONE == pNode->OpeningBrace()->GetToken().eType )
+ m_pSerializer->singleElementNS(XML_m, XML_begChr, FSNS(XML_m, XML_val), "");
+ else
+ m_pSerializer->singleElementNS( XML_m, XML_begChr,
+ FSNS( XML_m, XML_val ), mathSymbolToString( pNode->OpeningBrace()) );
+
+ std::vector< const SmNode* > subnodes;
+ if( pNode->Body()->GetType() == SmNodeType::Bracebody )
+ {
+ const SmBracebodyNode* body = static_cast< const SmBracebodyNode* >( pNode->Body());
+ bool separatorWritten = false; // assume all separators are the same
+ for (size_t i = 0; i < body->GetNumSubNodes(); ++i)
+ {
+ const SmNode* subnode = body->GetSubNode( i );
+ if (subnode->GetType() == SmNodeType::Math || subnode->GetType() == SmNodeType::MathIdent)
+ { // do not write, but write what separator it is
+ const SmMathSymbolNode* math = static_cast< const SmMathSymbolNode* >( subnode );
+ if( !separatorWritten )
+ {
+ m_pSerializer->singleElementNS( XML_m, XML_sepChr,
+ FSNS( XML_m, XML_val ), mathSymbolToString(math) );
+ separatorWritten = true;
+ }
+ }
+ else
+ subnodes.push_back( subnode );
+ }
+ }
+ else
+ subnodes.push_back( pNode->Body());
+
+ if( TNONE == pNode->ClosingBrace()->GetToken().eType )
+ m_pSerializer->singleElementNS(XML_m, XML_endChr, FSNS(XML_m, XML_val), "");
+ else
+ m_pSerializer->singleElementNS( XML_m, XML_endChr,
+ FSNS( XML_m, XML_val ), mathSymbolToString(pNode->ClosingBrace()) );
+
+ m_pSerializer->endElementNS( XML_m, XML_dPr );
+ for(const SmNode* subnode : subnodes)
+ {
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( subnode, nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ }
+ m_pSerializer->endElementNS( XML_m, XML_d );
+}
+
+void SmOoxmlExport::HandleVerticalBrace( const SmVerticalBraceNode* pNode, int nLevel )
+{
+ SAL_INFO( "starmath.ooxml", "Vertical: " << int( pNode->GetToken().eType ));
+ switch( pNode->GetToken().eType )
+ {
+ case TOVERBRACE:
+ case TUNDERBRACE:
+ {
+ bool top = ( pNode->GetToken().eType == TOVERBRACE );
+ m_pSerializer->startElementNS(XML_m, top ? XML_limUpp : XML_limLow);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ m_pSerializer->startElementNS(XML_m, XML_groupChr);
+ m_pSerializer->startElementNS(XML_m, XML_groupChrPr);
+ m_pSerializer->singleElementNS( XML_m, XML_chr,
+ FSNS( XML_m, XML_val ), mathSymbolToString(pNode->Brace()) );
+ // TODO not sure if pos and vertJc are correct
+ m_pSerializer->singleElementNS( XML_m, XML_pos,
+ FSNS( XML_m, XML_val ), top ? "top" : "bot" );
+ m_pSerializer->singleElementNS(XML_m, XML_vertJc, FSNS(XML_m, XML_val),
+ top ? "bot" : "top");
+ m_pSerializer->endElementNS( XML_m, XML_groupChrPr );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->Body(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_groupChr );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_lim);
+ HandleNode( pNode->Script(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_lim );
+ m_pSerializer->endElementNS( XML_m, top ? XML_limUpp : XML_limLow );
+ break;
+ }
+ default:
+ SAL_WARN("starmath.ooxml", "Unhandled vertical brace");
+ HandleAllSubNodes( pNode, nLevel );
+ break;
+ }
+}
+
+void SmOoxmlExport::HandleBlank()
+{
+ m_pSerializer->startElementNS(XML_m, XML_r);
+ m_pSerializer->startElementNS(XML_m, XML_t, FSNS(XML_xml, XML_space), "preserve");
+ m_pSerializer->write( " " );
+ m_pSerializer->endElementNS( XML_m, XML_t );
+ m_pSerializer->endElementNS( XML_m, XML_r );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/ooxmlexport.hxx b/starmath/source/ooxmlexport.hxx
new file mode 100644
index 000000000..fec33ab8e
--- /dev/null
+++ b/starmath/source/ooxmlexport.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_OOXMLEXPORT_HXX
+#define INCLUDED_STARMATH_SOURCE_OOXMLEXPORT_HXX
+
+#include "wordexportbase.hxx"
+
+#include <sax/fshelper.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/export/utils.hxx>
+
+/**
+ Class implementing writing of formulas to OOXML.
+ */
+class SmOoxmlExport : public SmWordExportBase
+{
+public:
+ SmOoxmlExport(const SmNode* pIn, oox::core::OoxmlVersion version,
+ oox::drawingml::DocumentType documentType);
+ void ConvertFromStarMath( const ::sax_fastparser::FSHelperPtr& m_pSerializer, const sal_Int8 );
+private:
+ void HandleVerticalStack( const SmNode* pNode, int nLevel ) override;
+ void HandleText( const SmNode* pNode, int nLevel ) override;
+ void HandleFractions( const SmNode* pNode, int nLevel, const char* type ) override;
+ void HandleRoot( const SmRootNode* pNode, int nLevel ) override;
+ void HandleAttribute( const SmAttributNode* pNode, int nLevel ) override;
+ void HandleOperator( const SmOperNode* pNode, int nLevel ) override;
+ void HandleSubSupScriptInternal( const SmSubSupNode* pNode, int nLevel, int flags ) override;
+ void HandleMatrix( const SmMatrixNode* pNode, int nLevel ) override;
+ void HandleBrace( const SmBraceNode* pNode, int nLevel ) override;
+ void HandleVerticalBrace( const SmVerticalBraceNode* pNode, int nLevel ) override;
+ void HandleBlank() override;
+ ::sax_fastparser::FSHelperPtr m_pSerializer;
+ oox::core::OoxmlVersion version;
+ /// needed to determine markup for nested run properties
+ oox::drawingml::DocumentType const m_DocumentType;
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/ooxmlimport.cxx b/starmath/source/ooxmlimport.cxx
new file mode 100644
index 000000000..034919787
--- /dev/null
+++ b/starmath/source/ooxmlimport.cxx
@@ -0,0 +1,681 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "ooxmlimport.hxx"
+#include <types.hxx>
+
+#include <oox/mathml/importutils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+using namespace oox::formulaimport;
+
+/*
+The primary internal data structure for the formula is the text representation
+(the SmNode tree is built from it), so read data must be converted into this format.
+*/
+
+#define OPENING( token ) XML_STREAM_OPENING( token )
+#define CLOSING( token ) XML_STREAM_CLOSING( token )
+
+// TODO create IS_OPENING(), IS_CLOSING() instead of doing 'next == OPENING( next )' ?
+
+SmOoxmlImport::SmOoxmlImport( oox::formulaimport::XmlStream& s )
+ : m_rStream( s )
+{
+}
+
+OUString SmOoxmlImport::ConvertToStarMath()
+{
+ return handleStream();
+}
+
+// "toplevel" of reading, there will be oMath (if there was oMathPara, that was
+// up to the parent component to handle)
+
+// NOT complete
+OUString SmOoxmlImport::handleStream()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( oMath ));
+ OUStringBuffer ret;
+ while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( M_TOKEN( oMath )))
+ {
+ // strictly speaking, it is not OMathArg here, but currently supported
+ // functionality is the same like OMathArg, in the future this may need improving
+ OUString item = readOMathArg( M_TOKEN( oMath ));
+ if( item.isEmpty())
+ continue;
+ if( !ret.isEmpty())
+ ret.append(" ");
+ ret.append(item);
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( oMath ));
+ // Placeholders are written out as nothing (i.e. nothing inside e.g. the <e> element),
+ // which will result in "{}" in the formula text. Fix this up.
+ OUString ret2 = ret.makeStringAndClear().replaceAll( "{}", "<?>" );
+ // And as a result, empty parts of the formula that are not placeholders are written out
+ // as a single space, so fix that up too.
+ ret2 = ret2.replaceAll( "{ }", "{}" );
+ SAL_INFO( "starmath.ooxml", "Formula: " << ret2 );
+ return ret2;
+}
+
+OUString SmOoxmlImport::readOMathArg( int stoptoken )
+{
+ OUStringBuffer ret;
+ while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( stoptoken ))
+ {
+ if( !ret.isEmpty())
+ ret.append(" ");
+ switch( m_rStream.currentToken())
+ {
+ case OPENING( M_TOKEN( acc )):
+ ret.append(handleAcc());
+ break;
+ case OPENING( M_TOKEN( bar )):
+ ret.append(handleBar());
+ break;
+ case OPENING( M_TOKEN( box )):
+ ret.append(handleBox());
+ break;
+ case OPENING( M_TOKEN( borderBox )):
+ ret.append(handleBorderBox());
+ break;
+ case OPENING( M_TOKEN( d )):
+ ret.append(handleD());
+ break;
+ case OPENING( M_TOKEN( eqArr )):
+ ret.append(handleEqArr());
+ break;
+ case OPENING( M_TOKEN( f )):
+ ret.append(handleF());
+ break;
+ case OPENING( M_TOKEN( func )):
+ ret.append(handleFunc());
+ break;
+ case OPENING( M_TOKEN( limLow )):
+ ret.append(handleLimLowUpp( LimLow ));
+ break;
+ case OPENING( M_TOKEN( limUpp )):
+ ret.append(handleLimLowUpp( LimUpp ));
+ break;
+ case OPENING( M_TOKEN( groupChr )):
+ ret.append(handleGroupChr());
+ break;
+ case OPENING( M_TOKEN( m )):
+ ret.append(handleM());
+ break;
+ case OPENING( M_TOKEN( nary )):
+ ret.append(handleNary());
+ break;
+ case OPENING( M_TOKEN( r )):
+ ret.append(handleR());
+ break;
+ case OPENING( M_TOKEN( rad )):
+ ret.append(handleRad());
+ break;
+ case OPENING( M_TOKEN( sPre )):
+ ret.append(handleSpre());
+ break;
+ case OPENING( M_TOKEN( sSub )):
+ ret.append(handleSsub());
+ break;
+ case OPENING( M_TOKEN( sSubSup )):
+ ret.append(handleSsubsup());
+ break;
+ case OPENING( M_TOKEN( sSup )):
+ ret.append(handleSsup());
+ break;
+ default:
+ m_rStream.handleUnexpectedTag();
+ break;
+ }
+ }
+ return ret.makeStringAndClear();
+}
+
+OUString SmOoxmlImport::readOMathArgInElement( int token )
+{
+ m_rStream.ensureOpeningTag( token );
+ OUString ret = readOMathArg( token );
+ m_rStream.ensureClosingTag( token );
+ return ret;
+}
+
+OUString SmOoxmlImport::handleAcc()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( acc ));
+ sal_Unicode accChr = 0x302;
+ if( XmlStream::Tag accPr = m_rStream.checkOpeningTag( M_TOKEN( accPr )))
+ {
+ if( XmlStream::Tag chr = m_rStream.checkOpeningTag( M_TOKEN( chr )))
+ {
+ accChr = chr.attribute( M_TOKEN( val ), accChr );
+ m_rStream.ensureClosingTag( M_TOKEN( chr ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( accPr ));
+ }
+ // see aTokenTable in parse.cxx
+ OUString acc;
+ switch( accChr )
+ {
+ case MS_BAR:
+ case MS_COMBBAR:
+ acc = "bar";
+ break;
+ case MS_CHECK:
+ case MS_COMBCHECK:
+ acc = "check";
+ break;
+ case MS_ACUTE:
+ case MS_COMBACUTE:
+ acc = "acute";
+ break;
+ case MS_COMBOVERLINE:
+ acc = "overline";
+ break;
+ case MS_GRAVE:
+ case MS_COMBGRAVE:
+ acc = "grave";
+ break;
+ case MS_BREVE:
+ case MS_COMBBREVE:
+ acc = "breve";
+ break;
+ case MS_CIRCLE:
+ case MS_COMBCIRCLE:
+ acc = "circle";
+ break;
+ case MS_RIGHTARROW:
+ case MS_VEC:
+ // prefer wide variants for these 3, .docx can't seem to differentiate
+ // between e.g. 'vec' and 'widevec', if whatever the accent is above is short, this
+ // shouldn't matter, but short above a longer expression doesn't look right
+ acc = "widevec";
+ break;
+ case MS_HARPOON:
+ acc = "wideharpoon";
+ break;
+ case MS_TILDE:
+ case MS_COMBTILDE:
+ acc = "widetilde";
+ break;
+ case MS_HAT:
+ case MS_COMBHAT:
+ acc = "widehat";
+ break;
+ case MS_DOT:
+ case MS_COMBDOT:
+ acc = "dot";
+ break;
+ case MS_DDOT:
+ case MS_COMBDDOT:
+ acc = "ddot";
+ break;
+ case MS_DDDOT:
+ acc = "dddot";
+ break;
+ default:
+ acc = "acute";
+ SAL_WARN( "starmath.ooxml", "Unknown m:chr in m:acc \'" << OUString(accChr) << "\'" );
+ break;
+ }
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( acc ));
+ return acc + " {" + e + "}";
+}
+
+OUString SmOoxmlImport::handleBar()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( bar ));
+ enum pos_t { top, bot } topbot = bot;
+ if( m_rStream.checkOpeningTag( M_TOKEN( barPr )))
+ {
+ if( XmlStream::Tag pos = m_rStream.checkOpeningTag( M_TOKEN( pos )))
+ {
+ if( pos.attribute( M_TOKEN( val )) == "top" )
+ topbot = top;
+ else if( pos.attribute( M_TOKEN( val )) == "bot" )
+ topbot = bot;
+ m_rStream.ensureClosingTag( M_TOKEN( pos ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( barPr ));
+ }
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( bar ));
+ if( topbot == top )
+ return "overline {" + e + "}";
+ else
+ return "underline {" + e + "}";
+}
+
+OUString SmOoxmlImport::handleBox()
+{
+ // there does not seem to be functionality in LO to actually implement this
+ // (or is there), but at least read in the contents instead of ignoring them
+ m_rStream.ensureOpeningTag( M_TOKEN( box ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( box ));
+ return e;
+}
+
+
+OUString SmOoxmlImport::handleBorderBox()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( borderBox ));
+ bool isStrikeH = false;
+ if( m_rStream.checkOpeningTag( M_TOKEN( borderBoxPr )))
+ {
+ if( XmlStream::Tag strikeH = m_rStream.checkOpeningTag( M_TOKEN( strikeH )))
+ {
+ if( strikeH.attribute( M_TOKEN( val ), false ))
+ isStrikeH = true;
+ m_rStream.ensureClosingTag( M_TOKEN( strikeH ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( borderBoxPr ));
+ }
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( borderBox ));
+ if( isStrikeH )
+ return "overstrike {" + e + "}";
+ // LO does not seem to implement anything for handling the other cases
+ return e;
+}
+
+OUString SmOoxmlImport::handleD()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( d ));
+ OUString opening = "(";
+ OUString closing = ")";
+ OUString separator = "|";
+ if( XmlStream::Tag dPr = m_rStream.checkOpeningTag( M_TOKEN( dPr )))
+ {
+ if( XmlStream::Tag begChr = m_rStream.checkOpeningTag( M_TOKEN( begChr )))
+ {
+ opening = begChr.attribute( M_TOKEN( val ), opening );
+ m_rStream.ensureClosingTag( M_TOKEN( begChr ));
+ }
+ if( XmlStream::Tag sepChr = m_rStream.checkOpeningTag( M_TOKEN( sepChr )))
+ {
+ separator = sepChr.attribute( M_TOKEN( val ), separator );
+ m_rStream.ensureClosingTag( M_TOKEN( sepChr ));
+ }
+ if( XmlStream::Tag endChr = m_rStream.checkOpeningTag( M_TOKEN( endChr )))
+ {
+ closing = endChr.attribute( M_TOKEN( val ), closing );
+ m_rStream.ensureClosingTag( M_TOKEN( endChr ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( dPr ));
+ }
+ if( opening == "{" )
+ opening = "left lbrace ";
+ if( closing == "}" )
+ closing = " right rbrace";
+ if( opening == u"\u27e6" )
+ opening = "left ldbracket ";
+ if( closing == u"\u27e7" )
+ closing = " right rdbracket";
+ if( opening == "|" )
+ opening = "left lline ";
+ if( closing == "|" )
+ closing = " right rline";
+ if (opening == OUStringChar(MS_DLINE)
+ || opening == OUStringChar(MS_DVERTLINE))
+ opening = "left ldline ";
+ if (closing == OUStringChar(MS_DLINE)
+ || closing == OUStringChar(MS_DVERTLINE))
+ closing = " right rdline";
+ if (opening == OUStringChar(MS_LANGLE)
+ || opening == OUStringChar(MS_LMATHANGLE))
+ opening = "left langle ";
+ if (closing == OUStringChar(MS_RANGLE)
+ || closing == OUStringChar(MS_RMATHANGLE))
+ closing = " right rangle";
+ // use scalable brackets (the explicit "left" or "right")
+ if( opening == "(" || opening == "[" )
+ opening = "left " + opening;
+ if( closing == ")" || closing == "]" )
+ closing = " right " + closing;
+ if( separator == "|" ) // plain "|" would be actually "V" (logical or)
+ separator = " mline ";
+ if( opening.isEmpty())
+ opening = "left none ";
+ if( closing.isEmpty())
+ closing = " right none";
+ OUStringBuffer ret;
+ ret.append( opening );
+ bool first = true;
+ while( m_rStream.findTag( OPENING( M_TOKEN( e ))))
+ {
+ if( !first )
+ ret.append( separator );
+ first = false;
+ ret.append( readOMathArgInElement( M_TOKEN( e )));
+ }
+ ret.append( closing );
+ m_rStream.ensureClosingTag( M_TOKEN( d ));
+ return ret.makeStringAndClear();
+}
+
+OUString SmOoxmlImport::handleEqArr()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( eqArr ));
+ OUStringBuffer ret;
+ do
+ { // there must be at least one m:e
+ if( !ret.isEmpty())
+ ret.append("#");
+ ret.append(" ");
+ ret.append(readOMathArgInElement( M_TOKEN( e )));
+ ret.append(" ");
+ } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( e ))));
+ m_rStream.ensureClosingTag( M_TOKEN( eqArr ));
+ return "stack {" + ret.makeStringAndClear() + "}";
+}
+
+OUString SmOoxmlImport::handleF()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( f ));
+ enum operation_t { bar, lin, noBar } operation = bar;
+ if( m_rStream.checkOpeningTag( M_TOKEN( fPr )))
+ {
+ if( XmlStream::Tag type = m_rStream.checkOpeningTag( M_TOKEN( type )))
+ {
+ if( type.attribute( M_TOKEN( val )) == "bar" )
+ operation = bar;
+ else if( type.attribute( M_TOKEN( val )) == "lin" )
+ operation = lin;
+ else if( type.attribute( M_TOKEN( val )) == "noBar" )
+ operation = noBar;
+ m_rStream.ensureClosingTag( M_TOKEN( type ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( fPr ));
+ }
+ OUString num = readOMathArgInElement( M_TOKEN( num ));
+ OUString den = readOMathArgInElement( M_TOKEN( den ));
+ m_rStream.ensureClosingTag( M_TOKEN( f ));
+ if( operation == bar )
+ return "{" + num + "} over {" + den + "}";
+ else if( operation == lin )
+ return "{" + num + "} / {" + den + "}";
+ else // noBar
+ {
+ return "binom {" + num + "} {" + den + "}";
+ }
+}
+
+OUString SmOoxmlImport::handleFunc()
+{
+//lim from{x rightarrow 1} x
+ m_rStream.ensureOpeningTag( M_TOKEN( func ));
+ OUString fname = readOMathArgInElement( M_TOKEN( fName ));
+ // fix the various functions
+ if( fname.startsWith( "lim csub {" ))
+ fname = "lim from {" + fname.copy( 10 );
+ OUString ret = fname + " {" + readOMathArgInElement( M_TOKEN( e )) + "}";
+ m_rStream.ensureClosingTag( M_TOKEN( func ));
+ return ret;
+}
+
+OUString SmOoxmlImport::handleLimLowUpp( LimLowUpp_t limlowupp )
+{
+ int token = limlowupp == LimLow ? M_TOKEN( limLow ) : M_TOKEN( limUpp );
+ m_rStream.ensureOpeningTag( token );
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ OUString lim = readOMathArgInElement( M_TOKEN( lim ));
+ m_rStream.ensureClosingTag( token );
+ // fix up overbrace/underbrace (use { }, as {} will be converted to a placeholder)
+ if( limlowupp == LimUpp && e.endsWith( " overbrace { }" ))
+ return e.copy( 0, e.getLength() - 2 ) + lim + "}";
+ if( limlowupp == LimLow && e.endsWith( " underbrace { }" ))
+ return e.copy( 0, e.getLength() - 2 ) + lim + "}";
+ return e
+ + ( limlowupp == LimLow ? OUStringLiteral( " csub {" ) : OUStringLiteral( " csup {" ))
+ + lim + "}";
+}
+
+OUString SmOoxmlImport::handleGroupChr()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( groupChr ));
+ sal_Unicode chr = 0x23df;
+ enum pos_t { top, bot } pos = bot;
+ if( m_rStream.checkOpeningTag( M_TOKEN( groupChrPr )))
+ {
+ if( XmlStream::Tag chrTag = m_rStream.checkOpeningTag( M_TOKEN( chr )))
+ {
+ chr = chrTag.attribute( M_TOKEN( val ), chr );
+ m_rStream.ensureClosingTag( M_TOKEN( chr ));
+ }
+ if( XmlStream::Tag posTag = m_rStream.checkOpeningTag( M_TOKEN( pos )))
+ {
+ if( posTag.attribute( M_TOKEN( val ), OUString( "bot" )) == "top" )
+ pos = top;
+ m_rStream.ensureClosingTag( M_TOKEN( pos ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( groupChrPr ));
+ }
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( groupChr ));
+ if( pos == top && chr == u'\x23de')
+ return "{" + e + "} overbrace { }";
+ if( pos == bot && chr == u'\x23df')
+ return "{" + e + "} underbrace { }";
+ if( pos == top )
+ return "{" + e + "} csup {" + OUStringChar( chr ) + "}";
+ else
+ return "{" + e + "} csub {" + OUStringChar( chr ) + "}";
+}
+
+OUString SmOoxmlImport::handleM()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( m ));
+ OUStringBuffer allrows;
+ do // there must be at least one m:mr
+ {
+ m_rStream.ensureOpeningTag( M_TOKEN( mr ));
+ OUStringBuffer row;
+ do // there must be at least one m:e
+ {
+ if( !row.isEmpty())
+ row.append(" # ");
+ row.append(readOMathArgInElement( M_TOKEN( e )));
+ } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( e ))));
+ if( !allrows.isEmpty())
+ allrows.append(" ## ");
+ allrows.append(row);
+ m_rStream.ensureClosingTag( M_TOKEN( mr ));
+ } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( mr ))));
+ m_rStream.ensureClosingTag( M_TOKEN( m ));
+ return "matrix {" + allrows.makeStringAndClear() + "}";
+}
+
+OUString SmOoxmlImport::handleNary()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( nary ));
+ sal_Unicode chr = 0x222b;
+ bool subHide = false;
+ bool supHide = false;
+ if( m_rStream.checkOpeningTag( M_TOKEN( naryPr )))
+ {
+ if( XmlStream::Tag chrTag = m_rStream.checkOpeningTag( M_TOKEN( chr )))
+ {
+ chr = chrTag.attribute( M_TOKEN( val ), chr );
+ m_rStream.ensureClosingTag( M_TOKEN( chr ));
+ }
+ if( XmlStream::Tag subHideTag = m_rStream.checkOpeningTag( M_TOKEN( subHide )))
+ {
+ subHide = subHideTag.attribute( M_TOKEN( val ), subHide );
+ m_rStream.ensureClosingTag( M_TOKEN( subHide ));
+ }
+ if( XmlStream::Tag supHideTag = m_rStream.checkOpeningTag( M_TOKEN( supHide )))
+ {
+ supHide = supHideTag.attribute( M_TOKEN( val ), supHide );
+ m_rStream.ensureClosingTag( M_TOKEN( supHide ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( naryPr ));
+ }
+ OUString sub = readOMathArgInElement( M_TOKEN( sub ));
+ OUString sup = readOMathArgInElement( M_TOKEN( sup ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ OUString ret;
+ switch( chr )
+ {
+ case MS_INT:
+ ret = "int";
+ break;
+ case MS_IINT:
+ ret = "iint";
+ break;
+ case MS_IIINT:
+ ret = "iiint";
+ break;
+ case MS_LINT:
+ ret = "lint";
+ break;
+ case MS_LLINT:
+ ret = "llint";
+ break;
+ case MS_LLLINT:
+ ret = "lllint";
+ break;
+ case MS_PROD:
+ ret = "prod";
+ break;
+ case MS_COPROD:
+ ret = "coprod";
+ break;
+ case MS_SUM:
+ ret = "sum";
+ break;
+ default:
+ SAL_WARN( "starmath.ooxml", "Unknown m:nary chr \'" << OUString(chr) << "\'" );
+ break;
+ }
+ if( !subHide )
+ ret += " from {" + sub + "}";
+ if( !supHide )
+ ret += " to {" + sup + "}";
+ ret += " {" + e + "}";
+ m_rStream.ensureClosingTag( M_TOKEN( nary ));
+ return ret;
+}
+
+// NOT complete
+OUString SmOoxmlImport::handleR()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( r ));
+ bool normal = false;
+ bool literal = false;
+ if( XmlStream::Tag rPr = m_rStream.checkOpeningTag( M_TOKEN( rPr )))
+ {
+ if( XmlStream::Tag litTag = m_rStream.checkOpeningTag( M_TOKEN( lit )))
+ {
+ literal = litTag.attribute( M_TOKEN( val ), true );
+ m_rStream.ensureClosingTag( M_TOKEN( lit ));
+ }
+ if( XmlStream::Tag norTag = m_rStream.checkOpeningTag( M_TOKEN( nor )))
+ {
+ normal = norTag.attribute( M_TOKEN( val ), true );
+ m_rStream.ensureClosingTag( M_TOKEN( nor ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( rPr ));
+ }
+ OUStringBuffer text;
+ while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( m_rStream.currentToken()))
+ {
+ switch( m_rStream.currentToken())
+ {
+ case OPENING( M_TOKEN( t )):
+ {
+ XmlStream::Tag rtag = m_rStream.ensureOpeningTag( M_TOKEN( t ));
+ if( rtag.attribute( OOX_TOKEN( xml, space )) != "preserve" )
+ text.append(rtag.text.trim());
+ else
+ text.append(rtag.text);
+ m_rStream.ensureClosingTag( M_TOKEN( t ));
+ break;
+ }
+ default:
+ m_rStream.handleUnexpectedTag();
+ break;
+ }
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( r ));
+ if( normal || literal )
+ {
+ text.insert(0, "\"");
+ text.append("\"");
+ }
+ return text.makeStringAndClear().replaceAll("{", "\\{").replaceAll("}", "\\}");
+}
+
+OUString SmOoxmlImport::handleRad()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( rad ));
+ bool degHide = false;
+ if( m_rStream.checkOpeningTag( M_TOKEN( radPr )))
+ {
+ if( XmlStream::Tag degHideTag = m_rStream.checkOpeningTag( M_TOKEN( degHide )))
+ {
+ degHide = degHideTag.attribute( M_TOKEN( val ), degHide );
+ m_rStream.ensureClosingTag( M_TOKEN( degHide ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( radPr ));
+ }
+ OUString deg = readOMathArgInElement( M_TOKEN( deg ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( rad ));
+ if( degHide )
+ return "sqrt {" + e + "}";
+ else
+ return "nroot {" + deg + "} {" + e + "}";
+}
+
+OUString SmOoxmlImport::handleSpre()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( sPre ));
+ OUString sub = readOMathArgInElement( M_TOKEN( sub ));
+ OUString sup = readOMathArgInElement( M_TOKEN( sup ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( sPre ));
+ return "{" + e + "} lsub {" + sub + "} lsup {" + sup + "}";
+}
+
+OUString SmOoxmlImport::handleSsub()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( sSub ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ OUString sub = readOMathArgInElement( M_TOKEN( sub ));
+ m_rStream.ensureClosingTag( M_TOKEN( sSub ));
+ return "{" + e + "} rsub {" + sub + "}";
+}
+
+OUString SmOoxmlImport::handleSsubsup()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( sSubSup ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ OUString sub = readOMathArgInElement( M_TOKEN( sub ));
+ OUString sup = readOMathArgInElement( M_TOKEN( sup ));
+ m_rStream.ensureClosingTag( M_TOKEN( sSubSup ));
+ return "{" + e + "} rsub {" + sub + "} rsup {" + sup + "}";
+}
+
+OUString SmOoxmlImport::handleSsup()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( sSup ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ OUString sup = readOMathArgInElement( M_TOKEN( sup ));
+ m_rStream.ensureClosingTag( M_TOKEN( sSup ));
+ return "{" + e + "} ^ {" + sup + "}";
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/ooxmlimport.hxx b/starmath/source/ooxmlimport.hxx
new file mode 100644
index 000000000..5fc7a6a33
--- /dev/null
+++ b/starmath/source/ooxmlimport.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_OOXMLIMPORT_HXX
+#define INCLUDED_STARMATH_SOURCE_OOXMLIMPORT_HXX
+
+#include <rtl/ustring.hxx>
+
+namespace oox::formulaimport { class XmlStream; }
+/**
+ Class implementing reading of formulas from OOXML. The toplevel element is expected
+ to be oMath (handle oMathPara outside of this code).
+ */
+class SmOoxmlImport
+{
+public:
+ explicit SmOoxmlImport( oox::formulaimport::XmlStream& stream );
+ OUString ConvertToStarMath();
+private:
+ OUString handleStream();
+ OUString handleAcc();
+ OUString handleBar();
+ OUString handleBox();
+ OUString handleBorderBox();
+ OUString handleD();
+ OUString handleEqArr();
+ OUString handleF();
+ OUString handleFunc();
+ enum LimLowUpp_t { LimLow, LimUpp };
+ OUString handleLimLowUpp( LimLowUpp_t limlowupp );
+ OUString handleGroupChr();
+ OUString handleM();
+ OUString handleNary();
+ OUString handleR();
+ OUString handleRad();
+ OUString handleSpre();
+ OUString handleSsub();
+ OUString handleSsubsup();
+ OUString handleSsup();
+ OUString readOMathArg( int stoptoken );
+ OUString readOMathArgInElement( int token );
+
+ oox::formulaimport::XmlStream& m_rStream;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/parse.cxx b/starmath/source/parse.cxx
new file mode 100644
index 000000000..67512e4a4
--- /dev/null
+++ b/starmath/source/parse.cxx
@@ -0,0 +1,2502 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/UnicodeType.hpp>
+#include <com/sun/star/i18n/KParseTokens.hpp>
+#include <com/sun/star/i18n/KParseType.hpp>
+#include <i18nlangtag/lang.h>
+#include <tools/lineend.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/syslocale.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <rtl/character.hxx>
+#include <node.hxx>
+#include <parse.hxx>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include "cfgitem.hxx"
+#include <cassert>
+#include <stack>
+
+using namespace ::com::sun::star::i18n;
+
+
+SmToken::SmToken()
+ : eType(TUNKNOWN)
+ , cMathChar('\0')
+ , nGroup(TG::NONE)
+ , nLevel(0)
+ , nRow(0)
+ , nCol(0)
+{
+}
+
+SmToken::SmToken(SmTokenType eTokenType,
+ sal_Unicode cMath,
+ const char* pText,
+ TG nTokenGroup,
+ sal_uInt16 nTokenLevel)
+ : aText(OUString::createFromAscii(pText))
+ , eType(eTokenType)
+ , cMathChar(cMath)
+ , nGroup(nTokenGroup)
+ , nLevel(nTokenLevel)
+ , nRow(0)
+ , nCol(0)
+{
+}
+
+//Definition of math keywords
+static const SmTokenTableEntry aTokenTable[] =
+{
+ { "abs", TABS, '\0', TG::UnOper, 13 },
+ { "acute", TACUTE, MS_ACUTE, TG::Attribute, 5 },
+ { "aleph" , TALEPH, MS_ALEPH, TG::Standalone, 5 },
+ { "alignb", TALIGNC, '\0', TG::Align, 0},
+ { "alignc", TALIGNC, '\0', TG::Align, 0},
+ { "alignl", TALIGNL, '\0', TG::Align, 0},
+ { "alignm", TALIGNC, '\0', TG::Align, 0},
+ { "alignr", TALIGNR, '\0', TG::Align, 0},
+ { "alignt", TALIGNC, '\0', TG::Align, 0},
+ { "and", TAND, MS_AND, TG::Product, 0},
+ { "approx", TAPPROX, MS_APPROX, TG::Relation, 0},
+ { "aqua", TAQUA, '\0', TG::Color, 0},
+ { "arccos", TACOS, '\0', TG::Function, 5},
+ { "arccot", TACOT, '\0', TG::Function, 5},
+ { "arcosh", TACOSH, '\0', TG::Function, 5 },
+ { "arcoth", TACOTH, '\0', TG::Function, 5 },
+ { "arcsin", TASIN, '\0', TG::Function, 5},
+ { "arctan", TATAN, '\0', TG::Function, 5},
+ { "arsinh", TASINH, '\0', TG::Function, 5},
+ { "artanh", TATANH, '\0', TG::Function, 5},
+ { "backepsilon" , TBACKEPSILON, MS_BACKEPSILON, TG::Standalone, 5},
+ { "bar", TBAR, MS_BAR, TG::Attribute, 5},
+ { "binom", TBINOM, '\0', TG::NONE, 5 },
+ { "black", TBLACK, '\0', TG::Color, 0},
+ { "blue", TBLUE, '\0', TG::Color, 0},
+ { "bold", TBOLD, '\0', TG::FontAttr, 5},
+ { "boper", TBOPER, '\0', TG::Product, 0},
+ { "breve", TBREVE, MS_BREVE, TG::Attribute, 5},
+ { "bslash", TBACKSLASH, MS_BACKSLASH, TG::Product, 0 },
+ { "cdot", TCDOT, MS_CDOT, TG::Product, 0},
+ { "check", TCHECK, MS_CHECK, TG::Attribute, 5},
+ { "circ" , TCIRC, MS_CIRC, TG::Standalone, 5},
+ { "circle", TCIRCLE, MS_CIRCLE, TG::Attribute, 5},
+ { "color", TCOLOR, '\0', TG::FontAttr, 5},
+ { "coprod", TCOPROD, MS_COPROD, TG::Oper, 5},
+ { "cos", TCOS, '\0', TG::Function, 5},
+ { "cosh", TCOSH, '\0', TG::Function, 5},
+ { "cot", TCOT, '\0', TG::Function, 5},
+ { "coth", TCOTH, '\0', TG::Function, 5},
+ { "csub", TCSUB, '\0', TG::Power, 0},
+ { "csup", TCSUP, '\0', TG::Power, 0},
+ { "cyan", TCYAN, '\0', TG::Color, 0},
+ { "dddot", TDDDOT, MS_DDDOT, TG::Attribute, 5},
+ { "ddot", TDDOT, MS_DDOT, TG::Attribute, 5},
+ { "def", TDEF, MS_DEF, TG::Relation, 0},
+ { "div", TDIV, MS_DIV, TG::Product, 0},
+ { "divides", TDIVIDES, MS_LINE, TG::Relation, 0},
+ { "dlarrow" , TDLARROW, MS_DLARROW, TG::Standalone, 5},
+ { "dlrarrow" , TDLRARROW, MS_DLRARROW, TG::Standalone, 5},
+ { "dot", TDOT, MS_DOT, TG::Attribute, 5},
+ { "dotsaxis", TDOTSAXIS, MS_DOTSAXIS, TG::Standalone, 5}, // 5 to continue expression
+ { "dotsdiag", TDOTSDIAG, MS_DOTSUP, TG::Standalone, 5},
+ { "dotsdown", TDOTSDOWN, MS_DOTSDOWN, TG::Standalone, 5},
+ { "dotslow", TDOTSLOW, MS_DOTSLOW, TG::Standalone, 5},
+ { "dotsup", TDOTSUP, MS_DOTSUP, TG::Standalone, 5},
+ { "dotsvert", TDOTSVERT, MS_DOTSVERT, TG::Standalone, 5},
+ { "downarrow" , TDOWNARROW, MS_DOWNARROW, TG::Standalone, 5},
+ { "drarrow" , TDRARROW, MS_DRARROW, TG::Standalone, 5},
+ { "emptyset" , TEMPTYSET, MS_EMPTYSET, TG::Standalone, 5},
+ { "equiv", TEQUIV, MS_EQUIV, TG::Relation, 0},
+ { "exists", TEXISTS, MS_EXISTS, TG::Standalone, 5},
+ { "exp", TEXP, '\0', TG::Function, 5},
+ { "fact", TFACT, MS_FACT, TG::UnOper, 5},
+ { "fixed", TFIXED, '\0', TG::Font, 0},
+ { "font", TFONT, '\0', TG::FontAttr, 5},
+ { "forall", TFORALL, MS_FORALL, TG::Standalone, 5},
+ { "from", TFROM, '\0', TG::Limit, 0},
+ { "fuchsia", TFUCHSIA, '\0', TG::Color, 0},
+ { "func", TFUNC, '\0', TG::Function, 5},
+ { "ge", TGE, MS_GE, TG::Relation, 0},
+ { "geslant", TGESLANT, MS_GESLANT, TG::Relation, 0 },
+ { "gg", TGG, MS_GG, TG::Relation, 0},
+ { "grave", TGRAVE, MS_GRAVE, TG::Attribute, 5},
+ { "gray", TGRAY, '\0', TG::Color, 0},
+ { "green", TGREEN, '\0', TG::Color, 0},
+ { "gt", TGT, MS_GT, TG::Relation, 0},
+ { "harpoon", THARPOON, MS_HARPOON, TG::Attribute, 5},
+ { "hat", THAT, MS_HAT, TG::Attribute, 5},
+ { "hbar" , THBAR, MS_HBAR, TG::Standalone, 5},
+ { "iiint", TIIINT, MS_IIINT, TG::Oper, 5},
+ { "iint", TIINT, MS_IINT, TG::Oper, 5},
+ { "im" , TIM, MS_IM, TG::Standalone, 5 },
+ { "in", TIN, MS_IN, TG::Relation, 0},
+ { "infinity" , TINFINITY, MS_INFINITY, TG::Standalone, 5},
+ { "infty" , TINFINITY, MS_INFINITY, TG::Standalone, 5},
+ { "int", TINT, MS_INT, TG::Oper, 5},
+ { "intd", TINTD, MS_INT, TG::Oper, 5},
+ { "intersection", TINTERSECT, MS_INTERSECT, TG::Product, 0},
+ { "ital", TITALIC, '\0', TG::FontAttr, 5},
+ { "italic", TITALIC, '\0', TG::FontAttr, 5},
+ { "lambdabar" , TLAMBDABAR, MS_LAMBDABAR, TG::Standalone, 5},
+ { "langle", TLANGLE, MS_LMATHANGLE, TG::LBrace, 5},
+ { "laplace", TLAPLACE, MS_LAPLACE, TG::Standalone, 5},
+ { "lbrace", TLBRACE, MS_LBRACE, TG::LBrace, 5},
+ { "lceil", TLCEIL, MS_LCEIL, TG::LBrace, 5},
+ { "ldbracket", TLDBRACKET, MS_LDBRACKET, TG::LBrace, 5},
+ { "ldline", TLDLINE, MS_DVERTLINE, TG::LBrace, 5},
+ { "le", TLE, MS_LE, TG::Relation, 0},
+ { "left", TLEFT, '\0', TG::NONE, 5},
+ { "leftarrow" , TLEFTARROW, MS_LEFTARROW, TG::Standalone, 5},
+ { "leslant", TLESLANT, MS_LESLANT, TG::Relation, 0 },
+ { "lfloor", TLFLOOR, MS_LFLOOR, TG::LBrace, 5},
+ { "lim", TLIM, '\0', TG::Oper, 5},
+ { "lime", TLIME, '\0', TG::Color, 0},
+ { "liminf", TLIMINF, '\0', TG::Oper, 5},
+ { "limsup", TLIMSUP, '\0', TG::Oper, 5},
+ { "lint", TLINT, MS_LINT, TG::Oper, 5},
+ { "ll", TLL, MS_LL, TG::Relation, 0},
+ { "lline", TLLINE, MS_VERTLINE, TG::LBrace, 5},
+ { "llint", TLLINT, MS_LLINT, TG::Oper, 5},
+ { "lllint", TLLLINT, MS_LLLINT, TG::Oper, 5},
+ { "ln", TLN, '\0', TG::Function, 5},
+ { "log", TLOG, '\0', TG::Function, 5},
+ { "lsub", TLSUB, '\0', TG::Power, 0},
+ { "lsup", TLSUP, '\0', TG::Power, 0},
+ { "lt", TLT, MS_LT, TG::Relation, 0},
+ { "magenta", TMAGENTA, '\0', TG::Color, 0},
+ { "maroon", TMAROON, '\0', TG::Color, 0},
+ { "matrix", TMATRIX, '\0', TG::NONE, 5},
+ { "minusplus", TMINUSPLUS, MS_MINUSPLUS, TG::UnOper | TG::Sum, 5},
+ { "mline", TMLINE, MS_VERTLINE, TG::NONE, 0}, //! not in TG::RBrace, Level 0
+ { "nabla", TNABLA, MS_NABLA, TG::Standalone, 5},
+ { "navy", TNAVY, '\0', TG::Color, 0},
+ { "nbold", TNBOLD, '\0', TG::FontAttr, 5},
+ { "ndivides", TNDIVIDES, MS_NDIVIDES, TG::Relation, 0},
+ { "neg", TNEG, MS_NEG, TG::UnOper, 5 },
+ { "neq", TNEQ, MS_NEQ, TG::Relation, 0},
+ { "newline", TNEWLINE, '\0', TG::NONE, 0},
+ { "ni", TNI, MS_NI, TG::Relation, 0},
+ { "nitalic", TNITALIC, '\0', TG::FontAttr, 5},
+ { "none", TNONE, '\0', TG::LBrace | TG::RBrace, 0},
+ { "nospace", TNOSPACE, '\0', TG::Standalone, 5},
+ { "notexists", TNOTEXISTS, MS_NOTEXISTS, TG::Standalone, 5},
+ { "notin", TNOTIN, MS_NOTIN, TG::Relation, 0},
+ { "nprec", TNOTPRECEDES, MS_NOTPRECEDES, TG::Relation, 0 },
+ { "nroot", TNROOT, MS_SQRT, TG::UnOper, 5},
+ { "nsubset", TNSUBSET, MS_NSUBSET, TG::Relation, 0 },
+ { "nsubseteq", TNSUBSETEQ, MS_NSUBSETEQ, TG::Relation, 0 },
+ { "nsucc", TNOTSUCCEEDS, MS_NOTSUCCEEDS, TG::Relation, 0 },
+ { "nsupset", TNSUPSET, MS_NSUPSET, TG::Relation, 0 },
+ { "nsupseteq", TNSUPSETEQ, MS_NSUPSETEQ, TG::Relation, 0 },
+ { "odivide", TODIVIDE, MS_ODIVIDE, TG::Product, 0},
+ { "odot", TODOT, MS_ODOT, TG::Product, 0},
+ { "olive", TOLIVE, '\0', TG::Color, 0},
+ { "ominus", TOMINUS, MS_OMINUS, TG::Sum, 0},
+ { "oper", TOPER, '\0', TG::Oper, 5},
+ { "oplus", TOPLUS, MS_OPLUS, TG::Sum, 0},
+ { "or", TOR, MS_OR, TG::Sum, 0},
+ { "ortho", TORTHO, MS_ORTHO, TG::Relation, 0},
+ { "otimes", TOTIMES, MS_OTIMES, TG::Product, 0},
+ { "over", TOVER, '\0', TG::Product, 0},
+ { "overbrace", TOVERBRACE, MS_OVERBRACE, TG::Product, 5},
+ { "overline", TOVERLINE, '\0', TG::Attribute, 5},
+ { "overstrike", TOVERSTRIKE, '\0', TG::Attribute, 5},
+ { "owns", TNI, MS_NI, TG::Relation, 0},
+ { "parallel", TPARALLEL, MS_DLINE, TG::Relation, 0},
+ { "partial", TPARTIAL, MS_PARTIAL, TG::Standalone, 5 },
+ { "phantom", TPHANTOM, '\0', TG::FontAttr, 5},
+ { "plusminus", TPLUSMINUS, MS_PLUSMINUS, TG::UnOper | TG::Sum, 5},
+ { "prec", TPRECEDES, MS_PRECEDES, TG::Relation, 0 },
+ { "preccurlyeq", TPRECEDESEQUAL, MS_PRECEDESEQUAL, TG::Relation, 0 },
+ { "precsim", TPRECEDESEQUIV, MS_PRECEDESEQUIV, TG::Relation, 0 },
+ { "prod", TPROD, MS_PROD, TG::Oper, 5},
+ { "prop", TPROP, MS_PROP, TG::Relation, 0},
+ { "purple", TPURPLE, '\0', TG::Color, 0},
+ { "rangle", TRANGLE, MS_RMATHANGLE, TG::RBrace, 0}, //! 0 to terminate expression
+ { "rbrace", TRBRACE, MS_RBRACE, TG::RBrace, 0},
+ { "rceil", TRCEIL, MS_RCEIL, TG::RBrace, 0},
+ { "rdbracket", TRDBRACKET, MS_RDBRACKET, TG::RBrace, 0},
+ { "rdline", TRDLINE, MS_DVERTLINE, TG::RBrace, 0},
+ { "re" , TRE, MS_RE, TG::Standalone, 5 },
+ { "red", TRED, '\0', TG::Color, 0},
+ { "rfloor", TRFLOOR, MS_RFLOOR, TG::RBrace, 0}, //! 0 to terminate expression
+ { "rgb", TRGB, '\0', TG::Color, 0},
+ { "right", TRIGHT, '\0', TG::NONE, 0},
+ { "rightarrow" , TRIGHTARROW, MS_RIGHTARROW, TG::Standalone, 5},
+ { "rline", TRLINE, MS_VERTLINE, TG::RBrace, 0}, //! 0 to terminate expression
+ { "rsub", TRSUB, '\0', TG::Power, 0},
+ { "rsup", TRSUP, '\0', TG::Power, 0},
+ { "sans", TSANS, '\0', TG::Font, 0},
+ { "serif", TSERIF, '\0', TG::Font, 0},
+ { "setC" , TSETC, MS_SETC, TG::Standalone, 5},
+ { "setminus", TBACKSLASH, MS_BACKSLASH, TG::Product, 0 },
+ { "setN" , TSETN, MS_SETN, TG::Standalone, 5},
+ { "setQ" , TSETQ, MS_SETQ, TG::Standalone, 5},
+ { "setR" , TSETR, MS_SETR, TG::Standalone, 5},
+ { "setZ" , TSETZ, MS_SETZ, TG::Standalone, 5},
+ { "silver", TSILVER, '\0', TG::Color, 0},
+ { "sim", TSIM, MS_SIM, TG::Relation, 0},
+ { "simeq", TSIMEQ, MS_SIMEQ, TG::Relation, 0},
+ { "sin", TSIN, '\0', TG::Function, 5},
+ { "sinh", TSINH, '\0', TG::Function, 5},
+ { "size", TSIZE, '\0', TG::FontAttr, 5},
+ { "slash", TSLASH, MS_SLASH, TG::Product, 0 },
+ { "sqrt", TSQRT, MS_SQRT, TG::UnOper, 5},
+ { "stack", TSTACK, '\0', TG::NONE, 5},
+ { "sub", TRSUB, '\0', TG::Power, 0},
+ { "subset", TSUBSET, MS_SUBSET, TG::Relation, 0},
+ { "subseteq", TSUBSETEQ, MS_SUBSETEQ, TG::Relation, 0},
+ { "succ", TSUCCEEDS, MS_SUCCEEDS, TG::Relation, 0 },
+ { "succcurlyeq", TSUCCEEDSEQUAL, MS_SUCCEEDSEQUAL, TG::Relation, 0 },
+ { "succsim", TSUCCEEDSEQUIV, MS_SUCCEEDSEQUIV, TG::Relation, 0 },
+ { "sum", TSUM, MS_SUM, TG::Oper, 5},
+ { "sup", TRSUP, '\0', TG::Power, 0},
+ { "supset", TSUPSET, MS_SUPSET, TG::Relation, 0},
+ { "supseteq", TSUPSETEQ, MS_SUPSETEQ, TG::Relation, 0},
+ { "tan", TTAN, '\0', TG::Function, 5},
+ { "tanh", TTANH, '\0', TG::Function, 5},
+ { "teal", TTEAL, '\0', TG::Color, 0},
+ { "tilde", TTILDE, MS_TILDE, TG::Attribute, 5},
+ { "times", TTIMES, MS_TIMES, TG::Product, 0},
+ { "to", TTO, '\0', TG::Limit, 0},
+ { "toward", TTOWARD, MS_RIGHTARROW, TG::Relation, 0},
+ { "transl", TTRANSL, MS_TRANSL, TG::Relation, 0},
+ { "transr", TTRANSR, MS_TRANSR, TG::Relation, 0},
+ { "underbrace", TUNDERBRACE, MS_UNDERBRACE, TG::Product, 5},
+ { "underline", TUNDERLINE, '\0', TG::Attribute, 5},
+ { "union", TUNION, MS_UNION, TG::Sum, 0},
+ { "uoper", TUOPER, '\0', TG::UnOper, 5},
+ { "uparrow" , TUPARROW, MS_UPARROW, TG::Standalone, 5},
+ { "vec", TVEC, MS_VEC, TG::Attribute, 5},
+ { "white", TWHITE, '\0', TG::Color, 0},
+ { "widebslash", TWIDEBACKSLASH, MS_BACKSLASH, TG::Product, 0 },
+ { "wideharpoon", TWIDEHARPOON, MS_HARPOON, TG::Attribute, 5},
+ { "widehat", TWIDEHAT, MS_HAT, TG::Attribute, 5},
+ { "wideslash", TWIDESLASH, MS_SLASH, TG::Product, 0 },
+ { "widetilde", TWIDETILDE, MS_TILDE, TG::Attribute, 5},
+ { "widevec", TWIDEVEC, MS_VEC, TG::Attribute, 5},
+ { "wp" , TWP, MS_WP, TG::Standalone, 5},
+ { "yellow", TYELLOW, '\0', TG::Color, 0}
+};
+
+//Checks if keyword is in the list by SmTokenTableEntry.
+#if !defined NDEBUG
+static bool sortCompare(const SmTokenTableEntry & lhs, const SmTokenTableEntry & rhs)
+{
+ return OUString::createFromAscii(lhs.pIdent).compareToIgnoreAsciiCase(OUString::createFromAscii(rhs.pIdent)) < 0;
+}
+#endif
+
+//Checks if keyword is in the list.
+static bool findCompare(const SmTokenTableEntry & lhs, const OUString & s)
+{
+ return s.compareToIgnoreAsciiCaseAscii(lhs.pIdent) > 0;
+}
+
+//Returns the SmTokenTableEntry for a keyword
+const SmTokenTableEntry * SmParser::GetTokenTableEntry( const OUString &rName )
+{
+ static bool bSortKeyWords = false; // Flag: RTF-token table has been sorted.
+ if( !bSortKeyWords ) //First time sorts it.
+ {
+ assert( std::is_sorted( std::begin(aTokenTable), std::end(aTokenTable), sortCompare ) );
+ bSortKeyWords = true;
+ }
+
+ if (rName.isEmpty())return nullptr; //avoid null pointer exceptions
+
+ //Looks for the first keyword after or equal to rName in alphabetical order.
+ auto findIter = std::lower_bound( std::begin(aTokenTable), std::end(aTokenTable), rName, findCompare );
+ if ( findIter != std::end(aTokenTable) && rName.equalsIgnoreAsciiCaseAscii( findIter->pIdent ))return &*findIter; //check is equal
+
+ return nullptr; //not found
+}
+
+namespace {
+
+bool IsDelimiter( const OUString &rTxt, sal_Int32 nPos )
+ // returns 'true' iff cChar is '\0' or a delimiter
+{
+ assert(nPos <= rTxt.getLength()); //index out of range
+
+ if (nPos == rTxt.getLength())return true; //This is EOF
+
+ sal_Unicode cChar = rTxt[nPos];
+
+ // check if 'cChar' is in the delimiter table
+ static const sal_Unicode aDelimiterTable[] =
+ {
+ ' ', '{', '}', '(', ')', '\t', '\n', '\r', '+', '-',
+ '*', '/', '=', '[', ']', '^', '_', '#',
+ '%', '>', '<', '&', '|', '\\', '"', '~', '`'
+ };//reordered by usage (by eye) for nanoseconds saving.
+
+ //checks the array
+ for (auto const &cDelimiter : aDelimiterTable)
+ {
+ if (cDelimiter == cChar)return true;
+ }
+
+ //special chars support
+ sal_Int16 nTypJp = SM_MOD()->GetSysLocale().GetCharClass().getType( rTxt, nPos );
+ return ( nTypJp == css::i18n::UnicodeType::SPACE_SEPARATOR ||
+ nTypJp == css::i18n::UnicodeType::CONTROL);
+}
+
+}//end namespace
+
+//Text replace onto m_aBufferString
+void SmParser::Replace( sal_Int32 nPos, sal_Int32 nLen, const OUString &rText )
+{
+ assert( nPos + nLen <= m_aBufferString.getLength() ); //checks if length allows text replace
+
+ m_aBufferString = m_aBufferString.replaceAt( nPos, nLen, rText ); //replace and reindex
+ sal_Int32 nChg = rText.getLength() - nLen;
+ m_nBufferIndex = m_nBufferIndex + nChg;
+ m_nTokenIndex = m_nTokenIndex + nChg;
+}
+
+void SmParser::NextToken() //Central part of the parser
+{
+ // First character may be any alphabetic
+ static const sal_Int32 coStartFlags =
+ KParseTokens::ANY_LETTER |
+ KParseTokens::IGNORE_LEADING_WS;
+
+ // Continuing characters may be any alphabetic
+ static const sal_Int32 coContFlags =
+ (coStartFlags & ~KParseTokens::IGNORE_LEADING_WS)
+ | KParseTokens::TWO_DOUBLE_QUOTES_BREAK_STRING;
+
+ // user-defined char continuing characters may be any alphanumeric or dot.
+ static const sal_Int32 coUserDefinedCharContFlags =
+ KParseTokens::ANY_LETTER_OR_NUMBER |
+ KParseTokens::ASC_DOT |
+ KParseTokens::TWO_DOUBLE_QUOTES_BREAK_STRING;
+
+ // First character for numbers, may be any numeric or dot
+ static const sal_Int32 coNumStartFlags =
+ KParseTokens::ASC_DIGIT |
+ KParseTokens::ASC_DOT |
+ KParseTokens::IGNORE_LEADING_WS;
+
+ // Continuing characters for numbers, may be any numeric or dot.
+ // tdf#127873: additionally accept ',' comma group separator as too many
+ // existing documents unwittingly may have used that as decimal separator
+ // in such locales (though it never was as this is always the en-US locale
+ // and the group separator is only parsed away).
+ static const sal_Int32 coNumContFlags =
+ (coNumStartFlags & ~KParseTokens::IGNORE_LEADING_WS) |
+ KParseTokens::GROUP_SEPARATOR_IN_NUMBER;
+
+ sal_Int32 nBufLen = m_aBufferString.getLength();
+ ParseResult aRes;
+ sal_Int32 nRealStart;
+ bool bCont;
+ do
+ {
+ // skip white spaces
+ while (UnicodeType::SPACE_SEPARATOR ==
+ m_pSysCC->getType( m_aBufferString, m_nBufferIndex ))
+ ++m_nBufferIndex;
+
+ // Try to parse a number in a locale-independent manner using
+ // '.' as decimal separator.
+ // See https://bz.apache.org/ooo/show_bug.cgi?id=45779
+ aRes = m_aNumCC.parsePredefinedToken(KParseType::ASC_NUMBER,
+ m_aBufferString, m_nBufferIndex,
+ coNumStartFlags, "",
+ coNumContFlags, "");
+
+ if (aRes.TokenType == 0)
+ {
+ // Try again with the default token parsing.
+ aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex,
+ coStartFlags, "",
+ coContFlags, "");
+ }
+
+ nRealStart = m_nBufferIndex + aRes.LeadingWhiteSpace;
+ m_nBufferIndex = nRealStart;
+
+ bCont = false;
+ if ( aRes.TokenType == 0 &&
+ nRealStart < nBufLen &&
+ '\n' == m_aBufferString[ nRealStart ] )
+ {
+ // keep data needed for tokens row and col entry up to date
+ ++m_nRow;
+ m_nBufferIndex = m_nColOff = nRealStart + 1;
+ bCont = true;
+ }
+ else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
+ {
+ if (nRealStart + 2 <= nBufLen && m_aBufferString.match("%%", nRealStart))
+ {
+ //SkipComment
+ m_nBufferIndex = nRealStart + 2;
+ while (m_nBufferIndex < nBufLen &&
+ '\n' != m_aBufferString[ m_nBufferIndex ])
+ ++m_nBufferIndex;
+ bCont = true;
+ }
+ }
+
+ } while (bCont);
+
+ // set index of current token
+ m_nTokenIndex = m_nBufferIndex;
+
+ m_aCurToken.nRow = m_nRow;
+ m_aCurToken.nCol = nRealStart - m_nColOff + 1;
+
+ bool bHandled = true;
+ if (nRealStart >= nBufLen)
+ {
+ m_aCurToken.eType = TEND;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText.clear();
+ }
+ else if (aRes.TokenType & KParseType::ANY_NUMBER)
+ {
+ assert(aRes.EndPos > 0);
+ if ( m_aBufferString[aRes.EndPos-1] == ',' &&
+ aRes.EndPos < nBufLen &&
+ m_pSysCC->getType( m_aBufferString, aRes.EndPos ) != UnicodeType::SPACE_SEPARATOR )
+ {
+ // Comma followed by a non-space char is unlikely for decimal/thousands separator.
+ --aRes.EndPos;
+ }
+ sal_Int32 n = aRes.EndPos - nRealStart;
+ assert(n >= 0);
+ m_aCurToken.eType = TNUMBER;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = m_aBufferString.copy( nRealStart, n );
+
+ SAL_WARN_IF( !IsDelimiter( m_aBufferString, aRes.EndPos ), "starmath", "identifier really finished? (compatibility!)" );
+ }
+ else if (aRes.TokenType & KParseType::DOUBLE_QUOTE_STRING)
+ {
+ m_aCurToken.eType = TTEXT;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = aRes.DequotedNameOrString;
+ m_aCurToken.nRow = m_nRow;
+ m_aCurToken.nCol = nRealStart - m_nColOff + 2;
+ }
+ else if (aRes.TokenType & KParseType::IDENTNAME)
+ {
+ sal_Int32 n = aRes.EndPos - nRealStart;
+ assert(n >= 0);
+ OUString aName( m_aBufferString.copy( nRealStart, n ) );
+ const SmTokenTableEntry *pEntry = GetTokenTableEntry( aName );
+
+ if (pEntry)
+ {
+ m_aCurToken.eType = pEntry->eType;
+ m_aCurToken.cMathChar = pEntry->cMathChar;
+ m_aCurToken.nGroup = pEntry->nGroup;
+ m_aCurToken.nLevel = pEntry->nLevel;
+ m_aCurToken.aText = OUString::createFromAscii( pEntry->pIdent );
+ }
+ else
+ {
+ m_aCurToken.eType = TIDENT;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = aName;
+
+ SAL_WARN_IF(!IsDelimiter(m_aBufferString, aRes.EndPos),"starmath", "identifier really finished? (compatibility!)");
+ }
+ }
+ else if (aRes.TokenType == 0 && '_' == m_aBufferString[ nRealStart ])
+ {
+ m_aCurToken.eType = TRSUB;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::Power;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "_";
+
+ aRes.EndPos = nRealStart + 1;
+ }
+ else if (aRes.TokenType & KParseType::BOOLEAN)
+ {
+ sal_Int32 &rnEndPos = aRes.EndPos;
+ if (rnEndPos - nRealStart <= 2)
+ {
+ sal_Unicode ch = m_aBufferString[ nRealStart ];
+ switch (ch)
+ {
+ case '<':
+ {
+ if (m_aBufferString.match("<<", nRealStart))
+ {
+ m_aCurToken.eType = TLL;
+ m_aCurToken.cMathChar = MS_LL;
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "<<";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match("<=", nRealStart))
+ {
+ m_aCurToken.eType = TLE;
+ m_aCurToken.cMathChar = MS_LE;
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "<=";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match("<-", nRealStart))
+ {
+ m_aCurToken.eType = TLEFTARROW;
+ m_aCurToken.cMathChar = MS_LEFTARROW;
+ m_aCurToken.nGroup = TG::Standalone;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "<-";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match("<>", nRealStart))
+ {
+ m_aCurToken.eType = TNEQ;
+ m_aCurToken.cMathChar = MS_NEQ;
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "<>";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match("<?>", nRealStart))
+ {
+ m_aCurToken.eType = TPLACE;
+ m_aCurToken.cMathChar = MS_PLACE;
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "<?>";
+
+ rnEndPos = nRealStart + 3;
+ }
+ else
+ {
+ m_aCurToken.eType = TLT;
+ m_aCurToken.cMathChar = MS_LT;
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "<";
+ }
+ }
+ break;
+ case '>':
+ {
+ if (m_aBufferString.match(">=", nRealStart))
+ {
+ m_aCurToken.eType = TGE;
+ m_aCurToken.cMathChar = MS_GE;
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = ">=";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match(">>", nRealStart))
+ {
+ m_aCurToken.eType = TGG;
+ m_aCurToken.cMathChar = MS_GG;
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = ">>";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else
+ {
+ m_aCurToken.eType = TGT;
+ m_aCurToken.cMathChar = MS_GT;
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = ">";
+ }
+ }
+ break;
+ default:
+ bHandled = false;
+ }
+ }
+ }
+ else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
+ {
+ sal_Int32 &rnEndPos = aRes.EndPos;
+ if (rnEndPos - nRealStart == 1)
+ {
+ sal_Unicode ch = m_aBufferString[ nRealStart ];
+ switch (ch)
+ {
+ case '%':
+ {
+ //! modifies aRes.EndPos
+
+ OSL_ENSURE( rnEndPos >= nBufLen ||
+ '%' != m_aBufferString[ rnEndPos ],
+ "unexpected comment start" );
+
+ // get identifier of user-defined character
+ ParseResult aTmpRes = m_pSysCC->parseAnyToken(
+ m_aBufferString, rnEndPos,
+ KParseTokens::ANY_LETTER,
+ "",
+ coUserDefinedCharContFlags,
+ "" );
+
+ sal_Int32 nTmpStart = rnEndPos + aTmpRes.LeadingWhiteSpace;
+
+ // default setting for the case that no identifier
+ // i.e. a valid symbol-name is following the '%'
+ // character
+ m_aCurToken.eType = TTEXT;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText ="%";
+ m_aCurToken.nRow = m_nRow;
+ m_aCurToken.nCol = nTmpStart - m_nColOff;
+
+ if (aTmpRes.TokenType & KParseType::IDENTNAME)
+ {
+
+ sal_Int32 n = aTmpRes.EndPos - nTmpStart;
+ m_aCurToken.eType = TSPECIAL;
+ m_aCurToken.aText = m_aBufferString.copy( nTmpStart-1, n+1 );
+
+ OSL_ENSURE( aTmpRes.EndPos > rnEndPos,
+ "empty identifier" );
+ if (aTmpRes.EndPos > rnEndPos)
+ rnEndPos = aTmpRes.EndPos;
+ else
+ ++rnEndPos;
+ }
+
+ // if no symbol-name was found we start-over with
+ // finding the next token right after the '%' sign.
+ // I.e. we leave rnEndPos unmodified.
+ }
+ break;
+ case '[':
+ {
+ m_aCurToken.eType = TLBRACKET;
+ m_aCurToken.cMathChar = MS_LBRACKET;
+ m_aCurToken.nGroup = TG::LBrace;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "[";
+ }
+ break;
+ case '\\':
+ {
+ m_aCurToken.eType = TESCAPE;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "\\";
+ }
+ break;
+ case ']':
+ {
+ m_aCurToken.eType = TRBRACKET;
+ m_aCurToken.cMathChar = MS_RBRACKET;
+ m_aCurToken.nGroup = TG::RBrace;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "]";
+ }
+ break;
+ case '^':
+ {
+ m_aCurToken.eType = TRSUP;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::Power;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "^";
+ }
+ break;
+ case '`':
+ {
+ m_aCurToken.eType = TSBLANK;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::Blank;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "`";
+ }
+ break;
+ case '{':
+ {
+ m_aCurToken.eType = TLGROUP;
+ m_aCurToken.cMathChar = MS_LBRACE;
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "{";
+ }
+ break;
+ case '|':
+ {
+ m_aCurToken.eType = TOR;
+ m_aCurToken.cMathChar = MS_OR;
+ m_aCurToken.nGroup = TG::Sum;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "|";
+ }
+ break;
+ case '}':
+ {
+ m_aCurToken.eType = TRGROUP;
+ m_aCurToken.cMathChar = MS_RBRACE;
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "}";
+ }
+ break;
+ case '~':
+ {
+ m_aCurToken.eType = TBLANK;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::Blank;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "~";
+ }
+ break;
+ case '#':
+ {
+ if (m_aBufferString.match("##", nRealStart))
+ {
+ m_aCurToken.eType = TDPOUND;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "##";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else
+ {
+ m_aCurToken.eType = TPOUND;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "#";
+ }
+ }
+ break;
+ case '&':
+ {
+ m_aCurToken.eType = TAND;
+ m_aCurToken.cMathChar = MS_AND;
+ m_aCurToken.nGroup = TG::Product;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "&";
+ }
+ break;
+ case '(':
+ {
+ m_aCurToken.eType = TLPARENT;
+ m_aCurToken.cMathChar = MS_LPARENT;
+ m_aCurToken.nGroup = TG::LBrace;
+ m_aCurToken.nLevel = 5; //! 0 to continue expression
+ m_aCurToken.aText = "(";
+ }
+ break;
+ case ')':
+ {
+ m_aCurToken.eType = TRPARENT;
+ m_aCurToken.cMathChar = MS_RPARENT;
+ m_aCurToken.nGroup = TG::RBrace;
+ m_aCurToken.nLevel = 0; //! 0 to terminate expression
+ m_aCurToken.aText = ")";
+ }
+ break;
+ case '*':
+ {
+ m_aCurToken.eType = TMULTIPLY;
+ m_aCurToken.cMathChar = MS_MULTIPLY;
+ m_aCurToken.nGroup = TG::Product;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "*";
+ }
+ break;
+ case '+':
+ {
+ if (m_aBufferString.match("+-", nRealStart))
+ {
+ m_aCurToken.eType = TPLUSMINUS;
+ m_aCurToken.cMathChar = MS_PLUSMINUS;
+ m_aCurToken.nGroup = TG::UnOper | TG::Sum;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "+-";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else
+ {
+ m_aCurToken.eType = TPLUS;
+ m_aCurToken.cMathChar = MS_PLUS;
+ m_aCurToken.nGroup = TG::UnOper | TG::Sum;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "+";
+ }
+ }
+ break;
+ case '-':
+ {
+ if (m_aBufferString.match("-+", nRealStart))
+ {
+ m_aCurToken.eType = TMINUSPLUS;
+ m_aCurToken.cMathChar = MS_MINUSPLUS;
+ m_aCurToken.nGroup = TG::UnOper | TG::Sum;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "-+";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match("->", nRealStart))
+ {
+ m_aCurToken.eType = TRIGHTARROW;
+ m_aCurToken.cMathChar = MS_RIGHTARROW;
+ m_aCurToken.nGroup = TG::Standalone;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "->";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else
+ {
+ m_aCurToken.eType = TMINUS;
+ m_aCurToken.cMathChar = MS_MINUS;
+ m_aCurToken.nGroup = TG::UnOper | TG::Sum;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "-";
+ }
+ }
+ break;
+ case '.':
+ {
+ // Only one character? Then it can't be a number.
+ if (m_nBufferIndex < m_aBufferString.getLength() - 1)
+ {
+ // for compatibility with SO5.2
+ // texts like .34 ...56 ... h ...78..90
+ // will be treated as numbers
+ m_aCurToken.eType = TNUMBER;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+
+ sal_Int32 nTxtStart = m_nBufferIndex;
+ sal_Unicode cChar;
+ // if the equation ends with dot(.) then increment m_nBufferIndex till end of string only
+ do
+ {
+ cChar = m_aBufferString[ ++m_nBufferIndex ];
+ }
+ while ( (cChar == '.' || rtl::isAsciiDigit( cChar )) &&
+ ( m_nBufferIndex < m_aBufferString.getLength() - 1 ) );
+
+ m_aCurToken.aText = m_aBufferString.copy( nTxtStart, m_nBufferIndex - nTxtStart );
+ aRes.EndPos = m_nBufferIndex;
+ }
+ else
+ bHandled = false;
+ }
+ break;
+ case '/':
+ {
+ m_aCurToken.eType = TDIVIDEBY;
+ m_aCurToken.cMathChar = MS_SLASH;
+ m_aCurToken.nGroup = TG::Product;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "/";
+ }
+ break;
+ case '=':
+ {
+ m_aCurToken.eType = TASSIGN;
+ m_aCurToken.cMathChar = MS_ASSIGN;
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "=";
+ }
+ break;
+ default:
+ bHandled = false;
+ }
+ }
+ }
+ else
+ bHandled = false;
+
+ if (!bHandled)
+ {
+ m_aCurToken.eType = TCHARACTER;
+ m_aCurToken.cMathChar = '\0';
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+
+ // tdf#129372: we may have to deal with surrogate pairs
+ // (see https://en.wikipedia.org/wiki/Universal_Character_Set_characters#Surrogates)
+ // in this case, we must read 2 sal_Unicode instead of 1
+ int nOffset(rtl::isSurrogate(m_aBufferString[nRealStart])? 2 : 1);
+ m_aCurToken.aText = m_aBufferString.copy( nRealStart, nOffset );
+
+ aRes.EndPos = nRealStart + nOffset;
+ }
+
+ if (TEND != m_aCurToken.eType)
+ m_nBufferIndex = aRes.EndPos;
+}
+
+namespace
+{
+ SmNodeArray buildNodeArray(std::vector<std::unique_ptr<SmNode>>& rSubNodes)
+ {
+ SmNodeArray aSubArray(rSubNodes.size());
+ for (size_t i = 0; i < rSubNodes.size(); ++i)
+ aSubArray[i] = rSubNodes[i].release();
+ return aSubArray;
+ }
+} //end namespace
+
+// grammar
+
+std::unique_ptr<SmTableNode> SmParser::DoTable()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ std::vector<std::unique_ptr<SmNode>> aLineArray;
+ aLineArray.push_back(DoLine());
+ while (m_aCurToken.eType == TNEWLINE)
+ {
+ NextToken();
+ aLineArray.push_back(DoLine());
+ }
+ assert(m_aCurToken.eType == TEND);
+ std::unique_ptr<SmTableNode> xSNode(new SmTableNode(m_aCurToken));
+ xSNode->SetSubNodes(buildNodeArray(aLineArray));
+ return xSNode;
+}
+
+std::unique_ptr<SmNode> SmParser::DoAlign(bool bUseExtraSpaces)
+ // parse alignment info (if any), then go on with rest of expression
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ std::unique_ptr<SmStructureNode> xSNode;
+
+ if (TokenInGroup(TG::Align))
+ {
+ xSNode.reset(new SmAlignNode(m_aCurToken));
+
+ NextToken();
+
+ // allow for just one align statement in 5.0
+ if (TokenInGroup(TG::Align))
+ return DoError(SmParseError::DoubleAlign);
+ }
+
+ auto pNode = DoExpression(bUseExtraSpaces);
+
+ if (xSNode)
+ {
+ xSNode->SetSubNode(0, pNode.release());
+ return xSNode;
+ }
+ return pNode;
+}
+
+// Postcondition: m_aCurToken.eType == TEND || m_aCurToken.eType == TNEWLINE
+std::unique_ptr<SmNode> SmParser::DoLine()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ std::vector<std::unique_ptr<SmNode>> ExpressionArray;
+
+ // start with single expression that may have an alignment statement
+ // (and go on with expressions that must not have alignment
+ // statements in 'while' loop below. See also 'Expression()'.)
+ if (m_aCurToken.eType != TEND && m_aCurToken.eType != TNEWLINE)
+ ExpressionArray.push_back(DoAlign());
+
+ while (m_aCurToken.eType != TEND && m_aCurToken.eType != TNEWLINE)
+ ExpressionArray.push_back(DoExpression());
+
+ //If there's no expression, add an empty one.
+ //this is to avoid a formula tree without any caret
+ //positions, in visual formula editor.
+ if(ExpressionArray.empty())
+ {
+ SmToken aTok;
+ aTok.eType = TNEWLINE;
+ ExpressionArray.emplace_back(std::unique_ptr<SmNode>(new SmExpressionNode(aTok)));
+ }
+
+ auto xSNode = std::make_unique<SmLineNode>(m_aCurToken);
+ xSNode->SetSubNodes(buildNodeArray(ExpressionArray));
+ return xSNode;
+}
+
+std::unique_ptr<SmNode> SmParser::DoExpression(bool bUseExtraSpaces)
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ std::vector<std::unique_ptr<SmNode>> RelationArray;
+ RelationArray.push_back(DoRelation());
+ while (m_aCurToken.nLevel >= 4)
+ RelationArray.push_back(DoRelation());
+
+ if (RelationArray.size() > 1)
+ {
+ std::unique_ptr<SmExpressionNode> xSNode(new SmExpressionNode(m_aCurToken));
+ xSNode->SetSubNodes(buildNodeArray(RelationArray));
+ xSNode->SetUseExtraSpaces(bUseExtraSpaces);
+ return xSNode;
+ }
+ else
+ {
+ // This expression has only one node so just push this node.
+ return std::move(RelationArray[0]);
+ }
+}
+
+std::unique_ptr<SmNode> SmParser::DoRelation()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ auto xFirst = DoSum();
+ while (TokenInGroup(TG::Relation))
+ {
+ std::unique_ptr<SmStructureNode> xSNode(new SmBinHorNode(m_aCurToken));
+ auto xSecond = DoOpSubSup();
+ auto xThird = DoSum();
+ xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond), std::move(xThird));
+ xFirst = std::move(xSNode);
+ }
+ return xFirst;
+}
+
+std::unique_ptr<SmNode> SmParser::DoSum()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ auto xFirst = DoProduct();
+ while (TokenInGroup(TG::Sum))
+ {
+ std::unique_ptr<SmStructureNode> xSNode(new SmBinHorNode(m_aCurToken));
+ auto xSecond = DoOpSubSup();
+ auto xThird = DoProduct();
+ xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond), std::move(xThird));
+ xFirst = std::move(xSNode);
+ }
+ return xFirst;
+}
+
+std::unique_ptr<SmNode> SmParser::DoProduct()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ auto xFirst = DoPower();
+
+ int nDepthLimit = 0;
+
+ while (TokenInGroup(TG::Product))
+ {
+ //this linear loop builds a recursive structure, if it gets
+ //too deep then later processing, e.g. releasing the tree,
+ //can exhaust stack
+ if (nDepthLimit > DEPTH_LIMIT)
+ throw std::range_error("parser depth limit");
+
+ std::unique_ptr<SmStructureNode> xSNode;
+ std::unique_ptr<SmNode> xOper;
+ bool bSwitchArgs = false;
+
+ SmTokenType eType = m_aCurToken.eType;
+ switch (eType)
+ {
+ case TOVER:
+ xSNode.reset(new SmBinVerNode(m_aCurToken));
+ xOper.reset(new SmRectangleNode(m_aCurToken));
+ NextToken();
+ break;
+
+ case TBOPER:
+ xSNode.reset(new SmBinHorNode(m_aCurToken));
+
+ NextToken();
+
+ //Let the glyph node know it's a binary operation
+ m_aCurToken.eType = TBOPER;
+ m_aCurToken.nGroup = TG::Product;
+ xOper = DoGlyphSpecial();
+ break;
+
+ case TOVERBRACE :
+ case TUNDERBRACE :
+ xSNode.reset(new SmVerticalBraceNode(m_aCurToken));
+ xOper.reset(new SmMathSymbolNode(m_aCurToken));
+
+ NextToken();
+ break;
+
+ case TWIDEBACKSLASH:
+ case TWIDESLASH:
+ {
+ SmBinDiagonalNode *pSTmp = new SmBinDiagonalNode(m_aCurToken);
+ pSTmp->SetAscending(eType == TWIDESLASH);
+ xSNode.reset(pSTmp);
+
+ xOper.reset(new SmPolyLineNode(m_aCurToken));
+ NextToken();
+
+ bSwitchArgs = true;
+ break;
+ }
+
+ default:
+ xSNode.reset(new SmBinHorNode(m_aCurToken));
+
+ xOper = DoOpSubSup();
+ }
+
+ auto xArg = DoPower();
+
+ if (bSwitchArgs)
+ {
+ //! vgl siehe SmBinDiagonalNode::Arrange
+ xSNode->SetSubNodes(std::move(xFirst), std::move(xArg), std::move(xOper));
+ }
+ else
+ {
+ xSNode->SetSubNodes(std::move(xFirst), std::move(xOper), std::move(xArg));
+ }
+ xFirst = std::move(xSNode);
+ ++nDepthLimit;
+ }
+ return xFirst;
+}
+
+std::unique_ptr<SmNode> SmParser::DoSubSup(TG nActiveGroup, SmNode *pGivenNode)
+{
+ std::unique_ptr<SmNode> xGivenNode(pGivenNode);
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ assert(nActiveGroup == TG::Power || nActiveGroup == TG::Limit);
+ assert(m_aCurToken.nGroup == nActiveGroup);
+
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(m_aCurToken));
+ //! Of course 'm_aCurToken' is just the first sub-/supscript token.
+ //! It should be of no further interest. The positions of the
+ //! sub-/supscripts will be identified by the corresponding subnodes
+ //! index in the 'aSubNodes' array (enum value from 'SmSubSup').
+
+ pNode->SetUseLimits(nActiveGroup == TG::Limit);
+
+ // initialize subnodes array
+ std::vector<std::unique_ptr<SmNode>> aSubNodes(1 + SUBSUP_NUM_ENTRIES);
+ aSubNodes[0] = std::move(xGivenNode);
+
+ // process all sub-/supscripts
+ int nIndex = 0;
+ while (TokenInGroup(nActiveGroup))
+ {
+ SmTokenType eType (m_aCurToken.eType);
+
+ switch (eType)
+ {
+ case TRSUB : nIndex = static_cast<int>(RSUB); break;
+ case TRSUP : nIndex = static_cast<int>(RSUP); break;
+ case TFROM :
+ case TCSUB : nIndex = static_cast<int>(CSUB); break;
+ case TTO :
+ case TCSUP : nIndex = static_cast<int>(CSUP); break;
+ case TLSUB : nIndex = static_cast<int>(LSUB); break;
+ case TLSUP : nIndex = static_cast<int>(LSUP); break;
+ default :
+ SAL_WARN( "starmath", "unknown case");
+ }
+ nIndex++;
+ assert(1 <= nIndex && nIndex <= SUBSUP_NUM_ENTRIES);
+
+ std::unique_ptr<SmNode> xENode;
+ if (aSubNodes[nIndex]) // if already occupied at earlier iteration
+ {
+ // forget the earlier one, remember an error instead
+ aSubNodes[nIndex].reset();
+ xENode = DoError(SmParseError::DoubleSubsupscript); // this also skips current token.
+ }
+ else
+ {
+ // skip sub-/supscript token
+ NextToken();
+ }
+
+ // get sub-/supscript node
+ // (even when we saw a double-sub/supscript error in the above
+ // in order to minimize mess and continue parsing.)
+ std::unique_ptr<SmNode> xSNode;
+ if (eType == TFROM || eType == TTO)
+ {
+ // parse limits in old 4.0 and 5.0 style
+ xSNode = DoRelation();
+ }
+ else
+ xSNode = DoTerm(true);
+
+ aSubNodes[nIndex] = std::move(xENode ? xENode : xSNode);
+ }
+
+ pNode->SetSubNodes(buildNodeArray(aSubNodes));
+ return pNode;
+}
+
+std::unique_ptr<SmNode> SmParser::DoOpSubSup()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ // get operator symbol
+ auto pNode = std::make_unique<SmMathSymbolNode>(m_aCurToken);
+ // skip operator token
+ NextToken();
+ // get sub- supscripts if any
+ if (m_aCurToken.nGroup == TG::Power)
+ return DoSubSup(TG::Power, pNode.release());
+ return pNode;
+}
+
+std::unique_ptr<SmNode> SmParser::DoPower()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ // get body for sub- supscripts on top of stack
+ std::unique_ptr<SmNode> xNode(DoTerm(false));
+
+ if (m_aCurToken.nGroup == TG::Power)
+ return DoSubSup(TG::Power, xNode.release());
+ return xNode;
+}
+
+std::unique_ptr<SmBlankNode> SmParser::DoBlank()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ assert(TokenInGroup(TG::Blank));
+ std::unique_ptr<SmBlankNode> pBlankNode(new SmBlankNode(m_aCurToken));
+
+ do
+ {
+ pBlankNode->IncreaseBy(m_aCurToken);
+ NextToken();
+ }
+ while (TokenInGroup(TG::Blank));
+
+ // Ignore trailing spaces, if corresponding option is set
+ if ( m_aCurToken.eType == TNEWLINE ||
+ (m_aCurToken.eType == TEND && !utl::ConfigManager::IsFuzzing() && SM_MOD()->GetConfig()->IsIgnoreSpacesRight()) )
+ {
+ pBlankNode->Clear();
+ }
+ return pBlankNode;
+}
+
+std::unique_ptr<SmNode> SmParser::DoTerm(bool bGroupNumberIdent)
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ switch (m_aCurToken.eType)
+ {
+ case TESCAPE :
+ return DoEscape();
+
+ case TNOSPACE :
+ case TLGROUP :
+ {
+ bool bNoSpace = m_aCurToken.eType == TNOSPACE;
+ if (bNoSpace)
+ NextToken();
+ if (m_aCurToken.eType != TLGROUP)
+ return DoTerm(false); // nospace is no longer concerned
+
+ NextToken();
+
+ // allow for empty group
+ if (m_aCurToken.eType == TRGROUP)
+ {
+ std::unique_ptr<SmStructureNode> xSNode(new SmExpressionNode(m_aCurToken));
+ xSNode->SetSubNodes(nullptr, nullptr);
+
+ NextToken();
+ return std::unique_ptr<SmNode>(xSNode.release());
+ }
+
+ auto pNode = DoAlign(!bNoSpace);
+ if (m_aCurToken.eType == TRGROUP) {
+ NextToken();
+ return pNode;
+ }
+ auto xSNode = std::make_unique<SmExpressionNode>(m_aCurToken);
+ std::unique_ptr<SmNode> xError(DoError(SmParseError::RgroupExpected));
+ xSNode->SetSubNodes(std::move(pNode), std::move(xError));
+ return std::unique_ptr<SmNode>(xSNode.release());
+ }
+
+ case TLEFT :
+ return DoBrace();
+
+ case TBLANK :
+ case TSBLANK :
+ return DoBlank();
+
+ case TTEXT :
+ {
+ auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_TEXT);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+ case TCHARACTER :
+ {
+ auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_VARIABLE);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+ case TIDENT :
+ case TNUMBER :
+ {
+ auto pTextNode = std::make_unique<SmTextNode>(m_aCurToken,
+ m_aCurToken.eType == TNUMBER ?
+ FNT_NUMBER :
+ FNT_VARIABLE);
+ if (!bGroupNumberIdent)
+ {
+ NextToken();
+ return std::unique_ptr<SmNode>(pTextNode.release());
+ }
+ std::vector<std::unique_ptr<SmNode>> aNodes;
+ // Some people want to be able to write "x_2n" for "x_{2n}"
+ // although e.g. LaTeX or AsciiMath interpret that as "x_2 n".
+ // The tokenizer skips whitespaces so we need some additional
+ // work to distinguish from "x_2 n".
+ // See https://bz.apache.org/ooo/show_bug.cgi?id=11752 and
+ // https://bugs.libreoffice.org/show_bug.cgi?id=55853
+ sal_Int32 nBufLen = m_aBufferString.getLength();
+
+ // We need to be careful to call NextToken() only after having
+ // tested for a whitespace separator (otherwise it will be
+ // skipped!)
+ bool moveToNextToken = true;
+ while (m_nBufferIndex < nBufLen &&
+ m_pSysCC->getType(m_aBufferString, m_nBufferIndex) !=
+ UnicodeType::SPACE_SEPARATOR)
+ {
+ NextToken();
+ if (m_aCurToken.eType != TNUMBER &&
+ m_aCurToken.eType != TIDENT)
+ {
+ // Neither a number nor an identifier. We just moved to
+ // the next token, so no need to do that again.
+ moveToNextToken = false;
+ break;
+ }
+ aNodes.emplace_back(std::unique_ptr<SmNode>(new SmTextNode(m_aCurToken,
+ m_aCurToken.eType ==
+ TNUMBER ?
+ FNT_NUMBER :
+ FNT_VARIABLE)));
+ }
+ if (moveToNextToken)
+ NextToken();
+ if (aNodes.empty())
+ return std::unique_ptr<SmNode>(pTextNode.release());
+ // We have several concatenated identifiers and numbers.
+ // Let's group them into one SmExpressionNode.
+ aNodes.insert(aNodes.begin(), std::move(pTextNode));
+ std::unique_ptr<SmExpressionNode> xNode(new SmExpressionNode(SmToken()));
+ xNode->SetSubNodes(buildNodeArray(aNodes));
+ return std::unique_ptr<SmNode>(xNode.release());
+ }
+ case TLEFTARROW :
+ case TRIGHTARROW :
+ case TUPARROW :
+ case TDOWNARROW :
+ case TCIRC :
+ case TDRARROW :
+ case TDLARROW :
+ case TDLRARROW :
+ case TEXISTS :
+ case TNOTEXISTS :
+ case TFORALL :
+ case TPARTIAL :
+ case TNABLA :
+ case TLAPLACE :
+ case TTOWARD :
+ case TDOTSAXIS :
+ case TDOTSDIAG :
+ case TDOTSDOWN :
+ case TDOTSLOW :
+ case TDOTSUP :
+ case TDOTSVERT :
+ {
+ auto pNode = std::make_unique<SmMathSymbolNode>(m_aCurToken);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+
+ case TSETN :
+ case TSETZ :
+ case TSETQ :
+ case TSETR :
+ case TSETC :
+ case THBAR :
+ case TLAMBDABAR :
+ case TBACKEPSILON :
+ case TALEPH :
+ case TIM :
+ case TRE :
+ case TWP :
+ case TEMPTYSET :
+ case TINFINITY :
+ {
+ auto pNode = std::make_unique<SmMathIdentifierNode>(m_aCurToken);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+
+ case TPLACE:
+ {
+ auto pNode = std::make_unique<SmPlaceNode>(m_aCurToken);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+
+ case TSPECIAL:
+ return DoSpecial();
+
+ case TBINOM:
+ return DoBinom();
+
+ case TSTACK:
+ return DoStack();
+
+ case TMATRIX:
+ return DoMatrix();
+
+ default:
+ if (TokenInGroup(TG::LBrace))
+ return DoBrace();
+ if (TokenInGroup(TG::Oper))
+ return DoOperator();
+ if (TokenInGroup(TG::UnOper))
+ return DoUnOper();
+ if ( TokenInGroup(TG::Attribute) ||
+ TokenInGroup(TG::FontAttr) )
+ {
+ std::stack<std::unique_ptr<SmStructureNode>> aStack;
+ bool bIsAttr;
+ for (;;)
+ {
+ bIsAttr = TokenInGroup(TG::Attribute);
+ if (!bIsAttr && !TokenInGroup(TG::FontAttr))
+ break;
+ aStack.push(bIsAttr ? DoAttribut() : DoFontAttribut());
+ }
+
+ auto xFirstNode = DoPower();
+ while (!aStack.empty())
+ {
+ std::unique_ptr<SmStructureNode> xNode = std::move(aStack.top());
+ aStack.pop();
+ xNode->SetSubNodes(nullptr, std::move(xFirstNode));
+ xFirstNode = std::move(xNode);
+ }
+ return xFirstNode;
+ }
+ if (TokenInGroup(TG::Function))
+ return DoFunction();
+ return DoError(SmParseError::UnexpectedChar);
+ }
+}
+
+std::unique_ptr<SmNode> SmParser::DoEscape()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ NextToken();
+
+ switch (m_aCurToken.eType)
+ {
+ case TLPARENT :
+ case TRPARENT :
+ case TLBRACKET :
+ case TRBRACKET :
+ case TLDBRACKET :
+ case TRDBRACKET :
+ case TLBRACE :
+ case TLGROUP :
+ case TRBRACE :
+ case TRGROUP :
+ case TLANGLE :
+ case TRANGLE :
+ case TLCEIL :
+ case TRCEIL :
+ case TLFLOOR :
+ case TRFLOOR :
+ case TLLINE :
+ case TRLINE :
+ case TLDLINE :
+ case TRDLINE :
+ {
+ auto pNode = std::make_unique<SmMathSymbolNode>(m_aCurToken);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+ default:
+ return DoError(SmParseError::UnexpectedToken);
+ }
+}
+
+std::unique_ptr<SmOperNode> SmParser::DoOperator()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ assert(TokenInGroup(TG::Oper));
+
+ auto xSNode = std::make_unique<SmOperNode>(m_aCurToken);
+
+ // get operator
+ auto xOperator = DoOper();
+
+ if (m_aCurToken.nGroup == TG::Limit || m_aCurToken.nGroup == TG::Power)
+ xOperator = DoSubSup(m_aCurToken.nGroup, xOperator.release());
+
+ // get argument
+ auto xArg = DoPower();
+
+ xSNode->SetSubNodes(std::move(xOperator), std::move(xArg));
+ return xSNode;
+}
+
+std::unique_ptr<SmNode> SmParser::DoOper()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ SmTokenType eType (m_aCurToken.eType);
+ std::unique_ptr<SmNode> pNode;
+
+ switch (eType)
+ {
+ case TSUM :
+ case TPROD :
+ case TCOPROD :
+ case TINT :
+ case TINTD :
+ case TIINT :
+ case TIIINT :
+ case TLINT :
+ case TLLINT :
+ case TLLLINT :
+ pNode.reset(new SmMathSymbolNode(m_aCurToken));
+ break;
+
+ case TLIM :
+ case TLIMSUP :
+ case TLIMINF :
+ {
+ const char* pLim = nullptr;
+ switch (eType)
+ {
+ case TLIM : pLim = "lim"; break;
+ case TLIMSUP : pLim = "lim sup"; break;
+ case TLIMINF : pLim = "lim inf"; break;
+ default:
+ break;
+ }
+ if( pLim )
+ m_aCurToken.aText = OUString::createFromAscii(pLim);
+ pNode.reset(new SmTextNode(m_aCurToken, FNT_TEXT));
+ }
+ break;
+
+ case TOPER :
+ NextToken();
+
+ OSL_ENSURE(m_aCurToken.eType == TSPECIAL, "Sm: wrong token");
+ pNode.reset(new SmGlyphSpecialNode(m_aCurToken));
+ break;
+
+ default :
+ assert(false && "unknown case");
+ }
+
+ NextToken();
+ return pNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser::DoUnOper()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ assert(TokenInGroup(TG::UnOper));
+
+ SmToken aNodeToken = m_aCurToken;
+ SmTokenType eType = m_aCurToken.eType;
+ bool bIsPostfix = eType == TFACT;
+
+ std::unique_ptr<SmStructureNode> xSNode;
+ std::unique_ptr<SmNode> xOper;
+ std::unique_ptr<SmNode> xExtra;
+ std::unique_ptr<SmNode> xArg;
+
+ switch (eType)
+ {
+ case TABS :
+ case TSQRT :
+ NextToken();
+ break;
+
+ case TNROOT :
+ NextToken();
+ xExtra = DoPower();
+ break;
+
+ case TUOPER :
+ NextToken();
+ //Let the glyph know what it is...
+ m_aCurToken.eType = TUOPER;
+ m_aCurToken.nGroup = TG::UnOper;
+ xOper = DoGlyphSpecial();
+ break;
+
+ case TPLUS :
+ case TMINUS :
+ case TPLUSMINUS :
+ case TMINUSPLUS :
+ case TNEG :
+ case TFACT :
+ xOper = DoOpSubSup();
+ break;
+
+ default :
+ assert(false);
+ }
+
+ // get argument
+ xArg = DoPower();
+
+ if (eType == TABS)
+ {
+ xSNode.reset(new SmBraceNode(aNodeToken));
+ xSNode->SetScaleMode(SmScaleMode::Height);
+
+ // build nodes for left & right lines
+ // (text, group, level of the used token are of no interest here)
+ // we'll use row & column of the keyword for abs
+ aNodeToken.eType = TABS;
+
+ aNodeToken.cMathChar = MS_VERTLINE;
+ std::unique_ptr<SmNode> xLeft(new SmMathSymbolNode(aNodeToken));
+ std::unique_ptr<SmNode> xRight(new SmMathSymbolNode(aNodeToken));
+
+ xSNode->SetSubNodes(std::move(xLeft), std::move(xArg), std::move(xRight));
+ }
+ else if (eType == TSQRT || eType == TNROOT)
+ {
+ xSNode.reset(new SmRootNode(aNodeToken));
+ xOper.reset(new SmRootSymbolNode(aNodeToken));
+ xSNode->SetSubNodes(std::move(xExtra), std::move(xOper), std::move(xArg));
+ }
+ else
+ {
+ xSNode.reset(new SmUnHorNode(aNodeToken));
+ if (bIsPostfix)
+ xSNode->SetSubNodes(std::move(xArg), std::move(xOper));
+ else
+ {
+ // prefix operator
+ xSNode->SetSubNodes(std::move(xOper), std::move(xArg));
+ }
+ }
+ return xSNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser::DoAttribut()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ assert(TokenInGroup(TG::Attribute));
+
+ auto xSNode = std::make_unique<SmAttributNode>(m_aCurToken);
+ std::unique_ptr<SmNode> xAttr;
+ SmScaleMode eScaleMode = SmScaleMode::None;
+
+ // get appropriate node for the attribute itself
+ switch (m_aCurToken.eType)
+ { case TUNDERLINE :
+ case TOVERLINE :
+ case TOVERSTRIKE :
+ xAttr.reset(new SmRectangleNode(m_aCurToken));
+ eScaleMode = SmScaleMode::Width;
+ break;
+
+ case TWIDEVEC :
+ case TWIDEHARPOON :
+ case TWIDEHAT :
+ case TWIDETILDE :
+ xAttr.reset(new SmMathSymbolNode(m_aCurToken));
+ eScaleMode = SmScaleMode::Width;
+ break;
+
+ default :
+ xAttr.reset(new SmMathSymbolNode(m_aCurToken));
+ }
+
+ NextToken();
+
+ xSNode->SetSubNodes(std::move(xAttr), nullptr); // the body will be filled later
+ xSNode->SetScaleMode(eScaleMode);
+ return xSNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser::DoFontAttribut()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ assert(TokenInGroup(TG::FontAttr));
+
+ switch (m_aCurToken.eType)
+ {
+ case TITALIC :
+ case TNITALIC :
+ case TBOLD :
+ case TNBOLD :
+ case TPHANTOM :
+ {
+ auto pNode = std::make_unique<SmFontNode>(m_aCurToken);
+ NextToken();
+ return pNode;
+ }
+
+ case TSIZE :
+ return DoFontSize();
+
+ case TFONT :
+ return DoFont();
+
+ case TCOLOR :
+ return DoColor();
+
+ default :
+ assert(false);
+ return {};
+ }
+}
+
+std::unique_ptr<SmStructureNode> SmParser::DoColor()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ assert(m_aCurToken.eType == TCOLOR);
+
+ std::unique_ptr<SmStructureNode> xNode;
+ // last color rules, get that one
+ SmToken aToken;
+ do
+ {
+
+ NextToken();
+
+ if (TokenInGroup(TG::Color))
+ {
+ aToken = m_aCurToken;
+ if(m_aCurToken.eType==TRGB){
+ SmToken r,g,b;
+ sal_Int32 nr, ng, nb, nc;
+ NextToken();
+ if(m_aCurToken.eType!=TNUMBER)return DoError(SmParseError::ColorExpected);
+ r = m_aCurToken;
+ NextToken();
+ if(m_aCurToken.eType!=TNUMBER)return DoError(SmParseError::ColorExpected);
+ g = m_aCurToken;
+ NextToken();
+ if(m_aCurToken.eType!=TNUMBER)return DoError(SmParseError::ColorExpected);
+ b = m_aCurToken;
+ nr = r.aText.toInt32();
+ if( nr < 0 || nr > 255 )return DoError(SmParseError::ColorExpected);
+ ng = g.aText.toInt32();
+ if( ng < 0 || ng > 255 )return DoError(SmParseError::ColorExpected);
+ nb = b.aText.toInt32();
+ if( nb < 0 || nb > 255 )return DoError(SmParseError::ColorExpected);
+ nc = nb + 256 * ( ng + nr*256 );
+ aToken.aText = OUString::number(nc);
+ }
+ NextToken();
+ }
+ else
+ {
+ return DoError(SmParseError::ColorExpected);
+ }
+ } while (m_aCurToken.eType == TCOLOR);
+
+ xNode.reset(new SmFontNode(aToken));
+ return xNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser::DoFont()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ assert(m_aCurToken.eType == TFONT);
+
+ std::unique_ptr<SmStructureNode> xNode;
+ // last font rules, get that one
+ SmToken aToken;
+ do
+ { NextToken();
+
+ if (TokenInGroup(TG::Font))
+ { aToken = m_aCurToken;
+ NextToken();
+ }
+ else
+ {
+ return DoError(SmParseError::FontExpected);
+ }
+ } while (m_aCurToken.eType == TFONT);
+
+ xNode.reset(new SmFontNode(aToken));
+ return xNode;
+}
+
+
+// gets number used as arguments in Math formulas (e.g. 'size' command)
+// Format: no negative numbers, must start with a digit, no exponent notation, ...
+static bool lcl_IsNumber(const OUString& rText)
+{
+ bool bPoint = false;
+ const sal_Unicode* pBuffer = rText.getStr();
+ for(sal_Int32 nPos = 0; nPos < rText.getLength(); nPos++, pBuffer++)
+ {
+ const sal_Unicode cChar = *pBuffer;
+ if(cChar == '.')
+ {
+ if(bPoint)
+ return false;
+ else
+ bPoint = true;
+ }
+ else if ( !rtl::isAsciiDigit( cChar ) )
+ return false;
+ }
+ return true;
+}
+
+std::unique_ptr<SmStructureNode> SmParser::DoFontSize()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ assert(m_aCurToken.eType == TSIZE);
+
+ FontSizeType Type;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(m_aCurToken));
+
+ NextToken();
+
+ switch (m_aCurToken.eType)
+ {
+ case TNUMBER: Type = FontSizeType::ABSOLUT; break;
+ case TPLUS: Type = FontSizeType::PLUS; break;
+ case TMINUS: Type = FontSizeType::MINUS; break;
+ case TMULTIPLY: Type = FontSizeType::MULTIPLY; break;
+ case TDIVIDEBY: Type = FontSizeType::DIVIDE; break;
+
+ default:
+ return DoError(SmParseError::SizeExpected);
+ }
+
+ if (Type != FontSizeType::ABSOLUT)
+ {
+ NextToken();
+ if (m_aCurToken.eType != TNUMBER)
+ return DoError(SmParseError::SizeExpected);
+ }
+
+ // get number argument
+ Fraction aValue( 1 );
+ if (lcl_IsNumber( m_aCurToken.aText ))
+ {
+ double fTmp = m_aCurToken.aText.toDouble();
+ if (fTmp != 0.0)
+ {
+ aValue = fTmp;
+
+ //!! keep the numerator and denominator from being too large
+ //!! otherwise ongoing multiplications may result in overflows
+ //!! (for example in SmNode::SetFontSize the font size calculated
+ //!! may become 0 because of this!!! Happens e.g. for ftmp = 2.9 with Linux
+ //!! or ftmp = 1.11111111111111111... (11/9) on every platform.)
+ if (aValue.GetDenominator() > 1000)
+ {
+ long nNum = aValue.GetNumerator();
+ long nDenom = aValue.GetDenominator();
+ while (nDenom > 1000)
+ {
+ nNum /= 10;
+ nDenom /= 10;
+ }
+ aValue = Fraction( nNum, nDenom );
+ }
+ }
+ }
+
+ NextToken();
+
+ pFontNode->SetSizeParameter(aValue, Type);
+ return pFontNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser::DoBrace()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ assert(m_aCurToken.eType == TLEFT || TokenInGroup(TG::LBrace));
+
+ std::unique_ptr<SmStructureNode> xSNode(new SmBraceNode(m_aCurToken));
+ std::unique_ptr<SmNode> pBody, pLeft, pRight;
+ SmScaleMode eScaleMode = SmScaleMode::None;
+ SmParseError eError = SmParseError::None;
+
+ if (m_aCurToken.eType == TLEFT)
+ { NextToken();
+
+ eScaleMode = SmScaleMode::Height;
+
+ // check for left bracket
+ if (TokenInGroup(TG::LBrace) || TokenInGroup(TG::RBrace))
+ {
+ pLeft.reset(new SmMathSymbolNode(m_aCurToken));
+
+ NextToken();
+ pBody = DoBracebody(true);
+
+ if (m_aCurToken.eType == TRIGHT)
+ { NextToken();
+
+ // check for right bracket
+ if (TokenInGroup(TG::LBrace) || TokenInGroup(TG::RBrace))
+ {
+ pRight.reset(new SmMathSymbolNode(m_aCurToken));
+ NextToken();
+ }
+ else
+ eError = SmParseError::RbraceExpected;
+ }
+ else
+ eError = SmParseError::RightExpected;
+ }
+ else
+ eError = SmParseError::LbraceExpected;
+ }
+ else
+ {
+ assert(TokenInGroup(TG::LBrace));
+
+ pLeft.reset(new SmMathSymbolNode(m_aCurToken));
+
+ NextToken();
+ pBody = DoBracebody(false);
+
+ SmTokenType eExpectedType = TUNKNOWN;
+ switch (pLeft->GetToken().eType)
+ { case TLPARENT : eExpectedType = TRPARENT; break;
+ case TLBRACKET : eExpectedType = TRBRACKET; break;
+ case TLBRACE : eExpectedType = TRBRACE; break;
+ case TLDBRACKET : eExpectedType = TRDBRACKET; break;
+ case TLLINE : eExpectedType = TRLINE; break;
+ case TLDLINE : eExpectedType = TRDLINE; break;
+ case TLANGLE : eExpectedType = TRANGLE; break;
+ case TLFLOOR : eExpectedType = TRFLOOR; break;
+ case TLCEIL : eExpectedType = TRCEIL; break;
+ default :
+ SAL_WARN("starmath", "unknown case");
+ }
+
+ if (m_aCurToken.eType == eExpectedType)
+ {
+ pRight.reset(new SmMathSymbolNode(m_aCurToken));
+ NextToken();
+ }
+ else
+ eError = SmParseError::ParentMismatch;
+ }
+
+ if (eError == SmParseError::None)
+ {
+ assert(pLeft);
+ assert(pRight);
+ xSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ xSNode->SetScaleMode(eScaleMode);
+ return xSNode;
+ }
+ return DoError(eError);
+}
+
+std::unique_ptr<SmBracebodyNode> SmParser::DoBracebody(bool bIsLeftRight)
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ auto pBody = std::make_unique<SmBracebodyNode>(m_aCurToken);
+
+ std::vector<std::unique_ptr<SmNode>> aNodes;
+ // get body if any
+ if (bIsLeftRight)
+ {
+ do
+ {
+ if (m_aCurToken.eType == TMLINE)
+ {
+ aNodes.emplace_back(std::make_unique<SmMathSymbolNode>(m_aCurToken));
+ NextToken();
+ }
+ else if (m_aCurToken.eType != TRIGHT)
+ {
+ aNodes.push_back(DoAlign());
+ if (m_aCurToken.eType != TMLINE && m_aCurToken.eType != TRIGHT)
+ aNodes.emplace_back(DoError(SmParseError::RightExpected));
+ }
+ } while (m_aCurToken.eType != TEND && m_aCurToken.eType != TRIGHT);
+ }
+ else
+ {
+ do
+ {
+ if (m_aCurToken.eType == TMLINE)
+ {
+ aNodes.emplace_back(std::make_unique<SmMathSymbolNode>(m_aCurToken));
+ NextToken();
+ }
+ else if (!TokenInGroup(TG::RBrace))
+ {
+ aNodes.push_back(DoAlign());
+ if (m_aCurToken.eType != TMLINE && !TokenInGroup(TG::RBrace))
+ aNodes.emplace_back(DoError(SmParseError::RbraceExpected));
+ }
+ } while (m_aCurToken.eType != TEND && !TokenInGroup(TG::RBrace));
+ }
+
+ pBody->SetSubNodes(buildNodeArray(aNodes));
+ pBody->SetScaleMode(bIsLeftRight ? SmScaleMode::Height : SmScaleMode::None);
+ return pBody;
+}
+
+std::unique_ptr<SmTextNode> SmParser::DoFunction()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ switch (m_aCurToken.eType)
+ {
+ case TFUNC:
+ NextToken(); // skip "FUNC"-statement
+ [[fallthrough]];
+
+ case TSIN :
+ case TCOS :
+ case TTAN :
+ case TCOT :
+ case TASIN :
+ case TACOS :
+ case TATAN :
+ case TACOT :
+ case TSINH :
+ case TCOSH :
+ case TTANH :
+ case TCOTH :
+ case TASINH :
+ case TACOSH :
+ case TATANH :
+ case TACOTH :
+ case TLN :
+ case TLOG :
+ case TEXP :
+ {
+ auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_FUNCTION);
+ NextToken();
+ return pNode;
+ }
+
+ default:
+ assert(false);
+ return nullptr;
+ }
+}
+
+std::unique_ptr<SmTableNode> SmParser::DoBinom()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ auto xSNode = std::make_unique<SmTableNode>(m_aCurToken);
+
+ NextToken();
+
+ auto xFirst = DoSum();
+ auto xSecond = DoSum();
+ xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond));
+ return xSNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser::DoStack()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ std::unique_ptr<SmStructureNode> xSNode(new SmTableNode(m_aCurToken));
+ NextToken();
+ if (m_aCurToken.eType != TLGROUP)
+ return DoError(SmParseError::LgroupExpected);
+ std::vector<std::unique_ptr<SmNode>> aExprArr;
+ do
+ {
+ NextToken();
+ aExprArr.push_back(DoAlign());
+ }
+ while (m_aCurToken.eType == TPOUND);
+
+ if (m_aCurToken.eType == TRGROUP)
+ NextToken();
+ else
+ aExprArr.emplace_back(DoError(SmParseError::RgroupExpected));
+
+ xSNode->SetSubNodes(buildNodeArray(aExprArr));
+ return xSNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser::DoMatrix()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ std::unique_ptr<SmMatrixNode> xMNode(new SmMatrixNode(m_aCurToken));
+ NextToken();
+ if (m_aCurToken.eType != TLGROUP)
+ return DoError(SmParseError::LgroupExpected);
+
+ std::vector<std::unique_ptr<SmNode>> aExprArr;
+ do
+ {
+ NextToken();
+ aExprArr.push_back(DoAlign());
+ }
+ while (m_aCurToken.eType == TPOUND);
+
+ size_t nCol = aExprArr.size();
+ size_t nRow = 1;
+ while (m_aCurToken.eType == TDPOUND)
+ {
+ NextToken();
+ for (size_t i = 0; i < nCol; i++)
+ {
+ auto xNode = DoAlign();
+ if (i < (nCol - 1))
+ {
+ if (m_aCurToken.eType == TPOUND)
+ NextToken();
+ else
+ xNode = DoError(SmParseError::PoundExpected);
+ }
+ aExprArr.emplace_back(std::move(xNode));
+ }
+ ++nRow;
+ }
+
+ if (m_aCurToken.eType == TRGROUP)
+ NextToken();
+ else
+ {
+ std::unique_ptr<SmNode> xENode(DoError(SmParseError::RgroupExpected));
+ if (aExprArr.empty())
+ nRow = nCol = 1;
+ else
+ aExprArr.pop_back();
+ aExprArr.emplace_back(std::move(xENode));
+ }
+
+ xMNode->SetSubNodes(buildNodeArray(aExprArr));
+ xMNode->SetRowCol(static_cast<sal_uInt16>(nRow),
+ static_cast<sal_uInt16>(nCol));
+ return std::unique_ptr<SmStructureNode>(xMNode.release());
+}
+
+std::unique_ptr<SmSpecialNode> SmParser::DoSpecial()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ bool bReplace = false;
+ OUString &rName = m_aCurToken.aText;
+ OUString aNewName;
+
+ // conversion of symbol names for 6.0 (XML) file format
+ // (name change on import / export.
+ // UI uses localized names XML file format does not.)
+ if( rName.startsWith("%") )
+ {
+ if (IsImportSymbolNames())
+ {
+ aNewName = SmLocalizedSymbolData::GetUiSymbolName(rName.copy(1));
+ bReplace = true;
+ }
+ else if (IsExportSymbolNames())
+ {
+ aNewName = SmLocalizedSymbolData::GetExportSymbolName(rName.copy(1));
+ bReplace = true;
+ }
+ }
+ if (!aNewName.isEmpty())
+ aNewName = "%" + aNewName;
+
+
+ if (bReplace && !aNewName.isEmpty() && rName != aNewName)
+ {
+ Replace(GetTokenIndex(), rName.getLength(), aNewName);
+ rName = aNewName;
+ }
+
+ // add symbol name to list of used symbols
+ const OUString aSymbolName(m_aCurToken.aText.copy(1));
+ if (!aSymbolName.isEmpty())
+ m_aUsedSymbols.insert( aSymbolName );
+
+ auto pNode = std::make_unique<SmSpecialNode>(m_aCurToken);
+ NextToken();
+ return pNode;
+}
+
+std::unique_ptr<SmGlyphSpecialNode> SmParser::DoGlyphSpecial()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ auto pNode = std::make_unique<SmGlyphSpecialNode>(m_aCurToken);
+ NextToken();
+ return pNode;
+}
+
+std::unique_ptr<SmExpressionNode> SmParser::DoError(SmParseError eError)
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ if (aDepthGuard.TooDeep())
+ throw std::range_error("parser depth limit");
+
+ auto xSNode = std::make_unique<SmExpressionNode>(m_aCurToken);
+ std::unique_ptr<SmErrorNode> pErr(new SmErrorNode(m_aCurToken));
+ xSNode->SetSubNodes(std::move(pErr), nullptr);
+
+ AddError(eError, xSNode.get());
+
+ NextToken();
+
+ return xSNode;
+}
+
+// end grammar
+
+
+SmParser::SmParser()
+ : m_nCurError( 0 )
+ , m_nBufferIndex( 0 )
+ , m_nTokenIndex( 0 )
+ , m_nRow( 0 )
+ , m_nColOff( 0 )
+ , m_bImportSymNames( false )
+ , m_bExportSymNames( false )
+ , m_nParseDepth(0)
+ , m_aNumCC( LanguageTag( LANGUAGE_ENGLISH_US ) )
+ , m_pSysCC( SM_MOD()->GetSysLocale().GetCharClassPtr() )
+{
+}
+
+std::unique_ptr<SmTableNode> SmParser::Parse(const OUString &rBuffer)
+{
+ m_aUsedSymbols.clear();
+
+ m_aBufferString = convertLineEnd(rBuffer, LINEEND_LF);
+ m_nBufferIndex = 0;
+ m_nTokenIndex = 0;
+ m_nRow = 1;
+ m_nColOff = 0;
+ m_nCurError = -1;
+
+ m_aErrDescList.clear();
+
+ NextToken();
+ return DoTable();
+}
+
+std::unique_ptr<SmNode> SmParser::ParseExpression(const OUString &rBuffer)
+{
+ m_aBufferString = convertLineEnd(rBuffer, LINEEND_LF);
+ m_nBufferIndex = 0;
+ m_nTokenIndex = 0;
+ m_nRow = 1;
+ m_nColOff = 0;
+ m_nCurError = -1;
+
+ m_aErrDescList.clear();
+
+ NextToken();
+ return DoExpression();
+}
+
+
+void SmParser::AddError(SmParseError Type, SmNode *pNode)
+{
+ std::unique_ptr<SmErrorDesc> pErrDesc(new SmErrorDesc);
+
+ pErrDesc->m_eType = Type;
+ pErrDesc->m_pNode = pNode;
+ pErrDesc->m_aText = SmResId(RID_ERR_IDENT);
+
+ const char* pRID;
+ switch (Type)
+ {
+ case SmParseError::UnexpectedChar: pRID = RID_ERR_UNEXPECTEDCHARACTER; break;
+ case SmParseError::UnexpectedToken: pRID = RID_ERR_UNEXPECTEDTOKEN; break;
+ case SmParseError::PoundExpected: pRID = RID_ERR_POUNDEXPECTED; break;
+ case SmParseError::ColorExpected: pRID = RID_ERR_COLOREXPECTED; break;
+ case SmParseError::LgroupExpected: pRID = RID_ERR_LGROUPEXPECTED; break;
+ case SmParseError::RgroupExpected: pRID = RID_ERR_RGROUPEXPECTED; break;
+ case SmParseError::LbraceExpected: pRID = RID_ERR_LBRACEEXPECTED; break;
+ case SmParseError::RbraceExpected: pRID = RID_ERR_RBRACEEXPECTED; break;
+ case SmParseError::ParentMismatch: pRID = RID_ERR_PARENTMISMATCH; break;
+ case SmParseError::RightExpected: pRID = RID_ERR_RIGHTEXPECTED; break;
+ case SmParseError::FontExpected: pRID = RID_ERR_FONTEXPECTED; break;
+ case SmParseError::SizeExpected: pRID = RID_ERR_SIZEEXPECTED; break;
+ case SmParseError::DoubleAlign: pRID = RID_ERR_DOUBLEALIGN; break;
+ case SmParseError::DoubleSubsupscript: pRID = RID_ERR_DOUBLESUBSUPSCRIPT; break;
+ default:
+ assert(false);
+ return;
+ }
+ pErrDesc->m_aText += SmResId(pRID);
+
+ m_aErrDescList.push_back(std::move(pErrDesc));
+}
+
+
+const SmErrorDesc *SmParser::NextError()
+{
+ if ( !m_aErrDescList.empty() )
+ if (m_nCurError > 0) return m_aErrDescList[ --m_nCurError ].get();
+ else
+ {
+ m_nCurError = 0;
+ return m_aErrDescList[ m_nCurError ].get();
+ }
+ else return nullptr;
+}
+
+
+const SmErrorDesc *SmParser::PrevError()
+{
+ if ( !m_aErrDescList.empty() )
+ if (m_nCurError < static_cast<int>(m_aErrDescList.size() - 1)) return m_aErrDescList[ ++m_nCurError ].get();
+ else
+ {
+ m_nCurError = static_cast<int>(m_aErrDescList.size() - 1);
+ return m_aErrDescList[ m_nCurError ].get();
+ }
+ else return nullptr;
+}
+
+
+const SmErrorDesc *SmParser::GetError()
+{
+ if ( !m_aErrDescList.empty() )
+ return m_aErrDescList.front().get();
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/rect.cxx b/starmath/source/rect.cxx
new file mode 100644
index 000000000..d49daf747
--- /dev/null
+++ b/starmath/source/rect.cxx
@@ -0,0 +1,623 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <o3tl/sorted_vector.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <sal/log.hxx>
+
+#include <format.hxx>
+#include <rect.hxx>
+#include <types.hxx>
+#include <smmod.hxx>
+
+#include <cassert>
+
+namespace {
+
+bool SmGetGlyphBoundRect(const vcl::RenderContext &rDev,
+ const OUString &rText, tools::Rectangle &rRect)
+ // basically the same as 'GetTextBoundRect' (in class 'OutputDevice')
+ // but with a string as argument.
+{
+ // handle special case first
+ if (rText.isEmpty())
+ {
+ rRect.SetEmpty();
+ return true;
+ }
+
+ // get a device where 'OutputDevice::GetTextBoundRect' will be successful
+ OutputDevice *pGlyphDev;
+ if (rDev.GetOutDevType() != OUTDEV_PRINTER)
+ pGlyphDev = const_cast<OutputDevice *>(&rDev);
+ else
+ {
+ // since we format for the printer (where GetTextBoundRect will fail)
+ // we need a virtual device here.
+ pGlyphDev = &SM_MOD()->GetDefaultVirtualDev();
+ }
+
+ const FontMetric aDevFM (rDev.GetFontMetric());
+
+ pGlyphDev->Push(PushFlags::FONT | PushFlags::MAPMODE);
+ vcl::Font aFnt(rDev.GetFont());
+ aFnt.SetAlignment(ALIGN_TOP);
+
+ // use scale factor when calling GetTextBoundRect to counter
+ // negative effects from antialiasing which may otherwise result
+ // in significant incorrect bounding rectangles for some characters.
+ Size aFntSize = aFnt.GetFontSize();
+
+ // Workaround to avoid HUGE font sizes and resulting problems
+ long nScaleFactor = 1;
+ while( aFntSize.Height() > 2000 * nScaleFactor )
+ nScaleFactor *= 2;
+
+ aFnt.SetFontSize( Size( aFntSize.Width() / nScaleFactor, aFntSize.Height() / nScaleFactor ) );
+ pGlyphDev->SetFont(aFnt);
+
+ long nTextWidth = rDev.GetTextWidth(rText);
+ tools::Rectangle aResult (Point(), Size(nTextWidth, rDev.GetTextHeight())),
+ aTmp;
+
+ bool bSuccess = pGlyphDev->GetTextBoundRect(aTmp, rText);
+ OSL_ENSURE( bSuccess, "GetTextBoundRect failed" );
+
+
+ if (!aTmp.IsEmpty())
+ {
+ aResult = tools::Rectangle(aTmp.Left() * nScaleFactor, aTmp.Top() * nScaleFactor,
+ aTmp.Right() * nScaleFactor, aTmp.Bottom() * nScaleFactor);
+ if (&rDev != pGlyphDev) /* only when rDev is a printer... */
+ {
+ long nGDTextWidth = pGlyphDev->GetTextWidth(rText);
+ if (nGDTextWidth != 0 &&
+ nTextWidth != nGDTextWidth)
+ {
+ aResult.SetRight( aResult.Right() * nTextWidth );
+ aResult.SetRight( aResult.Right() / ( nGDTextWidth * nScaleFactor) );
+ }
+ }
+ }
+
+ // move rectangle to match possibly different baselines
+ // (because of different devices)
+ long nDelta = aDevFM.GetAscent() - pGlyphDev->GetFontMetric().GetAscent() * nScaleFactor;
+ aResult.Move(0, nDelta);
+
+ pGlyphDev->Pop();
+
+ rRect = aResult;
+ return bSuccess;
+}
+
+bool SmIsMathAlpha(const OUString &rText)
+ // true iff symbol (from StarMath Font) should be treated as letter
+{
+ // Set of symbols, which should be treated as letters in StarMath Font
+ // (to get a normal (non-clipped) SmRect in contrast to the other operators
+ // and symbols).
+ static o3tl::sorted_vector<sal_Unicode> const aMathAlpha({
+ MS_ALEPH, MS_IM, MS_RE,
+ MS_WP, u'\xE070', MS_EMPTYSET,
+ u'\x2113', u'\xE0D6', u'\x2107',
+ u'\x2127', u'\x210A', MS_HBAR,
+ MS_LAMBDABAR, MS_SETN, MS_SETZ,
+ MS_SETQ, MS_SETR, MS_SETC,
+ u'\x2373', u'\xE0A5', u'\x2112',
+ u'\x2130', u'\x2131'
+ });
+
+ if (rText.isEmpty())
+ return false;
+
+ OSL_ENSURE(rText.getLength() == 1, "Sm : string must be exactly one character long");
+ sal_Unicode cChar = rText[0];
+
+ // is it a greek symbol?
+ if (u'\xE0AC' <= cChar && cChar <= u'\xE0D4')
+ return true;
+ // or, does it appear in 'aMathAlpha'?
+ return aMathAlpha.find(cChar) != aMathAlpha.end();
+}
+
+}
+
+
+SmRect::SmRect()
+ // constructs empty rectangle at (0, 0) with width and height 0.
+ : aTopLeft(0, 0)
+ , aSize(0, 0)
+ , nBaseline(0)
+ , nAlignT(0)
+ , nAlignM(0)
+ , nAlignB(0)
+ , nGlyphTop(0)
+ , nGlyphBottom(0)
+ , nItalicLeftSpace(0)
+ , nItalicRightSpace(0)
+ , nLoAttrFence(0)
+ , nHiAttrFence(0)
+ , nBorderWidth(0)
+ , bHasBaseline(false)
+ , bHasAlignInfo(false)
+{
+}
+
+
+void SmRect::CopyAlignInfo(const SmRect &rRect)
+{
+ nBaseline = rRect.nBaseline;
+ bHasBaseline = rRect.bHasBaseline;
+ nAlignT = rRect.nAlignT;
+ nAlignM = rRect.nAlignM;
+ nAlignB = rRect.nAlignB;
+ bHasAlignInfo = rRect.bHasAlignInfo;
+ nLoAttrFence = rRect.nLoAttrFence;
+ nHiAttrFence = rRect.nHiAttrFence;
+}
+
+
+SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
+ const OUString &rText, sal_uInt16 nBorder)
+ // get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
+ : aTopLeft(0, 0)
+ , aSize(rDev.GetTextWidth(rText), rDev.GetTextHeight())
+{
+ const FontMetric aFM (rDev.GetFontMetric());
+ bool bIsMath = aFM.GetFamilyName().equalsIgnoreAsciiCase( FONTNAME_MATH );
+ bool bAllowSmaller = bIsMath && !SmIsMathAlpha(rText);
+ const long nFontHeight = rDev.GetFont().GetFontSize().Height();
+
+ nBorderWidth = nBorder;
+ bHasAlignInfo = true;
+ bHasBaseline = true;
+ nBaseline = aFM.GetAscent();
+ nAlignT = nBaseline - nFontHeight * 750 / 1000;
+ nAlignM = nBaseline - nFontHeight * 121 / 422;
+ // that's where the horizontal bars of '+', '-', ... are
+ // (1/3 of ascent over baseline)
+ // (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight)
+ nAlignB = nBaseline;
+
+ // workaround for printer fonts with very small (possible 0 or even
+ // negative(!)) leading
+ if (aFM.GetInternalLeading() < 5 && rDev.GetOutDevType() == OUTDEV_PRINTER)
+ {
+ OutputDevice *pWindow = Application::GetDefaultDevice();
+
+ pWindow->Push(PushFlags::MAPMODE | PushFlags::FONT);
+
+ pWindow->SetMapMode(rDev.GetMapMode());
+ pWindow->SetFont(rDev.GetFontMetric());
+
+ long nDelta = pWindow->GetFontMetric().GetInternalLeading();
+ if (nDelta == 0)
+ { // this value approx. fits a Leading of 80 at a
+ // Fontheight of 422 (12pt)
+ nDelta = nFontHeight * 8 / 43;
+ }
+ SetTop(GetTop() - nDelta);
+
+ pWindow->Pop();
+ }
+
+ // get GlyphBoundRect
+ tools::Rectangle aGlyphRect;
+ bool bSuccess = SmGetGlyphBoundRect(rDev, rText, aGlyphRect);
+ if (!bSuccess)
+ SAL_WARN("starmath", "Ooops... (Font missing?)");
+
+ nItalicLeftSpace = GetLeft() - aGlyphRect.Left() + nBorderWidth;
+ nItalicRightSpace = aGlyphRect.Right() - GetRight() + nBorderWidth;
+ if (nItalicLeftSpace < 0 && !bAllowSmaller)
+ nItalicLeftSpace = 0;
+ if (nItalicRightSpace < 0 && !bAllowSmaller)
+ nItalicRightSpace = 0;
+
+ long nDist = 0;
+ if (pFormat)
+ nDist = (rDev.GetFont().GetFontSize().Height()
+ * pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100;
+
+ nHiAttrFence = aGlyphRect.TopLeft().Y() - 1 - nBorderWidth - nDist;
+ nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0);
+
+ nGlyphTop = aGlyphRect.Top() - nBorderWidth;
+ nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth;
+
+ if (bAllowSmaller)
+ {
+ // for symbols and operators from the StarMath Font
+ // we adjust upper and lower margin of the symbol
+ SetTop(nGlyphTop);
+ SetBottom(nGlyphBottom);
+ }
+
+ if (nHiAttrFence < GetTop())
+ nHiAttrFence = GetTop();
+
+ if (nLoAttrFence > GetBottom())
+ nLoAttrFence = GetBottom();
+
+ OSL_ENSURE(rText.isEmpty() || !IsEmpty(),
+ "Sm: empty rectangle created");
+}
+
+
+SmRect::SmRect(long nWidth, long nHeight)
+ // this constructor should never be used for anything textlike because
+ // it will not provide useful values for baseline, AlignT and AlignB!
+ // It's purpose is to get a 'SmRect' for the horizontal line in fractions
+ // as used in 'SmBinVerNode'.
+ : aTopLeft(0, 0)
+ , aSize(nWidth, nHeight)
+ , nBaseline(0)
+ , nItalicLeftSpace(0)
+ , nItalicRightSpace(0)
+ , nBorderWidth(0)
+ , bHasBaseline(false)
+ , bHasAlignInfo(true)
+{
+ nAlignT = nGlyphTop = nHiAttrFence = GetTop();
+ nAlignB = nGlyphBottom = nLoAttrFence = GetBottom();
+ nAlignM = (nAlignT + nAlignB) / 2; // this is the default
+}
+
+
+void SmRect::SetLeft(long nLeft)
+{
+ if (nLeft <= GetRight())
+ { aSize.setWidth( GetRight() - nLeft + 1 );
+ aTopLeft.setX( nLeft );
+ }
+}
+
+
+void SmRect::SetRight(long nRight)
+{
+ if (nRight >= GetLeft())
+ aSize.setWidth( nRight - GetLeft() + 1 );
+}
+
+
+void SmRect::SetBottom(long nBottom)
+{
+ if (nBottom >= GetTop())
+ aSize.setHeight( nBottom - GetTop() + 1 );
+}
+
+
+void SmRect::SetTop(long nTop)
+{
+ if (nTop <= GetBottom())
+ { aSize.setHeight( GetBottom() - nTop + 1 );
+ aTopLeft.setY( nTop );
+ }
+}
+
+
+void SmRect::Move(const Point &rPosition)
+ // move rectangle by position 'rPosition'.
+{
+ aTopLeft += rPosition;
+
+ long nDelta = rPosition.Y();
+ nBaseline += nDelta;
+ nAlignT += nDelta;
+ nAlignM += nDelta;
+ nAlignB += nDelta;
+ nGlyphTop += nDelta;
+ nGlyphBottom += nDelta;
+ nHiAttrFence += nDelta;
+ nLoAttrFence += nDelta;
+}
+
+
+Point SmRect::AlignTo(const SmRect &rRect, RectPos ePos,
+ RectHorAlign eHor, RectVerAlign eVer) const
+{ Point aPos (GetTopLeft());
+ // will become the topleft point of the new rectangle position
+
+ // set horizontal or vertical new rectangle position depending on ePos
+ switch (ePos)
+ { case RectPos::Left:
+ aPos.setX( rRect.GetItalicLeft() - GetItalicRightSpace()
+ - GetWidth() );
+ break;
+ case RectPos::Right:
+ aPos.setX( rRect.GetItalicRight() + 1 + GetItalicLeftSpace() );
+ break;
+ case RectPos::Top:
+ aPos.setY( rRect.GetTop() - GetHeight() );
+ break;
+ case RectPos::Bottom:
+ aPos.setY( rRect.GetBottom() + 1 );
+ break;
+ case RectPos::Attribute:
+ aPos.setX( rRect.GetItalicCenterX() - GetItalicWidth() / 2
+ + GetItalicLeftSpace() );
+ break;
+ default:
+ assert(false);
+ }
+
+ // check if horizontal position is already set
+ if (ePos == RectPos::Left || ePos == RectPos::Right || ePos == RectPos::Attribute)
+ // correct error in current vertical position
+ switch (eVer)
+ { case RectVerAlign::Top :
+ aPos.AdjustY(rRect.GetAlignT() - GetAlignT() );
+ break;
+ case RectVerAlign::Mid :
+ aPos.AdjustY(rRect.GetAlignM() - GetAlignM() );
+ break;
+ case RectVerAlign::Baseline :
+ // align baselines if possible else align mid's
+ if (HasBaseline() && rRect.HasBaseline())
+ aPos.AdjustY(rRect.GetBaseline() - GetBaseline() );
+ else
+ aPos.AdjustY(rRect.GetAlignM() - GetAlignM() );
+ break;
+ case RectVerAlign::Bottom :
+ aPos.AdjustY(rRect.GetAlignB() - GetAlignB() );
+ break;
+ case RectVerAlign::CenterY :
+ aPos.AdjustY(rRect.GetCenterY() - GetCenterY() );
+ break;
+ case RectVerAlign::AttributeHi:
+ aPos.AdjustY(rRect.GetHiAttrFence() - GetBottom() );
+ break;
+ case RectVerAlign::AttributeMid :
+ aPos.AdjustY(SmFromTo(rRect.GetAlignB(), rRect.GetAlignT(), 0.4)
+ - GetCenterY() );
+ break;
+ case RectVerAlign::AttributeLo :
+ aPos.AdjustY(rRect.GetLoAttrFence() - GetTop() );
+ break;
+ default :
+ assert(false);
+ }
+
+ // check if vertical position is already set
+ if (ePos == RectPos::Top || ePos == RectPos::Bottom)
+ // correct error in current horizontal position
+ switch (eHor)
+ { case RectHorAlign::Left:
+ aPos.AdjustX(rRect.GetItalicLeft() - GetItalicLeft() );
+ break;
+ case RectHorAlign::Center:
+ aPos.AdjustX(rRect.GetItalicCenterX() - GetItalicCenterX() );
+ break;
+ case RectHorAlign::Right:
+ aPos.AdjustX(rRect.GetItalicRight() - GetItalicRight() );
+ break;
+ default:
+ assert(false);
+ }
+
+ return aPos;
+}
+
+
+void SmRect::Union(const SmRect &rRect)
+ // rectangle union of current one with 'rRect'. The result is to be the
+ // smallest rectangles that covers the space of both rectangles.
+ // (empty rectangles cover no space)
+ //! Italic correction is NOT taken into account here!
+{
+ if (rRect.IsEmpty())
+ return;
+
+ long nL = rRect.GetLeft(),
+ nR = rRect.GetRight(),
+ nT = rRect.GetTop(),
+ nB = rRect.GetBottom(),
+ nGT = rRect.nGlyphTop,
+ nGB = rRect.nGlyphBottom;
+ if (!IsEmpty())
+ { long nTmp;
+
+ if ((nTmp = GetLeft()) < nL)
+ nL = nTmp;
+ if ((nTmp = GetRight()) > nR)
+ nR = nTmp;
+ if ((nTmp = GetTop()) < nT)
+ nT = nTmp;
+ if ((nTmp = GetBottom()) > nB)
+ nB = nTmp;
+ if ((nTmp = nGlyphTop) < nGT)
+ nGT = nTmp;
+ if ((nTmp = nGlyphBottom) > nGB)
+ nGB = nTmp;
+ }
+
+ SetLeft(nL);
+ SetRight(nR);
+ SetTop(nT);
+ SetBottom(nB);
+ nGlyphTop = nGT;
+ nGlyphBottom = nGB;
+}
+
+
+SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode)
+ // let current rectangle be the union of itself and 'rRect'
+ // (the smallest rectangle surrounding both). Also adapt values for
+ // 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces.
+ // The baseline is set according to 'eCopyMode'.
+ // If one of the rectangles has no relevant info the other one is copied.
+{
+ // get some values used for (italic) spaces adaptation
+ // ! (need to be done before changing current SmRect) !
+ long nL = std::min(GetItalicLeft(), rRect.GetItalicLeft()),
+ nR = std::max(GetItalicRight(), rRect.GetItalicRight());
+
+ Union(rRect);
+
+ SetItalicSpaces(GetLeft() - nL, nR - GetRight());
+
+ if (!HasAlignInfo())
+ CopyAlignInfo(rRect);
+ else if (rRect.HasAlignInfo())
+ {
+ assert(HasAlignInfo());
+ nAlignT = std::min(GetAlignT(), rRect.GetAlignT());
+ nAlignB = std::max(GetAlignB(), rRect.GetAlignB());
+ nHiAttrFence = std::min(GetHiAttrFence(), rRect.GetHiAttrFence());
+ nLoAttrFence = std::max(GetLoAttrFence(), rRect.GetLoAttrFence());
+
+ switch (eCopyMode)
+ { case RectCopyMBL::This:
+ // already done
+ break;
+ case RectCopyMBL::Arg:
+ CopyMBL(rRect);
+ break;
+ case RectCopyMBL::None:
+ bHasBaseline = false;
+ nAlignM = (nAlignT + nAlignB) / 2;
+ break;
+ case RectCopyMBL::Xor:
+ if (!HasBaseline())
+ CopyMBL(rRect);
+ break;
+ default :
+ assert(false);
+ }
+ }
+
+ return *this;
+}
+
+
+void SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
+ long nNewAlignM)
+ // as 'ExtendBy' but sets AlignM value to 'nNewAlignM'.
+ // (this version will be used in 'SmBinVerNode' to provide means to
+ // align eg "{a over b} over c" correctly where AlignM should not
+ // be (AlignT + AlignB) / 2)
+{
+ OSL_ENSURE(HasAlignInfo(), "Sm: no align info");
+
+ ExtendBy(rRect, eCopyMode);
+ nAlignM = nNewAlignM;
+}
+
+
+SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
+ bool bKeepVerAlignParams)
+ // as 'ExtendBy' but keeps original values for AlignT, -M and -B and
+ // baseline.
+ // (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't
+ // be allowed to modify these values.)
+{
+ long nOldAlignT = GetAlignT(),
+ nOldAlignM = GetAlignM(),
+ nOldAlignB = GetAlignB(),
+ nOldBaseline = nBaseline; //! depends not on 'HasBaseline'
+ bool bOldHasAlignInfo = HasAlignInfo();
+
+ ExtendBy(rRect, eCopyMode);
+
+ if (bKeepVerAlignParams)
+ { nAlignT = nOldAlignT;
+ nAlignM = nOldAlignM;
+ nAlignB = nOldAlignB;
+ nBaseline = nOldBaseline;
+ bHasAlignInfo = bOldHasAlignInfo;
+ }
+
+ return *this;
+}
+
+
+long SmRect::OrientedDist(const Point &rPoint) const
+ // return oriented distance of rPoint to the current rectangle,
+ // especially the return value is <= 0 iff the point is inside the
+ // rectangle.
+ // For simplicity the maximum-norm is used.
+{
+ bool bIsInside = IsInsideItalicRect(rPoint);
+
+ // build reference point to define the distance
+ Point aRef;
+ if (bIsInside)
+ { Point aIC (GetItalicCenterX(), GetCenterY());
+
+ aRef.setX( rPoint.X() >= aIC.X() ? GetItalicRight() : GetItalicLeft() );
+ aRef.setY( rPoint.Y() >= aIC.Y() ? GetBottom() : GetTop() );
+ }
+ else
+ {
+ // x-coordinate
+ if (rPoint.X() > GetItalicRight())
+ aRef.setX( GetItalicRight() );
+ else if (rPoint.X() < GetItalicLeft())
+ aRef.setX( GetItalicLeft() );
+ else
+ aRef.setX( rPoint.X() );
+ // y-coordinate
+ if (rPoint.Y() > GetBottom())
+ aRef.setY( GetBottom() );
+ else if (rPoint.Y() < GetTop())
+ aRef.setY( GetTop() );
+ else
+ aRef.setY( rPoint.Y() );
+ }
+
+ // build distance vector
+ Point aDist (aRef - rPoint);
+
+ long nAbsX = labs(aDist.X()),
+ nAbsY = labs(aDist.Y());
+
+ return bIsInside ? - std::min(nAbsX, nAbsY) : std::max (nAbsX, nAbsY);
+}
+
+
+bool SmRect::IsInsideRect(const Point &rPoint) const
+{
+ return rPoint.Y() >= GetTop()
+ && rPoint.Y() <= GetBottom()
+ && rPoint.X() >= GetLeft()
+ && rPoint.X() <= GetRight();
+}
+
+
+bool SmRect::IsInsideItalicRect(const Point &rPoint) const
+{
+ return rPoint.Y() >= GetTop()
+ && rPoint.Y() <= GetBottom()
+ && rPoint.X() >= GetItalicLeft()
+ && rPoint.X() <= GetItalicRight();
+}
+
+SmRect SmRect::AsGlyphRect() const
+{
+ SmRect aRect (*this);
+ aRect.SetTop(nGlyphTop);
+ aRect.SetBottom(nGlyphBottom);
+ return aRect;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/register.cxx b/starmath/source/register.cxx
new file mode 100644
index 000000000..89f7c2dd3
--- /dev/null
+++ b/starmath/source/register.cxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustring.hxx>
+
+#include <sfx2/sfxmodelfactory.hxx>
+
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+
+#include "register.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+extern "C" {
+
+SAL_DLLPUBLIC_EXPORT void* sm_component_getFactory( const char* pImplementationName,
+ void* pServiceManager,
+ void* /*pRegistryKey*/ )
+{
+ // Set default return value for this operation - if it failed.
+ void* pReturn = nullptr ;
+
+ if (
+ ( pImplementationName != nullptr ) &&
+ ( pServiceManager != nullptr )
+ )
+ {
+ // Define variables which are used in following macros.
+ Reference< XSingleServiceFactory > xFactory ;
+ Reference< XMultiServiceFactory > xServiceManager( static_cast< XMultiServiceFactory* >( pServiceManager ) ) ;
+
+ if (SmDocument_getImplementationName().equalsAscii(pImplementationName))
+ {
+ xFactory = ::sfx2::createSfxModelFactory( xServiceManager,
+ SmDocument_getImplementationName(),
+ SmDocument_createInstance,
+ SmDocument_getSupportedServiceNames() );
+ }
+
+ // Factory is valid - service was found.
+ if ( xFactory.is() )
+ {
+ xFactory->acquire();
+ pReturn = xFactory.get();
+ }
+ }
+
+ // Return with result of this operation.
+ return pReturn ;
+}
+} // extern "C"
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/register.hxx b/starmath/source/register.hxx
new file mode 100644
index 000000000..be875fbec
--- /dev/null
+++ b/starmath/source/register.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_REGISTER_HXX
+#define INCLUDED_STARMATH_SOURCE_REGISTER_HXX
+
+#include <sal/config.h>
+#include <sfx2/sfxmodelfactory.hxx>
+
+//Math document
+css::uno::Sequence< OUString >
+ SmDocument_getSupportedServiceNames() throw();
+OUString
+ SmDocument_getImplementationName() throw();
+/// @throws css::uno::Exception
+css::uno::Reference< css::uno::XInterface >
+ SmDocument_createInstance(const css::uno::Reference< css::lang::XMultiServiceFactory > & rSMgr, SfxModelFlags _nCreationFlags);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/rtfexport.cxx b/starmath/source/rtfexport.cxx
new file mode 100644
index 000000000..87e51a3b9
--- /dev/null
+++ b/starmath/source/rtfexport.cxx
@@ -0,0 +1,505 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "rtfexport.hxx"
+#include <node.hxx>
+
+#include <svtools/rtfkeywd.hxx>
+#include <filter/msfilter/rtfutil.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+SmRtfExport::SmRtfExport(const SmNode* pIn)
+ : SmWordExportBase(pIn)
+ , m_pBuffer(nullptr)
+ , m_nEncoding(RTL_TEXTENCODING_DONTKNOW)
+{
+}
+
+void SmRtfExport::ConvertFromStarMath(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding)
+{
+ if (!m_pTree)
+ return;
+ m_pBuffer = &rBuffer;
+ m_nEncoding = nEncoding;
+ m_pBuffer->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE LO_STRING_SVTOOLS_RTF_MOMATH " ");
+ HandleNode(m_pTree, 0);
+ m_pBuffer->append("}"); // moMath
+}
+
+// NOTE: This is still work in progress and unfinished, but it already covers a good
+// part of the rtf math stuff.
+
+void SmRtfExport::HandleVerticalStack(const SmNode* pNode, int nLevel)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MEQARR " ");
+ int size = pNode->GetNumSubNodes();
+ for (int i = 0; i < size; ++i)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->GetSubNode(i), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ }
+ m_pBuffer->append("}"); // meqArr
+}
+
+void SmRtfExport::HandleText(const SmNode* pNode, int /*nLevel*/)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MR " ");
+
+ if (pNode->GetToken().eType == TTEXT) // literal text
+ m_pBuffer->append(LO_STRING_SVTOOLS_RTF_MNOR " ");
+
+ auto pTemp = static_cast<const SmTextNode*>(pNode);
+ SAL_INFO("starmath.rtf", "Text: " << pTemp->GetText());
+ for (sal_Int32 i = 0; i < pTemp->GetText().getLength(); i++)
+ {
+ sal_uInt16 nChar = pTemp->GetText()[i];
+ OUString aValue(SmTextNode::ConvertSymbolToUnicode(nChar));
+ m_pBuffer->append(msfilter::rtfutil::OutString(aValue, m_nEncoding));
+ }
+
+ m_pBuffer->append("}"); // mr
+}
+
+void SmRtfExport::HandleFractions(const SmNode* pNode, int nLevel, const char* type)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MF " ");
+ if (type)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MTYPE " ");
+ m_pBuffer->append(type);
+ m_pBuffer->append("}"); // mtype
+ m_pBuffer->append("}"); // mfPr
+ }
+ assert(pNode->GetNumSubNodes() == 3);
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNUM " ");
+ HandleNode(pNode->GetSubNode(0), nLevel + 1);
+ m_pBuffer->append("}"); // mnum
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEN " ");
+ HandleNode(pNode->GetSubNode(2), nLevel + 1);
+ m_pBuffer->append("}"); // mden
+ m_pBuffer->append("}"); // mf
+}
+
+void SmRtfExport::HandleAttribute(const SmAttributNode* pNode, int nLevel)
+{
+ switch (pNode->Attribute()->GetToken().eType)
+ {
+ case TCHECK:
+ case TACUTE:
+ case TGRAVE:
+ case TBREVE:
+ case TCIRCLE:
+ case TVEC:
+ case TTILDE:
+ case THAT:
+ case TDOT:
+ case TDDOT:
+ case TDDDOT:
+ case TWIDETILDE:
+ case TWIDEHAT:
+ case TWIDEHARPOON:
+ case TWIDEVEC:
+ case TBAR:
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MACC " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MACCPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " ");
+ OUString aValue(pNode->Attribute()->GetToken().cMathChar);
+ m_pBuffer->append(msfilter::rtfutil::OutString(aValue, m_nEncoding));
+ m_pBuffer->append("}"); // mchr
+ m_pBuffer->append("}"); // maccPr
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->Body(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // macc
+ break;
+ }
+ case TOVERLINE:
+ case TUNDERLINE:
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBAR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBARPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MPOS " ");
+ m_pBuffer->append((pNode->Attribute()->GetToken().eType == TUNDERLINE) ? "bot" : "top");
+ m_pBuffer->append("}"); // mpos
+ m_pBuffer->append("}"); // mbarPr
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->Body(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mbar
+ break;
+ case TOVERSTRIKE:
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBORDERBOX " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBORDERBOXPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDETOP " 1}");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDEBOT " 1}");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDELEFT " 1}");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDERIGHT " 1}");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSTRIKEH " 1}");
+ m_pBuffer->append("}"); // mborderBoxPr
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->Body(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mborderBox
+ break;
+ default:
+ HandleAllSubNodes(pNode, nLevel);
+ break;
+ }
+}
+
+void SmRtfExport::HandleRoot(const SmRootNode* pNode, int nLevel)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MRAD " ");
+ if (const SmNode* argument = pNode->Argument())
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEG " ");
+ HandleNode(argument, nLevel + 1);
+ m_pBuffer->append("}"); // mdeg
+ }
+ else
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MRADPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEGHIDE " 1}");
+ m_pBuffer->append("}"); // mradPr
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEG " }"); // empty but present
+ }
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->Body(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mrad
+}
+
+namespace
+{
+OString mathSymbolToString(const SmNode* node, rtl_TextEncoding nEncoding)
+{
+ assert(node->GetType() == SmNodeType::Math || node->GetType() == SmNodeType::MathIdent);
+ auto txtnode = static_cast<const SmTextNode*>(node);
+ if (txtnode->GetText().isEmpty())
+ return OString();
+ assert(txtnode->GetText().getLength() == 1);
+ sal_Unicode chr = SmTextNode::ConvertSymbolToUnicode(txtnode->GetText()[0]);
+ OUString aValue(chr);
+ return msfilter::rtfutil::OutString(aValue, nEncoding);
+}
+}
+
+void SmRtfExport::HandleOperator(const SmOperNode* pNode, int nLevel)
+{
+ SAL_INFO("starmath.rtf", "Operator: " << int(pNode->GetToken().eType));
+ switch (pNode->GetToken().eType)
+ {
+ case TINT:
+ case TINTD:
+ case TIINT:
+ case TIIINT:
+ case TLINT:
+ case TLLINT:
+ case TLLLINT:
+ case TPROD:
+ case TCOPROD:
+ case TSUM:
+ {
+ const SmSubSupNode* subsup
+ = pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup
+ ? static_cast<const SmSubSupNode*>(pNode->GetSubNode(0))
+ : nullptr;
+ const SmNode* operation = subsup ? subsup->GetBody() : pNode->GetSubNode(0);
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNARY " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNARYPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " ");
+ m_pBuffer->append(mathSymbolToString(operation, m_nEncoding));
+ m_pBuffer->append("}"); // mchr
+ if (!subsup || !subsup->GetSubSup(CSUB))
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUBHIDE " 1}");
+ if (!subsup || !subsup->GetSubSup(CSUP))
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUPHIDE " 1}");
+ m_pBuffer->append("}"); // mnaryPr
+ if (!subsup || !subsup->GetSubSup(CSUB))
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " }");
+ else
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
+ HandleNode(subsup->GetSubSup(CSUB), nLevel + 1);
+ m_pBuffer->append("}"); // msub
+ }
+ if (!subsup || !subsup->GetSubSup(CSUP))
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " }");
+ else
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
+ HandleNode(subsup->GetSubSup(CSUP), nLevel + 1);
+ m_pBuffer->append("}"); // msup
+ }
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->GetSubNode(1), nLevel + 1); // body
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mnary
+ break;
+ }
+ case TLIM:
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFUNC " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFNAME " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->GetSymbol(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
+ if (const SmSubSupNode* subsup
+ = pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup
+ ? static_cast<const SmSubSupNode*>(pNode->GetSubNode(0))
+ : nullptr)
+ if (subsup->GetSubSup(CSUB))
+ HandleNode(subsup->GetSubSup(CSUB), nLevel + 1);
+ m_pBuffer->append("}"); // mlim
+ m_pBuffer->append("}"); // mlimLow
+ m_pBuffer->append("}"); // mfName
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->GetSubNode(1), nLevel + 1); // body
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mfunc
+ break;
+ default:
+ SAL_INFO("starmath.rtf", "TODO: " << OSL_THIS_FUNC << " unhandled oper type");
+ break;
+ }
+}
+
+void SmRtfExport::HandleSubSupScriptInternal(const SmSubSupNode* pNode, int nLevel, int flags)
+{
+ // rtf supports only a certain combination of sub/super scripts, but LO can have any,
+ // so try to merge it using several tags if necessary
+ if (flags == 0) // none
+ return;
+ if ((flags & (1 << RSUP | 1 << RSUB)) == (1 << RSUP | 1 << RSUB))
+ {
+ // m:sSubSup
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUBSUP " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << RSUP | 1 << RSUB);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
+ HandleNode(pNode->GetSubSup(RSUB), nLevel + 1);
+ m_pBuffer->append("}"); // msub
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
+ HandleNode(pNode->GetSubSup(RSUP), nLevel + 1);
+ m_pBuffer->append("}"); // msup
+ m_pBuffer->append("}"); // msubSup
+ }
+ else if ((flags & (1 << RSUB)) == 1 << RSUB)
+ {
+ // m:sSub
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUB " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << RSUB);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
+ HandleNode(pNode->GetSubSup(RSUB), nLevel + 1);
+ m_pBuffer->append("}"); // msub
+ m_pBuffer->append("}"); // msSub
+ }
+ else if ((flags & (1 << RSUP)) == 1 << RSUP)
+ {
+ // m:sSup
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUP " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << RSUP);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
+ HandleNode(pNode->GetSubSup(RSUP), nLevel + 1);
+ m_pBuffer->append("}"); // msup
+ m_pBuffer->append("}"); // msSup
+ }
+ else if ((flags & (1 << LSUP | 1 << LSUB)) == (1 << LSUP | 1 << LSUB))
+ {
+ // m:sPre
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSPRE " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
+ HandleNode(pNode->GetSubSup(LSUB), nLevel + 1);
+ m_pBuffer->append("}"); // msub
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
+ HandleNode(pNode->GetSubSup(LSUP), nLevel + 1);
+ m_pBuffer->append("}"); // msup
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << LSUP | 1 << LSUB);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // msPre
+ }
+ else if ((flags & (1 << CSUB)) == (1 << CSUB))
+ {
+ // m:limLow looks like a good element for central superscript
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << CSUB);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
+ HandleNode(pNode->GetSubSup(CSUB), nLevel + 1);
+ m_pBuffer->append("}"); // mlim
+ m_pBuffer->append("}"); // mlimLow
+ }
+ else if ((flags & (1 << CSUP)) == (1 << CSUP))
+ {
+ // m:limUpp looks like a good element for central superscript
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMUPP " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << CSUP);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
+ HandleNode(pNode->GetSubSup(CSUP), nLevel + 1);
+ m_pBuffer->append("}"); // mlim
+ m_pBuffer->append("}"); // mlimUpp
+ }
+ else
+ SAL_INFO("starmath.rtf", "TODO: " << OSL_THIS_FUNC << " unhandled subsup type");
+}
+
+void SmRtfExport::HandleMatrix(const SmMatrixNode* pNode, int nLevel)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MM " ");
+ for (size_t row = 0; row < pNode->GetNumRows(); ++row)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MMR " ");
+ for (size_t col = 0; col < pNode->GetNumCols(); ++col)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ if (const SmNode* node = pNode->GetSubNode(row * pNode->GetNumCols() + col))
+ HandleNode(node, nLevel + 1);
+ m_pBuffer->append("}"); // me
+ }
+ m_pBuffer->append("}"); // mmr
+ }
+ m_pBuffer->append("}"); // mm
+}
+
+void SmRtfExport::HandleBrace(const SmBraceNode* pNode, int nLevel)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MD " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBEGCHR " ");
+ m_pBuffer->append(mathSymbolToString(pNode->OpeningBrace(), m_nEncoding));
+ m_pBuffer->append("}"); // mbegChr
+ std::vector<const SmNode*> subnodes;
+ if (pNode->Body()->GetType() == SmNodeType::Bracebody)
+ {
+ auto body = static_cast<const SmBracebodyNode*>(pNode->Body());
+ bool separatorWritten = false; // assume all separators are the same
+ for (size_t i = 0; i < body->GetNumSubNodes(); ++i)
+ {
+ const SmNode* subnode = body->GetSubNode(i);
+ if (subnode->GetType() == SmNodeType::Math
+ || subnode->GetType() == SmNodeType::MathIdent)
+ {
+ // do not write, but write what separator it is
+ auto math = static_cast<const SmMathSymbolNode*>(subnode);
+ if (!separatorWritten)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSEPCHR " ");
+ m_pBuffer->append(mathSymbolToString(math, m_nEncoding));
+ m_pBuffer->append("}"); // msepChr
+ separatorWritten = true;
+ }
+ }
+ else
+ subnodes.push_back(subnode);
+ }
+ }
+ else
+ subnodes.push_back(pNode->Body());
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MENDCHR " ");
+ m_pBuffer->append(mathSymbolToString(pNode->ClosingBrace(), m_nEncoding));
+ m_pBuffer->append("}"); // mendChr
+ m_pBuffer->append("}"); // mdPr
+ for (const SmNode* subnode : subnodes)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(subnode, nLevel + 1);
+ m_pBuffer->append("}"); // me
+ }
+ m_pBuffer->append("}"); // md
+}
+
+void SmRtfExport::HandleVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel)
+{
+ SAL_INFO("starmath.rtf", "Vertical: " << int(pNode->GetToken().eType));
+ switch (pNode->GetToken().eType)
+ {
+ case TOVERBRACE:
+ case TUNDERBRACE:
+ {
+ bool top = (pNode->GetToken().eType == TOVERBRACE);
+ if (top)
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMUPP " ");
+ else
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MGROUPCHR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MGROUPCHRPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " ");
+ m_pBuffer->append(mathSymbolToString(pNode->Brace(), m_nEncoding));
+ m_pBuffer->append("}"); // mchr
+ // TODO not sure if pos and vertJc are correct
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MPOS " ")
+ .append(top ? "top" : "bot")
+ .append("}");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MVERTJC " ")
+ .append(top ? "bot" : "top")
+ .append("}");
+ m_pBuffer->append("}"); // mgroupChrPr
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->Body(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mgroupChr
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
+ HandleNode(pNode->Script(), nLevel + 1);
+ m_pBuffer->append("}"); // mlim
+ m_pBuffer->append("}"); // mlimUpp or mlimLow
+ break;
+ }
+ default:
+ SAL_INFO("starmath.rtf", "TODO: " << OSL_THIS_FUNC << " unhandled vertical brace type");
+ break;
+ }
+}
+
+void SmRtfExport::HandleBlank()
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MR " ");
+ m_pBuffer->append(" ");
+ m_pBuffer->append("}"); // mr
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/rtfexport.hxx b/starmath/source/rtfexport.hxx
new file mode 100644
index 000000000..3a1dd4feb
--- /dev/null
+++ b/starmath/source/rtfexport.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/.
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_RTFEXPORT_HXX
+#define INCLUDED_STARMATH_SOURCE_RTFEXPORT_HXX
+
+#include "wordexportbase.hxx"
+
+#include <rtl/strbuf.hxx>
+
+/**
+ Class implementing writing of formulas to RTF.
+ */
+class SmRtfExport : public SmWordExportBase
+{
+public:
+ explicit SmRtfExport(const SmNode* pIn);
+ void ConvertFromStarMath(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding);
+
+private:
+ void HandleVerticalStack(const SmNode* pNode, int nLevel) override;
+ void HandleText(const SmNode* pNode, int nLevel) override;
+ void HandleFractions(const SmNode* pNode, int nLevel, const char* type) override;
+ void HandleRoot(const SmRootNode* pNode, int nLevel) override;
+ void HandleAttribute(const SmAttributNode* pNode, int nLevel) override;
+ void HandleOperator(const SmOperNode* pNode, int nLevel) override;
+ void HandleSubSupScriptInternal(const SmSubSupNode* pNode, int nLevel, int flags) override;
+ void HandleMatrix(const SmMatrixNode* pNode, int nLevel) override;
+ void HandleBrace(const SmBraceNode* pNode, int nLevel) override;
+ void HandleVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel) override;
+ void HandleBlank() override;
+
+ OStringBuffer* m_pBuffer;
+ rtl_TextEncoding m_nEncoding;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/smdetect.cxx b/starmath/source/smdetect.cxx
new file mode 100644
index 000000000..aa6280156
--- /dev/null
+++ b/starmath/source/smdetect.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 "smdetect.hxx"
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <sfx2/docfile.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <sal/log.hxx>
+#include <sot/storage.hxx>
+#include <tools/diagnose_ex.h>
+
+#include "eqnolefilehdr.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using utl::MediaDescriptor;
+
+SmFilterDetect::SmFilterDetect()
+{
+}
+
+SmFilterDetect::~SmFilterDetect()
+{
+}
+
+OUString SAL_CALL SmFilterDetect::detect( Sequence< PropertyValue >& lDescriptor )
+{
+ MediaDescriptor aMediaDesc( lDescriptor );
+ uno::Reference< io::XInputStream > xInStream ( aMediaDesc[MediaDescriptor::PROP_INPUTSTREAM()], uno::UNO_QUERY );
+ if ( !xInStream.is() )
+ return OUString();
+
+ SfxMedium aMedium;
+ aMedium.UseInteractionHandler( false );
+ aMedium.setStreamToLoadFrom( xInStream, true );
+
+ SvStream *pInStrm = aMedium.GetInStream();
+ if ( !pInStrm || pInStrm->GetError() )
+ return OUString();
+
+ // Do not attempt to create an SotStorage on a
+ // 0-length stream as that would create the compound
+ // document header on the stream and effectively write to
+ // disk!
+ pInStrm->Seek( STREAM_SEEK_TO_BEGIN );
+ if ( pInStrm->remainingSize() == 0 )
+ return OUString();
+
+ bool bStorageOk = false;
+ try
+ {
+ tools::SvRef<SotStorage> aStorage = new SotStorage( pInStrm, false );
+ bStorageOk = !aStorage->GetError();
+ if (bStorageOk)
+ {
+ if ( aStorage->IsStream("Equation Native") )
+ {
+ sal_uInt8 nVersion;
+ if ( GetMathTypeVersion( aStorage.get(), nVersion ) && nVersion <=3 )
+ return "math_MathType_3x";
+ }
+ }
+ }
+ catch (const css::ucb::ContentCreationException &)
+ {
+ TOOLS_WARN_EXCEPTION("starmath", "SmFilterDetect::detect caught" );
+ }
+
+ if (!bStorageOk)
+ {
+ // 200 should be enough for the XML
+ // version, encoding and !DOCTYPE
+ // stuff I hope?
+ static const sal_uInt16 nBufferSize = 200;
+ char aBuffer[nBufferSize+1];
+ pInStrm->Seek( STREAM_SEEK_TO_BEGIN );
+ pInStrm->StartReadingUnicodeText( RTL_TEXTENCODING_DONTKNOW ); // avoid BOM marker
+ auto nBytesRead = pInStrm->ReadBytes( aBuffer, nBufferSize );
+ if (nBytesRead >= 6)
+ {
+ aBuffer[nBytesRead] = 0;
+ bool bIsMathType = false;
+ if (0 == strncmp( "<?xml", aBuffer, 5))
+ bIsMathType = (strstr( aBuffer, "<math>" ) ||
+ strstr( aBuffer, "<math " ) ||
+ strstr( aBuffer, "<math:math " ));
+ else
+ // this is the old <math tag to MathML in the beginning of the XML file
+ bIsMathType = (0 == strncmp( "<math ", aBuffer, 6) ||
+ 0 == strncmp( "<math> ", aBuffer, 7) ||
+ 0 == strncmp( "<math:math> ", aBuffer, 12));
+
+ if ( bIsMathType )
+ return "math_MathML_XML_Math";
+ }
+ }
+
+ return OUString();
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SmFilterDetect::getImplementationName()
+{
+ return "com.sun.star.comp.math.FormatDetector";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SmFilterDetect::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SmFilterDetect::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ExtendedTypeDetection" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+math_FormatDetector_get_implementation(uno::XComponentContext* /*pCtx*/,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SmFilterDetect);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/smdetect.hxx b/starmath/source/smdetect.hxx
new file mode 100644
index 000000000..7869146f4
--- /dev/null
+++ b/starmath/source/smdetect.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_SMDETECT_HXX
+#define INCLUDED_STARMATH_SOURCE_SMDETECT_HXX
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/document/XExtendedFilterDetection.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+
+namespace com
+{
+ namespace sun
+ {
+ namespace star
+ {
+ namespace beans
+ {
+ struct PropertyValue;
+ }
+ }
+ }
+}
+
+class SmFilterDetect : public ::cppu::WeakImplHelper< css::document::XExtendedFilterDetection, css::lang::XServiceInfo >
+{
+public:
+ explicit SmFilterDetect();
+ virtual ~SmFilterDetect() override;
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XExtendedFilterDetect
+ virtual OUString SAL_CALL detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/smdll.cxx b/starmath/source/smdll.cxx
new file mode 100644
index 000000000..78dc2183b
--- /dev/null
+++ b/starmath/source/smdll.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <svx/svxids.hrc>
+#include <svx/modctrl.hxx>
+#include <svx/zoomctrl.hxx>
+#include <svx/zoomsliderctrl.hxx>
+#include <sfx2/docfac.hxx>
+#include <svx/lboxctrl.hxx>
+#include <sfx2/app.hxx>
+
+#include <smdll.hxx>
+#include <smmod.hxx>
+#include <document.hxx>
+#include <view.hxx>
+
+#include <ElementsDockingWindow.hxx>
+
+#include <starmath.hrc>
+
+#include <svx/xmlsecctrl.hxx>
+
+namespace
+{
+ class SmDLL
+ {
+ public:
+ SmDLL();
+ };
+
+ SmDLL::SmDLL()
+ {
+ if ( SfxApplication::GetModule(SfxToolsModule::Math) ) // Module already active
+ return;
+
+ SfxObjectFactory& rFactory = SmDocShell::Factory();
+
+ auto pUniqueModule = std::make_unique<SmModule>(&rFactory);
+ SmModule* pModule = pUniqueModule.get();
+ SfxApplication::SetModule(SfxToolsModule::Math, std::move(pUniqueModule));
+
+ rFactory.SetDocumentServiceName( "com.sun.star.formula.FormulaProperties" );
+
+ SmModule::RegisterInterface(pModule);
+ SmDocShell::RegisterInterface(pModule);
+ SmViewShell::RegisterInterface(pModule);
+
+ SmViewShell::RegisterFactory(SFX_INTERFACE_SFXAPP);
+
+ SvxZoomStatusBarControl::RegisterControl(SID_ATTR_ZOOM, pModule);
+ SvxZoomSliderControl::RegisterControl(SID_ATTR_ZOOMSLIDER, pModule);
+ SvxModifyControl::RegisterControl(SID_TEXTSTATUS, pModule);
+ XmlSecStatusBarControl::RegisterControl(SID_SIGNATURE, pModule);
+
+ SmCmdBoxWrapper::RegisterChildWindow(true);
+ SmElementsDockingWindowWrapper::RegisterChildWindow(true);
+ }
+
+ struct theSmDLLInstance : public rtl::Static<SmDLL, theSmDLLInstance> {};
+}
+
+namespace SmGlobals
+{
+ void ensure()
+ {
+ theSmDLLInstance::get();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/smmod.cxx b/starmath/source/smmod.cxx
new file mode 100644
index 000000000..788f5f5e2
--- /dev/null
+++ b/starmath/source/smmod.cxx
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <sfx2/objface.hxx>
+#include <svl/whiter.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svx/svxids.hrc>
+#include <unotools/resmgr.hxx>
+#include <vcl/virdev.hxx>
+#include <unotools/syslocale.hxx>
+#include <smmod.hxx>
+#include "cfgitem.hxx"
+#include <dialog.hxx>
+#include <edit.hxx>
+#include <view.hxx>
+#include <smmod.hrc>
+#include <starmath.hrc>
+#include <svx/modctrl.hxx>
+#include <svtools/colorcfg.hxx>
+
+
+#define ShellClass_SmModule
+#include <smslots.hxx>
+
+OUString SmResId(const char* pId)
+{
+ return Translate::get(pId, SM_MOD()->GetResLocale());
+}
+
+OUString SmLocalizedSymbolData::GetUiSymbolName( const OUString &rExportName )
+{
+ OUString aRes;
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UI_SYMBOL_NAMES); ++i)
+ {
+ if (rExportName.equalsAscii(strchr(RID_UI_SYMBOL_NAMES[i], '\004') + 1))
+ {
+ aRes = SmResId(RID_UI_SYMBOL_NAMES[i]);
+ break;
+ }
+ }
+
+ return aRes;
+}
+
+OUString SmLocalizedSymbolData::GetExportSymbolName( const OUString &rUiName )
+{
+ OUString aRes;
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UI_SYMBOL_NAMES); ++i)
+ {
+ if (rUiName == SmResId(RID_UI_SYMBOL_NAMES[i]))
+ {
+ const char *pKey = strchr(RID_UI_SYMBOL_NAMES[i], '\004') + 1;
+ aRes = OUString(pKey, strlen(pKey), RTL_TEXTENCODING_UTF8);
+ break;
+ }
+ }
+
+ return aRes;
+}
+
+OUString SmLocalizedSymbolData::GetUiSymbolSetName( const OUString &rExportName )
+{
+ OUString aRes;
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UI_SYMBOLSET_NAMES); ++i)
+ {
+ if (rExportName.equalsAscii(strchr(RID_UI_SYMBOLSET_NAMES[i], '\004') + 1))
+ {
+ aRes = SmResId(RID_UI_SYMBOLSET_NAMES[i]);
+ break;
+ }
+ }
+
+ return aRes;
+}
+
+OUString SmLocalizedSymbolData::GetExportSymbolSetName( const OUString &rUiName )
+{
+ OUString aRes;
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UI_SYMBOLSET_NAMES); ++i)
+ {
+ if (rUiName == SmResId(RID_UI_SYMBOLSET_NAMES[i]))
+ {
+ const char *pKey = strchr(RID_UI_SYMBOLSET_NAMES[i], '\004') + 1;
+ aRes = OUString(pKey, strlen(pKey), RTL_TEXTENCODING_UTF8);
+ break;
+ }
+ }
+
+ return aRes;
+}
+
+SFX_IMPL_INTERFACE(SmModule, SfxModule)
+
+void SmModule::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterStatusBar(StatusBarId::MathStatusBar);
+}
+
+SmModule::SmModule(SfxObjectFactory* pObjFact)
+ : SfxModule("sm", {pObjFact})
+{
+ SetName("StarMath");
+
+ SvxModifyControl::RegisterControl(SID_DOC_MODIFIED, this);
+}
+
+SmModule::~SmModule()
+{
+ if (mpColorConfig)
+ mpColorConfig->RemoveListener(this);
+ mpVirtualDev.disposeAndClear();
+}
+
+svtools::ColorConfig & SmModule::GetColorConfig()
+{
+ if(!mpColorConfig)
+ {
+ mpColorConfig.reset(new svtools::ColorConfig);
+ mpColorConfig->AddListener(this);
+ }
+ return *mpColorConfig;
+}
+
+void SmModule::ConfigurationChanged(utl::ConfigurationBroadcaster* pBrdCst, ConfigurationHints)
+{
+ if (pBrdCst == mpColorConfig.get())
+ {
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (dynamic_cast<const SmViewShell *>(pViewShell) != nullptr)
+ pViewShell->GetWindow()->Invalidate();
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ }
+}
+
+SmMathConfig * SmModule::GetConfig()
+{
+ if(!mpConfig)
+ mpConfig.reset(new SmMathConfig);
+ return mpConfig.get();
+}
+
+SmSymbolManager & SmModule::GetSymbolManager()
+{
+ return GetConfig()->GetSymbolManager();
+}
+
+const SvtSysLocale& SmModule::GetSysLocale()
+{
+ if( !mpSysLocale )
+ mpSysLocale.reset(new SvtSysLocale);
+ return *mpSysLocale;
+}
+
+VirtualDevice &SmModule::GetDefaultVirtualDev()
+{
+ if (!mpVirtualDev)
+ {
+ mpVirtualDev.reset( VclPtr<VirtualDevice>::Create() );
+ mpVirtualDev->SetReferenceDevice( VirtualDevice::RefDevMode::MSO1 );
+ }
+ return *mpVirtualDev;
+}
+
+void SmModule::GetState(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter(rSet);
+
+ for (sal_uInt16 nWh = aIter.FirstWhich(); 0 != nWh; nWh = aIter.NextWhich())
+ switch (nWh)
+ {
+ case SID_CONFIGEVENT :
+ rSet.DisableItem(SID_CONFIGEVENT);
+ break;
+ }
+}
+
+std::unique_ptr<SfxItemSet> SmModule::CreateItemSet( sal_uInt16 nId )
+{
+ std::unique_ptr<SfxItemSet> pRet;
+ if(nId == SID_SM_EDITOPTIONS)
+ {
+ pRet = std::make_unique<SfxItemSet>(
+ GetPool(),
+ svl::Items< //TP_SMPRINT
+ SID_PRINTTITLE, SID_PRINTZOOM,
+ SID_NO_RIGHT_SPACES, SID_SAVE_ONLY_USED_SYMBOLS,
+ SID_AUTO_CLOSE_BRACKETS, SID_AUTO_CLOSE_BRACKETS>{});
+
+ GetConfig()->ConfigToItemSet(*pRet);
+ }
+ return pRet;
+}
+
+void SmModule::ApplyItemSet( sal_uInt16 nId, const SfxItemSet& rSet )
+{
+ if(nId == SID_SM_EDITOPTIONS)
+ {
+ GetConfig()->ItemSetToConfig(rSet);
+ }
+}
+
+std::unique_ptr<SfxTabPage> SmModule::CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet )
+{
+ std::unique_ptr<SfxTabPage> xRet;
+ if (nId == SID_SM_TP_PRINTOPTIONS)
+ xRet = SmPrintOptionsTabPage::Create(pPage, pController, rSet);
+ return xRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/symbol.cxx b/starmath/source/symbol.cxx
new file mode 100644
index 000000000..5e6a6486a
--- /dev/null
+++ b/starmath/source/symbol.cxx
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vector>
+
+#include <symbol.hxx>
+#include <utility.hxx>
+#include "cfgitem.hxx"
+#include <smmod.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+
+SmSym::SmSym() :
+ m_aName(OUString("unknown")),
+ m_aSetName(OUString("unknown")),
+ m_cChar('\0'),
+ m_bPredefined(false)
+{
+ m_aExportName = m_aName;
+ m_aFace.SetTransparent(true);
+ m_aFace.SetAlignment(ALIGN_BASELINE);
+}
+
+
+SmSym::SmSym(const SmSym& rSymbol)
+{
+ *this = rSymbol;
+}
+
+
+SmSym::SmSym(const OUString& rName, const vcl::Font& rFont, sal_UCS4 cChar,
+ const OUString& rSet, bool bIsPredefined)
+{
+ m_aName = m_aExportName = rName;
+
+ m_aFace = rFont;
+ m_aFace.SetTransparent(true);
+ m_aFace.SetAlignment(ALIGN_BASELINE);
+
+ m_cChar = cChar;
+ m_aSetName = rSet;
+ m_bPredefined = bIsPredefined;
+}
+
+
+SmSym& SmSym::operator = (const SmSym& rSymbol)
+{
+ m_aName = rSymbol.m_aName;
+ m_aExportName = rSymbol.m_aExportName;
+ m_cChar = rSymbol.m_cChar;
+ m_aFace = rSymbol.m_aFace;
+ m_aSetName = rSymbol.m_aSetName;
+ m_bPredefined = rSymbol.m_bPredefined;
+
+ SM_MOD()->GetSymbolManager().SetModified(true);
+
+ return *this;
+}
+
+
+bool SmSym::IsEqualInUI( const SmSym& rSymbol ) const
+{
+ return m_aName == rSymbol.m_aName &&
+ m_aFace == rSymbol.m_aFace &&
+ m_cChar == rSymbol.m_cChar;
+}
+
+/**************************************************************************/
+
+
+SmSymbolManager::SmSymbolManager()
+{
+ m_bModified = false;
+}
+
+
+SmSymbolManager::SmSymbolManager(const SmSymbolManager& rSymbolSetManager)
+{
+ m_aSymbols = rSymbolSetManager.m_aSymbols;
+ m_bModified = true;
+}
+
+
+SmSymbolManager::~SmSymbolManager()
+{
+}
+
+
+SmSymbolManager& SmSymbolManager::operator = (const SmSymbolManager& rSymbolSetManager)
+{
+ m_aSymbols = rSymbolSetManager.m_aSymbols;
+ m_bModified = true;
+ return *this;
+}
+
+
+SmSym *SmSymbolManager::GetSymbolByName(const OUString& rSymbolName)
+{
+ SmSym *pRes = nullptr;
+ SymbolMap_t::iterator aIt( m_aSymbols.find( rSymbolName ) );
+ if (aIt != m_aSymbols.end())
+ pRes = &aIt->second;
+ return pRes;
+}
+
+
+SymbolPtrVec_t SmSymbolManager::GetSymbols() const
+{
+ SymbolPtrVec_t aRes;
+ for (const auto& rEntry : m_aSymbols)
+ aRes.push_back( &rEntry.second );
+// OSL_ENSURE( sSymbols.size() == m_aSymbols.size(), "number of symbols mismatch " );
+ return aRes;
+}
+
+
+bool SmSymbolManager::AddOrReplaceSymbol( const SmSym &rSymbol, bool bForceChange )
+{
+ bool bAdded = false;
+
+ const OUString& aSymbolName( rSymbol.GetName() );
+ if (!aSymbolName.isEmpty() && !rSymbol.GetSymbolSetName().isEmpty())
+ {
+ const SmSym *pFound = GetSymbolByName( aSymbolName );
+ const bool bSymbolConflict = pFound && !pFound->IsEqualInUI( rSymbol );
+
+ // avoid having the same symbol name twice but with different symbols in use
+ if (!pFound || bForceChange)
+ {
+ m_aSymbols[ aSymbolName ] = rSymbol;
+ bAdded = true;
+ }
+ else if (bSymbolConflict)
+ {
+ // TODO: to solve this a document owned symbol manager would be required ...
+ SAL_WARN("starmath", "symbol conflict, different symbol with same name found!");
+ // symbols in all formulas. A copy of the global one would be needed here
+ // and then the new symbol has to be forcefully applied. This would keep
+ // the current formula intact but will leave the set of symbols in the
+ // global symbol manager somewhat to chance.
+ }
+
+ OSL_ENSURE( bAdded, "failed to add symbol" );
+ if (bAdded)
+ m_bModified = true;
+ OSL_ENSURE( bAdded || (pFound && !bSymbolConflict), "AddOrReplaceSymbol: unresolved symbol conflict" );
+ }
+
+ return bAdded;
+}
+
+
+void SmSymbolManager::RemoveSymbol( const OUString & rSymbolName )
+{
+ if (!rSymbolName.isEmpty())
+ {
+ size_t nOldSize = m_aSymbols.size();
+ m_aSymbols.erase( rSymbolName );
+ m_bModified = nOldSize != m_aSymbols.size();
+ }
+}
+
+
+std::set< OUString > SmSymbolManager::GetSymbolSetNames() const
+{
+ std::set< OUString > aRes;
+ for (const auto& rEntry : m_aSymbols)
+ aRes.insert( rEntry.second.GetSymbolSetName() );
+ return aRes;
+}
+
+
+SymbolPtrVec_t SmSymbolManager::GetSymbolSet( const OUString& rSymbolSetName )
+{
+ SymbolPtrVec_t aRes;
+ if (!rSymbolSetName.isEmpty())
+ {
+ for (const auto& rEntry : m_aSymbols)
+ {
+ if (rEntry.second.GetSymbolSetName() == rSymbolSetName)
+ aRes.push_back( &rEntry.second );
+ }
+ }
+ return aRes;
+}
+
+
+void SmSymbolManager::Load()
+{
+ std::vector< SmSym > aSymbols;
+ SmMathConfig &rCfg = *SM_MOD()->GetConfig();
+ rCfg.GetSymbols( aSymbols );
+ size_t nSymbolCount = aSymbols.size();
+
+ m_aSymbols.clear();
+ for (size_t i = 0; i < nSymbolCount; ++i)
+ {
+ const SmSym &rSym = aSymbols[i];
+ OSL_ENSURE( !rSym.GetName().isEmpty(), "symbol without name!" );
+ if (!rSym.GetName().isEmpty())
+ AddOrReplaceSymbol( rSym );
+ }
+ m_bModified = true;
+
+ if (0 == nSymbolCount)
+ {
+ SAL_WARN("starmath", "no symbol set found");
+ m_bModified = false;
+ }
+
+ // now add a %i... symbol to the 'iGreek' set for every symbol found in the 'Greek' set.
+ const OUString aGreekSymbolSetName(SmLocalizedSymbolData::GetUiSymbolSetName("Greek"));
+ const SymbolPtrVec_t aGreekSymbols( GetSymbolSet( aGreekSymbolSetName ) );
+ OUString aSymbolSetName = "i" + aGreekSymbolSetName;
+ size_t nSymbols = aGreekSymbols.size();
+ for (size_t i = 0; i < nSymbols; ++i)
+ {
+ // make the new symbol a copy but with ITALIC_NORMAL, and add it to iGreek
+ const SmSym &rSym = *aGreekSymbols[i];
+ vcl::Font aFont( rSym.GetFace() );
+ OSL_ENSURE( aFont.GetItalic() == ITALIC_NONE, "expected Font with ITALIC_NONE, failed." );
+ aFont.SetItalic( ITALIC_NORMAL );
+ OUString aSymbolName = "i" + rSym.GetName();
+ SmSym aSymbol( aSymbolName, aFont, rSym.GetCharacter(),
+ aSymbolSetName, true /*bIsPredefined*/ );
+
+ AddOrReplaceSymbol( aSymbol );
+ }
+}
+
+void SmSymbolManager::Save()
+{
+ if (!m_bModified)
+ return;
+
+ SmMathConfig &rCfg = *SM_MOD()->GetConfig();
+
+ // prepare to skip symbols from iGreek on saving
+ OUString aSymbolSetName = "i" +
+ SmLocalizedSymbolData::GetUiSymbolSetName("Greek");
+
+ SymbolPtrVec_t aTmp( GetSymbols() );
+ std::vector< SmSym > aSymbols;
+ for (const SmSym* i : aTmp)
+ {
+ // skip symbols from iGreek set since those symbols always get added
+ // by computational means in SmSymbolManager::Load
+ if (i->GetSymbolSetName() != aSymbolSetName)
+ aSymbols.push_back( *i );
+ }
+ rCfg.SetSymbols( aSymbols );
+
+ m_bModified = false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/tmpdevice.cxx b/starmath/source/tmpdevice.cxx
new file mode 100644
index 000000000..074903d3c
--- /dev/null
+++ b/starmath/source/tmpdevice.cxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <smmod.hxx>
+#include "tmpdevice.hxx"
+
+#include <svtools/colorcfg.hxx>
+#include <vcl/window.hxx>
+#include <sal/log.hxx>
+
+// SmTmpDevice
+// Allows for font and color changes. The original settings will be restored
+// in the destructor.
+// It's main purpose is to allow for the "const" in the 'OutputDevice'
+// argument in the 'Arrange' functions and restore changes made in the 'Draw'
+// functions.
+// Usually a MapMode of 1/100th mm will be used.
+
+SmTmpDevice::SmTmpDevice(OutputDevice &rTheDev, bool bUseMap100th_mm) :
+ rOutDev(rTheDev)
+{
+ rOutDev.Push(PushFlags::FONT | PushFlags::MAPMODE |
+ PushFlags::LINECOLOR | PushFlags::FILLCOLOR | PushFlags::TEXTCOLOR);
+ if (bUseMap100th_mm && MapUnit::Map100thMM != rOutDev.GetMapMode().GetMapUnit())
+ {
+ SAL_WARN("starmath", "incorrect MapMode?");
+ rOutDev.SetMapMode(MapMode(MapUnit::Map100thMM)); // format for 100% always
+ }
+}
+
+
+Color SmTmpDevice::GetTextColor(const Color& rTextColor)
+{
+ if (rTextColor == COL_AUTO)
+ {
+ Color aConfigFontColor = SM_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+ return rOutDev.GetReadableFontColor(aConfigFontColor, rOutDev.GetBackgroundColor());
+ }
+
+ return rTextColor;
+}
+
+
+void SmTmpDevice::SetFont(const vcl::Font &rNewFont)
+{
+ rOutDev.SetFont(rNewFont);
+ rOutDev.SetTextColor(GetTextColor(rNewFont.GetColor()));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/tmpdevice.hxx b/starmath/source/tmpdevice.hxx
new file mode 100644
index 000000000..e7c812cd6
--- /dev/null
+++ b/starmath/source/tmpdevice.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_TMPDEVICE_HXX
+#define INCLUDED_STARMATH_SOURCE_TMPDEVICE_HXX
+
+#include <tools/color.hxx>
+#include <vcl/outdev.hxx>
+
+class SmTmpDevice
+{
+ OutputDevice &rOutDev;
+
+ SmTmpDevice(const SmTmpDevice&) = delete;
+ SmTmpDevice& operator=(const SmTmpDevice&) = delete;
+
+ Color GetTextColor(const Color& rTextColor);
+
+public:
+ SmTmpDevice(OutputDevice &rTheDev, bool bUseMap100th_mm);
+ ~SmTmpDevice() COVERITY_NOEXCEPT_FALSE { rOutDev.Pop(); }
+
+ void SetFont(const vcl::Font &rNewFont);
+
+ void SetLineColor(const Color& rColor) { rOutDev.SetLineColor(GetTextColor(rColor)); }
+ void SetFillColor(const Color& rColor) { rOutDev.SetFillColor(GetTextColor(rColor)); }
+
+ operator OutputDevice & () { return rOutDev; }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/typemap.cxx b/starmath/source/typemap.cxx
new file mode 100644
index 000000000..ba7910747
--- /dev/null
+++ b/starmath/source/typemap.cxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_options.h>
+
+#include <sfx2/msg.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <svx/zoomslideritem.hxx>
+#include <svl/slstitm.hxx>
+
+#ifdef DISABLE_DYNLOADING
+/* Avoid clash with the ones from svx/source/form/typemap.cxx */
+#define aSfxInt16Item_Impl starmath_source_appl_typemap_aSfxInt16Item_Impl
+#endif
+
+#define SFX_TYPEMAP
+#include <smslots.hxx>
+
+#ifdef DISABLE_DYNLOADING
+#undef aSfxInt16Item_Impl
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/uiobject.cxx b/starmath/source/uiobject.cxx
new file mode 100644
index 000000000..a6f0c47cb
--- /dev/null
+++ b/starmath/source/uiobject.cxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include "uiobject.hxx"
+#include <vcl/layout.hxx>
+#include <ElementsDockingWindow.hxx>
+
+ElementUIObject::ElementUIObject(SmElementsControl* pElementSelector,
+ const OUString& rID):
+ mpElementsSelector(pElementSelector),
+ maID(rID)
+{
+}
+
+SmElement* ElementUIObject::get_element()
+{
+ sal_uInt32 nID = maID.toUInt32();
+ size_t n = mpElementsSelector->maElementList.size();
+ if (nID >= n)
+ return nullptr;
+
+ return mpElementsSelector->maElementList[nID].get();
+}
+
+StringMap ElementUIObject::get_state()
+{
+ StringMap aMap;
+ aMap["ID"] = maID;
+
+ SmElement* pElement = get_element();
+ if (pElement)
+ aMap["Text"] = pElement->getText();
+
+ return aMap;
+}
+
+void ElementUIObject::execute(const OUString& rAction,
+ const StringMap& /*rParameters*/)
+{
+ if (rAction == "SELECT")
+ {
+ SmElement* pElement = get_element();
+ if (pElement)
+ mpElementsSelector->maSelectHdlLink.Call(*pElement);
+ }
+}
+
+ElementSelectorUIObject::ElementSelectorUIObject(vcl::Window* pElementSelectorWindow, SmElementsControl* pElementSelector)
+ : WindowUIObject(pElementSelectorWindow)
+ , mpElementsSelector(pElementSelector)
+{
+}
+
+StringMap ElementSelectorUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+
+ SmElement* pElement = mpElementsSelector->current();
+ if (pElement)
+ aMap["CurrentEntry"] = pElement->getText();
+
+ aMap["CurrentSelection"] = OUString::fromUtf8(mpElementsSelector->msCurrentSetId);
+
+ return aMap;
+}
+
+std::unique_ptr<UIObject> ElementSelectorUIObject::get_child(const OUString& rID)
+{
+ size_t nID = rID.toInt32();
+ size_t n = mpElementsSelector->maElementList.size();
+ if (nID >= n)
+ throw css::uno::RuntimeException("invalid id");
+
+ return std::unique_ptr<UIObject>(new ElementUIObject(mpElementsSelector, rID));
+}
+
+std::set<OUString> ElementSelectorUIObject::get_children() const
+{
+ std::set<OUString> aChildren;
+
+ size_t n = mpElementsSelector->maElementList.size();
+ for (size_t i = 0; i < n; ++i)
+ {
+ aChildren.insert(OUString::number(i));
+ }
+
+ return aChildren;
+}
+
+std::unique_ptr<UIObject> ElementSelectorUIObject::create(vcl::Window* pWindow)
+{
+ VclDrawingArea* pSmElementsWin = dynamic_cast<VclDrawingArea*>(pWindow);
+ assert(pSmElementsWin);
+ return std::unique_ptr<UIObject>(new ElementSelectorUIObject(pSmElementsWin, static_cast<SmElementsControl*>(pSmElementsWin->GetUserData())));
+}
+
+OUString ElementSelectorUIObject::get_name() const
+{
+ return "SmElementSelector";
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/uiobject.hxx b/starmath/source/uiobject.hxx
new file mode 100644
index 000000000..607c9194c
--- /dev/null
+++ b/starmath/source/uiobject.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_UIOBJECT_HXX
+#define INCLUDED_STARMATH_SOURCE_UIOBJECT_HXX
+
+#include <memory>
+#include <vcl/uitest/uiobject.hxx>
+
+#include <ElementsDockingWindow.hxx>
+
+class ElementUIObject : public UIObject
+{
+private:
+ SmElementsControl* mpElementsSelector;
+ OUString maID;
+
+public:
+
+ ElementUIObject(SmElementsControl* pElementSelector,
+ const OUString& rID);
+
+ virtual StringMap get_state() override;
+
+ virtual void execute(const OUString& rAction,
+ const StringMap& rParameters) override;
+
+private:
+ SmElement* get_element();
+};
+
+class ElementSelectorUIObject : public WindowUIObject
+{
+private:
+ SmElementsControl* mpElementsSelector;
+
+public:
+
+ explicit ElementSelectorUIObject(vcl::Window* pElementSelectorWindow, SmElementsControl* pElementSelector);
+
+ virtual StringMap get_state() override;
+
+ static std::unique_ptr<UIObject> create(vcl::Window* pWindow);
+
+ virtual std::unique_ptr<UIObject> get_child(const OUString& rID) override;
+
+ virtual std::set<OUString> get_children() const override;
+
+protected:
+
+ virtual OUString get_name() const override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/unodoc.cxx b/starmath/source/unodoc.cxx
new file mode 100644
index 000000000..ac7a36439
--- /dev/null
+++ b/starmath/source/unodoc.cxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/sfxmodelfactory.hxx>
+
+#include "register.hxx"
+#include <smdll.hxx>
+#include <document.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+
+OUString SmDocument_getImplementationName() throw()
+{
+ return "com.sun.star.comp.Math.FormulaDocument";
+}
+
+uno::Sequence< OUString > SmDocument_getSupportedServiceNames() throw()
+{
+ return uno::Sequence<OUString>{ "com.sun.star.formula.FormulaProperties" };
+}
+
+uno::Reference< uno::XInterface > SmDocument_createInstance(
+ const uno::Reference< lang::XMultiServiceFactory > & /*rSMgr*/, SfxModelFlags _nCreationFlags )
+{
+ SolarMutexGuard aGuard;
+ SmGlobals::ensure();
+ SfxObjectShell* pShell = new SmDocShell( _nCreationFlags );
+ return uno::Reference< uno::XInterface >( pShell->GetModel() );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/unofilter.cxx b/starmath/source/unofilter.cxx
new file mode 100644
index 000000000..719681af4
--- /dev/null
+++ b/starmath/source/unofilter.cxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <sot/storage.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <document.hxx>
+#include "mathtype.hxx"
+#include <unomodel.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Invokes the MathType importer via UNO.
+class MathTypeFilter
+ : public cppu::WeakImplHelper<document::XFilter, document::XImporter, lang::XServiceInfo>
+{
+ uno::Reference<lang::XComponent> m_xDstDoc;
+
+public:
+ MathTypeFilter();
+
+ // XFilter
+ sal_Bool SAL_CALL filter(const uno::Sequence<beans::PropertyValue>& rDescriptor) override;
+ void SAL_CALL cancel() override;
+
+ // XImporter
+ void SAL_CALL setTargetDocument(const uno::Reference<lang::XComponent>& xDoc) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
+ uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+}
+
+MathTypeFilter::MathTypeFilter() = default;
+
+sal_Bool MathTypeFilter::filter(const uno::Sequence<beans::PropertyValue>& rDescriptor)
+{
+ bool bSuccess = false;
+ try
+ {
+ utl::MediaDescriptor aMediaDesc(rDescriptor);
+ aMediaDesc.addInputStream();
+ uno::Reference<io::XInputStream> xInputStream;
+ aMediaDesc[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= xInputStream;
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream));
+ if (pStream)
+ {
+ if (SotStorage::IsStorageFile(pStream.get()))
+ {
+ tools::SvRef<SotStorage> aStorage(new SotStorage(pStream.get(), false));
+ // Is this a MathType Storage?
+ if (aStorage->IsStream("Equation Native"))
+ {
+ if (auto pModel = dynamic_cast<SmModel*>(m_xDstDoc.get()))
+ {
+ auto pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ OUStringBuffer aText(pDocShell->GetText());
+ MathType aEquation(aText);
+ bSuccess = aEquation.Parse(aStorage.get());
+ if (bSuccess)
+ {
+ pDocShell->SetText(aText.makeStringAndClear());
+ pDocShell->Parse();
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("starmath");
+ }
+ return bSuccess;
+}
+
+void MathTypeFilter::cancel() {}
+
+void MathTypeFilter::setTargetDocument(const uno::Reference<lang::XComponent>& xDoc)
+{
+ m_xDstDoc = xDoc;
+}
+
+OUString MathTypeFilter::getImplementationName() { return "com.sun.star.comp.Math.MathTypeFilter"; }
+
+sal_Bool MathTypeFilter::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> MathTypeFilter::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aRet = { OUString("com.sun.star.document.ImportFilter") };
+ return aRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_comp_Math_MathTypeFilter_get_implementation(uno::XComponentContext* /*pCtx*/,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new MathTypeFilter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/unomodel.cxx b/starmath/source/unomodel.cxx
new file mode 100644
index 000000000..17d52ce7b
--- /dev/null
+++ b/starmath/source/unomodel.cxx
@@ -0,0 +1,1100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <utility>
+
+#include <o3tl/any.hxx>
+#include <sfx2/printer.hxx>
+#include <svl/itemprop.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/print.hxx>
+#include <toolkit/awt/vclxdevice.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/formula/SymbolDescriptor.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/stream.hxx>
+
+#include <unomodel.hxx>
+#include <document.hxx>
+#include <view.hxx>
+#include <symbol.hxx>
+#include <starmath.hrc>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include "cfgitem.hxx"
+
+using namespace ::cppu;
+using namespace ::std;
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::formula;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::script;
+
+SmPrintUIOptions::SmPrintUIOptions()
+{
+ SmModule *pp = SM_MOD();
+ SmMathConfig *pConfig = pp->GetConfig();
+ SAL_WARN_IF( !pConfig, "starmath", "SmConfig not found" );
+ if (!pConfig)
+ return;
+
+ sal_Int32 nNumProps = 10, nIdx=0;
+
+ // create sequence of print UI options
+ // (Actually IsIgnoreSpacesRight is a parser option. Without it we need only 8 properties here.)
+ m_aUIProperties.resize( nNumProps );
+
+ // load the math PrinterOptions into the custom tab
+ m_aUIProperties[nIdx].Name = "OptionsUIFile";
+ m_aUIProperties[nIdx++].Value <<= OUString("modules/smath/ui/printeroptions.ui");
+
+ // create Section for formula (results in an extra tab page in dialog)
+ SvtModuleOptions aOpt;
+ OUString aAppGroupname(
+ SmResId( RID_PRINTUIOPT_PRODNAME ).
+ replaceFirst( "%s", aOpt.GetModuleName( SvtModuleOptions::EModule::MATH ) ) );
+ m_aUIProperties[nIdx++].Value = setGroupControlOpt("tabcontrol-page2", aAppGroupname, ".HelpID:vcl:PrintDialog:TabPage:AppPage");
+
+ // create subgroup for print options
+ m_aUIProperties[nIdx++].Value = setSubgroupControlOpt("contents", SmResId( RID_PRINTUIOPT_CONTENTS ), OUString());
+
+ // create a bool option for title row (matches to SID_PRINTTITLE)
+ m_aUIProperties[nIdx++].Value = setBoolControlOpt("title", SmResId( RID_PRINTUIOPT_TITLE ),
+ ".HelpID:vcl:PrintDialog:TitleRow:CheckBox",
+ PRTUIOPT_TITLE_ROW,
+ pConfig->IsPrintTitle());
+ // create a bool option for formula text (matches to SID_PRINTTEXT)
+ m_aUIProperties[nIdx++].Value = setBoolControlOpt("formulatext", SmResId( RID_PRINTUIOPT_FRMLTXT ),
+ ".HelpID:vcl:PrintDialog:FormulaText:CheckBox",
+ PRTUIOPT_FORMULA_TEXT,
+ pConfig->IsPrintFormulaText());
+ // create a bool option for border (matches to SID_PRINTFRAME)
+ m_aUIProperties[nIdx++].Value = setBoolControlOpt("borders", SmResId( RID_PRINTUIOPT_BORDERS ),
+ ".HelpID:vcl:PrintDialog:Border:CheckBox",
+ PRTUIOPT_BORDER,
+ pConfig->IsPrintFrame());
+
+ // create subgroup for print format
+ m_aUIProperties[nIdx++].Value = setSubgroupControlOpt("size", SmResId( RID_PRINTUIOPT_SIZE ), OUString());
+
+ // create a radio button group for print format (matches to SID_PRINTSIZE)
+ Sequence< OUString > aChoices{
+ SmResId( RID_PRINTUIOPT_ORIGSIZE ),
+ SmResId( RID_PRINTUIOPT_FITTOPAGE ),
+ SmResId( RID_PRINTUIOPT_SCALING )
+ };
+ Sequence< OUString > aHelpIds{
+ ".HelpID:vcl:PrintDialog:PrintFormat:RadioButton:0",
+ ".HelpID:vcl:PrintDialog:PrintFormat:RadioButton:1",
+ ".HelpID:vcl:PrintDialog:PrintFormat:RadioButton:2"
+ };
+ Sequence< OUString > aWidgetIds{
+ "originalsize",
+ "fittopage",
+ "scaling"
+ };
+ OUString aPrintFormatProp( PRTUIOPT_PRINT_FORMAT );
+ m_aUIProperties[nIdx++].Value = setChoiceRadiosControlOpt(aWidgetIds, OUString(),
+ aHelpIds,
+ aPrintFormatProp,
+ aChoices, static_cast< sal_Int32 >(pConfig->GetPrintSize())
+ );
+
+ // create a numeric box for scale dependent on PrintFormat = "Scaling" (matches to SID_PRINTZOOM)
+ vcl::PrinterOptionsHelper::UIControlOptions aRangeOpt( aPrintFormatProp, 2, true );
+ m_aUIProperties[nIdx++].Value = setRangeControlOpt("scalingspin", OUString(),
+ ".HelpID:vcl:PrintDialog:PrintScale:NumericField",
+ PRTUIOPT_PRINT_SCALE,
+ pConfig->GetPrintZoomFactor(), // initial value
+ 10, // min value
+ 1000, // max value
+ aRangeOpt);
+
+ Sequence< PropertyValue > aHintNoLayoutPage( 1 );
+ aHintNoLayoutPage[0].Name = "HintNoLayoutPage";
+ aHintNoLayoutPage[0].Value <<= true;
+ m_aUIProperties[nIdx++].Value <<= aHintNoLayoutPage;
+
+ assert(nIdx == nNumProps);
+}
+
+
+
+namespace {
+
+enum SmModelPropertyHandles
+{
+ HANDLE_FORMULA,
+ HANDLE_FONT_NAME_VARIABLES,
+ HANDLE_FONT_NAME_FUNCTIONS,
+ HANDLE_FONT_NAME_NUMBERS,
+ HANDLE_FONT_NAME_TEXT,
+ HANDLE_CUSTOM_FONT_NAME_SERIF,
+ HANDLE_CUSTOM_FONT_NAME_SANS,
+ HANDLE_CUSTOM_FONT_NAME_FIXED,
+ HANDLE_CUSTOM_FONT_FIXED_POSTURE,
+ HANDLE_CUSTOM_FONT_FIXED_WEIGHT,
+ HANDLE_CUSTOM_FONT_SANS_POSTURE,
+ HANDLE_CUSTOM_FONT_SANS_WEIGHT,
+ HANDLE_CUSTOM_FONT_SERIF_POSTURE,
+ HANDLE_CUSTOM_FONT_SERIF_WEIGHT,
+ HANDLE_FONT_VARIABLES_POSTURE,
+ HANDLE_FONT_VARIABLES_WEIGHT,
+ HANDLE_FONT_FUNCTIONS_POSTURE,
+ HANDLE_FONT_FUNCTIONS_WEIGHT,
+ HANDLE_FONT_NUMBERS_POSTURE,
+ HANDLE_FONT_NUMBERS_WEIGHT,
+ HANDLE_FONT_TEXT_POSTURE,
+ HANDLE_FONT_TEXT_WEIGHT,
+ HANDLE_BASE_FONT_HEIGHT,
+ HANDLE_RELATIVE_FONT_HEIGHT_TEXT,
+ HANDLE_RELATIVE_FONT_HEIGHT_INDICES,
+ HANDLE_RELATIVE_FONT_HEIGHT_FUNCTIONS,
+ HANDLE_RELATIVE_FONT_HEIGHT_OPERATORS,
+ HANDLE_RELATIVE_FONT_HEIGHT_LIMITS,
+ HANDLE_IS_TEXT_MODE,
+ HANDLE_GREEK_CHAR_STYLE,
+ HANDLE_ALIGNMENT,
+ HANDLE_RELATIVE_SPACING,
+ HANDLE_RELATIVE_LINE_SPACING,
+ HANDLE_RELATIVE_ROOT_SPACING,
+ HANDLE_RELATIVE_INDEX_SUPERSCRIPT,
+ HANDLE_RELATIVE_INDEX_SUBSCRIPT,
+ HANDLE_RELATIVE_FRACTION_NUMERATOR_HEIGHT,
+ HANDLE_RELATIVE_FRACTION_DENOMINATOR_DEPTH,
+ HANDLE_RELATIVE_FRACTION_BAR_EXCESS_LENGTH,
+ HANDLE_RELATIVE_FRACTION_BAR_LINE_WEIGHT,
+ HANDLE_RELATIVE_UPPER_LIMIT_DISTANCE,
+ HANDLE_RELATIVE_LOWER_LIMIT_DISTANCE,
+ HANDLE_RELATIVE_BRACKET_EXCESS_SIZE,
+ HANDLE_RELATIVE_BRACKET_DISTANCE,
+ HANDLE_IS_SCALE_ALL_BRACKETS,
+ HANDLE_RELATIVE_SCALE_BRACKET_EXCESS_SIZE,
+ HANDLE_RELATIVE_MATRIX_LINE_SPACING,
+ HANDLE_RELATIVE_MATRIX_COLUMN_SPACING,
+ HANDLE_RELATIVE_SYMBOL_PRIMARY_HEIGHT,
+ HANDLE_RELATIVE_SYMBOL_MINIMUM_HEIGHT,
+ HANDLE_RELATIVE_OPERATOR_EXCESS_SIZE,
+ HANDLE_RELATIVE_OPERATOR_SPACING,
+ HANDLE_LEFT_MARGIN,
+ HANDLE_RIGHT_MARGIN,
+ HANDLE_TOP_MARGIN,
+ HANDLE_BOTTOM_MARGIN,
+ HANDLE_PRINTER_NAME,
+ HANDLE_PRINTER_SETUP,
+ HANDLE_SYMBOLS,
+ HANDLE_SAVE_THUMBNAIL,
+ HANDLE_USED_SYMBOLS,
+ HANDLE_BASIC_LIBRARIES,
+ HANDLE_RUNTIME_UID,
+ HANDLE_LOAD_READONLY, // Security Options
+ HANDLE_DIALOG_LIBRARIES, // #i73329#
+ HANDLE_BASELINE,
+ HANDLE_INTEROP_GRAB_BAG,
+};
+
+}
+
+static rtl::Reference<PropertySetInfo> lcl_createModelPropertyInfo ()
+{
+ static PropertyMapEntry aModelPropertyInfoMap[] =
+ {
+ { OUString("Alignment") , HANDLE_ALIGNMENT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { OUString("BaseFontHeight") , HANDLE_BASE_FONT_HEIGHT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { OUString("BasicLibraries") , HANDLE_BASIC_LIBRARIES , cppu::UnoType<script::XLibraryContainer>::get(), PropertyAttribute::READONLY, 0 },
+ { OUString("BottomMargin") , HANDLE_BOTTOM_MARGIN , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_BOTTOMSPACE },
+ { OUString("CustomFontNameFixed") , HANDLE_CUSTOM_FONT_NAME_FIXED , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_FIXED },
+ { OUString("CustomFontNameSans") , HANDLE_CUSTOM_FONT_NAME_SANS , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_SANS },
+ { OUString("CustomFontNameSerif") , HANDLE_CUSTOM_FONT_NAME_SERIF , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_SERIF },
+ { OUString("DialogLibraries") , HANDLE_DIALOG_LIBRARIES , cppu::UnoType<script::XLibraryContainer>::get(), PropertyAttribute::READONLY, 0 },
+ { OUString("FontFixedIsBold") , HANDLE_CUSTOM_FONT_FIXED_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_FIXED },
+ { OUString("FontFixedIsItalic") , HANDLE_CUSTOM_FONT_FIXED_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_FIXED },
+ { OUString("FontFunctionsIsBold") , HANDLE_FONT_FUNCTIONS_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_FUNCTION },
+ { OUString("FontFunctionsIsItalic") , HANDLE_FONT_FUNCTIONS_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_FUNCTION },
+ { OUString("FontNameFunctions") , HANDLE_FONT_NAME_FUNCTIONS , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_FUNCTION },
+ { OUString("FontNameNumbers") , HANDLE_FONT_NAME_NUMBERS , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_NUMBER },
+ { OUString("FontNameText") , HANDLE_FONT_NAME_TEXT , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_TEXT },
+ { OUString("FontNameVariables") , HANDLE_FONT_NAME_VARIABLES , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_VARIABLE },
+ { OUString("FontNumbersIsBold") , HANDLE_FONT_NUMBERS_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_NUMBER },
+ { OUString("FontNumbersIsItalic") , HANDLE_FONT_NUMBERS_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_NUMBER },
+ { OUString("FontSansIsBold") , HANDLE_CUSTOM_FONT_SANS_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_SANS },
+ { OUString("FontSansIsItalic") , HANDLE_CUSTOM_FONT_SANS_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_SANS },
+ { OUString("FontSerifIsBold") , HANDLE_CUSTOM_FONT_SERIF_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_SERIF },
+ { OUString("FontSerifIsItalic") , HANDLE_CUSTOM_FONT_SERIF_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_SERIF },
+ { OUString("FontTextIsBold") , HANDLE_FONT_TEXT_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_TEXT },
+ { OUString("FontTextIsItalic") , HANDLE_FONT_TEXT_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_TEXT },
+ { OUString("FontVariablesIsBold") , HANDLE_FONT_VARIABLES_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_VARIABLE },
+ { OUString("FontVariablesIsItalic") , HANDLE_FONT_VARIABLES_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_VARIABLE },
+ { OUString("Formula") , HANDLE_FORMULA , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { OUString("IsScaleAllBrackets") , HANDLE_IS_SCALE_ALL_BRACKETS , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { OUString("IsTextMode") , HANDLE_IS_TEXT_MODE , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { OUString("GreekCharStyle") , HANDLE_GREEK_CHAR_STYLE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { OUString("LeftMargin") , HANDLE_LEFT_MARGIN , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_LEFTSPACE },
+ { OUString("PrinterName") , HANDLE_PRINTER_NAME , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { OUString("PrinterSetup") , HANDLE_PRINTER_SETUP , cppu::UnoType<const Sequence < sal_Int8 >>::get(), PROPERTY_NONE, 0 },
+ { OUString("RelativeBracketDistance") , HANDLE_RELATIVE_BRACKET_DISTANCE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_BRACKETSPACE },
+ { OUString("RelativeBracketExcessSize") , HANDLE_RELATIVE_BRACKET_EXCESS_SIZE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_BRACKETSIZE },
+ { OUString("RelativeFontHeightFunctions") , HANDLE_RELATIVE_FONT_HEIGHT_FUNCTIONS , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, SIZ_FUNCTION },
+ { OUString("RelativeFontHeightIndices") , HANDLE_RELATIVE_FONT_HEIGHT_INDICES , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, SIZ_INDEX },
+ { OUString("RelativeFontHeightLimits") , HANDLE_RELATIVE_FONT_HEIGHT_LIMITS , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, SIZ_LIMITS },
+ { OUString("RelativeFontHeightOperators") , HANDLE_RELATIVE_FONT_HEIGHT_OPERATORS , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, SIZ_OPERATOR },
+ { OUString("RelativeFontHeightText") , HANDLE_RELATIVE_FONT_HEIGHT_TEXT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, SIZ_TEXT },
+ { OUString("RelativeFractionBarExcessLength") , HANDLE_RELATIVE_FRACTION_BAR_EXCESS_LENGTH, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_FRACTION },
+ { OUString("RelativeFractionBarLineWeight") , HANDLE_RELATIVE_FRACTION_BAR_LINE_WEIGHT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_STROKEWIDTH },
+ { OUString("RelativeFractionDenominatorDepth") , HANDLE_RELATIVE_FRACTION_DENOMINATOR_DEPTH, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_DENOMINATOR },
+ { OUString("RelativeFractionNumeratorHeight") , HANDLE_RELATIVE_FRACTION_NUMERATOR_HEIGHT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_NUMERATOR },
+ { OUString("RelativeIndexSubscript") , HANDLE_RELATIVE_INDEX_SUBSCRIPT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_SUBSCRIPT },
+ { OUString("RelativeIndexSuperscript") , HANDLE_RELATIVE_INDEX_SUPERSCRIPT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_SUPERSCRIPT },
+ { OUString("RelativeLineSpacing") , HANDLE_RELATIVE_LINE_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_VERTICAL },
+ { OUString("RelativeLowerLimitDistance") , HANDLE_RELATIVE_LOWER_LIMIT_DISTANCE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_LOWERLIMIT },
+ { OUString("RelativeMatrixColumnSpacing") , HANDLE_RELATIVE_MATRIX_COLUMN_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_MATRIXCOL },
+ { OUString("RelativeMatrixLineSpacing") , HANDLE_RELATIVE_MATRIX_LINE_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_MATRIXROW },
+ { OUString("RelativeOperatorExcessSize") , HANDLE_RELATIVE_OPERATOR_EXCESS_SIZE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_OPERATORSIZE },
+ { OUString("RelativeOperatorSpacing") , HANDLE_RELATIVE_OPERATOR_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_OPERATORSPACE },
+ { OUString("RelativeRootSpacing") , HANDLE_RELATIVE_ROOT_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_ROOT },
+ { OUString("RelativeScaleBracketExcessSize") , HANDLE_RELATIVE_SCALE_BRACKET_EXCESS_SIZE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_NORMALBRACKETSIZE },
+ { OUString("RelativeSpacing") , HANDLE_RELATIVE_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_HORIZONTAL },
+ { OUString("RelativeSymbolMinimumHeight") , HANDLE_RELATIVE_SYMBOL_MINIMUM_HEIGHT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_ORNAMENTSPACE },
+ { OUString("RelativeSymbolPrimaryHeight") , HANDLE_RELATIVE_SYMBOL_PRIMARY_HEIGHT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_ORNAMENTSIZE },
+ { OUString("RelativeUpperLimitDistance") , HANDLE_RELATIVE_UPPER_LIMIT_DISTANCE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_UPPERLIMIT },
+ { OUString("RightMargin") , HANDLE_RIGHT_MARGIN , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_RIGHTSPACE },
+ { OUString("RuntimeUID") , HANDLE_RUNTIME_UID , cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0 },
+ { OUString("SaveThumbnail") , HANDLE_SAVE_THUMBNAIL , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { OUString("Symbols") , HANDLE_SYMBOLS , cppu::UnoType<Sequence < SymbolDescriptor >>::get(), PROPERTY_NONE, 0 },
+ { OUString("UserDefinedSymbolsInUse") , HANDLE_USED_SYMBOLS , cppu::UnoType<Sequence < SymbolDescriptor >>::get(), PropertyAttribute::READONLY, 0 },
+ { OUString("TopMargin") , HANDLE_TOP_MARGIN , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_TOPSPACE },
+ // #i33095# Security Options
+ { OUString("LoadReadonly") , HANDLE_LOAD_READONLY , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ // #i972#
+ { OUString("BaseLine") , HANDLE_BASELINE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { OUString("InteropGrabBag") , HANDLE_INTEROP_GRAB_BAG , cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(), PROPERTY_NONE, 0 },
+ { OUString(), 0, css::uno::Type(), 0, 0 }
+ };
+ return rtl::Reference<PropertySetInfo>( new PropertySetInfo ( aModelPropertyInfoMap ) );
+}
+
+SmModel::SmModel( SfxObjectShell *pObjSh )
+: SfxBaseModel(pObjSh)
+, PropertySetHelper ( lcl_createModelPropertyInfo () )
+{
+}
+
+SmModel::~SmModel() throw ()
+{
+}
+
+uno::Any SAL_CALL SmModel::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet = ::cppu::queryInterface ( rType,
+ // OWeakObject interfaces
+ &dynamic_cast<XInterface&>(static_cast<XUnoTunnel&>(*this)),
+ static_cast< XWeak* > ( this ),
+ // PropertySetHelper interfaces
+ static_cast< XPropertySet* > ( this ),
+ static_cast< XMultiPropertySet* > ( this ),
+ // my own interfaces
+ static_cast< XServiceInfo* > ( this ),
+ static_cast< XRenderable* > ( this ) );
+ if (!aRet.hasValue())
+ aRet = SfxBaseModel::queryInterface ( rType );
+ return aRet;
+}
+
+void SAL_CALL SmModel::acquire() throw()
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL SmModel::release() throw()
+{
+ OWeakObject::release();
+}
+
+uno::Sequence< uno::Type > SAL_CALL SmModel::getTypes( )
+{
+ return comphelper::concatSequences(SfxBaseModel::getTypes(),
+ uno::Sequence {
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get(),
+ cppu::UnoType<XRenderable>::get() });
+}
+
+namespace
+{
+ class theSmModelUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSmModelUnoTunnelId> {};
+}
+
+const uno::Sequence< sal_Int8 > & SmModel::getUnoTunnelId()
+{
+ return theSmModelUnoTunnelId::get().getSeq();
+}
+
+sal_Int64 SAL_CALL SmModel::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ if( isUnoTunnelId<SmModel>(rId) )
+ {
+ return sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_uIntPtr >(this));
+ }
+
+ return SfxBaseModel::getSomething( rId );
+}
+
+static sal_Int16 lcl_AnyToINT16(const uno::Any& rAny)
+{
+ sal_Int16 nRet = 0;
+ if( auto x = o3tl::tryAccess<double>(rAny) )
+ nRet = static_cast<sal_Int16>(*x);
+ else
+ rAny >>= nRet;
+ return nRet;
+}
+
+OUString SmModel::getImplementationName()
+{
+ return "com.sun.star.comp.Math.FormulaDocument";
+}
+
+sal_Bool SmModel::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SmModel::getSupportedServiceNames()
+{
+ return uno::Sequence<OUString>{
+ "com.sun.star.document.OfficeDocument",
+ "com.sun.star.formula.FormulaProperties"
+ };
+}
+
+void SmModel::_setPropertyValues(const PropertyMapEntry** ppEntries, const Any* pValues)
+{
+ SolarMutexGuard aGuard;
+
+ SmDocShell *pDocSh = static_cast < SmDocShell * > (GetObjectShell());
+
+ if ( nullptr == pDocSh )
+ throw UnknownPropertyException();
+
+ SmFormat aFormat = pDocSh->GetFormat();
+
+ for (; *ppEntries; ppEntries++, pValues++ )
+ {
+ if ((*ppEntries)->mnAttributes & PropertyAttribute::READONLY)
+ throw PropertyVetoException();
+
+ switch ( (*ppEntries)->mnHandle )
+ {
+ case HANDLE_FORMULA:
+ {
+ OUString aText;
+ *pValues >>= aText;
+ pDocSh->SetText(aText);
+ }
+ break;
+ case HANDLE_FONT_NAME_VARIABLES :
+ case HANDLE_FONT_NAME_FUNCTIONS :
+ case HANDLE_FONT_NAME_NUMBERS :
+ case HANDLE_FONT_NAME_TEXT :
+ case HANDLE_CUSTOM_FONT_NAME_SERIF :
+ case HANDLE_CUSTOM_FONT_NAME_SANS :
+ case HANDLE_CUSTOM_FONT_NAME_FIXED :
+ {
+ OUString sFontName;
+ *pValues >>= sFontName;
+ if(sFontName.isEmpty())
+ throw IllegalArgumentException();
+
+ if(aFormat.GetFont((*ppEntries)->mnMemberId).GetFamilyName() != sFontName)
+ {
+ const SmFace rOld = aFormat.GetFont((*ppEntries)->mnMemberId);
+
+ SmFace aSet( sFontName, rOld.GetFontSize() );
+ aSet.SetBorderWidth( rOld.GetBorderWidth() );
+ aSet.SetAlignment( ALIGN_BASELINE );
+ aFormat.SetFont( (*ppEntries)->mnMemberId, aSet );
+ }
+ }
+ break;
+ case HANDLE_CUSTOM_FONT_FIXED_POSTURE:
+ case HANDLE_CUSTOM_FONT_SANS_POSTURE :
+ case HANDLE_CUSTOM_FONT_SERIF_POSTURE:
+ case HANDLE_FONT_VARIABLES_POSTURE :
+ case HANDLE_FONT_FUNCTIONS_POSTURE :
+ case HANDLE_FONT_NUMBERS_POSTURE :
+ case HANDLE_FONT_TEXT_POSTURE :
+ {
+ auto bVal = o3tl::tryAccess<bool>(*pValues);
+ if(!bVal)
+ throw IllegalArgumentException();
+ vcl::Font aNewFont(aFormat.GetFont((*ppEntries)->mnMemberId));
+ aNewFont.SetItalic(*bVal ? ITALIC_NORMAL : ITALIC_NONE);
+ aFormat.SetFont((*ppEntries)->mnMemberId, aNewFont);
+ }
+ break;
+ case HANDLE_CUSTOM_FONT_FIXED_WEIGHT :
+ case HANDLE_CUSTOM_FONT_SANS_WEIGHT :
+ case HANDLE_CUSTOM_FONT_SERIF_WEIGHT :
+ case HANDLE_FONT_VARIABLES_WEIGHT :
+ case HANDLE_FONT_FUNCTIONS_WEIGHT :
+ case HANDLE_FONT_NUMBERS_WEIGHT :
+ case HANDLE_FONT_TEXT_WEIGHT :
+ {
+ auto bVal = o3tl::tryAccess<bool>(*pValues);
+ if(!bVal)
+ throw IllegalArgumentException();
+ vcl::Font aNewFont(aFormat.GetFont((*ppEntries)->mnMemberId));
+ aNewFont.SetWeight(*bVal ? WEIGHT_BOLD : WEIGHT_NORMAL);
+ aFormat.SetFont((*ppEntries)->mnMemberId, aNewFont);
+ }
+ break;
+ case HANDLE_BASE_FONT_HEIGHT :
+ {
+ // Point!
+ sal_Int16 nVal = lcl_AnyToINT16(*pValues);
+ if(nVal < 1)
+ throw IllegalArgumentException();
+ Size aSize = aFormat.GetBaseSize();
+ aSize.setHeight( SmPtsTo100th_mm(nVal) );
+ aFormat.SetBaseSize(aSize);
+
+ // apply base size to fonts
+ const Size aTmp( aFormat.GetBaseSize() );
+ for (sal_uInt16 i = FNT_BEGIN; i <= FNT_END; i++)
+ aFormat.SetFontSize(i, aTmp);
+ }
+ break;
+ case HANDLE_RELATIVE_FONT_HEIGHT_TEXT :
+ case HANDLE_RELATIVE_FONT_HEIGHT_INDICES :
+ case HANDLE_RELATIVE_FONT_HEIGHT_FUNCTIONS :
+ case HANDLE_RELATIVE_FONT_HEIGHT_OPERATORS :
+ case HANDLE_RELATIVE_FONT_HEIGHT_LIMITS :
+ {
+ sal_Int16 nVal = 0;
+ *pValues >>= nVal;
+ if(nVal < 1)
+ throw IllegalArgumentException();
+ aFormat.SetRelSize((*ppEntries)->mnMemberId, nVal);
+ }
+ break;
+
+ case HANDLE_IS_TEXT_MODE :
+ {
+ aFormat.SetTextmode(*o3tl::doAccess<bool>(*pValues));
+ }
+ break;
+
+ case HANDLE_GREEK_CHAR_STYLE :
+ {
+ sal_Int16 nVal = 0;
+ *pValues >>= nVal;
+ if (nVal < 0 || nVal > 2)
+ throw IllegalArgumentException();
+ aFormat.SetGreekCharStyle( nVal );
+ }
+ break;
+
+ case HANDLE_ALIGNMENT :
+ {
+ // SmHorAlign uses the same values as HorizontalAlignment
+ sal_Int16 nVal = 0;
+ *pValues >>= nVal;
+ if(nVal < 0 || nVal > 2)
+ throw IllegalArgumentException();
+ aFormat.SetHorAlign(static_cast<SmHorAlign>(nVal));
+ }
+ break;
+
+ case HANDLE_RELATIVE_SPACING :
+ case HANDLE_RELATIVE_LINE_SPACING :
+ case HANDLE_RELATIVE_ROOT_SPACING :
+ case HANDLE_RELATIVE_INDEX_SUPERSCRIPT :
+ case HANDLE_RELATIVE_INDEX_SUBSCRIPT :
+ case HANDLE_RELATIVE_FRACTION_NUMERATOR_HEIGHT :
+ case HANDLE_RELATIVE_FRACTION_DENOMINATOR_DEPTH:
+ case HANDLE_RELATIVE_FRACTION_BAR_EXCESS_LENGTH:
+ case HANDLE_RELATIVE_FRACTION_BAR_LINE_WEIGHT :
+ case HANDLE_RELATIVE_UPPER_LIMIT_DISTANCE :
+ case HANDLE_RELATIVE_LOWER_LIMIT_DISTANCE :
+ case HANDLE_RELATIVE_BRACKET_EXCESS_SIZE :
+ case HANDLE_RELATIVE_BRACKET_DISTANCE :
+ case HANDLE_RELATIVE_SCALE_BRACKET_EXCESS_SIZE :
+ case HANDLE_RELATIVE_MATRIX_LINE_SPACING :
+ case HANDLE_RELATIVE_MATRIX_COLUMN_SPACING :
+ case HANDLE_RELATIVE_SYMBOL_PRIMARY_HEIGHT :
+ case HANDLE_RELATIVE_SYMBOL_MINIMUM_HEIGHT :
+ case HANDLE_RELATIVE_OPERATOR_EXCESS_SIZE :
+ case HANDLE_RELATIVE_OPERATOR_SPACING :
+ case HANDLE_LEFT_MARGIN :
+ case HANDLE_RIGHT_MARGIN :
+ case HANDLE_TOP_MARGIN :
+ case HANDLE_BOTTOM_MARGIN :
+ {
+ sal_Int16 nVal = 0;
+ *pValues >>= nVal;
+ if(nVal < 0)
+ throw IllegalArgumentException();
+ aFormat.SetDistance((*ppEntries)->mnMemberId, nVal);
+ }
+ break;
+ case HANDLE_IS_SCALE_ALL_BRACKETS :
+ aFormat.SetScaleNormalBrackets(*o3tl::doAccess<bool>(*pValues));
+ break;
+ case HANDLE_PRINTER_NAME:
+ {
+ // embedded documents just ignore this property for now
+ if ( pDocSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
+ {
+ SfxPrinter *pPrinter = pDocSh->GetPrinter ( );
+ if (pPrinter)
+ {
+ OUString sPrinterName;
+ if ( !(*pValues >>= sPrinterName) )
+ throw IllegalArgumentException();
+
+ if ( !sPrinterName.isEmpty() )
+ {
+ VclPtrInstance<SfxPrinter> pNewPrinter( pPrinter->GetOptions().Clone(), sPrinterName );
+ if (pNewPrinter->IsKnown())
+ pDocSh->SetPrinter ( pNewPrinter );
+ else
+ pNewPrinter.disposeAndClear();
+ }
+ }
+ }
+ }
+ break;
+ case HANDLE_PRINTER_SETUP:
+ {
+ Sequence < sal_Int8 > aSequence;
+ if ( !(*pValues >>= aSequence) )
+ throw IllegalArgumentException();
+
+ sal_uInt32 nSize = aSequence.getLength();
+ SvMemoryStream aStream ( aSequence.getArray(), nSize, StreamMode::READ );
+ aStream.Seek ( STREAM_SEEK_TO_BEGIN );
+ static sal_uInt16 const nRange[] =
+ {
+ SID_PRINTSIZE, SID_PRINTSIZE,
+ SID_PRINTZOOM, SID_PRINTZOOM,
+ SID_PRINTTITLE, SID_PRINTTITLE,
+ SID_PRINTTEXT, SID_PRINTTEXT,
+ SID_PRINTFRAME, SID_PRINTFRAME,
+ SID_NO_RIGHT_SPACES, SID_NO_RIGHT_SPACES,
+ SID_SAVE_ONLY_USED_SYMBOLS, SID_SAVE_ONLY_USED_SYMBOLS,
+ SID_AUTO_CLOSE_BRACKETS, SID_AUTO_CLOSE_BRACKETS,
+ 0
+ };
+ auto pItemSet = std::make_unique<SfxItemSet>( SmDocShell::GetPool(), nRange );
+ SmModule *pp = SM_MOD();
+ pp->GetConfig()->ConfigToItemSet(*pItemSet);
+ VclPtr<SfxPrinter> pPrinter = SfxPrinter::Create ( aStream, std::move(pItemSet) );
+
+ pDocSh->SetPrinter( pPrinter );
+ }
+ break;
+ case HANDLE_SYMBOLS:
+ {
+ // this is set
+ Sequence < SymbolDescriptor > aSequence;
+ if ( !(*pValues >>= aSequence) )
+ throw IllegalArgumentException();
+
+ SmModule *pp = SM_MOD();
+ SmSymbolManager &rManager = pp->GetSymbolManager();
+ for (const SymbolDescriptor& rDescriptor : std::as_const(aSequence))
+ {
+ vcl::Font aFont;
+ aFont.SetFamilyName ( rDescriptor.sFontName );
+ aFont.SetCharSet ( static_cast < rtl_TextEncoding > (rDescriptor.nCharSet) );
+ aFont.SetFamily ( static_cast < FontFamily > (rDescriptor.nFamily ) );
+ aFont.SetPitch ( static_cast < FontPitch > (rDescriptor.nPitch ) );
+ aFont.SetWeight ( static_cast < FontWeight > (rDescriptor.nWeight ) );
+ aFont.SetItalic ( static_cast < FontItalic > (rDescriptor.nItalic ) );
+ SmSym aSymbol ( rDescriptor.sName, aFont, static_cast < sal_Unicode > (rDescriptor.nCharacter),
+ rDescriptor.sSymbolSet );
+ aSymbol.SetExportName ( rDescriptor.sExportName );
+ rManager.AddOrReplaceSymbol ( aSymbol );
+ }
+ }
+ break;
+ // #i33095# Security Options
+ case HANDLE_LOAD_READONLY :
+ {
+ if ( (*pValues).getValueType() != cppu::UnoType<bool>::get() )
+ throw IllegalArgumentException();
+ bool bReadonly = false;
+ if ( *pValues >>= bReadonly )
+ pDocSh->SetLoadReadonly( bReadonly );
+ break;
+ }
+ case HANDLE_INTEROP_GRAB_BAG:
+ setGrabBagItem(*pValues);
+ break;
+ case HANDLE_SAVE_THUMBNAIL:
+ {
+ if ((*pValues).getValueType() != cppu::UnoType<bool>::get())
+ throw IllegalArgumentException();
+ bool bThumbnail = false;
+ if (*pValues >>= bThumbnail)
+ pDocSh->SetUseThumbnailSave(bThumbnail);
+ }
+ break;
+ }
+ }
+
+ pDocSh->SetFormat( aFormat );
+
+ // #i67283# since about all of the above changes are likely to change
+ // the formula size we have to recalculate the vis-area now
+ pDocSh->SetVisArea( tools::Rectangle( Point(0, 0), pDocSh->GetSize() ) );
+}
+
+void SmModel::_getPropertyValues( const PropertyMapEntry **ppEntries, Any *pValue )
+{
+ SmDocShell *pDocSh = static_cast < SmDocShell * > (GetObjectShell());
+
+ if ( nullptr == pDocSh )
+ throw UnknownPropertyException();
+
+ const SmFormat & aFormat = pDocSh->GetFormat();
+
+ for (; *ppEntries; ppEntries++, pValue++ )
+ {
+ switch ( (*ppEntries)->mnHandle )
+ {
+ case HANDLE_FORMULA:
+ *pValue <<= pDocSh->GetText();
+ break;
+ case HANDLE_FONT_NAME_VARIABLES :
+ case HANDLE_FONT_NAME_FUNCTIONS :
+ case HANDLE_FONT_NAME_NUMBERS :
+ case HANDLE_FONT_NAME_TEXT :
+ case HANDLE_CUSTOM_FONT_NAME_SERIF :
+ case HANDLE_CUSTOM_FONT_NAME_SANS :
+ case HANDLE_CUSTOM_FONT_NAME_FIXED :
+ {
+ const SmFace & rFace = aFormat.GetFont((*ppEntries)->mnMemberId);
+ *pValue <<= rFace.GetFamilyName();
+ }
+ break;
+ case HANDLE_CUSTOM_FONT_FIXED_POSTURE:
+ case HANDLE_CUSTOM_FONT_SANS_POSTURE :
+ case HANDLE_CUSTOM_FONT_SERIF_POSTURE:
+ case HANDLE_FONT_VARIABLES_POSTURE :
+ case HANDLE_FONT_FUNCTIONS_POSTURE :
+ case HANDLE_FONT_NUMBERS_POSTURE :
+ case HANDLE_FONT_TEXT_POSTURE :
+ {
+ const SmFace & rFace = aFormat.GetFont((*ppEntries)->mnMemberId);
+ *pValue <<= IsItalic( rFace );
+ }
+ break;
+ case HANDLE_CUSTOM_FONT_FIXED_WEIGHT :
+ case HANDLE_CUSTOM_FONT_SANS_WEIGHT :
+ case HANDLE_CUSTOM_FONT_SERIF_WEIGHT :
+ case HANDLE_FONT_VARIABLES_WEIGHT :
+ case HANDLE_FONT_FUNCTIONS_WEIGHT :
+ case HANDLE_FONT_NUMBERS_WEIGHT :
+ case HANDLE_FONT_TEXT_WEIGHT :
+ {
+ const SmFace & rFace = aFormat.GetFont((*ppEntries)->mnMemberId);
+ *pValue <<= IsBold( rFace );
+ }
+ break;
+ case HANDLE_BASE_FONT_HEIGHT :
+ {
+ // Point!
+ *pValue <<= sal_Int16(
+ SmRoundFraction(
+ Sm100th_mmToPts(aFormat.GetBaseSize().Height())));
+ }
+ break;
+ case HANDLE_RELATIVE_FONT_HEIGHT_TEXT :
+ case HANDLE_RELATIVE_FONT_HEIGHT_INDICES :
+ case HANDLE_RELATIVE_FONT_HEIGHT_FUNCTIONS :
+ case HANDLE_RELATIVE_FONT_HEIGHT_OPERATORS :
+ case HANDLE_RELATIVE_FONT_HEIGHT_LIMITS :
+ *pValue <<= static_cast<sal_Int16>(aFormat.GetRelSize((*ppEntries)->mnMemberId));
+ break;
+
+ case HANDLE_IS_TEXT_MODE :
+ *pValue <<= aFormat.IsTextmode();
+ break;
+
+ case HANDLE_GREEK_CHAR_STYLE :
+ *pValue <<= aFormat.GetGreekCharStyle();
+ break;
+
+ case HANDLE_ALIGNMENT :
+ // SmHorAlign uses the same values as HorizontalAlignment
+ *pValue <<= static_cast<sal_Int16>(aFormat.GetHorAlign());
+ break;
+
+ case HANDLE_RELATIVE_SPACING :
+ case HANDLE_RELATIVE_LINE_SPACING :
+ case HANDLE_RELATIVE_ROOT_SPACING :
+ case HANDLE_RELATIVE_INDEX_SUPERSCRIPT :
+ case HANDLE_RELATIVE_INDEX_SUBSCRIPT :
+ case HANDLE_RELATIVE_FRACTION_NUMERATOR_HEIGHT :
+ case HANDLE_RELATIVE_FRACTION_DENOMINATOR_DEPTH:
+ case HANDLE_RELATIVE_FRACTION_BAR_EXCESS_LENGTH:
+ case HANDLE_RELATIVE_FRACTION_BAR_LINE_WEIGHT :
+ case HANDLE_RELATIVE_UPPER_LIMIT_DISTANCE :
+ case HANDLE_RELATIVE_LOWER_LIMIT_DISTANCE :
+ case HANDLE_RELATIVE_BRACKET_EXCESS_SIZE :
+ case HANDLE_RELATIVE_BRACKET_DISTANCE :
+ case HANDLE_RELATIVE_SCALE_BRACKET_EXCESS_SIZE :
+ case HANDLE_RELATIVE_MATRIX_LINE_SPACING :
+ case HANDLE_RELATIVE_MATRIX_COLUMN_SPACING :
+ case HANDLE_RELATIVE_SYMBOL_PRIMARY_HEIGHT :
+ case HANDLE_RELATIVE_SYMBOL_MINIMUM_HEIGHT :
+ case HANDLE_RELATIVE_OPERATOR_EXCESS_SIZE :
+ case HANDLE_RELATIVE_OPERATOR_SPACING :
+ case HANDLE_LEFT_MARGIN :
+ case HANDLE_RIGHT_MARGIN :
+ case HANDLE_TOP_MARGIN :
+ case HANDLE_BOTTOM_MARGIN :
+ *pValue <<= static_cast<sal_Int16>(aFormat.GetDistance((*ppEntries)->mnMemberId));
+ break;
+ case HANDLE_IS_SCALE_ALL_BRACKETS :
+ *pValue <<= aFormat.IsScaleNormalBrackets();
+ break;
+ case HANDLE_PRINTER_NAME:
+ {
+ SfxPrinter *pPrinter = pDocSh->GetPrinter ( );
+ *pValue <<= pPrinter ? pPrinter->GetName() : OUString();
+ }
+ break;
+ case HANDLE_PRINTER_SETUP:
+ {
+ SfxPrinter *pPrinter = pDocSh->GetPrinter ();
+ if (pPrinter)
+ {
+ SvMemoryStream aStream;
+ pPrinter->Store( aStream );
+ sal_uInt32 nSize = aStream.TellEnd();
+ aStream.Seek ( STREAM_SEEK_TO_BEGIN );
+ Sequence < sal_Int8 > aSequence ( nSize );
+ aStream.ReadBytes(aSequence.getArray(), nSize);
+ *pValue <<= aSequence;
+ }
+ }
+ break;
+ case HANDLE_SYMBOLS:
+ case HANDLE_USED_SYMBOLS:
+ {
+ const bool bUsedSymbolsOnly = (*ppEntries)->mnHandle == HANDLE_USED_SYMBOLS;
+ const std::set< OUString > &rUsedSymbols = pDocSh->GetUsedSymbols();
+
+ // this is get
+ SmModule *pp = SM_MOD();
+ const SmSymbolManager &rManager = pp->GetSymbolManager();
+ vector < const SmSym * > aVector;
+
+ const SymbolPtrVec_t aSymbols( rManager.GetSymbols() );
+ for (const SmSym* pSymbol : aSymbols)
+ {
+ if (pSymbol && !pSymbol->IsPredefined() &&
+ (!bUsedSymbolsOnly ||
+ rUsedSymbols.find( pSymbol->GetName() ) != rUsedSymbols.end()))
+ aVector.push_back ( pSymbol );
+ }
+ Sequence < SymbolDescriptor > aSequence ( aVector.size() );
+ SymbolDescriptor * pDescriptor = aSequence.getArray();
+
+ for (const SmSym* pSymbol : aVector)
+ {
+ pDescriptor->sName = pSymbol->GetName();
+ pDescriptor->sExportName = pSymbol->GetExportName();
+ pDescriptor->sSymbolSet = pSymbol->GetSymbolSetName();
+ pDescriptor->nCharacter = static_cast < sal_Int32 > (pSymbol->GetCharacter());
+
+ vcl::Font rFont = pSymbol->GetFace();
+ pDescriptor->sFontName = rFont.GetFamilyName();
+ pDescriptor->nCharSet = sal::static_int_cast< sal_Int16 >(rFont.GetCharSet());
+ pDescriptor->nFamily = sal::static_int_cast< sal_Int16 >(rFont.GetFamilyType());
+ pDescriptor->nPitch = sal::static_int_cast< sal_Int16 >(rFont.GetPitch());
+ pDescriptor->nWeight = sal::static_int_cast< sal_Int16 >(rFont.GetWeight());
+ pDescriptor->nItalic = sal::static_int_cast< sal_Int16 >(rFont.GetItalic());
+ pDescriptor++;
+ }
+ *pValue <<= aSequence;
+ }
+ break;
+ case HANDLE_BASIC_LIBRARIES:
+ *pValue <<= pDocSh->GetBasicContainer();
+ break;
+ case HANDLE_DIALOG_LIBRARIES:
+ *pValue <<= pDocSh->GetDialogContainer();
+ break;
+ case HANDLE_RUNTIME_UID:
+ *pValue <<= getRuntimeUID();
+ break;
+ // #i33095# Security Options
+ case HANDLE_LOAD_READONLY :
+ {
+ *pValue <<= pDocSh->IsLoadReadonly();
+ break;
+ }
+ // #i972#
+ case HANDLE_BASELINE:
+ {
+ if ( !pDocSh->GetFormulaTree() )
+ pDocSh->Parse();
+ if ( pDocSh->GetFormulaTree() )
+ {
+ pDocSh->ArrangeFormula();
+
+ *pValue <<= static_cast<sal_Int32>( pDocSh->GetFormulaTree()->GetFormulaBaseline() );
+ }
+ break;
+ }
+ case HANDLE_INTEROP_GRAB_BAG:
+ getGrabBagItem(*pValue);
+ break;
+ case HANDLE_SAVE_THUMBNAIL:
+ {
+ *pValue <<= pDocSh->IsUseThumbnailSave();
+ }
+ break;
+ }
+ }
+}
+
+
+sal_Int32 SAL_CALL SmModel::getRendererCount(
+ const uno::Any& /*rSelection*/,
+ const uno::Sequence< beans::PropertyValue >& /*xOptions*/ )
+{
+ return 1;
+}
+
+
+static Size lcl_GuessPaperSize()
+{
+ Size aRes;
+ const LocaleDataWrapper& rLocWrp( AllSettings().GetLocaleDataWrapper() );
+ if( MeasurementSystem::Metric == rLocWrp.getMeasurementSystemEnum() )
+ {
+ // in 100th mm
+ PaperInfo aInfo( PAPER_A4 );
+ aRes.setWidth( aInfo.getWidth() );
+ aRes.setHeight( aInfo.getHeight() );
+ }
+ else
+ {
+ // in 100th mm
+ PaperInfo aInfo( PAPER_LETTER );
+ aRes.setWidth( aInfo.getWidth() );
+ aRes.setHeight( aInfo.getHeight() );
+ }
+ return aRes;
+}
+
+uno::Sequence< beans::PropertyValue > SAL_CALL SmModel::getRenderer(
+ sal_Int32 nRenderer,
+ const uno::Any& /*rSelection*/,
+ const uno::Sequence< beans::PropertyValue >& /*rxOptions*/ )
+{
+ SolarMutexGuard aGuard;
+
+ if (0 != nRenderer)
+ throw IllegalArgumentException();
+
+ SmDocShell *pDocSh = static_cast < SmDocShell * >( GetObjectShell() );
+ if (!pDocSh)
+ throw RuntimeException();
+
+ SmPrinterAccess aPrinterAccess( *pDocSh );
+ Printer *pPrinter = aPrinterAccess.GetPrinter();
+ Size aPrtPaperSize ( pPrinter->GetPaperSize() );
+
+ // if paper size is 0 (usually if no 'real' printer is found),
+ // guess the paper size
+ if (aPrtPaperSize.IsEmpty())
+ aPrtPaperSize = lcl_GuessPaperSize();
+ awt::Size aPageSize( aPrtPaperSize.Width(), aPrtPaperSize.Height() );
+
+ uno::Sequence< beans::PropertyValue > aRenderer(1);
+ PropertyValue &rValue = aRenderer.getArray()[0];
+ rValue.Name = "PageSize";
+ rValue.Value <<= aPageSize;
+
+ if (!m_pPrintUIOptions)
+ m_pPrintUIOptions.reset(new SmPrintUIOptions);
+ m_pPrintUIOptions->appendPrintUIOptions( aRenderer );
+
+ return aRenderer;
+}
+
+void SAL_CALL SmModel::render(
+ sal_Int32 nRenderer,
+ const uno::Any& rSelection,
+ const uno::Sequence< beans::PropertyValue >& rxOptions )
+{
+ SolarMutexGuard aGuard;
+
+ if (0 != nRenderer)
+ throw IllegalArgumentException();
+
+ SmDocShell *pDocSh = static_cast < SmDocShell * >( GetObjectShell() );
+ if (!pDocSh)
+ throw RuntimeException();
+
+ // get device to be rendered in
+ uno::Reference< awt::XDevice > xRenderDevice;
+ for (const auto& rxOption : rxOptions)
+ {
+ if( rxOption.Name == "RenderDevice" )
+ rxOption.Value >>= xRenderDevice;
+ }
+
+ if (!xRenderDevice.is())
+ return;
+
+ VCLXDevice* pDevice = comphelper::getUnoTunnelImplementation<VCLXDevice>( xRenderDevice );
+ VclPtr< OutputDevice> pOut = pDevice ? pDevice->GetOutputDevice()
+ : VclPtr< OutputDevice >();
+ if (!pOut)
+ throw RuntimeException();
+
+ pOut->SetMapMode(MapMode(MapUnit::Map100thMM));
+
+ uno::Reference< frame::XModel > xModel;
+ rSelection >>= xModel;
+ if (xModel != pDocSh->GetModel())
+ return;
+
+ //!! when called via API we may not have an active view
+ //!! thus we go and look for a view that can be used.
+ SfxViewShell* pViewSh = SfxViewShell::GetFirst( false /* search non-visible views as well*/, checkSfxViewShell<SmViewShell> );
+ while (pViewSh && pViewSh->GetObjectShell() != pDocSh)
+ pViewSh = SfxViewShell::GetNext( *pViewSh, false /* search non-visible views as well*/, checkSfxViewShell<SmViewShell> );
+ SmViewShell *pView = dynamic_cast< SmViewShell *>( pViewSh );
+ SAL_WARN_IF( !pView, "starmath", "SmModel::render : no SmViewShell found" );
+
+ if (!pView)
+ return;
+
+ SmPrinterAccess aPrinterAccess( *pDocSh );
+ Printer *pPrinter = aPrinterAccess.GetPrinter();
+
+ Size aPrtPaperSize ( pPrinter->GetPaperSize() );
+ Size aOutputSize ( pPrinter->GetOutputSize() );
+ Point aPrtPageOffset( pPrinter->GetPageOffset() );
+
+ // no real printer ??
+ if (aPrtPaperSize.IsEmpty())
+ {
+ aPrtPaperSize = lcl_GuessPaperSize();
+ // factors from Windows DIN A4
+ aOutputSize = Size( static_cast<long>(aPrtPaperSize.Width() * 0.941),
+ static_cast<long>(aPrtPaperSize.Height() * 0.961));
+ aPrtPageOffset = Point( static_cast<long>(aPrtPaperSize.Width() * 0.0250),
+ static_cast<long>(aPrtPaperSize.Height() * 0.0214));
+ }
+ tools::Rectangle OutputRect( Point(), aOutputSize );
+
+
+ // set minimum top and bottom border
+ if (aPrtPageOffset.Y() < 2000)
+ OutputRect.AdjustTop(2000 - aPrtPageOffset.Y() );
+ if ((aPrtPaperSize.Height() - (aPrtPageOffset.Y() + OutputRect.Bottom())) < 2000)
+ OutputRect.AdjustBottom( -(2000 - (aPrtPaperSize.Height() -
+ (aPrtPageOffset.Y() + OutputRect.Bottom()))) );
+
+ // set minimum left and right border
+ if (aPrtPageOffset.X() < 2500)
+ OutputRect.AdjustLeft(2500 - aPrtPageOffset.X() );
+ if ((aPrtPaperSize.Width() - (aPrtPageOffset.X() + OutputRect.Right())) < 1500)
+ OutputRect.AdjustRight( -(1500 - (aPrtPaperSize.Width() -
+ (aPrtPageOffset.X() + OutputRect.Right()))) );
+
+ if (!m_pPrintUIOptions)
+ m_pPrintUIOptions.reset(new SmPrintUIOptions);
+ m_pPrintUIOptions->processProperties( rxOptions );
+
+ pView->Impl_Print( *pOut, *m_pPrintUIOptions, OutputRect );
+
+ // release SmPrintUIOptions when everything is done.
+ // That way, when SmPrintUIOptions is needed again it will read the latest configuration settings in its c-tor.
+ if (m_pPrintUIOptions->getBoolValue( "IsLastPage" ))
+ {
+ m_pPrintUIOptions.reset();
+ }
+}
+
+void SAL_CALL SmModel::setParent( const uno::Reference< uno::XInterface >& xParent)
+{
+ SolarMutexGuard aGuard;
+ SfxBaseModel::setParent( xParent );
+ uno::Reference< lang::XUnoTunnel > xParentTunnel( xParent, uno::UNO_QUERY );
+ if ( xParentTunnel.is() )
+ {
+ SvGlobalName aSfxIdent( SFX_GLOBAL_CLASSID );
+ SfxObjectShell* pDoc = reinterpret_cast<SfxObjectShell *>(xParentTunnel->getSomething(
+ aSfxIdent.GetByteSequence() ) );
+ if ( pDoc )
+ GetObjectShell()->OnDocumentPrinterChanged( pDoc->GetDocumentPrinter() );
+ }
+}
+
+void SmModel::writeFormulaOoxml(
+ ::sax_fastparser::FSHelperPtr const pSerializer,
+ oox::core::OoxmlVersion const version,
+ oox::drawingml::DocumentType const documentType, sal_Int8 nAlign)
+{
+ static_cast<SmDocShell*>(GetObjectShell())->writeFormulaOoxml(pSerializer, version, documentType, nAlign);
+}
+
+void SmModel::writeFormulaRtf(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding)
+{
+ static_cast<SmDocShell*>(GetObjectShell())->writeFormulaRtf(rBuffer, nEncoding);
+}
+
+void SmModel::readFormulaOoxml( oox::formulaimport::XmlStream& stream )
+{
+ static_cast< SmDocShell* >( GetObjectShell())->readFormulaOoxml( stream );
+}
+
+Size SmModel::getFormulaSize() const
+{
+ return static_cast< SmDocShell* >( GetObjectShell())->GetSize();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/utility.cxx b/starmath/source/utility.cxx
new file mode 100644
index 000000000..a842c7ed6
--- /dev/null
+++ b/starmath/source/utility.cxx
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <utility.hxx>
+#include <dialog.hxx>
+#include <view.hxx>
+
+// return pointer to active SmViewShell, if this is not possible
+// return 0 instead.
+//!! Since this method is based on the current focus it is somewhat
+//!! unreliable and may return unexpected 0 pointers!
+SmViewShell * SmGetActiveView()
+{
+ SfxViewShell *pView = SfxViewShell::Current();
+ return dynamic_cast<SmViewShell*>( pView);
+}
+
+
+/**************************************************************************/
+
+void SmFontPickList::Clear()
+{
+ aFontVec.clear();
+}
+
+SmFontPickList& SmFontPickList::operator = (const SmFontPickList& rList)
+{
+ Clear();
+ nMaxItems = rList.nMaxItems;
+ for (const auto & nPos : rList.aFontVec)
+ aFontVec.push_back( nPos );
+
+ return *this;
+}
+
+vcl::Font SmFontPickList::Get(sal_uInt16 nPos) const
+{
+ return nPos < aFontVec.size() ? aFontVec[nPos] : vcl::Font();
+}
+
+namespace {
+
+bool lcl_CompareItem(const vcl::Font & rFirstFont, const vcl::Font & rSecondFont)
+{
+ return rFirstFont.GetFamilyName() == rSecondFont.GetFamilyName() &&
+ rFirstFont.GetFamilyType() == rSecondFont.GetFamilyType() &&
+ rFirstFont.GetCharSet() == rSecondFont.GetCharSet() &&
+ rFirstFont.GetWeight() == rSecondFont.GetWeight() &&
+ rFirstFont.GetItalic() == rSecondFont.GetItalic();
+}
+
+OUString lcl_GetStringItem(const vcl::Font &rFont)
+{
+ OUStringBuffer aString(rFont.GetFamilyName());
+
+ if (IsItalic( rFont ))
+ {
+ aString.append(", ");
+ aString.append(SmResId(RID_FONTITALIC));
+ }
+ if (IsBold( rFont ))
+ {
+ aString.append(", ");
+ aString.append(SmResId(RID_FONTBOLD));
+ }
+
+ return aString.makeStringAndClear();
+}
+
+}
+
+void SmFontPickList::Insert(const vcl::Font &rFont)
+{
+ for (size_t nPos = 0; nPos < aFontVec.size(); nPos++)
+ if (lcl_CompareItem( aFontVec[nPos], rFont))
+ {
+ aFontVec.erase( aFontVec.begin() + nPos );
+ break;
+ }
+
+ aFontVec.push_front( rFont );
+
+ if (aFontVec.size() > nMaxItems)
+ {
+ aFontVec.pop_back();
+ }
+}
+
+void SmFontPickList::ReadFrom(const SmFontDialog& rDialog)
+{
+ Insert(rDialog.GetFont());
+}
+
+void SmFontPickList::WriteTo(SmFontDialog& rDialog) const
+{
+ rDialog.SetFont(Get());
+}
+
+
+/**************************************************************************/
+
+SmFontPickListBox::SmFontPickListBox(std::unique_ptr<weld::ComboBox> pWidget)
+ : SmFontPickList(4)
+ , m_xWidget(std::move(pWidget))
+{
+ m_xWidget->connect_changed(LINK(this, SmFontPickListBox, SelectHdl));
+}
+
+IMPL_LINK_NOARG(SmFontPickListBox, SelectHdl, weld::ComboBox&, void)
+{
+ OUString aString;
+
+ const int nPos = m_xWidget->get_active();
+ if (nPos != 0)
+ {
+ SmFontPickList::Insert(Get(nPos));
+ aString = m_xWidget->get_text(nPos);
+ m_xWidget->remove(nPos);
+ m_xWidget->insert_text(0, aString);
+ }
+
+ m_xWidget->set_active(0);
+}
+
+SmFontPickListBox& SmFontPickListBox::operator=(const SmFontPickList& rList)
+{
+ *static_cast<SmFontPickList *>(this) = rList;
+
+ for (decltype(aFontVec)::size_type nPos = 0; nPos < aFontVec.size(); nPos++)
+ m_xWidget->insert_text(nPos, lcl_GetStringItem(aFontVec[nPos]));
+
+ if (!aFontVec.empty())
+ m_xWidget->set_active_text(lcl_GetStringItem(aFontVec.front()));
+
+ return *this;
+}
+
+void SmFontPickListBox::Insert(const vcl::Font &rFont)
+{
+ SmFontPickList::Insert(rFont);
+
+ OUString aEntry(lcl_GetStringItem(aFontVec.front()));
+ int nPos = m_xWidget->find_text(aEntry);
+ if (nPos != -1)
+ m_xWidget->remove(nPos);
+ m_xWidget->insert_text(0, aEntry);
+ m_xWidget->set_active(0);
+
+ while (m_xWidget->get_count() > nMaxItems)
+ m_xWidget->remove(m_xWidget->get_count() - 1);
+}
+
+bool IsItalic( const vcl::Font &rFont )
+{
+ FontItalic eItalic = rFont.GetItalic();
+ // the code below leaves only _NONE and _DONTKNOW as not italic
+ return eItalic == ITALIC_OBLIQUE || eItalic == ITALIC_NORMAL;
+}
+
+
+bool IsBold( const vcl::Font &rFont )
+{
+ FontWeight eWeight = rFont.GetWeight();
+ return eWeight > WEIGHT_NORMAL;
+}
+
+
+void SmFace::Impl_Init()
+{
+ SetSize( GetFontSize() );
+ SetTransparent( true );
+ SetAlignment( ALIGN_BASELINE );
+ SetColor( COL_AUTO );
+}
+
+void SmFace::SetSize(const Size& rSize)
+{
+ Size aSize (rSize);
+
+ // check the requested size against minimum value
+ static int const nMinVal = SmPtsTo100th_mm(2);
+
+ if (aSize.Height() < nMinVal)
+ aSize.setHeight( nMinVal );
+
+ //! we don't force a maximum value here because this may prevent eg the
+ //! parentheses in "left ( ... right )" from matching up with large
+ //! bodies (eg stack{...} with many entries).
+ //! Of course this is holds only if characters are used and not polygons.
+
+ Font::SetFontSize(aSize);
+}
+
+
+long SmFace::GetBorderWidth() const
+{
+ if (nBorderWidth < 0)
+ return GetDefaultBorderWidth();
+ else
+ return nBorderWidth;
+}
+
+SmFace & SmFace::operator = (const SmFace &rFace)
+{
+ Font::operator = (rFace);
+ nBorderWidth = -1;
+ return *this;
+}
+
+
+SmFace & operator *= (SmFace &rFace, const Fraction &rFrac)
+ // scales the width and height of 'rFace' by 'rFrac' and returns a
+ // reference to 'rFace'.
+ // It's main use is to make scaling fonts look easier.
+{ const Size &rFaceSize = rFace.GetFontSize();
+
+ rFace.SetSize(Size(long(rFaceSize.Width() * rFrac),
+ long(rFaceSize.Height() * rFrac)));
+ return rFace;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/view.cxx b/starmath/source/view.cxx
new file mode 100644
index 000000000..19274324a
--- /dev/null
+++ b/starmath/source/view.cxx
@@ -0,0 +1,2034 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XFramesSupplier.hpp>
+#include <com/sun/star/container/XChild.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/string.hxx>
+#include <i18nutil/unicode.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docinsert.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfac.hxx>
+#include <svl/eitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/transfer.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svtools/miscopt.hxx>
+#include <svl/whiter.hxx>
+#include <svx/zoomslideritem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <svx/svxdlg.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <sal/log.hxx>
+#include <tools/svborder.hxx>
+
+#include <unotools/streamwrap.hxx>
+
+#include <unomodel.hxx>
+#include <view.hxx>
+#include "cfgitem.hxx"
+#include <dialog.hxx>
+#include <document.hxx>
+#include <starmath.hrc>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include "mathmlimport.hxx"
+#include <cursor.hxx>
+#include "accessibility.hxx"
+#include <ElementsDockingWindow.hxx>
+#include <helpids.h>
+#include <cassert>
+#include <memory>
+
+#define MINZOOM sal_uInt16(25)
+#define MAXZOOM sal_uInt16(800)
+
+// space around the edit window, in pixels
+// fdo#69111: Increased border on the top so that the window is
+// easier to tear off.
+#define CMD_BOX_PADDING 4
+#define CMD_BOX_PADDING_TOP 10
+
+#define ShellClass_SmViewShell
+#include <smslots.hxx>
+
+using namespace css;
+using namespace css::accessibility;
+using namespace css::uno;
+
+SmGraphicWindow::SmGraphicWindow(SmViewShell* pShell)
+ : ScrollableWindow(&pShell->GetViewFrame()->GetWindow())
+ , pViewShell(pShell)
+ , nZoom(100)
+{
+ assert(pViewShell);
+ // docking windows are usually hidden (often already done in the
+ // resource) and will be shown by the sfx framework.
+ Hide();
+
+ const Fraction aFraction(1, 1);
+ SetMapMode(MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction));
+
+ SetTotalSize();
+
+ SetHelpId(HID_SMA_WIN_DOCUMENT);
+
+ ShowLine(false);
+ CaretBlinkInit();
+}
+
+SmGraphicWindow::~SmGraphicWindow()
+{
+ disposeOnce();
+}
+
+void SmGraphicWindow::dispose()
+{
+ if (mxAccessible.is())
+ mxAccessible->ClearWin(); // make Accessible nonfunctional
+ mxAccessible.clear();
+ CaretBlinkStop();
+ ScrollableWindow::dispose();
+}
+
+void SmGraphicWindow::StateChanged(StateChangedType eType)
+{
+ if (eType == StateChangedType::InitShow)
+ Show();
+ ScrollableWindow::StateChanged(eType);
+}
+
+void SmGraphicWindow::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ ScrollableWindow::MouseButtonDown(rMEvt);
+
+ GrabFocus();
+
+ // set formula-cursor and selection of edit window according to the
+ // position clicked at
+
+ SAL_WARN_IF( rMEvt.GetClicks() == 0, "starmath", "0 clicks" );
+ if ( !rMEvt.IsLeft() )
+ return;
+
+ // get click position relative to formula
+ Point aPos (PixelToLogic(rMEvt.GetPosPixel())
+ - GetFormulaDrawPos());
+
+ const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree();
+ if (!pTree)
+ return;
+
+ if (IsInlineEditEnabled()) {
+ pViewShell->GetDoc()->GetCursor().MoveTo(this, aPos, !rMEvt.IsShift());
+ return;
+ }
+ const SmNode *pNode = nullptr;
+ // if it was clicked inside the formula then get the appropriate node
+ if (pTree->OrientedDist(aPos) <= 0)
+ pNode = pTree->FindRectClosestTo(aPos);
+
+ if (!pNode)
+ return;
+
+ SmEditWindow *pEdit = pViewShell->GetEditWindow();
+ if (!pEdit)
+ return;
+ const SmToken aToken (pNode->GetToken());
+
+ // set selection to the beginning of the token
+ ESelection aSel (aToken.nRow - 1, aToken.nCol - 1);
+
+ if (rMEvt.GetClicks() != 1 || aToken.eType == TPLACE)
+ aSel.nEndPos = aSel.nEndPos + sal::static_int_cast< sal_uInt16 >(aToken.aText.getLength());
+
+ pEdit->SetSelection(aSel);
+ SetCursor(pNode);
+
+ // allow for immediate editing and
+ //! implicitly synchronize the cursor position mark in this window
+ pEdit->GrabFocus();
+}
+
+void SmGraphicWindow::MouseMove(const MouseEvent &rMEvt)
+{
+ ScrollableWindow::MouseMove(rMEvt);
+
+ if (rMEvt.IsLeft() && IsInlineEditEnabled())
+ {
+ Point aPos(PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
+ pViewShell->GetDoc()->GetCursor().MoveTo(this, aPos, false);
+
+ CaretBlinkStop();
+ SetIsCursorVisible(true);
+ CaretBlinkStart();
+ RepaintViewShellDoc();
+ }
+}
+
+bool SmGraphicWindow::IsInlineEditEnabled() const
+{
+ return pViewShell->IsInlineEditEnabled();
+}
+
+void SmGraphicWindow::GetFocus()
+{
+ if (!IsInlineEditEnabled())
+ return;
+ if (pViewShell->GetEditWindow())
+ pViewShell->GetEditWindow()->Flush();
+ //Let view shell know what insertions should be done in visual editor
+ pViewShell->SetInsertIntoEditWindow(false);
+ SetIsCursorVisible(true);
+ ShowLine(true);
+ CaretBlinkStart();
+ RepaintViewShellDoc();
+}
+
+void SmGraphicWindow::LoseFocus()
+{
+ ScrollableWindow::LoseFocus();
+ if (mxAccessible.is())
+ {
+ uno::Any aOldValue, aNewValue;
+ aOldValue <<= AccessibleStateType::FOCUSED;
+ // aNewValue remains empty
+ mxAccessible->LaunchEvent( AccessibleEventId::STATE_CHANGED,
+ aOldValue, aNewValue );
+ }
+ if (!IsInlineEditEnabled())
+ return;
+ SetIsCursorVisible(false);
+ ShowLine(false);
+ CaretBlinkStop();
+ RepaintViewShellDoc();
+}
+
+void SmGraphicWindow::RepaintViewShellDoc()
+{
+ SmDocShell* pDoc = pViewShell->GetDoc();
+ if (pDoc)
+ pDoc->Repaint();
+}
+
+IMPL_LINK_NOARG(SmGraphicWindow, CaretBlinkTimerHdl, Timer *, void)
+{
+ if (IsCursorVisible())
+ SetIsCursorVisible(false);
+ else
+ SetIsCursorVisible(true);
+
+ RepaintViewShellDoc();
+}
+
+void SmGraphicWindow::CaretBlinkInit()
+{
+ aCaretBlinkTimer.SetInvokeHandler(LINK(this, SmGraphicWindow, CaretBlinkTimerHdl));
+ aCaretBlinkTimer.SetTimeout( ScrollableWindow::GetSettings().GetStyleSettings().GetCursorBlinkTime() );
+}
+
+void SmGraphicWindow::CaretBlinkStart()
+{
+ if (!IsInlineEditEnabled())
+ return;
+ if (aCaretBlinkTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME)
+ aCaretBlinkTimer.Start();
+}
+
+void SmGraphicWindow::CaretBlinkStop()
+{
+ if (!IsInlineEditEnabled())
+ return;
+ aCaretBlinkTimer.Stop();
+}
+
+void SmGraphicWindow::ShowCursor(bool bShow)
+ // shows or hides the formula-cursor depending on 'bShow' is true or not
+{
+ if (IsInlineEditEnabled())
+ return;
+
+ bool bInvert = bShow != IsCursorVisible();
+
+ if (bInvert)
+ InvertTracking(aCursorRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow);
+
+ SetIsCursorVisible(bShow);
+}
+
+void SmGraphicWindow::ShowLine(bool bShow)
+{
+ if (!IsInlineEditEnabled())
+ return;
+
+ bIsLineVisible = bShow;
+}
+
+void SmGraphicWindow::SetCursor(const SmNode *pNode)
+{
+ if (IsInlineEditEnabled())
+ return;
+
+ const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree();
+
+ // get appropriate rectangle
+ Point aOffset (pNode->GetTopLeft() - pTree->GetTopLeft()),
+ aTLPos (GetFormulaDrawPos() + aOffset);
+ aTLPos.AdjustX( -(pNode->GetItalicLeftSpace()) );
+ Size aSize (pNode->GetItalicSize());
+
+ SetCursor(tools::Rectangle(aTLPos, aSize));
+}
+
+void SmGraphicWindow::SetCursor(const tools::Rectangle &rRect)
+ // sets cursor to new position (rectangle) 'rRect'.
+ // The old cursor will be removed, and the new one will be shown if
+ // that is activated in the ConfigItem
+{
+ if (IsInlineEditEnabled())
+ return;
+
+ SmModule *pp = SM_MOD();
+
+ if (IsCursorVisible())
+ ShowCursor(false); // clean up remainings of old cursor
+ aCursorRect = rRect;
+ if (pp->GetConfig()->IsShowFormulaCursor())
+ ShowCursor(true); // draw new cursor
+}
+
+const SmNode * SmGraphicWindow::SetCursorPos(sal_uInt16 nRow, sal_uInt16 nCol)
+ // looks for a VISIBLE node in the formula tree with its token at
+ // (or around) the position 'nRow', 'nCol' in the edit window
+ // (row and column numbering starts with 1 there!).
+ // If there is such a node the formula-cursor is set to cover that nodes
+ // rectangle. If not the formula-cursor will be hidden.
+ // In any case the search result is being returned.
+{
+ if (IsInlineEditEnabled())
+ return nullptr;
+
+ // find visible node with token at nRow, nCol
+ const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree(),
+ *pNode = nullptr;
+ if (pTree)
+ pNode = pTree->FindTokenAt(nRow, nCol);
+
+ if (pNode)
+ SetCursor(pNode);
+ else
+ ShowCursor(false);
+
+ return pNode;
+}
+
+void SmGraphicWindow::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ rRenderContext.SetBackground(SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
+}
+
+void SmGraphicWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ SmDocShell& rDoc = *pViewShell->GetDoc();
+ Point aPoint;
+
+ rDoc.DrawFormula(rRenderContext, aPoint, true); //! modifies aPoint to be the topleft
+ //! corner of the formula
+ aFormulaDrawPos = aPoint;
+ if (IsInlineEditEnabled())
+ {
+ //Draw cursor if any...
+ if (pViewShell->GetDoc()->HasCursor() && IsLineVisible())
+ pViewShell->GetDoc()->GetCursor().Draw(rRenderContext, aPoint, IsCursorVisible());
+ }
+ else
+ {
+ SetIsCursorVisible(false); // (old) cursor must be drawn again
+
+ const SmEditWindow* pEdit = pViewShell->GetEditWindow();
+ if (pEdit)
+ { // get new position for formula-cursor (for possible altered formula)
+ sal_Int32 nRow;
+ sal_uInt16 nCol;
+ SmGetLeftSelectionPart(pEdit->GetSelection(), nRow, nCol);
+ nRow++;
+ nCol++;
+ const SmNode *pFound = SetCursorPos(static_cast<sal_uInt16>(nRow), nCol);
+
+ SmModule *pp = SM_MOD();
+ if (pFound && pp->GetConfig()->IsShowFormulaCursor())
+ ShowCursor(true);
+ }
+ }
+}
+
+
+void SmGraphicWindow::SetTotalSize ()
+{
+ SmDocShell &rDoc = *pViewShell->GetDoc();
+ const Size aTmp( PixelToLogic( LogicToPixel( rDoc.GetSize() )));
+ if ( aTmp != ScrollableWindow::GetTotalSize() )
+ ScrollableWindow::SetTotalSize( aTmp );
+}
+
+void SmGraphicWindow::KeyInput(const KeyEvent& rKEvt)
+{
+ if (!IsInlineEditEnabled()) {
+ if (! (GetView() && GetView()->KeyInput(rKEvt)) )
+ ScrollableWindow::KeyInput(rKEvt);
+ return;
+ }
+
+ SmCursor& rCursor = pViewShell->GetDoc()->GetCursor();
+ KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
+ if (eFunc == KeyFuncType::COPY)
+ rCursor.Copy();
+ else if (eFunc == KeyFuncType::CUT)
+ rCursor.Cut();
+ else if (eFunc == KeyFuncType::PASTE)
+ rCursor.Paste();
+ else {
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+ switch(nCode)
+ {
+ case KEY_LEFT:
+ {
+ rCursor.Move(this, MoveLeft, !rKEvt.GetKeyCode().IsShift());
+ }break;
+ case KEY_RIGHT:
+ {
+ rCursor.Move(this, MoveRight, !rKEvt.GetKeyCode().IsShift());
+ }break;
+ case KEY_UP:
+ {
+ rCursor.Move(this, MoveUp, !rKEvt.GetKeyCode().IsShift());
+ }break;
+ case KEY_DOWN:
+ {
+ rCursor.Move(this, MoveDown, !rKEvt.GetKeyCode().IsShift());
+ }break;
+ case KEY_RETURN:
+ {
+ if(!rKEvt.GetKeyCode().IsShift())
+ rCursor.InsertRow();
+ }break;
+ case KEY_DELETE:
+ {
+ if(!rCursor.HasSelection()){
+ rCursor.Move(this, MoveRight, false);
+ if(rCursor.HasComplexSelection()) break;
+ }
+ rCursor.Delete();
+ }break;
+ case KEY_BACKSPACE:
+ {
+ rCursor.DeletePrev(this);
+ }break;
+ case KEY_ADD:
+ rCursor.InsertElement(PlusElement);
+ break;
+ case KEY_SUBTRACT:
+ if(rKEvt.GetKeyCode().IsShift())
+ rCursor.InsertSubSup(RSUB);
+ else
+ rCursor.InsertElement(MinusElement);
+ break;
+ case KEY_MULTIPLY:
+ rCursor.InsertElement(CDotElement);
+ break;
+ case KEY_DIVIDE:
+ rCursor.InsertFraction();
+ break;
+ case KEY_LESS:
+ rCursor.InsertElement(LessThanElement);
+ break;
+ case KEY_GREATER:
+ rCursor.InsertElement(GreaterThanElement);
+ break;
+ case KEY_EQUAL:
+ rCursor.InsertElement(EqualElement);
+ break;
+ default:
+ {
+ sal_Unicode code = rKEvt.GetCharCode();
+ SmBraceNode* pBraceNode = nullptr;
+
+ if(code == ' ') {
+ rCursor.InsertElement(BlankElement);
+ }else if(code == '^') {
+ rCursor.InsertSubSup(RSUP);
+ }else if(code == '(') {
+ rCursor.InsertBrackets(SmBracketType::Round);
+ }else if(code == '[') {
+ rCursor.InsertBrackets(SmBracketType::Square);
+ }else if(code == '{') {
+ rCursor.InsertBrackets(SmBracketType::Curly);
+ }else if(code == '!') {
+ rCursor.InsertElement(FactorialElement);
+ }else if(code == '%') {
+ rCursor.InsertElement(PercentElement);
+ }else if(code == ')' && rCursor.IsAtTailOfBracket(SmBracketType::Round, &pBraceNode)) {
+ rCursor.MoveAfterBracket(pBraceNode);
+ }else if(code == ']' && rCursor.IsAtTailOfBracket(SmBracketType::Square, &pBraceNode)) {
+ rCursor.MoveAfterBracket(pBraceNode);
+ }else if(code == '}' && rCursor.IsAtTailOfBracket(SmBracketType::Curly, &pBraceNode)) {
+ rCursor.MoveAfterBracket(pBraceNode);
+ }else{
+ if(code != 0){
+ rCursor.InsertText(OUString(code));
+ }else if (! (GetView() && GetView()->KeyInput(rKEvt)) )
+ ScrollableWindow::KeyInput(rKEvt);
+ }
+ }
+ }
+ }
+ CaretBlinkStop();
+ CaretBlinkStart();
+ SetIsCursorVisible(true);
+ RepaintViewShellDoc();
+}
+
+
+void SmGraphicWindow::Command(const CommandEvent& rCEvt)
+{
+ bool bCallBase = true;
+ if ( !pViewShell->GetViewFrame()->GetFrame().IsInPlace() )
+ {
+ switch ( rCEvt.GetCommand() )
+ {
+ case CommandEventId::ContextMenu:
+ {
+ GetParent()->ToTop();
+ Point aPos(5, 5);
+ if (rCEvt.IsMouseEvent())
+ aPos = rCEvt.GetMousePosPixel();
+
+ // added for replaceability of context menus
+ SfxDispatcher::ExecutePopup( this, &aPos );
+
+ bCallBase = false;
+ }
+ break;
+
+ case CommandEventId::Wheel:
+ {
+ const CommandWheelData* pWData = rCEvt.GetWheelData();
+ if ( pWData && CommandWheelMode::ZOOM == pWData->GetMode() )
+ {
+ sal_uInt16 nTmpZoom = GetZoom();
+ if( 0 > pWData->GetDelta() )
+ nTmpZoom -= 10;
+ else
+ nTmpZoom += 10;
+ SetZoom( nTmpZoom );
+ bCallBase = false;
+ }
+ }
+ break;
+
+ default: break;
+ }
+ }
+ if ( bCallBase )
+ ScrollableWindow::Command (rCEvt);
+}
+
+
+void SmGraphicWindow::SetZoom(sal_uInt16 Factor)
+{
+ nZoom = std::min(std::max(Factor, MINZOOM), MAXZOOM);
+ Fraction aFraction (nZoom, 100);
+ SetMapMode( MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction) );
+ SetTotalSize();
+ SmViewShell *pViewSh = GetView();
+ if (pViewSh)
+ {
+ pViewSh->GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOM);
+ pViewSh->GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOMSLIDER);
+ }
+ Invalidate();
+}
+
+
+void SmGraphicWindow::ZoomToFitInWindow()
+{
+ SmDocShell &rDoc = *pViewShell->GetDoc();
+
+ // set defined mapmode before calling 'LogicToPixel' below
+ SetMapMode(MapMode(MapUnit::Map100thMM));
+
+ Size aSize (LogicToPixel(rDoc.GetSize()));
+ Size aWindowSize (GetSizePixel());
+
+ if (!aSize.IsEmpty())
+ {
+ long nVal = std::min ((85 * aWindowSize.Width()) / aSize.Width(),
+ (85 * aWindowSize.Height()) / aSize.Height());
+ SetZoom ( sal::static_int_cast< sal_uInt16 >(nVal) );
+ }
+}
+
+uno::Reference< XAccessible > SmGraphicWindow::CreateAccessible()
+{
+ if (!mxAccessible.is())
+ {
+ mxAccessible = new SmGraphicAccessible( this );
+ }
+ return mxAccessible.get();
+}
+
+/**************************************************************************/
+
+
+SmGraphicController::SmGraphicController(SmGraphicWindow &rSmGraphic,
+ sal_uInt16 nId_,
+ SfxBindings &rBindings) :
+ SfxControllerItem(nId_, rBindings),
+ rGraphic(rSmGraphic)
+{
+}
+
+
+void SmGraphicController::StateChanged(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
+{
+ rGraphic.SetTotalSize();
+ rGraphic.Invalidate();
+ SfxControllerItem::StateChanged (nSID, eState, pState);
+}
+
+
+/**************************************************************************/
+
+
+SmEditController::SmEditController(SmEditWindow &rSmEdit,
+ sal_uInt16 nId_,
+ SfxBindings &rBindings) :
+ SfxControllerItem(nId_, rBindings),
+ rEdit(rSmEdit)
+{
+}
+
+
+
+void SmEditController::StateChanged(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
+{
+ const SfxStringItem *pItem = dynamic_cast<const SfxStringItem*>( pState);
+
+ if ((pItem != nullptr) && (rEdit.GetText() != pItem->GetValue()))
+ rEdit.SetText(pItem->GetValue());
+ SfxControllerItem::StateChanged (nSID, eState, pState);
+}
+
+/**************************************************************************/
+SmCmdBoxWindow::SmCmdBoxWindow(SfxBindings *pBindings_, SfxChildWindow *pChildWindow,
+ vcl::Window *pParent) :
+ SfxDockingWindow(pBindings_, pChildWindow, pParent, WB_MOVEABLE|WB_CLOSEABLE|WB_SIZEABLE|WB_DOCKABLE),
+ aEdit (VclPtr<SmEditWindow>::Create(*this)),
+ aController (*aEdit, SID_TEXT, *pBindings_),
+ bExiting (false)
+{
+ SetHelpId( HID_SMA_COMMAND_WIN );
+ SetSizePixel(LogicToPixel(Size(292 , 94), MapMode(MapUnit::MapAppFont)));
+ SetText(SmResId(STR_CMDBOXWINDOW));
+
+ Hide();
+
+ aInitialFocusTimer.SetInvokeHandler(LINK(this, SmCmdBoxWindow, InitialFocusTimerHdl));
+ aInitialFocusTimer.SetTimeout(100);
+}
+
+SmCmdBoxWindow::~SmCmdBoxWindow ()
+{
+ disposeOnce();
+}
+
+void SmCmdBoxWindow::dispose()
+{
+ aInitialFocusTimer.Stop();
+ bExiting = true;
+ aController.dispose();
+ aEdit.disposeAndClear();
+ SfxDockingWindow::dispose();
+}
+
+SmViewShell * SmCmdBoxWindow::GetView()
+{
+ SfxDispatcher *pDispatcher = GetBindings().GetDispatcher();
+ SfxViewShell *pView = pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr;
+ return dynamic_cast<SmViewShell*>( pView);
+}
+
+void SmCmdBoxWindow::Resize()
+{
+ tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel());
+ aRect.AdjustLeft(CMD_BOX_PADDING );
+ aRect.AdjustTop(CMD_BOX_PADDING_TOP );
+ aRect.AdjustRight( -(CMD_BOX_PADDING) );
+ aRect.AdjustBottom( -(CMD_BOX_PADDING) );
+
+ DecorationView aView(this);
+ aRect = aView.DrawFrame(aRect, DrawFrameStyle::In, DrawFrameFlags::NoDraw);
+
+ aEdit->SetPosSizePixel(aRect.TopLeft(), aRect.GetSize());
+ SfxDockingWindow::Resize();
+ Invalidate();
+}
+
+void SmCmdBoxWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
+{
+ tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel());
+ aRect.AdjustLeft(CMD_BOX_PADDING );
+ aRect.AdjustTop(CMD_BOX_PADDING_TOP );
+ aRect.AdjustRight( -(CMD_BOX_PADDING) );
+ aRect.AdjustBottom( -(CMD_BOX_PADDING) );
+
+ aEdit->SetPosSizePixel(aRect.TopLeft(), aRect.GetSize());
+
+ DecorationView aView(&rRenderContext);
+ aView.DrawFrame( aRect, DrawFrameStyle::In );
+}
+
+Size SmCmdBoxWindow::CalcDockingSize(SfxChildAlignment eAlign)
+{
+ switch (eAlign)
+ {
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::RIGHT:
+ return Size();
+ default:
+ break;
+ }
+ return SfxDockingWindow::CalcDockingSize(eAlign);
+}
+
+SfxChildAlignment SmCmdBoxWindow::CheckAlignment(SfxChildAlignment eActual,
+ SfxChildAlignment eWish)
+{
+ switch (eWish)
+ {
+ case SfxChildAlignment::TOP:
+ case SfxChildAlignment::BOTTOM:
+ case SfxChildAlignment::NOALIGNMENT:
+ return eWish;
+ default:
+ break;
+ }
+
+ return eActual;
+}
+
+void SmCmdBoxWindow::StateChanged( StateChangedType nStateChange )
+{
+ if (StateChangedType::InitShow == nStateChange)
+ {
+ Resize(); // avoid SmEditWindow not being painted correctly
+
+ // set initial position of window in floating mode
+ if (IsFloatingMode())
+ AdjustPosition(); //! don't change pos in docking-mode !
+
+ aInitialFocusTimer.Start();
+ }
+
+ SfxDockingWindow::StateChanged( nStateChange );
+}
+
+IMPL_LINK_NOARG( SmCmdBoxWindow, InitialFocusTimerHdl, Timer *, void )
+{
+ // We want to have the focus in the edit window once Math has been opened
+ // to allow for immediate typing.
+ // Problem: There is no proper way to do this
+ // Thus: this timer based solution has been implemented (see GrabFocus below)
+
+ // Follow-up problem (#i114910): grabbing the focus may bust the help system since
+ // it relies on getting the current frame which conflicts with grabbing the focus.
+ // Thus aside from the 'GrabFocus' call everything else is to get the
+ // help reliably working despite using 'GrabFocus'.
+
+ try
+ {
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( comphelper::getProcessComponentContext() );
+
+ aEdit->GrabFocus();
+
+ SmViewShell* pView = GetView();
+ assert(pView);
+ bool bInPlace = pView->GetViewFrame()->GetFrame().IsInPlace();
+ uno::Reference< frame::XFrame > xFrame( GetBindings().GetDispatcher()->GetFrame()->GetFrame().GetFrameInterface());
+ if ( bInPlace )
+ {
+ uno::Reference<container::XChild> xModel(pView->GetDoc()->GetModel(),
+ uno::UNO_QUERY_THROW);
+ uno::Reference< frame::XModel > xParent( xModel->getParent(), uno::UNO_QUERY_THROW );
+ uno::Reference< frame::XController > xParentCtrler( xParent->getCurrentController() );
+ uno::Reference< frame::XFramesSupplier > xParentFrame( xParentCtrler->getFrame(), uno::UNO_QUERY_THROW );
+ xParentFrame->setActiveFrame( xFrame );
+ }
+ else
+ {
+ xDesktop->setActiveFrame( xFrame );
+ }
+ }
+ catch (uno::Exception &)
+ {
+ SAL_WARN( "starmath", "failed to properly set initial focus to edit window" );
+ }
+}
+
+void SmCmdBoxWindow::AdjustPosition()
+{
+ const tools::Rectangle aRect( Point(), GetParent()->GetOutputSizePixel() );
+ Point aTopLeft( Point( aRect.Left(),
+ aRect.Bottom() - GetSizePixel().Height() ) );
+ Point aPos( GetParent()->OutputToScreenPixel( aTopLeft ) );
+ if (aPos.X() < 0)
+ aPos.setX( 0 );
+ if (aPos.Y() < 0)
+ aPos.setY( 0 );
+ SetPosPixel( aPos );
+}
+
+void SmCmdBoxWindow::ToggleFloatingMode()
+{
+ SfxDockingWindow::ToggleFloatingMode();
+
+ if (GetFloatingWindow())
+ GetFloatingWindow()->SetMinOutputSizePixel(Size (200, 50));
+}
+
+void SmCmdBoxWindow::GetFocus()
+{
+ if (!bExiting)
+ aEdit->GrabFocus();
+}
+
+SFX_IMPL_DOCKINGWINDOW_WITHID(SmCmdBoxWrapper, SID_CMDBOXWINDOW);
+
+SmCmdBoxWrapper::SmCmdBoxWrapper(vcl::Window *pParentWindow, sal_uInt16 nId,
+ SfxBindings *pBindings,
+ SfxChildWinInfo *pInfo) :
+ SfxChildWindow(pParentWindow, nId)
+{
+ SetWindow(VclPtr<SmCmdBoxWindow>::Create(pBindings, this, pParentWindow));
+
+ // make window docked to the bottom initially (after first start)
+ SetAlignment(SfxChildAlignment::BOTTOM);
+ static_cast<SfxDockingWindow *>(GetWindow())->Initialize(pInfo);
+}
+
+struct SmViewShell_Impl
+{
+ std::unique_ptr<sfx2::DocumentInserter> pDocInserter;
+ std::unique_ptr<SfxRequest> pRequest;
+ SvtMiscOptions aOpts;
+};
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SmViewShell, SfxViewShell)
+
+void SmViewShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server,
+ ToolbarId::Math_Toolbox);
+ //Dummy-Objectbar, to avoid quiver while activating
+
+ GetStaticInterface()->RegisterChildWindow(SmCmdBoxWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(SmElementsDockingWindowWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
+}
+
+SFX_IMPL_NAMED_VIEWFACTORY(SmViewShell, "Default")
+{
+ SFX_VIEW_REGISTRATION(SmDocShell);
+}
+
+void SmViewShell::InnerResizePixel(const Point &rOfs, const Size &rSize, bool)
+{
+ Size aObjSize = GetObjectShell()->GetVisArea().GetSize();
+ if ( !aObjSize.IsEmpty() )
+ {
+ Size aProvidedSize = GetWindow()->PixelToLogic(rSize, MapMode(MapUnit::Map100thMM));
+ SfxViewShell::SetZoomFactor( Fraction( aProvidedSize.Width(), aObjSize.Width() ),
+ Fraction( aProvidedSize.Height(), aObjSize.Height() ) );
+ }
+
+ SetBorderPixel( SvBorder() );
+ GetGraphicWindow().SetPosSizePixel(rOfs, rSize);
+ GetGraphicWindow().SetTotalSize();
+}
+
+void SmViewShell::OuterResizePixel(const Point &rOfs, const Size &rSize)
+{
+ SmGraphicWindow &rWin = GetGraphicWindow();
+ rWin.SetPosSizePixel(rOfs, rSize);
+ if (GetDoc()->IsPreview())
+ rWin.ZoomToFitInWindow();
+ rWin.PaintImmediately();
+}
+
+void SmViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const
+{
+ rRect.SetSize( GetGraphicWindow().GetSizePixel() );
+}
+
+void SmViewShell::SetZoomFactor( const Fraction &rX, const Fraction &rY )
+{
+ const Fraction &rFrac = std::min(rX, rY);
+ GetGraphicWindow().SetZoom(sal::static_int_cast<sal_uInt16>(long(rFrac * Fraction( 100, 1 ))));
+
+ //To avoid rounding errors base class regulates crooked values too
+ //if necessary
+ SfxViewShell::SetZoomFactor( rX, rY );
+}
+
+Size SmViewShell::GetTextLineSize(OutputDevice const & rDevice, const OUString& rLine)
+{
+ Size aSize(rDevice.GetTextWidth(rLine), rDevice.GetTextHeight());
+ const long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;
+
+ if (nTabPos)
+ {
+ aSize.setWidth( 0 );
+ sal_Int32 nPos = 0;
+ do
+ {
+ if (nPos > 0)
+ aSize.setWidth( ((aSize.Width() / nTabPos) + 1) * nTabPos );
+
+ const OUString aText = rLine.getToken(0, '\t', nPos);
+ aSize.AdjustWidth(rDevice.GetTextWidth(aText) );
+ }
+ while (nPos >= 0);
+ }
+
+ return aSize;
+}
+
+Size SmViewShell::GetTextSize(OutputDevice const & rDevice, const OUString& rText, long MaxWidth)
+{
+ Size aSize;
+ Size aTextSize;
+ if (rText.isEmpty())
+ return aTextSize;
+
+ sal_Int32 nPos = 0;
+ do
+ {
+ OUString aLine = rText.getToken(0, '\n', nPos);
+ aLine = aLine.replaceAll("\r", "");
+
+ aSize = GetTextLineSize(rDevice, aLine);
+
+ if (aSize.Width() > MaxWidth)
+ {
+ do
+ {
+ OUString aText;
+ sal_Int32 m = aLine.getLength();
+ sal_Int32 nLen = m;
+
+ for (sal_Int32 n = 0; n < nLen; n++)
+ {
+ sal_Unicode cLineChar = aLine[n];
+ if ((cLineChar == ' ') || (cLineChar == '\t'))
+ {
+ aText = aLine.copy(0, n);
+ if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
+ m = n;
+ else
+ break;
+ }
+ }
+
+ aText = aLine.copy(0, m);
+ aLine = aLine.replaceAt(0, m, "");
+ aSize = GetTextLineSize(rDevice, aText);
+ aTextSize.AdjustHeight(aSize.Height() );
+ aTextSize.setWidth( std::max(aTextSize.Width(), std::min(aSize.Width(), MaxWidth)) );
+
+ aLine = comphelper::string::stripStart(aLine, ' ');
+ aLine = comphelper::string::stripStart(aLine, '\t');
+ aLine = comphelper::string::stripStart(aLine, ' ');
+ }
+ while (!aLine.isEmpty());
+ }
+ else
+ {
+ aTextSize.AdjustHeight(aSize.Height() );
+ aTextSize.setWidth( std::max(aTextSize.Width(), aSize.Width()) );
+ }
+ }
+ while (nPos >= 0);
+
+ return aTextSize;
+}
+
+void SmViewShell::DrawTextLine(OutputDevice& rDevice, const Point& rPosition, const OUString& rLine)
+{
+ Point aPoint(rPosition);
+ const long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;
+
+ if (nTabPos)
+ {
+ sal_Int32 nPos = 0;
+ do
+ {
+ if (nPos > 0)
+ aPoint.setX( ((aPoint.X() / nTabPos) + 1) * nTabPos );
+
+ OUString aText = rLine.getToken(0, '\t', nPos);
+ rDevice.DrawText(aPoint, aText);
+ aPoint.AdjustX(rDevice.GetTextWidth(aText) );
+ }
+ while ( nPos >= 0 );
+ }
+ else
+ rDevice.DrawText(aPoint, rLine);
+}
+
+
+void SmViewShell::DrawText(OutputDevice& rDevice, const Point& rPosition, const OUString& rText, sal_uInt16 MaxWidth)
+{
+ if (rText.isEmpty())
+ return;
+
+ Point aPoint(rPosition);
+ Size aSize;
+
+ sal_Int32 nPos = 0;
+ do
+ {
+ OUString aLine = rText.getToken(0, '\n', nPos);
+ aLine = aLine.replaceAll("\r", "");
+ aSize = GetTextLineSize(rDevice, aLine);
+ if (aSize.Width() > MaxWidth)
+ {
+ do
+ {
+ OUString aText;
+ sal_Int32 m = aLine.getLength();
+ sal_Int32 nLen = m;
+
+ for (sal_Int32 n = 0; n < nLen; n++)
+ {
+ sal_Unicode cLineChar = aLine[n];
+ if ((cLineChar == ' ') || (cLineChar == '\t'))
+ {
+ aText = aLine.copy(0, n);
+ if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
+ m = n;
+ else
+ break;
+ }
+ }
+ aText = aLine.copy(0, m);
+ aLine = aLine.replaceAt(0, m, "");
+
+ DrawTextLine(rDevice, aPoint, aText);
+ aPoint.AdjustY(aSize.Height() );
+
+ aLine = comphelper::string::stripStart(aLine, ' ');
+ aLine = comphelper::string::stripStart(aLine, '\t');
+ aLine = comphelper::string::stripStart(aLine, ' ');
+ }
+ while (GetTextLineSize(rDevice, aLine).Width() > MaxWidth);
+
+ // print the remaining text
+ if (!aLine.isEmpty())
+ {
+ DrawTextLine(rDevice, aPoint, aLine);
+ aPoint.AdjustY(aSize.Height() );
+ }
+ }
+ else
+ {
+ DrawTextLine(rDevice, aPoint, aLine);
+ aPoint.AdjustY(aSize.Height() );
+ }
+ }
+ while ( nPos >= 0 );
+}
+
+void SmViewShell::Impl_Print(OutputDevice &rOutDev, const SmPrintUIOptions &rPrintUIOptions, tools::Rectangle aOutRect )
+{
+ const bool bIsPrintTitle = rPrintUIOptions.getBoolValue( PRTUIOPT_TITLE_ROW, true );
+ const bool bIsPrintFrame = rPrintUIOptions.getBoolValue( PRTUIOPT_BORDER, true );
+ const bool bIsPrintFormulaText = rPrintUIOptions.getBoolValue( PRTUIOPT_FORMULA_TEXT, true );
+ SmPrintSize ePrintSize( static_cast< SmPrintSize >( rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_FORMAT, PRINT_SIZE_NORMAL ) ));
+ const sal_uInt16 nZoomFactor = static_cast< sal_uInt16 >(rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_SCALE, 100 ));
+
+ rOutDev.Push();
+ rOutDev.SetLineColor( COL_BLACK );
+
+ // output text on top
+ if (bIsPrintTitle)
+ {
+ Size aSize600 (0, 600);
+ Size aSize650 (0, 650);
+ vcl::Font aFont(FAMILY_DONTKNOW, aSize600);
+
+ aFont.SetAlignment(ALIGN_TOP);
+ aFont.SetWeight(WEIGHT_BOLD);
+ aFont.SetFontSize(aSize650);
+ aFont.SetColor( COL_BLACK );
+ rOutDev.SetFont(aFont);
+
+ Size aTitleSize (GetTextSize(rOutDev, GetDoc()->GetTitle(), aOutRect.GetWidth() - 200));
+
+ aFont.SetWeight(WEIGHT_NORMAL);
+ aFont.SetFontSize(aSize600);
+ rOutDev.SetFont(aFont);
+
+ Size aDescSize (GetTextSize(rOutDev, GetDoc()->GetComment(), aOutRect.GetWidth() - 200));
+
+ if (bIsPrintFrame)
+ rOutDev.DrawRect(tools::Rectangle(aOutRect.TopLeft(),
+ Size(aOutRect.GetWidth(), 100 + aTitleSize.Height() + 200 + aDescSize.Height() + 100)));
+ aOutRect.AdjustTop(200 );
+
+ // output title
+ aFont.SetWeight(WEIGHT_BOLD);
+ aFont.SetFontSize(aSize650);
+ rOutDev.SetFont(aFont);
+ Point aPoint(aOutRect.Left() + (aOutRect.GetWidth() - aTitleSize.Width()) / 2,
+ aOutRect.Top());
+ DrawText(rOutDev, aPoint, GetDoc()->GetTitle(),
+ sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
+ aOutRect.AdjustTop(aTitleSize.Height() + 200 );
+
+ // output description
+ aFont.SetWeight(WEIGHT_NORMAL);
+ aFont.SetFontSize(aSize600);
+ rOutDev.SetFont(aFont);
+ aPoint.setX( aOutRect.Left() + (aOutRect.GetWidth() - aDescSize.Width()) / 2 );
+ aPoint.setY( aOutRect.Top() );
+ DrawText(rOutDev, aPoint, GetDoc()->GetComment(),
+ sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
+ aOutRect.AdjustTop(aDescSize.Height() + 300 );
+ }
+
+ // output text on bottom
+ if (bIsPrintFormulaText)
+ {
+ vcl::Font aFont(FAMILY_DONTKNOW, Size(0, 600));
+ aFont.SetAlignment(ALIGN_TOP);
+ aFont.SetColor( COL_BLACK );
+
+ // get size
+ rOutDev.SetFont(aFont);
+
+ Size aSize (GetTextSize(rOutDev, GetDoc()->GetText(), aOutRect.GetWidth() - 200));
+
+ aOutRect.AdjustBottom( -(aSize.Height() + 600) );
+
+ if (bIsPrintFrame)
+ rOutDev.DrawRect(tools::Rectangle(aOutRect.BottomLeft(),
+ Size(aOutRect.GetWidth(), 200 + aSize.Height() + 200)));
+
+ Point aPoint (aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
+ aOutRect.Bottom() + 300);
+ DrawText(rOutDev, aPoint, GetDoc()->GetText(),
+ sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
+ aOutRect.AdjustBottom( -200 );
+ }
+
+ if (bIsPrintFrame)
+ rOutDev.DrawRect(aOutRect);
+
+ aOutRect.AdjustTop(100 );
+ aOutRect.AdjustLeft(100 );
+ aOutRect.AdjustBottom( -100 );
+ aOutRect.AdjustRight( -100 );
+
+ Size aSize (GetDoc()->GetSize());
+
+ MapMode OutputMapMode;
+ // PDF export should always use PRINT_SIZE_NORMAL ...
+ if (!rPrintUIOptions.getBoolValue( "IsPrinter" ) )
+ ePrintSize = PRINT_SIZE_NORMAL;
+ switch (ePrintSize)
+ {
+ case PRINT_SIZE_NORMAL:
+ OutputMapMode = MapMode(MapUnit::Map100thMM);
+ break;
+
+ case PRINT_SIZE_SCALED:
+ if (!aSize.IsEmpty())
+ {
+ Size OutputSize (rOutDev.LogicToPixel(Size(aOutRect.GetWidth(),
+ aOutRect.GetHeight()), MapMode(MapUnit::Map100thMM)));
+ Size GraphicSize (rOutDev.LogicToPixel(aSize, MapMode(MapUnit::Map100thMM)));
+ sal_uInt16 nZ = sal::static_int_cast<sal_uInt16>(std::min(long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())),
+ long(Fraction(OutputSize.Height() * 100, GraphicSize.Height()))));
+ nZ -= 10;
+ Fraction aFraction (std::max(MINZOOM, std::min(MAXZOOM, nZ)), 100);
+
+ OutputMapMode = MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction);
+ }
+ else
+ OutputMapMode = MapMode(MapUnit::Map100thMM);
+ break;
+
+ case PRINT_SIZE_ZOOMED:
+ {
+ Fraction aFraction( nZoomFactor, 100 );
+
+ OutputMapMode = MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction);
+ break;
+ }
+ }
+
+ aSize = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aSize, OutputMapMode),
+ MapMode(MapUnit::Map100thMM));
+
+ Point aPos (aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
+ aOutRect.Top() + (aOutRect.GetHeight() - aSize.Height()) / 2);
+
+ aPos = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aPos, MapMode(MapUnit::Map100thMM)),
+ OutputMapMode);
+ aOutRect = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aOutRect, MapMode(MapUnit::Map100thMM)),
+ OutputMapMode);
+
+ rOutDev.SetMapMode(OutputMapMode);
+ rOutDev.SetClipRegion(vcl::Region(aOutRect));
+ GetDoc()->DrawFormula(rOutDev, aPos);
+ rOutDev.SetClipRegion();
+
+ rOutDev.Pop();
+}
+
+SfxPrinter* SmViewShell::GetPrinter(bool bCreate)
+{
+ SmDocShell* pDoc = GetDoc();
+ if (pDoc->HasPrinter() || bCreate)
+ return pDoc->GetPrinter();
+ return nullptr;
+}
+
+sal_uInt16 SmViewShell::SetPrinter(SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
+{
+ SfxPrinter *pOld = GetDoc()->GetPrinter();
+ if ( pOld && pOld->IsPrinting() )
+ return SFX_PRINTERROR_BUSY;
+
+ if ((nDiffFlags & SfxPrinterChangeFlags::PRINTER) == SfxPrinterChangeFlags::PRINTER)
+ GetDoc()->SetPrinter( pNewPrinter );
+
+ if ((nDiffFlags & SfxPrinterChangeFlags::OPTIONS) == SfxPrinterChangeFlags::OPTIONS)
+ {
+ SmModule *pp = SM_MOD();
+ pp->GetConfig()->ItemSetToConfig(pNewPrinter->GetOptions());
+ }
+ return 0;
+}
+
+bool SmViewShell::HasPrintOptionsPage() const
+{
+ return true;
+}
+
+std::unique_ptr<SfxTabPage> SmViewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet &rOptions)
+{
+ return SmPrintOptionsTabPage::Create(pPage, pController, rOptions);
+}
+
+SmEditWindow *SmViewShell::GetEditWindow()
+{
+ SmCmdBoxWrapper* pWrapper = static_cast<SmCmdBoxWrapper*>(
+ GetViewFrame()->GetChildWindow(SmCmdBoxWrapper::GetChildWindowId()));
+
+ if (pWrapper != nullptr)
+ {
+ SmEditWindow& rEditWin = pWrapper->GetEditWindow();
+ return &rEditWin;
+ }
+
+ return nullptr;
+}
+
+void SmViewShell::SetStatusText(const OUString& rText)
+{
+ maStatusText = rText;
+ GetViewFrame()->GetBindings().Invalidate(SID_TEXTSTATUS);
+}
+
+void SmViewShell::ShowError(const SmErrorDesc* pErrorDesc)
+{
+ assert(GetDoc());
+ if (pErrorDesc || nullptr != (pErrorDesc = GetDoc()->GetParser().GetError()) )
+ {
+ SetStatusText( pErrorDesc->m_aText );
+ GetEditWindow()->MarkError( Point( pErrorDesc->m_pNode->GetColumn(),
+ pErrorDesc->m_pNode->GetRow()));
+ }
+}
+
+void SmViewShell::NextError()
+{
+ assert(GetDoc());
+ const SmErrorDesc *pErrorDesc = GetDoc()->GetParser().NextError();
+
+ if (pErrorDesc)
+ ShowError( pErrorDesc );
+}
+
+void SmViewShell::PrevError()
+{
+ assert(GetDoc());
+ const SmErrorDesc *pErrorDesc = GetDoc()->GetParser().PrevError();
+
+ if (pErrorDesc)
+ ShowError( pErrorDesc );
+}
+
+void SmViewShell::Insert( SfxMedium& rMedium )
+{
+ SmDocShell *pDoc = GetDoc();
+ bool bRet = false;
+
+ uno::Reference <embed::XStorage> xStorage = rMedium.GetStorage();
+ if (xStorage.is() && xStorage->getElementNames().hasElements())
+ {
+ if (xStorage->hasByName("content.xml"))
+ {
+ // is this a fabulous math package ?
+ Reference<css::frame::XModel> xModel(pDoc->GetModel());
+ SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
+ bRet = ERRCODE_NONE == aEquation.Import(rMedium);
+ }
+ }
+
+ if (!bRet)
+ return;
+
+ OUString aText = pDoc->GetText();
+ SmEditWindow *pEditWin = GetEditWindow();
+ if (pEditWin)
+ pEditWin->InsertText( aText );
+ else
+ {
+ SAL_WARN( "starmath", "EditWindow missing" );
+ }
+
+ pDoc->Parse();
+ pDoc->SetModified();
+
+ SfxBindings &rBnd = GetViewFrame()->GetBindings();
+ rBnd.Invalidate(SID_GAPHIC_SM);
+ rBnd.Invalidate(SID_TEXT);
+}
+
+void SmViewShell::InsertFrom(SfxMedium &rMedium)
+{
+ bool bSuccess = false;
+ SmDocShell* pDoc = GetDoc();
+ SvStream* pStream = rMedium.GetInStream();
+
+ if (pStream)
+ {
+ const OUString& rFltName = rMedium.GetFilter()->GetFilterName();
+ if ( rFltName == MATHML_XML )
+ {
+ Reference<css::frame::XModel> xModel(pDoc->GetModel());
+ SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
+ bSuccess = ERRCODE_NONE == aEquation.Import(rMedium);
+ }
+ }
+
+ if (!bSuccess)
+ return;
+
+ OUString aText = pDoc->GetText();
+ SmEditWindow *pEditWin = GetEditWindow();
+ if (pEditWin)
+ pEditWin->InsertText(aText);
+ else
+ SAL_WARN( "starmath", "EditWindow missing" );
+
+ pDoc->Parse();
+ pDoc->SetModified();
+
+ SfxBindings& rBnd = GetViewFrame()->GetBindings();
+ rBnd.Invalidate(SID_GAPHIC_SM);
+ rBnd.Invalidate(SID_TEXT);
+}
+
+void SmViewShell::Execute(SfxRequest& rReq)
+{
+ SmEditWindow *pWin = GetEditWindow();
+
+ switch (rReq.GetSlot())
+ {
+ case SID_FORMULACURSOR:
+ {
+ SmModule *pp = SM_MOD();
+
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const SfxPoolItem *pItem;
+
+ bool bVal;
+ if ( pArgs &&
+ SfxItemState::SET == pArgs->GetItemState( SID_FORMULACURSOR, false, &pItem))
+ bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+ else
+ bVal = !pp->GetConfig()->IsShowFormulaCursor();
+
+ pp->GetConfig()->SetShowFormulaCursor(bVal);
+ if (!IsInlineEditEnabled())
+ GetGraphicWindow().ShowCursor(bVal);
+ break;
+ }
+ case SID_DRAW:
+ if (pWin)
+ {
+ GetDoc()->SetText( pWin->GetText() );
+ SetStatusText(OUString());
+ ShowError( nullptr );
+ GetDoc()->Repaint();
+ }
+ break;
+
+ case SID_ZOOM_OPTIMAL:
+ mpGraphic->ZoomToFitInWindow();
+ break;
+
+ case SID_ZOOMIN:
+ mpGraphic->SetZoom(mpGraphic->GetZoom() + 25);
+ break;
+
+ case SID_ZOOMOUT:
+ SAL_WARN_IF( mpGraphic->GetZoom() < 25, "starmath", "incorrect sal_uInt16 argument" );
+ mpGraphic->SetZoom(mpGraphic->GetZoom() - 25);
+ break;
+
+ case SID_COPYOBJECT:
+ {
+ //TODO/LATER: does not work because of UNO Tunneling - will be fixed later
+ Reference< datatransfer::XTransferable > xTrans( GetDoc()->GetModel(), uno::UNO_QUERY );
+ if( xTrans.is() )
+ {
+ auto pTrans = comphelper::getUnoTunnelImplementation<TransferableHelper>(xTrans);
+ if( pTrans )
+ pTrans->CopyToClipboard(GetEditWindow());
+ }
+ }
+ break;
+
+ case SID_PASTEOBJECT:
+ {
+ TransferableDataHelper aData( TransferableDataHelper::CreateFromSystemClipboard(GetEditWindow()) );
+ uno::Reference < io::XInputStream > xStrm;
+ SotClipboardFormatId nId;
+ if( aData.GetTransferable().is() &&
+ ( aData.HasFormat( nId = SotClipboardFormatId::EMBEDDED_OBJ ) ||
+ (aData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) &&
+ aData.HasFormat( nId = SotClipboardFormatId::EMBED_SOURCE ))))
+ xStrm = aData.GetInputStream(nId, OUString());
+
+ if (xStrm.is())
+ {
+ try
+ {
+ uno::Reference < embed::XStorage > xStorage =
+ ::comphelper::OStorageHelper::GetStorageFromInputStream( xStrm, ::comphelper::getProcessComponentContext() );
+ SfxMedium aMedium( xStorage, OUString() );
+ Insert( aMedium );
+ GetDoc()->UpdateText();
+ }
+ catch (uno::Exception &)
+ {
+ SAL_WARN( "starmath", "SmViewShell::Execute (SID_PASTEOBJECT): failed to get storage from input stream" );
+ }
+ }
+ }
+ break;
+
+
+ case SID_CUT:
+ if (pWin)
+ pWin->Cut();
+ break;
+
+ case SID_COPY:
+ if (pWin)
+ {
+ if (pWin->IsAllSelected())
+ {
+ GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_COPYOBJECT, SfxCallMode::RECORD,
+ { new SfxVoidItem(SID_COPYOBJECT) });
+ }
+ else
+ pWin->Copy();
+ }
+ break;
+
+ case SID_PASTE:
+ {
+ bool bCallExec = nullptr == pWin;
+ if( !bCallExec )
+ {
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard(
+ GetEditWindow()) );
+
+ if( aDataHelper.GetTransferable().is() &&
+ aDataHelper.HasFormat( SotClipboardFormatId::STRING ))
+ pWin->Paste();
+ else
+ bCallExec = true;
+ }
+ if( bCallExec )
+ {
+ GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_PASTEOBJECT, SfxCallMode::RECORD,
+ { new SfxVoidItem(SID_PASTEOBJECT) });
+ }
+ }
+ break;
+
+ case SID_DELETE:
+ if (pWin)
+ pWin->Delete();
+ break;
+
+ case SID_SELECT:
+ if (pWin)
+ pWin->SelectAll();
+ break;
+
+ case SID_INSERTCOMMANDTEXT:
+ {
+ const SfxStringItem& rItem = static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_INSERTCOMMANDTEXT));
+
+ if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled()))
+ {
+ pWin->InsertText(rItem.GetValue());
+ }
+ if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow))
+ {
+ GetDoc()->GetCursor().InsertCommandText(rItem.GetValue());
+ GetGraphicWindow().GrabFocus();
+ }
+ break;
+
+ }
+
+ case SID_INSERTSPECIAL:
+ {
+ const SfxStringItem& rItem =
+ static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_INSERTSPECIAL));
+
+ if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled()))
+ pWin->InsertText(rItem.GetValue());
+ if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow))
+ GetDoc()->GetCursor().InsertSpecial(rItem.GetValue());
+ break;
+ }
+
+ case SID_IMPORT_FORMULA:
+ {
+ mpImpl->pRequest.reset(new SfxRequest( rReq ));
+ mpImpl->pDocInserter.reset(new ::sfx2::DocumentInserter(pWin ? pWin->GetFrameWeld() : nullptr,
+ GetDoc()->GetFactory().GetFactoryName()));
+ mpImpl->pDocInserter->StartExecuteModal( LINK( this, SmViewShell, DialogClosedHdl ) );
+ break;
+ }
+
+ case SID_IMPORT_MATHML_CLIPBOARD:
+ {
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard(GetEditWindow()) );
+ uno::Reference < io::XInputStream > xStrm;
+ SotClipboardFormatId nId = SOT_FORMAT_SYSTEM_START; //dummy initialize to avoid warning
+ if ( aDataHelper.GetTransferable().is() )
+ {
+ nId = SotClipboardFormatId::MATHML;
+ if (aDataHelper.HasFormat(nId))
+ {
+ xStrm = aDataHelper.GetInputStream(nId, "");
+ if (xStrm.is())
+ {
+ std::unique_ptr<SfxMedium> pClipboardMedium(new SfxMedium());
+ pClipboardMedium->GetItemSet(); //generate initial itemset, not sure if necessary
+ std::shared_ptr<const SfxFilter> pMathFilter =
+ SfxFilter::GetFilterByName(MATHML_XML);
+ pClipboardMedium->SetFilter(pMathFilter);
+ pClipboardMedium->setStreamToLoadFrom(xStrm, true /*bIsReadOnly*/);
+ InsertFrom(*pClipboardMedium);
+ GetDoc()->UpdateText();
+ }
+ }
+ else
+ {
+ nId = SotClipboardFormatId::STRING;
+ if (aDataHelper.HasFormat(nId))
+ {
+ // In case of FORMAT_STRING no stream exists, need to generate one
+ OUString aString;
+ if (aDataHelper.GetString( nId, aString))
+ {
+ // tdf#117091 force xml declaration to exist
+ if (!aString.startsWith("<?xml"))
+ aString = "<?xml version=\"1.0\"?>\n" + aString;
+
+ std::unique_ptr<SfxMedium> pClipboardMedium(new SfxMedium());
+ pClipboardMedium->GetItemSet(); //generates initial itemset, not sure if necessary
+ std::shared_ptr<const SfxFilter> pMathFilter =
+ SfxFilter::GetFilterByName(MATHML_XML);
+ pClipboardMedium->SetFilter(pMathFilter);
+
+ std::unique_ptr<SvMemoryStream> pStrm;
+ // The text to be imported might asserts encoding like 'encoding="utf-8"' but FORMAT_STRING is UTF-16.
+ // Force encoding to UTF-16, if encoding exists.
+ bool bForceUTF16 = false;
+ sal_Int32 nPosL = aString.indexOf("encoding=\"");
+ sal_Int32 nPosU = -1;
+ if ( nPosL >= 0 && nPosL +10 < aString.getLength() )
+ {
+ nPosL += 10;
+ nPosU = aString.indexOf( '"',nPosL);
+ if (nPosU > nPosL)
+ {
+ bForceUTF16 = true;
+ }
+ }
+ if ( bForceUTF16 )
+ {
+ OUString aNewString = aString.replaceAt( nPosL,nPosU-nPosL,"UTF-16");
+ pStrm.reset(new SvMemoryStream( const_cast<sal_Unicode *>(aNewString.getStr()), aNewString.getLength() * sizeof(sal_Unicode), StreamMode::READ));
+ }
+ else
+ {
+ pStrm.reset(new SvMemoryStream( const_cast<sal_Unicode *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode), StreamMode::READ));
+ }
+ uno::Reference<io::XInputStream> xStrm2( new ::utl::OInputStreamWrapper(*pStrm) );
+ pClipboardMedium->setStreamToLoadFrom(xStrm2, true /*bIsReadOnly*/);
+ InsertFrom(*pClipboardMedium);
+ GetDoc()->UpdateText();
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case SID_NEXTERR:
+ NextError();
+ if (pWin)
+ pWin->GrabFocus();
+ break;
+
+ case SID_PREVERR:
+ PrevError();
+ if (pWin)
+ pWin->GrabFocus();
+ break;
+
+ case SID_NEXTMARK:
+ if (pWin)
+ {
+ pWin->SelNextMark();
+ pWin->GrabFocus();
+ }
+ break;
+
+ case SID_PREVMARK:
+ if (pWin)
+ {
+ pWin->SelPrevMark();
+ pWin->GrabFocus();
+ }
+ break;
+
+ case SID_TEXTSTATUS:
+ {
+ if (rReq.GetArgs() != nullptr)
+ {
+ const SfxStringItem& rItem =
+ static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_TEXTSTATUS));
+
+ SetStatusText(rItem.GetValue());
+ }
+
+ break;
+ }
+
+ case SID_GETEDITTEXT:
+ if (pWin && !pWin->GetText().isEmpty())
+ GetDoc()->SetText( pWin->GetText() );
+ break;
+
+ case SID_ATTR_ZOOM:
+ {
+ if ( !GetViewFrame()->GetFrame().IsInPlace() )
+ {
+ const SfxItemSet *pSet = rReq.GetArgs();
+ if ( pSet )
+ {
+ ZoomByItemSet(pSet);
+ }
+ else
+ {
+ SfxItemSet aSet( SmDocShell::GetPool(), svl::Items<SID_ATTR_ZOOM, SID_ATTR_ZOOM>{});
+ aSet.Put( SvxZoomItem( SvxZoomType::PERCENT, mpGraphic->GetZoom()));
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSvxZoomDialog> xDlg(pFact->CreateSvxZoomDialog(GetViewFrame()->GetWindow().GetFrameWeld(), aSet));
+ xDlg->SetLimits( MINZOOM, MAXZOOM );
+ if (xDlg->Execute() != RET_CANCEL)
+ ZoomByItemSet(xDlg->GetOutputItemSet());
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_ZOOMSLIDER:
+ {
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem;
+
+ if ( pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem ) )
+ {
+ const sal_uInt16 nCurrentZoom = static_cast<const SvxZoomSliderItem *>(pItem)->GetValue();
+ mpGraphic->SetZoom( nCurrentZoom );
+ }
+ }
+ break;
+
+ case SID_ELEMENTSDOCKINGWINDOW:
+ {
+ GetViewFrame()->ToggleChildWindow( SmElementsDockingWindowWrapper::GetChildWindowId() );
+ GetViewFrame()->GetBindings().Invalidate( SID_ELEMENTSDOCKINGWINDOW );
+
+ rReq.Ignore ();
+ }
+ break;
+
+ case SID_UNICODE_NOTATION_TOGGLE:
+ {
+ EditEngine* pEditEngine = nullptr;
+ if( pWin )
+ pEditEngine = pWin->GetEditEngine();
+
+ EditView* pEditView = nullptr;
+ if( pEditEngine )
+ pEditView = pEditEngine->GetView();
+
+ if( pEditView )
+ {
+ const OUString sInput = pEditView->GetSurroundingText();
+ ESelection aSel( pWin->GetSelection() );
+
+ if ( aSel.nStartPos > aSel.nEndPos )
+ aSel.nEndPos = aSel.nStartPos;
+
+ //calculate a valid end-position by reading logical characters
+ sal_Int32 nUtf16Pos=0;
+ while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) )
+ {
+ sInput.iterateCodePoints(&nUtf16Pos);
+ if( nUtf16Pos > aSel.nEndPos )
+ aSel.nEndPos = nUtf16Pos;
+ }
+
+ ToggleUnicodeCodepoint aToggle;
+ while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) )
+ --nUtf16Pos;
+ const OUString sReplacement = aToggle.ReplacementString();
+ if( !sReplacement.isEmpty() )
+ {
+ pEditView->SetSelection( aSel );
+ pEditEngine->UndoActionStart(EDITUNDO_REPLACEALL);
+ aSel.nStartPos = aSel.nEndPos - aToggle.StringToReplace().getLength();
+ pWin->SetSelection( aSel );
+ pEditView->InsertText( sReplacement, true );
+ pEditEngine->UndoActionEnd();
+ pWin->Flush();
+ }
+ }
+ }
+ break;
+
+ case SID_SYMBOLS_CATALOGUE:
+ {
+
+ // get device used to retrieve the FontList
+ SmDocShell *pDoc = GetDoc();
+ OutputDevice *pDev = pDoc->GetPrinter();
+ if (!pDev || pDev->GetDevFontCount() == 0)
+ pDev = &SM_MOD()->GetDefaultVirtualDev();
+ SAL_WARN_IF( !pDev, "starmath", "device for font list missing" );
+
+ SmModule *pp = SM_MOD();
+ SmSymbolDialog aDialog(pWin ? pWin->GetFrameWeld() : nullptr, pDev, pp->GetSymbolManager(), *this);
+ aDialog.run();
+ }
+ break;
+ }
+ rReq.Done();
+}
+
+
+void SmViewShell::GetState(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter(rSet);
+
+ SmEditWindow *pEditWin = GetEditWindow();
+ for (sal_uInt16 nWh = aIter.FirstWhich(); nWh != 0; nWh = aIter.NextWhich())
+ {
+ switch (nWh)
+ {
+ case SID_CUT:
+ case SID_COPY:
+ case SID_DELETE:
+ if (! pEditWin || ! pEditWin->IsSelected())
+ rSet.DisableItem(nWh);
+ break;
+
+ case SID_PASTE:
+ if (pEditWin)
+ {
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard(
+ pEditWin) );
+
+ mbPasteState = aDataHelper.GetTransferable().is() &&
+ ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ||
+ aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ ) ||
+ (aDataHelper.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR )
+ && aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE )));
+ }
+ if( !mbPasteState )
+ rSet.DisableItem( nWh );
+ break;
+
+ case SID_ATTR_ZOOM:
+ rSet.Put(SvxZoomItem( SvxZoomType::PERCENT, mpGraphic->GetZoom()));
+ [[fallthrough]];
+ case SID_ZOOMIN:
+ case SID_ZOOMOUT:
+ case SID_ZOOM_OPTIMAL:
+ if ( GetViewFrame()->GetFrame().IsInPlace() )
+ rSet.DisableItem( nWh );
+ break;
+
+ case SID_ATTR_ZOOMSLIDER :
+ {
+ const sal_uInt16 nCurrentZoom = mpGraphic->GetZoom();
+ SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM );
+ aZoomSliderItem.AddSnappingPoint( 100 );
+ rSet.Put( aZoomSliderItem );
+ }
+ break;
+
+ case SID_NEXTERR:
+ case SID_PREVERR:
+ case SID_NEXTMARK:
+ case SID_PREVMARK:
+ case SID_DRAW:
+ case SID_SELECT:
+ if (! pEditWin || pEditWin->IsEmpty())
+ rSet.DisableItem(nWh);
+ break;
+
+ case SID_TEXTSTATUS:
+ {
+ rSet.Put(SfxStringItem(nWh, maStatusText));
+ }
+ break;
+
+ case SID_FORMULACURSOR:
+ {
+ SmModule *pp = SM_MOD();
+ rSet.Put(SfxBoolItem(nWh, pp->GetConfig()->IsShowFormulaCursor()));
+ }
+ break;
+ case SID_ELEMENTSDOCKINGWINDOW:
+ {
+ bool bState = false;
+ SfxChildWindow *pChildWnd = GetViewFrame()->
+ GetChildWindow( SmElementsDockingWindowWrapper::GetChildWindowId() );
+ if (pChildWnd && pChildWnd->GetWindow()->IsVisible())
+ bState = true;
+ rSet.Put(SfxBoolItem(SID_ELEMENTSDOCKINGWINDOW, bState));
+ }
+ break;
+ }
+ }
+}
+
+SmViewShell::SmViewShell(SfxViewFrame *pFrame_, SfxViewShell *)
+ : SfxViewShell(pFrame_, SfxViewShellFlags::HAS_PRINTOPTIONS)
+ , mpImpl(new SmViewShell_Impl)
+ , mpGraphic(VclPtr<SmGraphicWindow>::Create(this))
+ , maGraphicController(*mpGraphic, SID_GAPHIC_SM, pFrame_->GetBindings())
+ , mbPasteState(false)
+ , mbInsertIntoEditWindow(false)
+{
+ SetStatusText(OUString());
+ SetWindow(mpGraphic.get());
+ SfxShell::SetName("SmView");
+ SfxShell::SetUndoManager( &GetDoc()->GetEditEngine().GetUndoManager() );
+}
+
+
+SmViewShell::~SmViewShell()
+{
+ //!! this view shell is not active anymore !!
+ // Thus 'SmGetActiveView' will give a 0 pointer.
+ // Thus we need to supply this view as argument
+ SmEditWindow *pEditWin = GetEditWindow();
+ if (pEditWin)
+ pEditWin->DeleteEditView();
+ mpGraphic.disposeAndClear();
+}
+
+void SmViewShell::Deactivate( bool bIsMDIActivate )
+{
+ SmEditWindow *pEdit = GetEditWindow();
+ if ( pEdit )
+ pEdit->Flush();
+
+ SfxViewShell::Deactivate( bIsMDIActivate );
+}
+
+void SmViewShell::Activate( bool bIsMDIActivate )
+{
+ SfxViewShell::Activate( bIsMDIActivate );
+
+ SmEditWindow *pEdit = GetEditWindow();
+ if ( pEdit )
+ {
+ //! Since there is no way to be informed if a "drag and drop"
+ //! event has taken place, we call SetText here in order to
+ //! synchronize the GraphicWindow display with the text in the
+ //! EditEngine.
+ SmDocShell *pDoc = GetDoc();
+ pDoc->SetText( pDoc->GetEditEngine().GetText() );
+
+ if ( bIsMDIActivate )
+ pEdit->GrabFocus();
+ }
+}
+
+IMPL_LINK( SmViewShell, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
+{
+ assert(_pFileDlg && "SmViewShell::DialogClosedHdl(): no file dialog");
+ assert(mpImpl->pDocInserter && "ScDocShell::DialogClosedHdl(): no document inserter");
+
+ if ( ERRCODE_NONE == _pFileDlg->GetError() )
+ {
+ std::unique_ptr<SfxMedium> pMedium = mpImpl->pDocInserter->CreateMedium();
+
+ if ( pMedium )
+ {
+ if ( pMedium->IsStorage() )
+ Insert( *pMedium );
+ else
+ InsertFrom( *pMedium );
+ pMedium.reset();
+
+ SmDocShell* pDoc = GetDoc();
+ pDoc->UpdateText();
+ pDoc->ArrangeFormula();
+ pDoc->Repaint();
+ // adjust window, repaint, increment ModifyCount,...
+ GetViewFrame()->GetBindings().Invalidate(SID_GAPHIC_SM);
+ }
+ }
+
+ mpImpl->pRequest->SetReturnValue( SfxBoolItem( mpImpl->pRequest->GetSlot(), true ) );
+ mpImpl->pRequest->Done();
+}
+
+void SmViewShell::Notify( SfxBroadcaster& , const SfxHint& rHint )
+{
+ switch( rHint.GetId() )
+ {
+ case SfxHintId::ModeChanged:
+ case SfxHintId::DocChanged:
+ GetViewFrame()->GetBindings().InvalidateAll(false);
+ break;
+ default:
+ break;
+ }
+}
+
+bool SmViewShell::IsInlineEditEnabled() const
+{
+ return mpImpl->aOpts.IsExperimentalMode();
+}
+
+void SmViewShell::ZoomByItemSet(const SfxItemSet *pSet)
+{
+ assert(pSet);
+ const SvxZoomItem &rZoom = pSet->Get(SID_ATTR_ZOOM);
+ switch( rZoom.GetType() )
+ {
+ case SvxZoomType::PERCENT:
+ mpGraphic->SetZoom(sal::static_int_cast<sal_uInt16>(rZoom.GetValue ()));
+ break;
+
+ case SvxZoomType::OPTIMAL:
+ mpGraphic->ZoomToFitInWindow();
+ break;
+
+ case SvxZoomType::PAGEWIDTH:
+ case SvxZoomType::WHOLEPAGE:
+ {
+ const MapMode aMap( MapUnit::Map100thMM );
+ SfxPrinter *pPrinter = GetPrinter( true );
+ tools::Rectangle OutputRect(Point(), pPrinter->GetOutputSize());
+ Size OutputSize(pPrinter->LogicToPixel(Size(OutputRect.GetWidth(),
+ OutputRect.GetHeight()), aMap));
+ Size GraphicSize(pPrinter->LogicToPixel(GetDoc()->GetSize(), aMap));
+ sal_uInt16 nZ = sal::static_int_cast<sal_uInt16>(std::min(long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())),
+ long(Fraction(OutputSize.Height() * 100, GraphicSize.Height()))));
+ mpGraphic->SetZoom (nZ);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/visitors.cxx b/starmath/source/visitors.cxx
new file mode 100644
index 000000000..5362551fe
--- /dev/null
+++ b/starmath/source/visitors.cxx
@@ -0,0 +1,2425 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <rtl/math.hxx>
+#include <sal/log.hxx>
+#include <tools/gen.hxx>
+#include <vcl/lineinfo.hxx>
+#include <visitors.hxx>
+#include "tmpdevice.hxx"
+#include <cursor.hxx>
+#include <cassert>
+
+// SmDefaultingVisitor
+
+void SmDefaultingVisitor::Visit( SmTableNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBraceNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBracebodyNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmOperNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmAlignNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmAttributNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmFontNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmUnHorNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBinHorNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBinVerNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBinDiagonalNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmSubSupNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmMatrixNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmPlaceNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmTextNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmSpecialNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmGlyphSpecialNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmMathSymbolNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBlankNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmErrorNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmLineNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmExpressionNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmPolyLineNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmRootNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmRootSymbolNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmRectangleNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmVerticalBraceNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+// SmCaretDrawingVisitor
+
+SmCaretDrawingVisitor::SmCaretDrawingVisitor( OutputDevice& rDevice,
+ SmCaretPos position,
+ Point offset,
+ bool caretVisible )
+ : mrDev( rDevice )
+ , maPos( position )
+ , maOffset( offset )
+ , mbCaretVisible( caretVisible )
+{
+ SAL_WARN_IF( !position.IsValid(), "starmath", "Cannot draw invalid position!" );
+ if( !position.IsValid( ) )
+ return;
+
+ //Save device state
+ mrDev.Push( PushFlags::FONT | PushFlags::MAPMODE | PushFlags::LINECOLOR | PushFlags::FILLCOLOR | PushFlags::TEXTCOLOR );
+
+ maPos.pSelectedNode->Accept( this );
+ //Restore device state
+ mrDev.Pop( );
+}
+
+void SmCaretDrawingVisitor::Visit( SmTextNode* pNode )
+{
+ long i = maPos.nIndex;
+
+ mrDev.SetFont( pNode->GetFont( ) );
+
+ //Find the line
+ SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );
+
+ //Find coordinates
+ long left = pNode->GetLeft( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, i ) + maOffset.X( );
+ long top = pLine->GetTop( ) + maOffset.Y( );
+ long height = pLine->GetHeight( );
+ long left_line = pLine->GetLeft( ) + maOffset.X( );
+ long right_line = pLine->GetRight( ) + maOffset.X( );
+
+ //Set color
+ mrDev.SetLineColor( COL_BLACK );
+
+ if ( mbCaretVisible ) {
+ //Draw vertical line
+ Point p1( left, top );
+ Point p2( left, top + height );
+ mrDev.DrawLine( p1, p2 );
+ }
+
+ //Underline the line
+ Point aLeft( left_line, top + height );
+ Point aRight( right_line, top + height );
+ mrDev.DrawLine( aLeft, aRight );
+}
+
+void SmCaretDrawingVisitor::DefaultVisit( SmNode* pNode )
+{
+ //Find the line
+ SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );
+
+ //Find coordinates
+ long left = pNode->GetLeft( ) + maOffset.X( ) + ( maPos.nIndex == 1 ? pNode->GetWidth( ) : 0 );
+ long top = pLine->GetTop( ) + maOffset.Y( );
+ long height = pLine->GetHeight( );
+ long left_line = pLine->GetLeft( ) + maOffset.X( );
+ long right_line = pLine->GetRight( ) + maOffset.X( );
+
+ //Set color
+ mrDev.SetLineColor( COL_BLACK );
+
+ if ( mbCaretVisible ) {
+ //Draw vertical line
+ Point p1( left, top );
+ Point p2( left, top + height );
+ mrDev.DrawLine( p1, p2 );
+ }
+
+ //Underline the line
+ Point aLeft( left_line, top + height );
+ Point aRight( right_line, top + height );
+ mrDev.DrawLine( aLeft, aRight );
+}
+
+// SmCaretPos2LineVisitor
+
+void SmCaretPos2LineVisitor::Visit( SmTextNode* pNode )
+{
+ //Save device state
+ mpDev->Push( PushFlags::FONT | PushFlags::TEXTCOLOR );
+
+ long i = maPos.nIndex;
+
+ mpDev->SetFont( pNode->GetFont( ) );
+
+ //Find coordinates
+ long left = pNode->GetLeft( ) + mpDev->GetTextWidth( pNode->GetText( ), 0, i );
+ long top = pNode->GetTop( );
+ long height = pNode->GetHeight( );
+
+ maLine = SmCaretLine( left, top, height );
+
+ //Restore device state
+ mpDev->Pop( );
+}
+
+void SmCaretPos2LineVisitor::DefaultVisit( SmNode* pNode )
+{
+ //Vertical line ( code from SmCaretDrawingVisitor )
+ Point p1 = pNode->GetTopLeft( );
+ if( maPos.nIndex == 1 )
+ p1.Move( pNode->GetWidth( ), 0 );
+
+ maLine = SmCaretLine( p1.X( ), p1.Y( ), pNode->GetHeight( ) );
+}
+
+
+// SmDrawingVisitor
+
+void SmDrawingVisitor::Visit( SmTableNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBraceNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBracebodyNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmOperNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmAlignNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmAttributNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmFontNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmUnHorNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBinHorNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBinVerNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBinDiagonalNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmSubSupNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmMatrixNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmPlaceNode* pNode )
+{
+ DrawSpecialNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmTextNode* pNode )
+{
+ DrawTextNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmSpecialNode* pNode )
+{
+ DrawSpecialNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmGlyphSpecialNode* pNode )
+{
+ DrawSpecialNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmMathSymbolNode* pNode )
+{
+ DrawSpecialNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBlankNode* )
+{
+}
+
+void SmDrawingVisitor::Visit( SmErrorNode* pNode )
+{
+ DrawSpecialNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmLineNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmExpressionNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmRootNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmVerticalBraceNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmRootSymbolNode* pNode )
+{
+ if ( pNode->IsPhantom( ) )
+ return;
+
+ // draw root-sign itself
+ DrawSpecialNode( pNode );
+
+ SmTmpDevice aTmpDev( mrDev, true );
+ aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
+ mrDev.SetLineColor( );
+ aTmpDev.SetFont( pNode->GetFont( ) );
+
+ // since the width is always unscaled it corresponds to the _original_
+ // _unscaled_ font height to be used, we use that to calculate the
+ // bar height. Thus it is independent of the arguments height.
+ // ( see display of sqrt QQQ versus sqrt stack{Q#Q#Q#Q} )
+ long nBarHeight = pNode->GetWidth( ) * 7 / 100;
+ long nBarWidth = pNode->GetBodyWidth( ) + pNode->GetBorderWidth( );
+ Point aBarOffset( pNode->GetWidth( ), +pNode->GetBorderWidth( ) );
+ Point aBarPos( maPosition + aBarOffset );
+
+ tools::Rectangle aBar( aBarPos, Size( nBarWidth, nBarHeight ) );
+ //! avoid GROWING AND SHRINKING of drawn rectangle when constantly
+ //! increasing zoomfactor.
+ // This is done by shifting its output-position to a point that
+ // corresponds exactly to a pixel on the output device.
+ Point aDrawPos( mrDev.PixelToLogic( mrDev.LogicToPixel( aBar.TopLeft( ) ) ) );
+ aBar.SetPos( aDrawPos );
+
+ mrDev.DrawRect( aBar );
+}
+
+void SmDrawingVisitor::Visit( SmPolyLineNode* pNode )
+{
+ if ( pNode->IsPhantom( ) )
+ return;
+
+ long nBorderwidth = pNode->GetFont( ).GetBorderWidth( );
+
+ LineInfo aInfo;
+ aInfo.SetWidth( pNode->GetWidth( ) - 2 * nBorderwidth );
+
+ Point aOffset ( Point( ) - pNode->GetPolygon( ).GetBoundRect( ).TopLeft( )
+ + Point( nBorderwidth, nBorderwidth ) ),
+ aPos ( maPosition + aOffset );
+ pNode->GetPolygon( ).Move( aPos.X( ), aPos.Y( ) ); //Works because Polygon wraps a pointer
+
+ SmTmpDevice aTmpDev ( mrDev, false );
+ aTmpDev.SetLineColor( pNode->GetFont( ).GetColor( ) );
+
+ mrDev.DrawPolyLine( pNode->GetPolygon( ), aInfo );
+}
+
+void SmDrawingVisitor::Visit( SmRectangleNode* pNode )
+{
+ if ( pNode->IsPhantom( ) )
+ return;
+
+ SmTmpDevice aTmpDev ( mrDev, false );
+ aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
+ mrDev.SetLineColor( );
+ aTmpDev.SetFont( pNode->GetFont( ) );
+
+ sal_uLong nTmpBorderWidth = pNode->GetFont( ).GetBorderWidth( );
+
+ // get rectangle and remove borderspace
+ tools::Rectangle aTmp ( pNode->AsRectangle( ) + maPosition - pNode->GetTopLeft( ) );
+ aTmp.AdjustLeft(nTmpBorderWidth );
+ aTmp.AdjustRight( -sal_Int32(nTmpBorderWidth) );
+ aTmp.AdjustTop(nTmpBorderWidth );
+ aTmp.AdjustBottom( -sal_Int32(nTmpBorderWidth) );
+
+ SAL_WARN_IF( aTmp.IsEmpty(), "starmath", "Empty rectangle" );
+
+ //! avoid GROWING AND SHRINKING of drawn rectangle when constantly
+ //! increasing zoomfactor.
+ // This is done by shifting its output-position to a point that
+ // corresponds exactly to a pixel on the output device.
+ Point aPos ( mrDev.PixelToLogic( mrDev.LogicToPixel( aTmp.TopLeft( ) ) ) );
+ aTmp.SetPos( aPos );
+
+ mrDev.DrawRect( aTmp );
+}
+
+void SmDrawingVisitor::DrawTextNode( SmTextNode* pNode )
+{
+ if ( pNode->IsPhantom() || pNode->GetText().isEmpty() || pNode->GetText()[0] == '\0' )
+ return;
+
+ SmTmpDevice aTmpDev ( mrDev, false );
+ aTmpDev.SetFont( pNode->GetFont( ) );
+
+ Point aPos ( maPosition );
+ aPos.AdjustY(pNode->GetBaselineOffset( ) );
+ // round to pixel coordinate
+ aPos = mrDev.PixelToLogic( mrDev.LogicToPixel( aPos ) );
+
+ mrDev.DrawStretchText( aPos, pNode->GetWidth( ), pNode->GetText( ) );
+}
+
+void SmDrawingVisitor::DrawSpecialNode( SmSpecialNode* pNode )
+{
+ //! since this chars might come from any font, that we may not have
+ //! set to ALIGN_BASELINE yet, we do it now.
+ pNode->GetFont( ).SetAlignment( ALIGN_BASELINE );
+
+ DrawTextNode( pNode );
+}
+
+void SmDrawingVisitor::DrawChildren( SmStructureNode* pNode )
+{
+ if ( pNode->IsPhantom( ) )
+ return;
+
+ Point rPosition = maPosition;
+
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ Point aOffset ( pChild->GetTopLeft( ) - pNode->GetTopLeft( ) );
+ maPosition = rPosition + aOffset;
+ pChild->Accept( this );
+ }
+}
+
+// SmSetSelectionVisitor
+
+SmSetSelectionVisitor::SmSetSelectionVisitor( SmCaretPos startPos, SmCaretPos endPos, SmNode* pTree)
+ : maStartPos(startPos)
+ , maEndPos(endPos)
+ , mbSelecting(false)
+{
+ //Assume that pTree is a SmTableNode
+ SAL_WARN_IF(pTree->GetType() != SmNodeType::Table, "starmath", "pTree should be a SmTableNode!");
+ //Visit root node, this is special as this node cannot be selected, but its children can!
+ if(pTree->GetType() == SmNodeType::Table){
+ //Change state if maStartPos is in front of this node
+ if( maStartPos.pSelectedNode == pTree && maStartPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+ //Change state if maEndPos is in front of this node
+ if( maEndPos.pSelectedNode == pTree && maEndPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+ SAL_WARN_IF(mbSelecting, "starmath", "Caret positions needed to set mbSelecting about, shouldn't be possible!");
+
+ //Visit lines
+ for( auto pChild : *static_cast<SmStructureNode*>(pTree) )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ //If we started a selection in this line and it haven't ended, we do that now!
+ if(mbSelecting) {
+ mbSelecting = false;
+ SetSelectedOnAll(pChild);
+ //Set maStartPos and maEndPos to invalid positions, this ensures that an unused
+ //start or end (because we forced end above), doesn't start a new selection.
+ maStartPos = maEndPos = SmCaretPos();
+ }
+ }
+ //Check if pTree isn't selected
+ SAL_WARN_IF(pTree->IsSelected(), "starmath", "pTree should never be selected!");
+ //Discard the selection if there's a bug (it's better than crashing)
+ if(pTree->IsSelected())
+ SetSelectedOnAll(pTree, false);
+ }else //This shouldn't happen, but I don't see any reason to die if it does
+ pTree->Accept(this);
+}
+
+void SmSetSelectionVisitor::SetSelectedOnAll( SmNode* pSubTree, bool IsSelected ) {
+ pSubTree->SetSelected( IsSelected );
+
+ if(pSubTree->GetNumSubNodes() == 0)
+ return;
+ //Quick BFS to set all selections
+ for( auto pChild : *static_cast<SmStructureNode*>(pSubTree) )
+ {
+ if(!pChild)
+ continue;
+ SetSelectedOnAll( pChild, IsSelected );
+ }
+}
+
+void SmSetSelectionVisitor::DefaultVisit( SmNode* pNode ) {
+ //Change state if maStartPos is in front of this node
+ if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+ //Change state if maEndPos is in front of this node
+ if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+
+ //Cache current state
+ bool WasSelecting = mbSelecting;
+ bool ChangedState = false;
+
+ //Set selected
+ pNode->SetSelected( mbSelecting );
+
+ //Visit children
+ if(pNode->GetNumSubNodes() > 0)
+ {
+ for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ ChangedState = ( WasSelecting != mbSelecting ) || ChangedState;
+ }
+ }
+
+ //If state changed
+ if( ChangedState )
+ {
+ //Select this node and all of its children
+ //(Make exception for SmBracebodyNode)
+ if( pNode->GetType() != SmNodeType::Bracebody ||
+ !pNode->GetParent() ||
+ pNode->GetParent()->GetType() != SmNodeType::Brace )
+ SetSelectedOnAll( pNode );
+ else
+ SetSelectedOnAll( pNode->GetParent() );
+ /* If the equation is: sqrt{2 + 4} + 5
+ * And the selection is: sqrt{2 + [4} +] 5
+ * Where [ denotes maStartPos and ] denotes maEndPos
+ * Then the sqrt node should be selected, so that the
+ * effective selection is: [sqrt{2 + 4} +] 5
+ * The same is the case if we swap maStartPos and maEndPos.
+ */
+ }
+
+ //Change state if maStartPos is after this node
+ if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
+ {
+ mbSelecting = !mbSelecting;
+ }
+ //Change state if maEndPos is after of this node
+ if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
+ {
+ mbSelecting = !mbSelecting;
+ }
+}
+
+void SmSetSelectionVisitor::VisitCompositionNode( SmStructureNode* pNode )
+{
+ //Change state if maStartPos is in front of this node
+ if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+ //Change state if maEndPos is in front of this node
+ if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+
+ //Cache current state
+ bool WasSelecting = mbSelecting;
+
+ //Visit children
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+
+ //Set selected, if everything was selected
+ pNode->SetSelected( WasSelecting && mbSelecting );
+
+ //Change state if maStartPos is after this node
+ if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
+ mbSelecting = !mbSelecting;
+ //Change state if maEndPos is after of this node
+ if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
+ mbSelecting = !mbSelecting;
+}
+
+void SmSetSelectionVisitor::Visit( SmTextNode* pNode ) {
+ long i1 = -1,
+ i2 = -1;
+ if( maStartPos.pSelectedNode == pNode )
+ i1 = maStartPos.nIndex;
+ if( maEndPos.pSelectedNode == pNode )
+ i2 = maEndPos.nIndex;
+
+ long start, end;
+ pNode->SetSelected(true);
+ if( i1 != -1 && i2 != -1 ) {
+ start = std::min(i1, i2);
+ end = std::max(i1, i2);
+ } else if( mbSelecting && i1 != -1 ) {
+ start = 0;
+ end = i1;
+ mbSelecting = false;
+ } else if( mbSelecting && i2 != -1 ) {
+ start = 0;
+ end = i2;
+ mbSelecting = false;
+ } else if( !mbSelecting && i1 != -1 ) {
+ start = i1;
+ end = pNode->GetText().getLength();
+ mbSelecting = true;
+ } else if( !mbSelecting && i2 != -1 ) {
+ start = i2;
+ end = pNode->GetText().getLength();
+ mbSelecting = true;
+ } else if( mbSelecting ) {
+ start = 0;
+ end = pNode->GetText().getLength();
+ } else {
+ pNode->SetSelected( false );
+ start = 0;
+ end = 0;
+ }
+ pNode->SetSelected( start != end );
+ pNode->SetSelectionStart( start );
+ pNode->SetSelectionEnd( end );
+}
+
+void SmSetSelectionVisitor::Visit( SmExpressionNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+void SmSetSelectionVisitor::Visit( SmLineNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+void SmSetSelectionVisitor::Visit( SmAlignNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+void SmSetSelectionVisitor::Visit( SmBinHorNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+void SmSetSelectionVisitor::Visit( SmUnHorNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+void SmSetSelectionVisitor::Visit( SmFontNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+// SmCaretPosGraphBuildingVisitor
+
+SmCaretPosGraphBuildingVisitor::SmCaretPosGraphBuildingVisitor( SmNode* pRootNode )
+ : mpRightMost(nullptr)
+ , mpGraph(new SmCaretPosGraph)
+{
+ //pRootNode should always be a table
+ SAL_WARN_IF( pRootNode->GetType( ) != SmNodeType::Table, "starmath", "pRootNode must be a table node");
+ //Handle the special case where SmNodeType::Table is used a rootnode
+ if( pRootNode->GetType( ) == SmNodeType::Table ){
+ //Children are SmLineNodes
+ //Or so I thought... Apparently, the children can be instances of SmExpression
+ //especially if there's an error in the formula... So here we go, a simple work around.
+ for( auto pChild : *static_cast<SmStructureNode*>(pRootNode) )
+ {
+ if(!pChild)
+ continue;
+ mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ) );
+ pChild->Accept( this );
+ }
+ }else
+ pRootNode->Accept(this);
+}
+
+SmCaretPosGraphBuildingVisitor::~SmCaretPosGraphBuildingVisitor()
+{
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmLineNode* pNode ){
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+/** Build SmCaretPosGraph for SmTableNode
+ * This method covers cases where SmTableNode is used in a binom or stack,
+ * the special case where it is used as root node for the entire formula is
+ * handled in the constructor.
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmTableNode* pNode ){
+ SmCaretPosGraphEntry *left = mpRightMost,
+ *right = mpGraph->Add( SmCaretPos( pNode, 1) );
+ bool bIsFirst = true;
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ), left);
+ if(bIsFirst)
+ left->SetRight(mpRightMost);
+ pChild->Accept( this );
+ mpRightMost->SetRight(right);
+ if(bIsFirst)
+ right->SetLeft(mpRightMost);
+ bIsFirst = false;
+ }
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmSubSupNode
+ *
+ * The child positions in a SubSupNode, where H is the body:
+ * \code
+ * CSUP
+ *
+ * LSUP H H RSUP
+ * H H
+ * HHHH
+ * H H
+ * LSUB H H RSUB
+ *
+ * CSUB
+ * \endcode
+ *
+ * Graph over these, where "left" is before the SmSubSupNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> H;
+ * H -> right;
+ * LSUP -> H;
+ * LSUB -> H;
+ * CSUP -> right;
+ * CSUB -> right;
+ * RSUP -> right;
+ * RSUB -> right;
+ * };
+ * \enddot
+ *
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmSubSupNode* pNode )
+{
+ SmCaretPosGraphEntry *left,
+ *right,
+ *bodyLeft,
+ *bodyRight;
+
+ assert(mpRightMost);
+ left = mpRightMost;
+
+ //Create bodyLeft
+ SAL_WARN_IF( !pNode->GetBody(), "starmath", "SmSubSupNode Doesn't have a body!" );
+ bodyLeft = mpGraph->Add( SmCaretPos( pNode->GetBody( ), 0 ), left );
+ left->SetRight( bodyLeft ); //TODO: Don't make this if LSUP or LSUB are NULL ( not sure??? )
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Visit the body, to get bodyRight
+ mpRightMost = bodyLeft;
+ pNode->GetBody( )->Accept( this );
+ bodyRight = mpRightMost;
+ bodyRight->SetRight( right );
+ right->SetLeft( bodyRight );
+
+ //If there's an LSUP
+ SmNode* pChild = pNode->GetSubSup( LSUP );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( bodyLeft );
+ }
+ //If there's an LSUB
+ pChild = pNode->GetSubSup( LSUB );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( bodyLeft );
+ }
+ //If there's a CSUP
+ pChild = pNode->GetSubSup( CSUP );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( right );
+ }
+ //If there's a CSUB
+ pChild = pNode->GetSubSup( CSUB );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( right );
+ }
+ //If there's an RSUP
+ pChild = pNode->GetSubSup( RSUP );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), bodyRight );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( right );
+ }
+ //If there's an RSUB
+ pChild = pNode->GetSubSup( RSUB );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), bodyRight );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( right );
+ }
+
+ //Set return parameters
+ mpRightMost = right;
+}
+
+/** Build caret position for SmOperNode
+ *
+ * If first child is an SmSubSupNode we will ignore its
+ * body, as this body is a SmMathSymbol, for SUM, INT or similar
+ * that shouldn't be subject to modification.
+ * If first child is not a SmSubSupNode, ignore it completely
+ * as it is a SmMathSymbol.
+ *
+ * The child positions in a SmOperNode, where H is symbol, e.g. int, sum or similar:
+ * \code
+ * TO
+ *
+ * LSUP H H RSUP BBB BB BBB B B
+ * H H B B B B B B B B
+ * HHHH BBB B B B B B
+ * H H B B B B B B B
+ * LSUB H H RSUB BBB BB BBB B
+ *
+ * FROM
+ * \endcode
+ * Notice, CSUP, etc. are actually grandchildren, but inorder to ignore H, these are visited
+ * from here. If they are present, that is if pOper is an instance of SmSubSupNode.
+ *
+ * Graph over these, where "left" is before the SmOperNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> BODY;
+ * BODY -> right;
+ * LSUP -> BODY;
+ * LSUB -> BODY;
+ * TO -> BODY;
+ * FROM -> BODY;
+ * RSUP -> BODY;
+ * RSUB -> BODY;
+ * };
+ * \enddot
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmOperNode* pNode )
+{
+ SmNode *pOper = pNode->GetSubNode( 0 ),
+ *pBody = pNode->GetSubNode( 1 );
+
+ SmCaretPosGraphEntry *left = mpRightMost,
+ *bodyLeft,
+ *bodyRight,
+ *right;
+ //Create body left
+ bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
+ left->SetRight( bodyLeft );
+
+ //Visit body, get bodyRight
+ mpRightMost = bodyLeft;
+ pBody->Accept( this );
+ bodyRight = mpRightMost;
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ), bodyRight );
+ bodyRight->SetRight( right );
+
+ //Get subsup pNode if any
+ SmSubSupNode* pSubSup = pOper->GetType( ) == SmNodeType::SubSup ? static_cast<SmSubSupNode*>(pOper) : nullptr;
+
+ SmNode* pChild;
+ SmCaretPosGraphEntry *childLeft;
+ if( pSubSup ) {
+ pChild = pSubSup->GetSubSup( LSUP );
+ if( pChild ) {
+ //Create position in front of pChild
+ childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ pChild = pSubSup->GetSubSup( LSUB );
+ if( pChild ) {
+ //Create position in front of pChild
+ childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ pChild = pSubSup->GetSubSup( CSUP );
+ if ( pChild ) {//TO
+ //Create position in front of pChild
+ childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ pChild = pSubSup->GetSubSup( CSUB );
+ if( pChild ) { //FROM
+ //Create position in front of pChild
+ childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ pChild = pSubSup->GetSubSup( RSUP );
+ if ( pChild ) {
+ //Create position in front of pChild
+ childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ pChild = pSubSup->GetSubSup( RSUB );
+ if ( pChild ) {
+ //Create position in front of pChild
+ childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+ }
+
+ //Return right
+ mpRightMost = right;
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmMatrixNode* pNode )
+{
+ SmCaretPosGraphEntry *left = mpRightMost,
+ *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ for (size_t i = 0; i < pNode->GetNumRows(); ++i)
+ {
+ SmCaretPosGraphEntry* r = left;
+ for (size_t j = 0; j < pNode->GetNumCols(); ++j)
+ {
+ SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
+
+ mpRightMost = mpGraph->Add( SmCaretPos( pSubNode, 0 ), r );
+ if( j != 0 || ( pNode->GetNumRows() - 1U ) / 2 == i )
+ r->SetRight( mpRightMost );
+
+ pSubNode->Accept( this );
+
+ r = mpRightMost;
+ }
+ mpRightMost->SetRight( right );
+ if( ( pNode->GetNumRows() - 1U ) / 2 == i )
+ right->SetLeft( mpRightMost );
+ }
+
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmTextNode
+ *
+ * Lines in an SmTextNode:
+ * \code
+ * A B C
+ * \endcode
+ * Where A B and C are characters in the text.
+ *
+ * Graph over these, where "left" is before the SmTextNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> A;
+ * A -> B
+ * B -> right;
+ * };
+ * \enddot
+ * Notice that C and right is the same position here.
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmTextNode* pNode )
+{
+ SAL_WARN_IF( pNode->GetText().isEmpty(), "starmath", "Empty SmTextNode is bad" );
+
+ int size = pNode->GetText().getLength();
+ for( int i = 1; i <= size; i++ ){
+ SmCaretPosGraphEntry* pRight = mpRightMost;
+ mpRightMost = mpGraph->Add( SmCaretPos( pNode, i ), pRight );
+ pRight->SetRight( mpRightMost );
+ }
+}
+
+/** Build SmCaretPosGraph for SmBinVerNode
+ *
+ * Lines in an SmBinVerNode:
+ * \code
+ * A
+ * -----
+ * B
+ * \endcode
+ *
+ * Graph over these, where "left" is before the SmBinVerNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> A;
+ * A -> right;
+ * B -> right;
+ * };
+ * \enddot
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmBinVerNode* pNode )
+{
+ //None if these children can be NULL, see SmBinVerNode::Arrange
+ SmNode *pNum = pNode->GetSubNode( 0 ),
+ *pDenom = pNode->GetSubNode( 2 );
+
+ SmCaretPosGraphEntry *left,
+ *right,
+ *numLeft,
+ *denomLeft;
+
+ assert(mpRightMost);
+ //Set left
+ left = mpRightMost;
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Create numLeft
+ numLeft = mpGraph->Add( SmCaretPos( pNum, 0 ), left );
+ left->SetRight( numLeft );
+
+ //Visit pNum
+ mpRightMost = numLeft;
+ pNum->Accept( this );
+ mpRightMost->SetRight( right );
+ right->SetLeft( mpRightMost );
+
+ //Create denomLeft
+ denomLeft = mpGraph->Add( SmCaretPos( pDenom, 0 ), left );
+
+ //Visit pDenom
+ mpRightMost = denomLeft;
+ pDenom->Accept( this );
+ mpRightMost->SetRight( right );
+
+ //Set return parameter
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmVerticalBraceNode
+ *
+ * Lines in an SmVerticalBraceNode:
+ * \code
+ * pScript
+ * ________
+ * / \
+ * pBody
+ * \endcode
+ *
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmVerticalBraceNode* pNode )
+{
+ SmNode *pBody = pNode->Body(),
+ *pScript = pNode->Script();
+ //None of these children can be NULL
+
+ SmCaretPosGraphEntry *left,
+ *bodyLeft,
+ *scriptLeft,
+ *right;
+
+ left = mpRightMost;
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Create bodyLeft
+ bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
+ left->SetRight( bodyLeft );
+ mpRightMost = bodyLeft;
+ pBody->Accept( this );
+ mpRightMost->SetRight( right );
+ right->SetLeft( mpRightMost );
+
+ //Create script
+ scriptLeft = mpGraph->Add( SmCaretPos( pScript, 0 ), left );
+ mpRightMost = scriptLeft;
+ pScript->Accept( this );
+ mpRightMost->SetRight( right );
+
+ //Set return value
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmBinDiagonalNode
+ *
+ * Lines in an SmBinDiagonalNode:
+ * \code
+ * A /
+ * /
+ * / B
+ * \endcode
+ * Where A and B are lines.
+ *
+ * Used in formulas such as "A wideslash B"
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmBinDiagonalNode* pNode )
+{
+ SmNode *A = pNode->GetSubNode( 0 ),
+ *B = pNode->GetSubNode( 1 );
+
+ SmCaretPosGraphEntry *left,
+ *leftA,
+ *rightA,
+ *leftB,
+ *right;
+ left = mpRightMost;
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Create left A
+ leftA = mpGraph->Add( SmCaretPos( A, 0 ), left );
+ left->SetRight( leftA );
+
+ //Visit A
+ mpRightMost = leftA;
+ A->Accept( this );
+ rightA = mpRightMost;
+
+ //Create left B
+ leftB = mpGraph->Add( SmCaretPos( B, 0 ), rightA );
+ rightA->SetRight( leftB );
+
+ //Visit B
+ mpRightMost = leftB;
+ B->Accept( this );
+ mpRightMost->SetRight( right );
+ right->SetLeft( mpRightMost );
+
+ //Set return value
+ mpRightMost = right;
+}
+
+//Straight forward ( I think )
+void SmCaretPosGraphBuildingVisitor::Visit( SmBinHorNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+void SmCaretPosGraphBuildingVisitor::Visit( SmUnHorNode* pNode )
+{
+ // Unary operator node
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmExpressionNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmFontNode* pNode )
+{
+ //Has only got one child, should act as an expression if possible
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+/** Build SmCaretPosGraph for SmBracebodyNode
+ * Acts as an SmExpressionNode
+ *
+ * Below is an example of a formula tree that has multiple children for SmBracebodyNode
+ * \dot
+ * digraph {
+ * labelloc = "t";
+ * label= "Equation: \"lbrace i mline i in setZ rbrace\"";
+ * n0 [label="SmTableNode"];
+ * n0 -> n1 [label="0"];
+ * n1 [label="SmLineNode"];
+ * n1 -> n2 [label="0"];
+ * n2 [label="SmExpressionNode"];
+ * n2 -> n3 [label="0"];
+ * n3 [label="SmBraceNode"];
+ * n3 -> n4 [label="0"];
+ * n4 [label="SmMathSymbolNode: {"];
+ * n3 -> n5 [label="1"];
+ * n5 [label="SmBracebodyNode"];
+ * n5 -> n6 [label="0"];
+ * n6 [label="SmExpressionNode"];
+ * n6 -> n7 [label="0"];
+ * n7 [label="SmTextNode: i"];
+ * n5 -> n8 [label="1"];
+ * n8 [label="SmMathSymbolNode: &#124;"]; // Unicode "VERTICAL LINE"
+ * n5 -> n9 [label="2"];
+ * n9 [label="SmExpressionNode"];
+ * n9 -> n10 [label="0"];
+ * n10 [label="SmBinHorNode"];
+ * n10 -> n11 [label="0"];
+ * n11 [label="SmTextNode: i"];
+ * n10 -> n12 [label="1"];
+ * n12 [label="SmMathSymbolNode: &#8712;"]; // Unicode "ELEMENT OF"
+ * n10 -> n13 [label="2"];
+ * n13 [label="SmMathSymbolNode: &#8484;"]; // Unicode "DOUBLE-STRUCK CAPITAL Z"
+ * n3 -> n14 [label="2"];
+ * n14 [label="SmMathSymbolNode: }"];
+ * }
+ * \enddot
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmBracebodyNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ SmCaretPosGraphEntry* pStart = mpGraph->Add( SmCaretPos( pChild, 0), mpRightMost );
+ mpRightMost->SetRight( pStart );
+ mpRightMost = pStart;
+ pChild->Accept( this );
+ }
+}
+
+/** Build SmCaretPosGraph for SmAlignNode
+ * Acts as an SmExpressionNode, as it only has one child this okay
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmAlignNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+/** Build SmCaretPosGraph for SmRootNode
+ *
+ * Lines in an SmRootNode:
+ * \code
+ * _________
+ * A/
+ * \/ B
+ *
+ * \endcode
+ * A: pExtra ( optional, can be NULL ),
+ * B: pBody
+ *
+ * Graph over these, where "left" is before the SmRootNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> B;
+ * B -> right;
+ * A -> B;
+ * }
+ * \enddot
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmRootNode* pNode )
+{
+ SmNode *pExtra = pNode->GetSubNode( 0 ), //Argument, NULL for sqrt, and SmTextNode if cubicroot
+ *pBody = pNode->GetSubNode( 2 ); //Body of the root
+ assert(pBody);
+
+ SmCaretPosGraphEntry *left,
+ *right,
+ *bodyLeft,
+ *bodyRight;
+
+ //Get left and save it
+ assert(mpRightMost);
+ left = mpRightMost;
+
+ //Create body left
+ bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
+ left->SetRight( bodyLeft );
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Visit body
+ mpRightMost = bodyLeft;
+ pBody->Accept( this );
+ bodyRight = mpRightMost;
+ bodyRight->SetRight( right );
+ right->SetLeft( bodyRight );
+
+ //Visit pExtra
+ if( pExtra ){
+ mpRightMost = mpGraph->Add( SmCaretPos( pExtra, 0 ), left );
+ pExtra->Accept( this );
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ mpRightMost = right;
+}
+
+
+/** Build SmCaretPosGraph for SmPlaceNode
+ * Consider this a single character.
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmPlaceNode* pNode )
+{
+ SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
+ mpRightMost->SetRight( right );
+ mpRightMost = right;
+}
+
+/** SmErrorNode is context dependent metadata, it can't be selected
+ *
+ * @remarks There's no point in deleting, copying and/or moving an instance
+ * of SmErrorNode as it may not exist in another context! Thus there are no
+ * positions to select an SmErrorNode.
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmErrorNode* )
+{
+}
+
+/** Build SmCaretPosGraph for SmBlankNode
+ * Consider this a single character, as it is only a blank space
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmBlankNode* pNode )
+{
+ SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
+ mpRightMost->SetRight( right );
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmBraceNode
+ *
+ * Lines in an SmBraceNode:
+ * \code
+ * | |
+ * | B |
+ * | |
+ * \endcode
+ * B: Body
+ *
+ * Graph over these, where "left" is before the SmBraceNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> B;
+ * B -> right;
+ * }
+ * \enddot
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmBraceNode* pNode )
+{
+ SmNode* pBody = pNode->Body();
+
+ SmCaretPosGraphEntry *left = mpRightMost,
+ *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ if( pBody->GetType() != SmNodeType::Bracebody ) {
+ mpRightMost = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
+ left->SetRight( mpRightMost );
+ }else
+ mpRightMost = left;
+
+ pBody->Accept( this );
+ mpRightMost->SetRight( right );
+ right->SetLeft( mpRightMost );
+
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmAttributNode
+ *
+ * Lines in an SmAttributNode:
+ * \code
+ * Attr
+ * Body
+ * \endcode
+ *
+ * There's a body and an attribute, the construction is used for "widehat A", where "A" is the body
+ * and "^" is the attribute ( note GetScaleMode( ) on SmAttributNode tells how the attribute should be
+ * scaled ).
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmAttributNode* pNode )
+{
+ SmNode *pAttr = pNode->Attribute(),
+ *pBody = pNode->Body();
+ assert(pAttr);
+ assert(pBody);
+
+ SmCaretPosGraphEntry *left = mpRightMost,
+ *attrLeft,
+ *bodyLeft,
+ *bodyRight,
+ *right;
+
+ //Creating bodyleft
+ bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
+ left->SetRight( bodyLeft );
+
+ //Creating right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Visit the body
+ mpRightMost = bodyLeft;
+ pBody->Accept( this );
+ bodyRight = mpRightMost;
+ bodyRight->SetRight( right );
+ right->SetLeft( bodyRight );
+
+ //Create attrLeft
+ attrLeft = mpGraph->Add( SmCaretPos( pAttr, 0 ), left );
+
+ //Visit attribute
+ mpRightMost = attrLeft;
+ pAttr->Accept( this );
+ mpRightMost->SetRight( right );
+
+ //Set return value
+ mpRightMost = right;
+}
+
+//Consider these single symbols
+void SmCaretPosGraphBuildingVisitor::Visit( SmSpecialNode* pNode )
+{
+ SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
+ mpRightMost->SetRight( right );
+ mpRightMost = right;
+}
+void SmCaretPosGraphBuildingVisitor::Visit( SmGlyphSpecialNode* pNode )
+{
+ SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
+ mpRightMost->SetRight( right );
+ mpRightMost = right;
+}
+void SmCaretPosGraphBuildingVisitor::Visit( SmMathSymbolNode* pNode )
+{
+ SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
+ mpRightMost->SetRight( right );
+ mpRightMost = right;
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmRootSymbolNode* )
+{
+ //Do nothing
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmRectangleNode* )
+{
+ //Do nothing
+}
+void SmCaretPosGraphBuildingVisitor::Visit( SmPolyLineNode* )
+{
+ //Do nothing
+}
+
+// SmCloningVisitor
+
+SmNode* SmCloningVisitor::Clone( SmNode* pNode )
+{
+ SmNode* pCurrResult = mpResult;
+ pNode->Accept( this );
+ SmNode* pClone = mpResult;
+ mpResult = pCurrResult;
+ return pClone;
+}
+
+void SmCloningVisitor::CloneNodeAttr( SmNode const * pSource, SmNode* pTarget )
+{
+ pTarget->SetScaleMode( pSource->GetScaleMode( ) );
+ //Other attributes are set when prepare or arrange is executed
+ //and may depend on stuff not being cloned here.
+}
+
+void SmCloningVisitor::CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget )
+{
+ //Cache current result
+ SmNode* pCurrResult = mpResult;
+
+ //Create array for holding clones
+ size_t nSize = pSource->GetNumSubNodes( );
+ SmNodeArray aNodes( nSize );
+
+ //Clone children
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ SmNode* pKid;
+ if( nullptr != ( pKid = pSource->GetSubNode( i ) ) )
+ pKid->Accept( this );
+ else
+ mpResult = nullptr;
+ aNodes[i] = mpResult;
+ }
+
+ //Set subnodes of pTarget
+ pTarget->SetSubNodes( std::move(aNodes) );
+
+ //Restore result as where prior to call
+ mpResult = pCurrResult;
+}
+
+void SmCloningVisitor::Visit( SmTableNode* pNode )
+{
+ SmTableNode* pClone = new SmTableNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmBraceNode* pNode )
+{
+ SmBraceNode* pClone = new SmBraceNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmBracebodyNode* pNode )
+{
+ SmBracebodyNode* pClone = new SmBracebodyNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmOperNode* pNode )
+{
+ SmOperNode* pClone = new SmOperNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmAlignNode* pNode )
+{
+ SmAlignNode* pClone = new SmAlignNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmAttributNode* pNode )
+{
+ SmAttributNode* pClone = new SmAttributNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmFontNode* pNode )
+{
+ SmFontNode* pClone = new SmFontNode( pNode->GetToken( ) );
+ pClone->SetSizeParameter( pNode->GetSizeParameter( ), pNode->GetSizeType( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmUnHorNode* pNode )
+{
+ SmUnHorNode* pClone = new SmUnHorNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmBinHorNode* pNode )
+{
+ SmBinHorNode* pClone = new SmBinHorNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmBinVerNode* pNode )
+{
+ SmBinVerNode* pClone = new SmBinVerNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmBinDiagonalNode* pNode )
+{
+ SmBinDiagonalNode *pClone = new SmBinDiagonalNode( pNode->GetToken( ) );
+ pClone->SetAscending( pNode->IsAscending( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmSubSupNode* pNode )
+{
+ SmSubSupNode *pClone = new SmSubSupNode( pNode->GetToken( ) );
+ pClone->SetUseLimits( pNode->IsUseLimits( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmMatrixNode* pNode )
+{
+ SmMatrixNode *pClone = new SmMatrixNode( pNode->GetToken( ) );
+ pClone->SetRowCol( pNode->GetNumRows( ), pNode->GetNumCols( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmPlaceNode* pNode )
+{
+ mpResult = new SmPlaceNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmTextNode* pNode )
+{
+ SmTextNode* pClone = new SmTextNode( pNode->GetToken( ), pNode->GetFontDesc( ) );
+ pClone->ChangeText( pNode->GetText( ) );
+ CloneNodeAttr( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmSpecialNode* pNode )
+{
+ mpResult = new SmSpecialNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmGlyphSpecialNode* pNode )
+{
+ mpResult = new SmGlyphSpecialNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmMathSymbolNode* pNode )
+{
+ mpResult = new SmMathSymbolNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmBlankNode* pNode )
+{
+ SmBlankNode* pClone = new SmBlankNode( pNode->GetToken( ) );
+ pClone->SetBlankNum( pNode->GetBlankNum( ) );
+ mpResult = pClone;
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmErrorNode* pNode )
+{
+ mpResult = new SmErrorNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmLineNode* pNode )
+{
+ SmLineNode* pClone = new SmLineNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmExpressionNode* pNode )
+{
+ SmExpressionNode* pClone = new SmExpressionNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmPolyLineNode* pNode )
+{
+ mpResult = new SmPolyLineNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmRootNode* pNode )
+{
+ SmRootNode* pClone = new SmRootNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmRootSymbolNode* pNode )
+{
+ mpResult = new SmRootSymbolNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmRectangleNode* pNode )
+{
+ mpResult = new SmRectangleNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmVerticalBraceNode* pNode )
+{
+ SmVerticalBraceNode* pClone = new SmVerticalBraceNode( pNode->GetToken( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+// SmSelectionDrawingVisitor
+
+SmSelectionDrawingVisitor::SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, const Point& rOffset )
+ : mrDev( rDevice )
+ , mbHasSelectionArea( false )
+{
+ //Visit everything
+ SAL_WARN_IF( !pTree, "starmath", "pTree can't be null!" );
+ if( pTree )
+ pTree->Accept( this );
+
+ //Draw selection if there's any
+ if( !mbHasSelectionArea ) return;
+
+ maSelectionArea.Move( rOffset.X( ), rOffset.Y( ) );
+
+ //Save device state
+ mrDev.Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
+ //Change colors
+ mrDev.SetLineColor( );
+ mrDev.SetFillColor( COL_LIGHTGRAY );
+
+ //Draw rectangle
+ mrDev.DrawRect( maSelectionArea );
+
+ //Restore device state
+ mrDev.Pop( );
+}
+
+void SmSelectionDrawingVisitor::ExtendSelectionArea(const tools::Rectangle& rArea)
+{
+ if ( ! mbHasSelectionArea ) {
+ maSelectionArea = rArea;
+ mbHasSelectionArea = true;
+ } else
+ maSelectionArea.Union(rArea);
+}
+
+void SmSelectionDrawingVisitor::DefaultVisit( SmNode* pNode )
+{
+ if( pNode->IsSelected( ) )
+ ExtendSelectionArea( pNode->AsRectangle( ) );
+ VisitChildren( pNode );
+}
+
+void SmSelectionDrawingVisitor::VisitChildren( SmNode* pNode )
+{
+ if(pNode->GetNumSubNodes() == 0)
+ return;
+ for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+void SmSelectionDrawingVisitor::Visit( SmTextNode* pNode )
+{
+ if( !pNode->IsSelected())
+ return;
+
+ mrDev.Push( PushFlags::TEXTCOLOR | PushFlags::FONT );
+
+ mrDev.SetFont( pNode->GetFont( ) );
+ Point Position = pNode->GetTopLeft( );
+ long left = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionStart( ) );
+ long right = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionEnd( ) );
+ long top = Position.getY( );
+ long bottom = top + pNode->GetHeight( );
+ tools::Rectangle rect( left, top, right, bottom );
+
+ ExtendSelectionArea( rect );
+
+ mrDev.Pop( );
+}
+
+// SmNodeToTextVisitor
+
+SmNodeToTextVisitor::SmNodeToTextVisitor( SmNode* pNode, OUString &rText )
+{
+ pNode->Accept( this );
+ rText = maCmdText.makeStringAndClear();
+}
+
+void SmNodeToTextVisitor::Visit( SmTableNode* pNode )
+{
+ if( pNode->GetToken( ).eType == TBINOM ) {
+ Append( "{ binom" );
+ LineToText( pNode->GetSubNode( 0 ) );
+ LineToText( pNode->GetSubNode( 1 ) );
+ Append("} ");
+ } else if( pNode->GetToken( ).eType == TSTACK ) {
+ Append( "stack{ " );
+ bool bFirst = true;
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ if(bFirst)
+ bFirst = false;
+ else
+ {
+ Separate( );
+ Append( "# " );
+ }
+ LineToText( pChild );
+ }
+ Separate( );
+ Append( "}" );
+ } else { //Assume it's a toplevel table, containing lines
+ bool bFirst = true;
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ if(bFirst)
+ bFirst = false;
+ else
+ {
+ Separate( );
+ Append( "newline" );
+ }
+ Separate( );
+ pChild->Accept( this );
+ }
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmBraceNode* pNode )
+{
+ SmNode *pLeftBrace = pNode->OpeningBrace(),
+ *pBody = pNode->Body(),
+ *pRightBrace = pNode->ClosingBrace();
+ //Handle special case where it's absolute function
+ if( pNode->GetToken( ).eType == TABS ) {
+ Append( "abs" );
+ LineToText( pBody );
+ } else {
+ if( pNode->GetScaleMode( ) == SmScaleMode::Height )
+ Append( "left " );
+ pLeftBrace->Accept( this );
+ Separate( );
+ pBody->Accept( this );
+ Separate( );
+ if( pNode->GetScaleMode( ) == SmScaleMode::Height )
+ Append( "right " );
+ pRightBrace->Accept( this );
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmBracebodyNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ Separate( );
+ pChild->Accept( this );
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmOperNode* pNode )
+{
+ Append( pNode->GetToken( ).aText );
+ Separate( );
+ if( pNode->GetToken( ).eType == TOPER ){
+ //There's an SmGlyphSpecialNode if eType == TOPER
+ if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup )
+ Append( pNode->GetSubNode( 0 )->GetSubNode( 0 )->GetToken( ).aText );
+ else
+ Append( pNode->GetSubNode( 0 )->GetToken( ).aText );
+ }
+ if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup ) {
+ SmSubSupNode *pSubSup = static_cast<SmSubSupNode*>( pNode->GetSubNode( 0 ) );
+ SmNode* pChild = pSubSup->GetSubSup( LSUP );
+ if( pChild ) {
+ Separate( );
+ Append( "lsup { " );
+ LineToText( pChild );
+ Append( "} " );
+ }
+ pChild = pSubSup->GetSubSup( LSUB );
+ if( pChild ) {
+ Separate( );
+ Append( "lsub { " );
+ LineToText( pChild );
+ Append( "} " );
+ }
+ pChild = pSubSup->GetSubSup( RSUP );
+ if( pChild ) {
+ Separate( );
+ Append( "^ { " );
+ LineToText( pChild );
+ Append( "} " );
+ }
+ pChild = pSubSup->GetSubSup( RSUB );
+ if( pChild ) {
+ Separate( );
+ Append( "_ { " );
+ LineToText( pChild );
+ Append( "} " );
+ }
+ pChild = pSubSup->GetSubSup( CSUP );
+ if( pChild ) {
+ Separate( );
+ if (pSubSup->IsUseLimits())
+ Append( "to { " );
+ else
+ Append( "csup { " );
+ LineToText( pChild );
+ Append( "} " );
+ }
+ pChild = pSubSup->GetSubSup( CSUB );
+ if( pChild ) {
+ Separate( );
+ if (pSubSup->IsUseLimits())
+ Append( "from { " );
+ else
+ Append( "csub { " );
+ LineToText( pChild );
+ Append( "} " );
+ }
+ }
+ LineToText( pNode->GetSubNode( 1 ) );
+}
+
+void SmNodeToTextVisitor::Visit( SmAlignNode* pNode )
+{
+ Append( pNode->GetToken( ).aText );
+ LineToText( pNode->GetSubNode( 0 ) );
+}
+
+void SmNodeToTextVisitor::Visit( SmAttributNode* pNode )
+{
+ Append( pNode->GetToken( ).aText );
+ LineToText( pNode->Body() );
+}
+
+void SmNodeToTextVisitor::Visit( SmFontNode* pNode )
+{
+ switch ( pNode->GetToken( ).eType )
+ {
+ case TBOLD:
+ Append( "bold " );
+ break;
+ case TNBOLD:
+ Append( "nbold " );
+ break;
+ case TITALIC:
+ Append( "italic " );
+ break;
+ case TNITALIC:
+ Append( "nitalic " );
+ break;
+ case TPHANTOM:
+ Append( "phantom " );
+ break;
+ case TSIZE:
+ {
+ Append( "size " );
+ switch ( pNode->GetSizeType( ) )
+ {
+ case FontSizeType::PLUS:
+ Append( "+" );
+ break;
+ case FontSizeType::MINUS:
+ Append( "-" );
+ break;
+ case FontSizeType::MULTIPLY:
+ Append( "*" );
+ break;
+ case FontSizeType::DIVIDE:
+ Append( "/" );
+ break;
+ case FontSizeType::ABSOLUT:
+ default:
+ break;
+ }
+ Append( ::rtl::math::doubleToUString(
+ static_cast<double>( pNode->GetSizeParameter( ) ),
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true ) );
+ Append( " " );
+ }
+ break;
+ case TBLACK:
+ Append( "color black " );
+ break;
+ case TWHITE:
+ Append( "color white " );
+ break;
+ case TRED:
+ Append( "color red " );
+ break;
+ case TGREEN:
+ Append( "color green " );
+ break;
+ case TBLUE:
+ Append( "color blue " );
+ break;
+ case TCYAN:
+ Append( "color cyan " );
+ break;
+ case TMAGENTA:
+ Append( "color magenta " );
+ break;
+ case TYELLOW:
+ Append( "color yellow " );
+ break;
+ case TSANS:
+ Append( "font sans " );
+ break;
+ case TSERIF:
+ Append( "font serif " );
+ break;
+ case TFIXED:
+ Append( "font fixed " );
+ break;
+ default:
+ break;
+ }
+ LineToText( pNode->GetSubNode( 1 ) );
+}
+
+void SmNodeToTextVisitor::Visit( SmUnHorNode* pNode )
+{
+ if(pNode->GetSubNode( 1 )->GetToken( ).eType == TFACT)
+ {
+ // visit children in the reverse order
+ for( auto it = pNode->rbegin(); it != pNode->rend(); ++it )
+ {
+ auto pChild = *it;
+ if(!pChild)
+ continue;
+ Separate( );
+ pChild->Accept( this );
+ }
+ }
+ else
+ {
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ Separate( );
+ pChild->Accept( this );
+ }
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmBinHorNode* pNode )
+{
+ const SmNode *pParent = pNode->GetParent();
+ bool bBraceNeeded = pParent && pParent->GetType() == SmNodeType::Font;
+ SmNode *pLeft = pNode->LeftOperand(),
+ *pOper = pNode->Symbol(),
+ *pRight = pNode->RightOperand();
+ Separate( );
+ if (bBraceNeeded)
+ Append( "{ " );
+ pLeft->Accept( this );
+ Separate( );
+ pOper->Accept( this );
+ Separate( );
+ pRight->Accept( this );
+ Separate( );
+ if (bBraceNeeded)
+ Append( "} " );
+}
+
+void SmNodeToTextVisitor::Visit( SmBinVerNode* pNode )
+{
+ SmNode *pNum = pNode->GetSubNode( 0 ),
+ *pDenom = pNode->GetSubNode( 2 );
+ Append( "{ " );
+ LineToText( pNum );
+ Append( "over" );
+ LineToText( pDenom );
+ Append( "} " );
+}
+
+void SmNodeToTextVisitor::Visit( SmBinDiagonalNode* pNode )
+{
+ SmNode *pLeftOperand = pNode->GetSubNode( 0 ),
+ *pRightOperand = pNode->GetSubNode( 1 );
+ Append( "{ " );
+ LineToText( pLeftOperand );
+ Separate( );
+ Append( "wideslash " );
+ LineToText( pRightOperand );
+ Append( "} " );
+}
+
+void SmNodeToTextVisitor::Visit( SmSubSupNode* pNode )
+{
+ LineToText( pNode->GetBody( ) );
+ SmNode *pChild = pNode->GetSubSup( LSUP );
+ if( pChild ) {
+ Separate( );
+ Append( "lsup " );
+ LineToText( pChild );
+ }
+ pChild = pNode->GetSubSup( LSUB );
+ if( pChild ) {
+ Separate( );
+ Append( "lsub " );
+ LineToText( pChild );
+ }
+ pChild = pNode->GetSubSup( RSUP );
+ if( pChild ) {
+ Separate( );
+ Append( "^ " );
+ LineToText( pChild );
+ }
+ pChild = pNode->GetSubSup( RSUB );
+ if( pChild ) {
+ Separate( );
+ Append( "_ " );
+ LineToText( pChild );
+ }
+ pChild = pNode->GetSubSup( CSUP );
+ if( pChild ) {
+ Separate( );
+ if (pNode->IsUseLimits())
+ Append( "to " );
+ else
+ Append( "csup " );
+ LineToText( pChild );
+ }
+ pChild = pNode->GetSubSup( CSUB );
+ if( pChild ) {
+ Separate( );
+ if (pNode->IsUseLimits())
+ Append( "from " );
+ else
+ Append( "csub " );
+ LineToText( pChild );
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmMatrixNode* pNode )
+{
+ Append( "matrix{" );
+ for (size_t i = 0; i < pNode->GetNumRows(); ++i)
+ {
+ for (size_t j = 0; j < pNode->GetNumCols( ); ++j)
+ {
+ SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
+ Separate( );
+ pSubNode->Accept( this );
+ Separate( );
+ if (j != pNode->GetNumCols() - 1U)
+ Append( "#" );
+ }
+ Separate( );
+ if (i != pNode->GetNumRows() - 1U)
+ Append( "##" );
+ }
+ Append( "} " );
+}
+
+void SmNodeToTextVisitor::Visit( SmPlaceNode* )
+{
+ Append( "<?>" );
+}
+
+void SmNodeToTextVisitor::Visit( SmTextNode* pNode )
+{
+ //TODO: This method might need improvements, see SmTextNode::CreateTextFromNode
+ if( pNode->GetToken( ).eType == TTEXT )
+ Append( "\"" );
+ Append( pNode->GetText( ) );
+ if( pNode->GetToken( ).eType == TTEXT )
+ Append( "\"" );
+}
+
+void SmNodeToTextVisitor::Visit( SmSpecialNode* pNode )
+{
+ Append( pNode->GetToken( ).aText );
+}
+
+void SmNodeToTextVisitor::Visit( SmGlyphSpecialNode* pNode )
+{
+ if( pNode->GetToken( ).eType == TBOPER )
+ Append( "boper " );
+ else
+ Append( "uoper " );
+ Append( pNode->GetToken( ).aText );
+}
+
+void SmNodeToTextVisitor::Visit( SmMathSymbolNode* pNode )
+{
+ Append( pNode->GetToken( ).aText );
+}
+
+void SmNodeToTextVisitor::Visit( SmBlankNode* pNode )
+{
+ sal_uInt16 nNum = pNode->GetBlankNum();
+ if (nNum <= 0)
+ return;
+ sal_uInt16 nWide = nNum / 4;
+ sal_uInt16 nNarrow = nNum % 4;
+ for (sal_uInt16 i = 0; i < nWide; i++)
+ Append( "~" );
+ for (sal_uInt16 i = 0; i < nNarrow; i++)
+ Append( "`" );
+ Append( " " );
+}
+
+void SmNodeToTextVisitor::Visit( SmErrorNode* )
+{
+}
+
+void SmNodeToTextVisitor::Visit( SmLineNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ Separate( );
+ pChild->Accept( this );
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmExpressionNode* pNode )
+{
+ bool bracketsNeeded = pNode->GetNumSubNodes() != 1 || pNode->GetSubNode(0)->GetType() == SmNodeType::BinHor;
+ if (!bracketsNeeded)
+ {
+ const SmNode *pParent = pNode->GetParent();
+ // nested subsups
+ bracketsNeeded =
+ pParent && pParent->GetType() == SmNodeType::SubSup &&
+ pNode->GetNumSubNodes() == 1 &&
+ pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup;
+ }
+
+ if (bracketsNeeded) {
+ Append( "{ " );
+ }
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ Separate( );
+ }
+ if (bracketsNeeded) {
+ Append( "} " );
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmPolyLineNode* )
+{
+}
+
+void SmNodeToTextVisitor::Visit( SmRootNode* pNode )
+{
+ SmNode *pExtra = pNode->GetSubNode( 0 ),
+ *pBody = pNode->GetSubNode( 2 );
+ if( pExtra ) {
+ Append( "nroot" );
+ LineToText( pExtra );
+ } else
+ Append( "sqrt" );
+ LineToText( pBody );
+}
+
+void SmNodeToTextVisitor::Visit( SmRootSymbolNode* )
+{
+}
+
+void SmNodeToTextVisitor::Visit( SmRectangleNode* )
+{
+}
+
+void SmNodeToTextVisitor::Visit( SmVerticalBraceNode* pNode )
+{
+ SmNode *pBody = pNode->Body(),
+ *pScript = pNode->Script();
+ LineToText( pBody );
+ Append( pNode->GetToken( ).aText );
+ LineToText( pScript );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/wordexportbase.cxx b/starmath/source/wordexportbase.cxx
new file mode 100644
index 000000000..7f4699dfb
--- /dev/null
+++ b/starmath/source/wordexportbase.cxx
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "wordexportbase.hxx"
+#include <node.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+SmWordExportBase::SmWordExportBase(const SmNode* pIn)
+ : m_pTree(pIn)
+{
+}
+
+SmWordExportBase::~SmWordExportBase() = default;
+
+void SmWordExportBase::HandleNode(const SmNode* pNode, int nLevel)
+{
+ SAL_INFO("starmath.wordbase",
+ "Node: " << nLevel << " " << int(pNode->GetType()) << " " << pNode->GetNumSubNodes());
+ switch (pNode->GetType())
+ {
+ case SmNodeType::Attribut:
+ HandleAttribute(static_cast<const SmAttributNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Text:
+ HandleText(pNode, nLevel);
+ break;
+ case SmNodeType::VerticalBrace:
+ HandleVerticalBrace(static_cast<const SmVerticalBraceNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Brace:
+ HandleBrace(static_cast<const SmBraceNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Oper:
+ HandleOperator(static_cast<const SmOperNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::UnHor:
+ HandleUnaryOperation(static_cast<const SmUnHorNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::BinHor:
+ HandleBinaryOperation(static_cast<const SmBinHorNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::BinVer:
+ HandleFractions(pNode, nLevel, nullptr);
+ break;
+ case SmNodeType::Root:
+ HandleRoot(static_cast<const SmRootNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Special:
+ {
+ auto pText = static_cast<const SmTextNode*>(pNode);
+ //if the token str and the result text are the same then this
+ //is to be seen as text, else assume it's a mathchar
+ if (pText->GetText() == pText->GetToken().aText)
+ HandleText(pText, nLevel);
+ else
+ HandleMath(pText, nLevel);
+ break;
+ }
+ case SmNodeType::Math:
+ case SmNodeType::MathIdent:
+ HandleMath(pNode, nLevel);
+ break;
+ case SmNodeType::SubSup:
+ HandleSubSupScript(static_cast<const SmSubSupNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Expression:
+ HandleAllSubNodes(pNode, nLevel);
+ break;
+ case SmNodeType::Table:
+ //Root Node, PILE equivalent, i.e. vertical stack
+ HandleTable(pNode, nLevel);
+ break;
+ case SmNodeType::Matrix:
+ HandleMatrix(static_cast<const SmMatrixNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Line:
+ {
+ // TODO
+ HandleAllSubNodes(pNode, nLevel);
+ }
+ break;
+#if 0
+ case SmNodeType::Align:
+ HandleMAlign(pNode,nLevel);
+ break;
+#endif
+ case SmNodeType::Place:
+ // explicitly do nothing, MSOffice treats that as a placeholder if item is missing
+ break;
+ case SmNodeType::Blank:
+ HandleBlank();
+ break;
+ default:
+ HandleAllSubNodes(pNode, nLevel);
+ break;
+ }
+}
+
+//Root Node, PILE equivalent, i.e. vertical stack
+void SmWordExportBase::HandleTable(const SmNode* pNode, int nLevel)
+{
+ //The root of the starmath is a table, if
+ //we convert this them each iteration of
+ //conversion from starmath to Word will
+ //add an extra unnecessary level to the
+ //Word output stack which would grow
+ //without bound in a multi step conversion
+ if (nLevel || pNode->GetNumSubNodes() > 1)
+ HandleVerticalStack(pNode, nLevel);
+ else
+ HandleAllSubNodes(pNode, nLevel);
+}
+
+void SmWordExportBase::HandleAllSubNodes(const SmNode* pNode, int nLevel)
+{
+ int size = pNode->GetNumSubNodes();
+ for (int i = 0; i < size; ++i)
+ {
+ // TODO remove when all types of nodes are handled properly
+ if (pNode->GetSubNode(i) == nullptr)
+ {
+ SAL_WARN("starmath.wordbase", "Subnode is NULL, parent node not handled properly");
+ continue;
+ }
+ HandleNode(pNode->GetSubNode(i), nLevel + 1);
+ }
+}
+
+void SmWordExportBase::HandleUnaryOperation(const SmUnHorNode* pNode, int nLevel)
+{
+ // update HandleMath() when adding new items
+ SAL_INFO("starmath.wordbase", "Unary: " << int(pNode->GetToken().eType));
+
+ HandleAllSubNodes(pNode, nLevel);
+}
+
+void SmWordExportBase::HandleBinaryOperation(const SmBinHorNode* pNode, int nLevel)
+{
+ SAL_INFO("starmath.wordbase", "Binary: " << int(pNode->Symbol()->GetToken().eType));
+ // update HandleMath() when adding new items
+ switch (pNode->Symbol()->GetToken().eType)
+ {
+ case TDIVIDEBY:
+ return HandleFractions(pNode, nLevel, "lin");
+ default:
+ HandleAllSubNodes(pNode, nLevel);
+ break;
+ }
+}
+
+void SmWordExportBase::HandleMath(const SmNode* pNode, int nLevel)
+{
+ SAL_INFO("starmath.wordbase", "Math: " << int(pNode->GetToken().eType));
+ switch (pNode->GetToken().eType)
+ {
+ case TDIVIDEBY:
+ case TACUTE:
+ OSL_ASSERT(false);
+ [[fallthrough]]; // the above are handled elsewhere, e.g. when handling BINHOR
+ default:
+ HandleText(pNode, nLevel);
+ break;
+ }
+}
+
+void SmWordExportBase::HandleSubSupScript(const SmSubSupNode* pNode, int nLevel)
+{
+ // set flags to a bitfield of which sub/sup items exists
+ int flags = (pNode->GetSubSup(CSUB) != nullptr ? (1 << CSUB) : 0)
+ | (pNode->GetSubSup(CSUP) != nullptr ? (1 << CSUP) : 0)
+ | (pNode->GetSubSup(RSUB) != nullptr ? (1 << RSUB) : 0)
+ | (pNode->GetSubSup(RSUP) != nullptr ? (1 << RSUP) : 0)
+ | (pNode->GetSubSup(LSUB) != nullptr ? (1 << LSUB) : 0)
+ | (pNode->GetSubSup(LSUP) != nullptr ? (1 << LSUP) : 0);
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/wordexportbase.hxx b/starmath/source/wordexportbase.hxx
new file mode 100644
index 000000000..4f191df2a
--- /dev/null
+++ b/starmath/source/wordexportbase.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_STARMATH_SOURCE_WORDEXPORTBASE_HXX
+#define INCLUDED_STARMATH_SOURCE_WORDEXPORTBASE_HXX
+
+class SmAttributNode;
+class SmBinHorNode;
+class SmBraceNode;
+class SmMatrixNode;
+class SmNode;
+class SmOperNode;
+class SmRootNode;
+class SmSubSupNode;
+class SmUnHorNode;
+class SmVerticalBraceNode;
+
+/**
+ Base class implementing writing of formulas to Word.
+ */
+class SmWordExportBase
+{
+public:
+ explicit SmWordExportBase(const SmNode* pIn);
+ virtual ~SmWordExportBase();
+
+protected:
+ void HandleNode(const SmNode* pNode, int nLevel);
+ void HandleAllSubNodes(const SmNode* pNode, int nLevel);
+ void HandleTable(const SmNode* pNode, int nLevel);
+ virtual void HandleVerticalStack(const SmNode* pNode, int nLevel) = 0;
+ virtual void HandleText(const SmNode* pNode, int nLevel) = 0;
+ void HandleMath(const SmNode* pNode, int nLevel);
+ virtual void HandleFractions(const SmNode* pNode, int nLevel, const char* type) = 0;
+ void HandleUnaryOperation(const SmUnHorNode* pNode, int nLevel);
+ void HandleBinaryOperation(const SmBinHorNode* pNode, int nLevel);
+ virtual void HandleRoot(const SmRootNode* pNode, int nLevel) = 0;
+ virtual void HandleAttribute(const SmAttributNode* pNode, int nLevel) = 0;
+ virtual void HandleOperator(const SmOperNode* pNode, int nLevel) = 0;
+ void HandleSubSupScript(const SmSubSupNode* pNode, int nLevel);
+ virtual void HandleSubSupScriptInternal(const SmSubSupNode* pNode, int nLevel, int flags) = 0;
+ virtual void HandleMatrix(const SmMatrixNode* pNode, int nLevel) = 0;
+ virtual void HandleBrace(const SmBraceNode* pNode, int nLevel) = 0;
+ virtual void HandleVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel) = 0;
+ virtual void HandleBlank() = 0;
+ const SmNode* const m_pTree;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */