diff options
Diffstat (limited to '')
140 files changed, 51509 insertions, 0 deletions
diff --git a/starmath/AllLangMoTarget_sm.mk b/starmath/AllLangMoTarget_sm.mk new file mode 100644 index 000000000..7073577fa --- /dev/null +++ b/starmath/AllLangMoTarget_sm.mk @@ -0,0 +1,13 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$(eval $(call gb_AllLangMoTarget_AllLangMoTarget,sm)) + +$(eval $(call gb_AllLangMoTarget_set_polocation,sm,starmath)) + +# vim: set noet sw=4 ts=4: diff --git a/starmath/CppunitTest_starmath_dialogs_test.mk b/starmath/CppunitTest_starmath_dialogs_test.mk new file mode 100644 index 000000000..ad091bc6f --- /dev/null +++ b/starmath/CppunitTest_starmath_dialogs_test.mk @@ -0,0 +1,68 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitScreenShot,starmath_dialogs_test)) + +$(eval $(call gb_CppunitTest_add_exception_objects,starmath_dialogs_test, \ + starmath/qa/unit/starmath-dialogs-test \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,starmath_dialogs_test)) + +$(eval $(call gb_CppunitTest_set_include,desktop_dialogs_test,\ + -I$(SRCDIR)/starmath/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,starmath_dialogs_test, \ + basegfx \ + comphelper \ + cppu \ + cppuhelper \ + drawinglayer \ + editeng \ + i18nlangtag \ + i18nutil \ + msfilter \ + oox \ + sal \ + salhelper \ + sax \ + sfx \ + sot \ + svl \ + svt \ + test \ + tl \ + tk \ + ucbhelper \ + unotest \ + utl \ + vcl \ + xo \ +)) + +$(eval $(call gb_CppunitTest_use_external,starmath_dialogs_test,boost_headers)) + +$(eval $(call gb_CppunitTest_use_sdk_api,starmath_dialogs_test)) + +$(eval $(call gb_CppunitTest_use_ure,starmath_dialogs_test)) +$(eval $(call gb_CppunitTest_use_vcl_non_headless_with_windows,starmath_dialogs_test)) + +$(eval $(call gb_CppunitTest_use_rdb,starmath_dialogs_test,services)) + +$(eval $(call gb_CppunitTest_use_configuration,starmath_dialogs_test)) + +$(eval $(call gb_CppunitTest_use_uiconfigs,starmath_dialogs_test,\ + modules/smath \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/starmath/CppunitTest_starmath_export.mk b/starmath/CppunitTest_starmath_export.mk new file mode 100644 index 000000000..3ea437c20 --- /dev/null +++ b/starmath/CppunitTest_starmath_export.mk @@ -0,0 +1,88 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,starmath_export)) + +$(eval $(call gb_CppunitTest_set_include,starmath_export,\ + -I$(SRCDIR)/starmath/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_use_externals,starmath_export,\ + boost_headers \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,starmath_export)) + +$(eval $(call gb_CppunitTest_add_exception_objects,starmath_export,\ + starmath/qa/extras/mmlexport-test \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,starmath_export,\ + comphelper \ + cppu \ + cppuhelper \ + editeng \ + i18nlangtag \ + i18nutil \ + msfilter \ + oox \ + sal \ + salhelper \ + sax \ + sfx \ + sm \ + smd \ + sot \ + svl \ + svt \ + svx \ + svxcore \ + test \ + tk \ + tl \ + unotest \ + unoxml \ + utl \ + vcl \ + xo \ +)) + +$(eval $(call gb_CppunitTest_use_ure,starmath_export)) +$(eval $(call gb_CppunitTest_use_vcl,starmath_export)) + +$(eval $(call gb_CppunitTest_use_components,starmath_export,\ + configmgr/source/configmgr \ + framework/util/fwk \ + i18npool/util/i18npool \ + package/source/xstor/xstor \ + package/util/package2 \ + sfx2/util/sfx \ + starmath/util/sm \ + starmath/util/smd \ + toolkit/util/tk \ + ucb/source/core/ucb1 \ + ucb/source/ucp/file/ucpfile1 \ + unotools/util/utl \ + comphelper/util/comphelp \ + filter/source/config/cache/filterconfig1 \ + oox/util/oox \ + sax/source/expatwrap/expwrap \ + svl/source/fsstor/fsstorage \ + svl/util/svl \ + svx/util/svx \ + unoxml/source/service/unoxml \ + uui/util/uui \ + xmloff/util/xo \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,starmath_export)) + +# vim: set noet sw=4 ts=4: diff --git a/starmath/CppunitTest_starmath_import.mk b/starmath/CppunitTest_starmath_import.mk new file mode 100644 index 000000000..2bbda9c70 --- /dev/null +++ b/starmath/CppunitTest_starmath_import.mk @@ -0,0 +1,85 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,starmath_import)) + +$(eval $(call gb_CppunitTest_set_include,starmath_import,\ + $$(INCLUDE) \ + -I$(SRCDIR)/starmath/inc \ +)) + +$(eval $(call gb_CppunitTest_use_external,starmath_import,boost_headers)) + +$(eval $(call gb_CppunitTest_use_sdk_api,starmath_import)) + +$(eval $(call gb_CppunitTest_add_exception_objects,starmath_import,\ + starmath/qa/extras/mmlimport-test \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,starmath_import,\ + comphelper \ + cppu \ + cppuhelper \ + editeng \ + i18nlangtag \ + i18nutil \ + msfilter \ + oox \ + sal \ + salhelper \ + sax \ + sfx \ + sm \ + smd \ + sot \ + svl \ + svt \ + svx \ + svxcore \ + test \ + tk \ + tl \ + unotest \ + unoxml \ + utl \ + vcl \ + xo \ +)) + +$(eval $(call gb_CppunitTest_use_ure,starmath_import)) +$(eval $(call gb_CppunitTest_use_vcl,starmath_import)) + +$(eval $(call gb_CppunitTest_use_components,starmath_import,\ + configmgr/source/configmgr \ + framework/util/fwk \ + i18npool/util/i18npool \ + package/source/xstor/xstor \ + package/util/package2 \ + sfx2/util/sfx \ + starmath/util/sm \ + starmath/util/smd \ + toolkit/util/tk \ + ucb/source/core/ucb1 \ + ucb/source/ucp/file/ucpfile1 \ + unotools/util/utl \ + comphelper/util/comphelp \ + filter/source/config/cache/filterconfig1 \ + oox/util/oox \ + sax/source/expatwrap/expwrap \ + svl/source/fsstor/fsstorage \ + svl/util/svl \ + svx/util/svx \ + unoxml/source/service/unoxml \ + uui/util/uui \ + xmloff/util/xo \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,starmath_import)) + +# vim: set noet sw=4 ts=4: diff --git a/starmath/CppunitTest_starmath_qa_cppunit.mk b/starmath/CppunitTest_starmath_qa_cppunit.mk new file mode 100644 index 000000000..ee436749f --- /dev/null +++ b/starmath/CppunitTest_starmath_qa_cppunit.mk @@ -0,0 +1,90 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,starmath_qa_cppunit)) + +$(eval $(call gb_CppunitTest_set_include,starmath_qa_cppunit,\ + $$(INCLUDE) \ + -I$(SRCDIR)/starmath/inc \ +)) + +$(eval $(call gb_CppunitTest_use_external,starmath_qa_cppunit,boost_headers)) + +$(eval $(call gb_CppunitTest_use_sdk_api,starmath_qa_cppunit)) + +$(eval $(call gb_CppunitTest_use_library_objects,starmath_qa_cppunit,\ + sm \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,starmath_qa_cppunit,\ + comphelper \ + cppu \ + cppuhelper \ + editeng \ + i18nlangtag \ + i18nutil \ + msfilter \ + oox \ + sal \ + sax \ + sfx \ + sot \ + svl \ + svt \ + svxcore \ + svx \ + test \ + tk \ + tl \ + unotest \ + utl \ + vcl \ + xo \ +)) + +$(eval $(call gb_CppunitTest_set_include,starmath_qa_cppunit,\ + -I$(SRCDIR)/starmath/source \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,starmath_qa_cppunit,\ + starmath/qa/cppunit/test_cursor \ + starmath/qa/cppunit/test_node \ + starmath/qa/cppunit/test_nodetotextvisitors \ + starmath/qa/cppunit/test_parse \ + starmath/qa/cppunit/test_starmath \ +)) + +$(eval $(call gb_CppunitTest_use_ure,starmath_qa_cppunit)) +$(eval $(call gb_CppunitTest_use_vcl,starmath_qa_cppunit)) + +$(eval $(call gb_CppunitTest_use_components,starmath_qa_cppunit,\ + configmgr/source/configmgr \ + dtrans/util/mcnttype \ + framework/util/fwk \ + i18npool/util/i18npool \ + package/source/xstor/xstor \ + package/util/package2 \ + toolkit/util/tk \ + sfx2/util/sfx \ + ucb/source/core/ucb1 \ + ucb/source/ucp/file/ucpfile1 \ + unotools/util/utl \ +)) + +ifeq ($(strip $(OS)),WNT) +$(eval $(call gb_CppunitTest_use_components,starmath_qa_cppunit,\ + dtrans/util/ftransl \ + dtrans/util/sysdtrans \ +)) +endif + +$(eval $(call gb_CppunitTest_use_configuration,starmath_qa_cppunit)) + +# vim: set noet sw=4 ts=4: diff --git a/starmath/IwyuFilter_starmath.yaml b/starmath/IwyuFilter_starmath.yaml new file mode 100644 index 000000000..d3072282f --- /dev/null +++ b/starmath/IwyuFilter_starmath.yaml @@ -0,0 +1,34 @@ +--- +assumeFilename: starmath/source/document.cxx +blacklist: + starmath/inc/smmod.hxx: + # Needed for define + - sfx2/app.hxx + starmath/source/accessibility.hxx: + # Base class needs complete type + - com/sun/star/accessibility/XAccessible.hpp + - com/sun/star/accessibility/XAccessibleComponent.hpp + - com/sun/star/accessibility/XAccessibleContext.hpp + - com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp + - com/sun/star/accessibility/XAccessibleText.hpp + - com/sun/star/lang/XServiceInfo.hpp + starmath/source/smdetect.hxx: + # Base class needs complete type + - com/sun/star/document/XExtendedFilterDetection.hpp + - com/sun/star/lang/XServiceInfo.hpp + starmath/source/accessibility.cxx: + # Needed for implicit dtor + - editeng/editobj.hxx + starmath/source/smdll.cxx: + # Needed to inherit linker visibility + - smdll.hxx + starmath/source/typemap.cxx: + # Needed for smslots.hxx to build + - config_options.h + - sfx2/msg.hxx + - sfx2/zoomitem.hxx + - svx/zoomslideritem.hxx + - svl/slstitm.hxx + starmath/source/unodoc.cxx: + # Avoid loplugin:unreffun error + - register.hxx diff --git a/starmath/JunitTest_starmath_unoapi.mk b/starmath/JunitTest_starmath_unoapi.mk new file mode 100644 index 000000000..d4440ffb9 --- /dev/null +++ b/starmath/JunitTest_starmath_unoapi.mk @@ -0,0 +1,14 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_JunitTest_JunitTest,starmath_unoapi)) + +$(eval $(call gb_JunitTest_set_unoapi_test_defaults,starmath_unoapi,,sm.sce)) + +# vim: set noet sw=4 ts=4: diff --git a/starmath/Library_sm.mk b/starmath/Library_sm.mk new file mode 100644 index 000000000..d4b22f4d6 --- /dev/null +++ b/starmath/Library_sm.mk @@ -0,0 +1,114 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Library_Library,sm)) + +$(eval $(call gb_Library_add_sdi_headers,sm,starmath/sdi/smslots)) + +$(eval $(call gb_Library_set_componentfile,sm,starmath/util/sm)) + +$(eval $(call gb_Library_set_precompiled_header,sm,starmath/inc/pch/precompiled_sm)) + +$(eval $(call gb_Library_set_include,sm,\ + -I$(SRCDIR)/starmath/inc \ + -I$(WORKDIR)/SdiTarget/starmath/sdi \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Library_add_defs,sm,\ + -DSM_DLLIMPLEMENTATION \ +)) + +$(eval $(call gb_Library_use_externals,sm, \ + boost_headers \ + icu_headers \ +)) + +$(eval $(call gb_Library_use_custom_headers,sm,\ + officecfg/registry \ + oox/generated \ +)) + +$(eval $(call gb_Library_use_sdk_api,sm)) + +$(eval $(call gb_Library_use_libraries,sm,\ + comphelper \ + cppu \ + cppuhelper \ + editeng \ + i18nutil \ + i18nlangtag \ + msfilter \ + oox \ + sal \ + sax \ + sfx \ + sot \ + svl \ + svt \ + svx \ + svxcore \ + tk \ + tl \ + utl \ + vcl \ + xo \ +)) + +$(eval $(call gb_Library_add_exception_objects,sm,\ + starmath/source/AccessibleSmElement \ + starmath/source/AccessibleSmElementsControl \ + starmath/source/ElementsDockingWindow \ + starmath/source/accessibility \ + starmath/source/action \ + starmath/source/caret \ + starmath/source/cfgitem \ + starmath/source/cursor \ + starmath/source/dialog \ + starmath/source/document \ + starmath/source/edit \ + starmath/source/format \ + starmath/source/mathmlattr \ + starmath/source/mathmlexport \ + starmath/source/mathmlimport \ + starmath/source/mathtype \ + starmath/source/node \ + starmath/source/ooxmlexport \ + starmath/source/ooxmlimport \ + starmath/source/rtfexport \ + starmath/source/parse \ + starmath/source/rect \ + starmath/source/register \ + starmath/source/smdll \ + starmath/source/smmod \ + starmath/source/symbol \ + starmath/source/tmpdevice \ + starmath/source/typemap \ + starmath/source/uiobject \ + starmath/source/unodoc \ + starmath/source/unofilter \ + starmath/source/unomodel \ + starmath/source/utility \ + starmath/source/view \ + starmath/source/visitors \ + starmath/source/wordexportbase \ +)) + + +$(eval $(call gb_SdiTarget_SdiTarget,starmath/sdi/smslots,starmath/sdi/smath)) + +$(eval $(call gb_SdiTarget_set_include,starmath/sdi/smslots,\ + -I$(SRCDIR)/starmath/inc \ + -I$(SRCDIR)/starmath/sdi \ + -I$(SRCDIR)/svx/sdi \ + -I$(SRCDIR)/sfx2/sdi \ + $$(INCLUDE) \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/starmath/Library_smd.mk b/starmath/Library_smd.mk new file mode 100644 index 000000000..565f7e9c0 --- /dev/null +++ b/starmath/Library_smd.mk @@ -0,0 +1,39 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Library_Library,smd)) + +$(eval $(call gb_Library_set_componentfile,smd,starmath/util/smd)) + +$(eval $(call gb_Library_set_include,smd,\ + -I$(SRCDIR)/starmath/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Library_use_external,smd,boost_headers)) + +$(eval $(call gb_Library_use_sdk_api,smd)) + +$(eval $(call gb_Library_use_libraries,smd,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + sfx \ + sot \ + tl \ + utl \ +)) + +$(eval $(call gb_Library_add_exception_objects,smd,\ + starmath/source/smdetect \ + starmath/source/eqnolefilehdr \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/starmath/Makefile b/starmath/Makefile new file mode 100644 index 000000000..0997e6284 --- /dev/null +++ b/starmath/Makefile @@ -0,0 +1,14 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +include $(module_directory)/../solenv/gbuild/partial_build.mk + +# vim: set noet sw=4 ts=4: diff --git a/starmath/Module_starmath.mk b/starmath/Module_starmath.mk new file mode 100644 index 000000000..f7a65618a --- /dev/null +++ b/starmath/Module_starmath.mk @@ -0,0 +1,38 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Module_Module,starmath)) + +$(eval $(call gb_Module_add_targets,starmath,\ + Library_sm \ + Library_smd \ + UIConfig_smath \ +)) + +$(eval $(call gb_Module_add_l10n_targets,starmath,\ + AllLangMoTarget_sm \ +)) + +$(eval $(call gb_Module_add_check_targets,starmath,\ + CppunitTest_starmath_export \ + CppunitTest_starmath_import \ + CppunitTest_starmath_qa_cppunit \ +)) + +$(eval $(call gb_Module_add_subsequentcheck_targets,starmath,\ + JunitTest_starmath_unoapi \ +)) + +# screenshots +$(eval $(call gb_Module_add_screenshot_targets,starmath,\ + CppunitTest_starmath_dialogs_test \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/starmath/README b/starmath/README new file mode 100644 index 000000000..d84b3d8e0 --- /dev/null +++ b/starmath/README @@ -0,0 +1,4 @@ +Formula editor code for writer ([[sw]]). + +Good overview from the original developer: +http://www.mail-archive.com/dev@sw.openoffice.org/msg00200.html diff --git a/starmath/UIConfig_smath.mk b/starmath/UIConfig_smath.mk new file mode 100644 index 000000000..5aa6e4cfb --- /dev/null +++ b/starmath/UIConfig_smath.mk @@ -0,0 +1,45 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_UIConfig_UIConfig,modules/smath)) + +$(eval $(call gb_UIConfig_add_menubarfiles,modules/smath,\ + starmath/uiconfig/smath/menubar/menubar \ +)) + +$(eval $(call gb_UIConfig_add_popupmenufiles,modules/smath,\ + starmath/uiconfig/smath/popupmenu/edit \ + starmath/uiconfig/smath/popupmenu/view \ +)) + +$(eval $(call gb_UIConfig_add_statusbarfiles,modules/smath,\ + starmath/uiconfig/smath/statusbar/statusbar \ +)) + +$(eval $(call gb_UIConfig_add_toolbarfiles,modules/smath,\ + starmath/uiconfig/smath/toolbar/toolbar \ + starmath/uiconfig/smath/toolbar/standardbar \ + starmath/uiconfig/smath/toolbar/fullscreenbar \ +)) + +$(eval $(call gb_UIConfig_add_uifiles,modules/smath,\ + starmath/uiconfig/smath/ui/alignmentdialog \ + starmath/uiconfig/smath/ui/catalogdialog \ + starmath/uiconfig/smath/ui/dockingelements \ + starmath/uiconfig/smath/ui/fontdialog \ + starmath/uiconfig/smath/ui/fontsizedialog \ + starmath/uiconfig/smath/ui/fonttypedialog \ + starmath/uiconfig/smath/ui/printeroptions \ + starmath/uiconfig/smath/ui/savedefaultsdialog \ + starmath/uiconfig/smath/ui/smathsettings \ + starmath/uiconfig/smath/ui/spacingdialog \ + starmath/uiconfig/smath/ui/symdefinedialog \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/starmath/inc/AccessibleSmElement.hxx b/starmath/inc/AccessibleSmElement.hxx new file mode 100644 index 000000000..202de6690 --- /dev/null +++ b/starmath/inc/AccessibleSmElement.hxx @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENT_HXX +#define INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENT_HXX + +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleAction.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleStateSet.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase3.hxx> +#include <comphelper/accessiblecomponenthelper.hxx> +#include <sal/types.h> +#include <vcl/vclptr.hxx> + +class SmElementsControl; + +typedef ::cppu::ImplHelper3<css::lang::XServiceInfo, css::accessibility::XAccessible, + css::accessibility::XAccessibleAction> + AccessibleSmElement_BASE; + +class AccessibleSmElement final : public comphelper::OAccessibleComponentHelper, + public AccessibleSmElement_BASE +{ + SmElementsControl* m_pSmElementsControl; + const sal_Int32 m_nIndexInParent; ///< index in the parent XAccessible + const sal_uInt16 m_nItemId; ///< index in the SmElementsControl + bool m_bHasFocus; + sal_Int16 m_nRole; + + ~AccessibleSmElement() override; + void SAL_CALL disposing() override; + css::awt::Rectangle implGetBounds() override; + + void testAction(sal_Int32) const; + +public: + explicit AccessibleSmElement(SmElementsControl* pSmElementsControl, sal_uInt16 nItemId, + sal_Int32 nIndexInParent); + + void SetFocus(bool _bFocus); + sal_uInt16 itemId() const { return m_nItemId; } + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XAccessible + css::uno::Reference<css::accessibility::XAccessibleContext> + SAL_CALL getAccessibleContext() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override; + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XAccessibleContext + sal_Int32 SAL_CALL getAccessibleChildCount() override; + css::uno::Reference<css::accessibility::XAccessible> + SAL_CALL getAccessibleChild(sal_Int32 i) override; + css::uno::Reference<css::accessibility::XAccessible> SAL_CALL getAccessibleParent() override; + sal_Int32 SAL_CALL getAccessibleIndexInParent() override; + sal_Int16 SAL_CALL getAccessibleRole() override; + OUString SAL_CALL getAccessibleDescription() override; + OUString SAL_CALL getAccessibleName() override; + css::uno::Reference<css::accessibility::XAccessibleRelationSet> + SAL_CALL getAccessibleRelationSet() override; + css::uno::Reference<css::accessibility::XAccessibleStateSet> + SAL_CALL getAccessibleStateSet() override; + + // XAccessibleComponent + css::uno::Reference<css::accessibility::XAccessible> + SAL_CALL getAccessibleAtPoint(const css::awt::Point& aPoint) override; + void SAL_CALL grabFocus() override; + sal_Int32 SAL_CALL getForeground() override; + sal_Int32 SAL_CALL getBackground() override; + + // XAccessibleAction + sal_Int32 SAL_CALL getAccessibleActionCount() override; + sal_Bool SAL_CALL doAccessibleAction(sal_Int32 nIndex) override; + OUString SAL_CALL getAccessibleActionDescription(sal_Int32 nIndex) override; + css::uno::Reference<css::accessibility::XAccessibleKeyBinding> + SAL_CALL getAccessibleActionKeyBinding(sal_Int32 nIndex) override; +}; + +#endif // INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/AccessibleSmElementsControl.hxx b/starmath/inc/AccessibleSmElementsControl.hxx new file mode 100644 index 000000000..66cbd80bc --- /dev/null +++ b/starmath/inc/AccessibleSmElementsControl.hxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENTSCONTROL_HXX +#define INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENTSCONTROL_HXX + +#include <comphelper/accessiblecomponenthelper.hxx> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase3.hxx> +#include <vcl/vclptr.hxx> + +#include <vector> + +class AccessibleSmElement; +class SmElementsControl; + +typedef ::cppu::ImplHelper3<css::lang::XServiceInfo, css::accessibility::XAccessible, + css::accessibility::XAccessibleSelection> + AccessibleSmElementsControl_BASE; + +class AccessibleSmElementsControl final : public comphelper::OAccessibleComponentHelper, + public AccessibleSmElementsControl_BASE +{ + std::vector<rtl::Reference<AccessibleSmElement>> m_aAccessibleChildren; + SmElementsControl* m_pControl; + + void UpdateFocus(sal_uInt16); + inline void TestControl(); + + ~AccessibleSmElementsControl() override; + void SAL_CALL disposing() override; + css::awt::Rectangle implGetBounds() override; + +public: + AccessibleSmElementsControl(SmElementsControl& rControl); + + void ReleaseAllItems(); + void AddAllItems(); + inline void AcquireFocus(); + inline void ReleaseFocus(sal_uInt16); + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XAccessible + css::uno::Reference<css::accessibility::XAccessibleContext> + SAL_CALL getAccessibleContext() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XAccessibleComponent + sal_Bool SAL_CALL containsPoint(const css::awt::Point& aPoint) override; + css::uno::Reference<css::accessibility::XAccessible> + SAL_CALL getAccessibleAtPoint(const css::awt::Point& aPoint) override; + void SAL_CALL grabFocus() override; + sal_Int32 SAL_CALL getForeground() override; + sal_Int32 SAL_CALL getBackground() override; + + // XAccessibleContext + sal_Int32 SAL_CALL getAccessibleChildCount() override; + css::uno::Reference<css::accessibility::XAccessible> + SAL_CALL getAccessibleChild(sal_Int32 i) override; + css::uno::Reference<css::accessibility::XAccessible> SAL_CALL getAccessibleParent() override; + sal_Int16 SAL_CALL getAccessibleRole() override; + OUString SAL_CALL getAccessibleDescription() override; + OUString SAL_CALL getAccessibleName() override; + css::uno::Reference<css::accessibility::XAccessibleRelationSet> + SAL_CALL getAccessibleRelationSet() override; + css::uno::Reference<css::accessibility::XAccessibleStateSet> + SAL_CALL getAccessibleStateSet() override; + + // XAccessibleSelection + void SAL_CALL selectAccessibleChild(sal_Int32 nChildIndex) override; + sal_Bool SAL_CALL isAccessibleChildSelected(sal_Int32 nChildIndex) override; + void SAL_CALL clearAccessibleSelection() override; + void SAL_CALL selectAllAccessibleChildren() override; + sal_Int32 SAL_CALL getSelectedAccessibleChildCount() override; + css::uno::Reference<css::accessibility::XAccessible> + SAL_CALL getSelectedAccessibleChild(sal_Int32 nSelectedChildIndex) override; + void SAL_CALL deselectAccessibleChild(sal_Int32 nChildIndex) override; +}; + +void AccessibleSmElementsControl::AcquireFocus() { UpdateFocus(SAL_MAX_UINT16); } + +void AccessibleSmElementsControl::ReleaseFocus(sal_uInt16 nPos) { UpdateFocus(nPos); } + +#endif // INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENTSCONTROL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/ElementsDockingWindow.hxx b/starmath/inc/ElementsDockingWindow.hxx new file mode 100644 index 000000000..e10273b88 --- /dev/null +++ b/starmath/inc/ElementsDockingWindow.hxx @@ -0,0 +1,199 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_STARMATH_INC_ELEMENTSDOCKINGWINDOW_HXX +#define INCLUDED_STARMATH_INC_ELEMENTSDOCKINGWINDOW_HXX + +#include <sfx2/dockwin.hxx> +#include <vcl/customweld.hxx> +#include <vcl/weld.hxx> + +#include "format.hxx" +#include <memory> +#include <tuple> + +#include "AccessibleSmElementsControl.hxx" + +class SmDocShell; +class SmNode; +class SmParser; + +class SmElement +{ + std::unique_ptr<SmNode> mpNode; + OUString maText; + OUString maHelpText; + +public: + Point mBoxLocation; + Size mBoxSize; + + SmElement(std::unique_ptr<SmNode>&& pNode, const OUString& aText, const OUString& aHelpText); + virtual ~SmElement(); + + const std::unique_ptr<SmNode>& getNode() const; + const OUString& getText() const { return maText; } + const OUString& getHelpText() const { return maHelpText; } + + virtual bool isSeparator() const { return false; } +}; + +class SmElementSeparator final : public SmElement +{ +public: + SmElementSeparator(); + + bool isSeparator() const override { return true; } +}; + +typedef std::pair<const char*, const char*> SmElementDescr; + +class SmElementsControl : public weld::CustomWidgetController +{ + friend class ElementSelectorUIObject; + friend class ElementUIObject; + + static const SmElementDescr m_aUnaryBinaryOperatorsList[]; + static const SmElementDescr m_aRelationsList[]; + static const SmElementDescr m_aSetOperationsList[]; + static const SmElementDescr m_aFunctionsList[]; + static const SmElementDescr m_aOperatorsList[]; + static const SmElementDescr m_aAttributesList[]; + static const SmElementDescr m_aBracketsList[]; + static const SmElementDescr m_aFormatsList[]; + static const SmElementDescr m_aOthersList[]; + static const SmElementDescr m_aExamplesList[]; + static const std::tuple<const char*, const SmElementDescr*, size_t> m_aCategories[]; + static const size_t m_aCategoriesSize; + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual OUString RequestHelp(tools::Rectangle& rRect) override; + virtual void Resize() override; + virtual void GetFocus() override; + virtual void LoseFocus() override; + virtual bool KeyInput(const KeyEvent& rKEvt) override; + css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override; + + SmDocShell* mpDocShell; + SmFormat maFormat; + OString msCurrentSetId; + sal_uInt16 m_nCurrentElement; + sal_uInt16 m_nCurrentRolloverElement; + sal_uInt16 m_nCurrentOffset; + Link<SmElement&,void> maSelectHdlLink; + + std::vector< std::unique_ptr<SmElement> > maElementList; + Size maMaxElementDimensions; + bool mbVerticalMode; + std::unique_ptr<weld::ScrolledWindow> mxScroll; + bool m_bFirstPaintAfterLayout; + rtl::Reference<AccessibleSmElementsControl> m_xAccessible; + + void addElement(SmParser &rParser, const OUString& aElementVisual, const OUString& aElementSource, const OUString& aHelpText); + void addElements(const SmElementDescr aElementsArray[], sal_uInt16 size); + SmElement* current() const; + void setCurrentElement(sal_uInt16); + bool hasRollover() const { return m_nCurrentRolloverElement != SAL_MAX_UINT16; } + + void stepFocus(const bool bBackward); + void pageFocus(const bool bBackward); + // common code of page and step focus + inline void scrollToElement(const bool, const SmElement*); + inline sal_uInt16 nextElement(const bool, const sal_uInt16, const sal_uInt16); + + void build(); + + //if bDraw is true, then draw, otherwise just layout + void LayoutOrPaintContents(vcl::RenderContext& rContext, bool bDraw); + +public: + explicit SmElementsControl(std::unique_ptr<weld::ScrolledWindow> xScrolledWindow); + virtual ~SmElementsControl() override; + + static const auto& categories() { return m_aCategories; } + static size_t categoriesSize() { return m_aCategoriesSize; } + const OString& elementSetId() const { return msCurrentSetId; } + void setElementSetId(const char* pSetId); + + void setVerticalMode(bool bVertical); + + sal_uInt16 itemCount() const; + sal_uInt16 itemHighlighted() const; + sal_uInt16 itemAtPos(const Point& rPos) const; + tools::Rectangle itemPosRect(sal_uInt16) const; + bool itemIsSeparator(sal_uInt16) const; + bool itemIsVisible(sal_uInt16) const; + OUString itemName(sal_uInt16) const; + bool itemTrigger(sal_uInt16); + void setItemHighlighted(sal_uInt16); + sal_uInt16 itemOffset() const { return m_nCurrentOffset; } + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + + DECL_LINK( ScrollHdl, weld::ScrolledWindow&, void ); + + void SetSelectHdl(const Link<SmElement&,void>& rLink) { maSelectHdlLink = rLink; } + + rtl::Reference<AccessibleSmElementsControl> GetAccessible() const { return m_xAccessible; } + static Color GetTextColor(); + static Color GetControlBackground(); + + virtual FactoryFunction GetUITestFactory() const override; +}; + +class SmElementsDockingWindow final : public SfxDockingWindow +{ + std::unique_ptr<SmElementsControl> mxElementsControl; + std::unique_ptr<weld::CustomWeld> mxElementsControlWin; + std::unique_ptr<weld::ComboBox> mxElementListBox; + + virtual void Resize() override; + SmViewShell* GetView(); + + DECL_LINK(SelectClickHandler, SmElement&, void); + DECL_LINK(ElementSelectedHandle, weld::ComboBox&, void); + +public: + + SmElementsDockingWindow( SfxBindings* pBindings, + SfxChildWindow* pChildWindow, + vcl::Window* pParent ); + virtual ~SmElementsDockingWindow() override; + virtual void dispose() override; + + virtual void EndDocking( const tools::Rectangle& rReactangle, bool bFloatMode) override; + virtual void ToggleFloatingMode() override; +}; + +class SmElementsDockingWindowWrapper final : public SfxChildWindow +{ + SFX_DECL_CHILDWINDOW_WITHID(SmElementsDockingWindowWrapper); + + SmElementsDockingWindowWrapper( vcl::Window* pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ); + virtual ~SmElementsDockingWindowWrapper() override; +}; + +#endif // INCLUDED_STARMATH_INC_ELEMENTSDOCKINGWINDOW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/action.hxx b/starmath/inc/action.hxx new file mode 100644 index 000000000..cb4a4eddf --- /dev/null +++ b/starmath/inc/action.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_ACTION_HXX +#define INCLUDED_STARMATH_INC_ACTION_HXX + +#include <svl/undo.hxx> +#include "format.hxx" + +class SmDocShell; + + +class SmFormatAction final : public SfxUndoAction +{ + SmDocShell *pDoc; + SmFormat aOldFormat; + SmFormat aNewFormat; + +public: + SmFormatAction(SmDocShell *pDocSh, const SmFormat& rOldFormat, const SmFormat& rNewFormat); + + virtual void Undo() override; + virtual void Redo() override; + virtual void Repeat(SfxRepeatTarget& rDocSh) override; + virtual OUString GetComment() const override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/caret.hxx b/starmath/inc/caret.hxx new file mode 100644 index 000000000..327ee1d6e --- /dev/null +++ b/starmath/inc/caret.hxx @@ -0,0 +1,428 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#ifndef INCLUDED_STARMATH_INC_CARET_HXX +#define INCLUDED_STARMATH_INC_CARET_HXX + +#include <sal/config.h> + +#include "node.hxx" + +#include <cassert> +#include <memory> +#include <vector> + +/** Representation of caret position with an equation */ +struct SmCaretPos{ + SmCaretPos(SmNode* selectedNode = nullptr, int iIndex = 0) + : pSelectedNode(selectedNode) + , nIndex(iIndex) + { + assert(nIndex >= 0); + } + + /** Selected node */ + SmNode* pSelectedNode; + + /** Index (invariant: non-negative) within the selected node + * + * 0: Position in front of a node + * 1: Position after a node or after first char in SmTextNode + * n: Position after n char in SmTextNode + * + * Notice how there's special cases for SmTextNode. + */ + //TODO: Special cases for SmBlankNode is needed + //TODO: Consider forgetting about the todo above... As it's really unpleasant. + int nIndex; + + /** True, if this is a valid caret position */ + bool IsValid() const { return pSelectedNode != nullptr; } + bool operator==(const SmCaretPos &pos) const { + return pos.pSelectedNode == pSelectedNode && nIndex == pos.nIndex; + } + /** Get the caret position after pNode, regardless of pNode + * + * Gets the caret position following pNode, this is SmCaretPos(pNode, 1). + * Unless pNode is an instance of SmTextNode, then the index is the text length. + */ + static SmCaretPos GetPosAfter(SmNode* pNode) { + if(pNode && pNode->GetType() == SmNodeType::Text) + return SmCaretPos(pNode, static_cast<SmTextNode*>(pNode)->GetText().getLength()); + return SmCaretPos(pNode, 1); + } +}; + +/** A line that represents a caret */ +class SmCaretLine{ +public: + SmCaretLine(long left = 0, long top = 0, long height = 0) { + _top = top; + _left = left; + _height = height; + } + long GetTop() const {return _top;} + long GetLeft() const {return _left;} + long GetHeight() const {return _height;} + long SquaredDistanceX(const SmCaretLine& line) const{ + return (GetLeft() - line.GetLeft()) * (GetLeft() - line.GetLeft()); + } + long SquaredDistanceX(const Point &pos) const{ + return (GetLeft() - pos.X()) * (GetLeft() - pos.X()); + } + long SquaredDistanceY(const SmCaretLine& line) const{ + long d = GetTop() - line.GetTop(); + if(d < 0) + d = (d * -1) - GetHeight(); + else + d = d - line.GetHeight(); + if(d < 0) + return 0; + return d * d; + } + long SquaredDistanceY(const Point &pos) const{ + long d = GetTop() - pos.Y(); + if(d < 0) + d = (d * -1) - GetHeight(); + if(d < 0) + return 0; + return d * d; + } +private: + long _top; + long _left; + long _height; +}; + +// SmCaretPosGraph + +/** An entry in SmCaretPosGraph */ +struct SmCaretPosGraphEntry{ + SmCaretPosGraphEntry(SmCaretPos pos, + SmCaretPosGraphEntry* left, + SmCaretPosGraphEntry* right) { + CaretPos = pos; + Left = left; + Right = right; + } + /** Caret position */ + SmCaretPos CaretPos; + /** Entry to the left visually */ + SmCaretPosGraphEntry* Left; + /** Entry to the right visually */ + SmCaretPosGraphEntry* Right; + void SetRight(SmCaretPosGraphEntry* right){ + Right = right; + } + void SetLeft(SmCaretPosGraphEntry* left){ + Left = left; + } +}; + +/** A graph over all caret positions + * @remarks Graphs can only grow, entries cannot be removed! + */ +class SmCaretPosGraph{ +public: + SmCaretPosGraph(); + + ~SmCaretPosGraph(); + + /** Add a caret position + * @remarks If left is NULL, they will point back to the entry. + */ + SmCaretPosGraphEntry* Add(SmCaretPos pos, + SmCaretPosGraphEntry* left = nullptr); + + std::vector<std::unique_ptr<SmCaretPosGraphEntry>>::iterator begin() + { + return mvEntries.begin(); + } + + std::vector<std::unique_ptr<SmCaretPosGraphEntry>>::iterator end() + { + return mvEntries.end(); + } + +private: + std::vector<std::unique_ptr<SmCaretPosGraphEntry>> mvEntries; +}; + +/** \page visual_formula_editing Visual Formula Editing + * A visual formula editor allows users to easily edit formulas without having to learn and + * use complicated commands. A visual formula editor is a WYSIWYG editor. For OpenOffice Math + * this essentially means that you can click on the formula image, to get a caret, which you + * can move with arrow keys, and use to modify the formula by entering text, clicking buttons + * or using shortcuts. + * + * \subsection formula_trees Formula Trees + * A formula in OpenOffice Math is a tree of nodes, take for instance the formula + * "A + {B cdot C} over D", it looks like this + * \f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$. The tree for this formula + * looks like this: + * + * \dot + * digraph { + * labelloc = "t"; + * label= "Equation: \"A + {B cdot C} over D\""; + * size = "9,9"; + * n0 [label="SmTableNode (1)"]; + * n0 -> n1 [label="0"]; + * n1 [label="SmLineNode (2)"]; + * n1 -> n2 [label="0"]; + * n2 [label="SmExpressionNode (3)"]; + * n2 -> n3 [label="0"]; + * n3 [label="SmBinHorNode (4)"]; + * n3 -> n4 [label="0"]; + * n4 [label="SmTextNode: A (5)"]; + * n3 -> n5 [label="1"]; + * n5 [label="SmMathSymbolNode: + (6)"]; + * n3 -> n6 [label="2"]; + * n6 [label="SmBinVerNode (7)"]; + * n6 -> n7 [label="0"]; + * n7 [label="SmExpressionNode (8)"]; + * n7 -> n8 [label="0"]; + * n8 [label="SmBinHorNode (9)"]; + * n8 -> n9 [label="0"]; + * n9 [label="SmTextNode: B (10)"]; + * n8 -> n10 [label="1"]; + * n10 [label="SmMathSymbolNode: · (11)"]; + * n8 -> n11 [label="2"]; + * n11 [label="SmTextNode: C (12)"]; + * n6 -> n12 [label="1"]; + * n12 [label="SmRectangleNode (13)"]; + * n6 -> n13 [label="2"]; + * n13 [label="SmTextNode: D (14)"]; + * } + * \enddot + * + * The vertices are nodes, their label says what kind of node and the number in parentheses is + * the identifier of the node (In practices a pointer is used instead of the id). The direction + * of the edges tells which node is parent and which is child. The label of the edges are the + * child node index number, given to SmNode::GetSubNode() of the parent to get the child node. + * + * + * \subsection visual_lines Visual Lines + * + * Inorder to do caret movement in visual lines, we need a definition of caret position and + * visual line. In a tree such as the above there are three visual lines. There's the outer most + * line, with entries such as + * \f$\mbox{A}\f$, \f$ + \f$ and \f$ \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$. Then there's + * the numerator line of the fraction it has entries \f$ \mbox{B} \f$, \f$ \cdot \f$ and \f$ \mbox{C} \f$. + * And last by not least there's the denominator line of the fraction it's only entry is \f$ \mbox{D} \f$. + * + * For visual editing it should be possible to place a caret on both sides of any line entry, + * consider a line entry a character or construction that in a line is treated as a character. + * Imagine the caret is placed to the right of the plus sign (id: 6), now if user presses + * backspace this should delete the plus sign (id: 6), and if the user presses delete this + * should delete the entire fraction (id: 7). This is because the caret is in the outer most + * line where the fraction is considered a line entry. + * + * However, inorder to prevent users from accidentally deleting large subtrees, just because + * they logically placed there caret a in the wrong line, require that complex constructions + * such as a fraction is selected before it is deleted. Thus in this case it wouldn't be + * deleted, but only selected and then deleted if the user hit delete again. Anyway, this is + * slightly off topic for now. + * + * Important about visual lines is that they don't always have an SmExpressionNode as root + * and the entries in a visual line is all the nodes of a subtree ordered left to right that + * isn't either an SmExpressionNode, SmBinHorNode or SmUnHorNode. + * + * + * \subsection caret_positions Caret Positions + * + * A caret position in OpenOffice Math is represented by an instance of SmCaretPos. + * That is a caret position is a node and an index related to this node. For most nodes the + * index 0, means caret is in front of this node, the index 1 means caret is after this node. + * For SmTextNode the index is the caret position after the specified number of characters, + * imagine an SmTextNode with the number 1337. The index 3 in such SmTextNode would mean a + * caret placed right before 7, e.g. "133|7". + * + * For SmExpressionNode, SmBinHorNode and SmUnHorNode the only legal index is 0, which means + * in front of the node. Actually the index 0 may only because for the first caret position + * in a visual line. From the example above, consider the following subtree that constitutes + * a visual line: + * + * \dot + * digraph { + * labelloc = "t"; + * label= "Subtree that constitutes a visual line"; + * size = "7,5"; + * n7 [label="SmExpressionNode (8)"]; + * n7 -> n8 [label="0"]; + * n8 [label="SmBinHorNode (9)"]; + * n8 -> n9 [label="0"]; + * n9 [label="SmTextNode: B (10)"]; + * n8 -> n10 [label="1"]; + * n10 [label="SmMathSymbolNode: · (11)"]; + * n8 -> n11 [label="2"]; + * n11 [label="SmTextNode: C (12)"]; + * } + * \enddot + * Here the caret positions are: + * + * <TABLE> + * <TR><TD><B>Caret position:</B></TD><TD><B>Example:</B></TD> + * </TR><TR> + * <TD>{id: 8, index: 0}</TD> + * <TD>\f$ \mid \mbox{C} \cdot \mbox{C} \f$</TD> + * </TR><TR> + * <TD>{id: 10, index: 1}</TD> + * <TD>\f$ \mbox{C} \mid \cdot \mbox{C} \f$</TD> + * </TR><TR> + * <TD>{id: 11, index: 1}</TD> + * <TD>\f$ \mbox{C} \cdot \mid \mbox{C} \f$</TD> + * </TR><TR> + * <TD>{id: 12, index: 1}</TD> + * <TD>\f$ \mbox{C} \cdot \mbox{C} \mid \f$</TD> + * </TR><TR> + * </TABLE> + * + * Where \f$ \mid \f$ is used to denote caret position. + * + * With these exceptions included in the definition the id and index: {id: 11, index: 0} does + * \b not constitute a caret position in the given context. Note the method + * SmCaretPos::IsValid() does not check if this invariant holds true, but code in SmCaret, + * SmSetSelectionVisitor and other places depends on this invariant to hold. + * + * + * \subsection caret_movement Caret Movement + * + * As the placement of caret positions depends very much on the context within which a node + * appears it is not trivial to find all caret positions and determine which follows which. + * In OpenOffice Math this is done by the SmCaretPosGraphBuildingVisitor. This visitor builds + * graph (an instance of SmCaretPosGraph) over the caret positions. For details on how this + * graph is build, and how new methods should be implemented see SmCaretPosGraphBuildingVisitor. + * + * The result of the SmCaretPosGraphBuildingVisitor is a graph over the caret positions in a + * formula, represented by an instance of SmCaretPosGraph. Each entry (instances of SmCaretPosGraphEntry) + * has a pointer to the entry to the left and right of itself. This way we can easily find + * the caret position to a right or left of a given caret position. Note each caret position + * only appears once in this graph. + * + * When searching for a caret position after a left click on the formula this map is also used. + * We simply iterate over all entries, uses the SmCaretPos2LineVisitor to find a line for each + * caret position. Then the distance from the click to the line is computed and we choose the + * caret position closest to the click. + * + * For up and down movement, we also iterator over all caret positions and use SmCaretPos2LineVisitor + * to find a line for each caret position. Then we compute the distance from the current + * caret position to every other caret position and chooses the one closest that is either + * above or below the current caret position, depending on whether we're doing up or down movement. + * + * This result of this approach to caret movement is that we have logically predictable + * movement for left and right, whilst leftclick, up and down movement depends on the sizes + * and placement of all node and may be less logically predictable. This solution also means + * that we only have one complex visitor generating the graph, imagine the nightmare if we + * had a visitor for movement in each direction. + * + * Making up and down movement independent of node sizes and placement wouldn't necessarily + * be a good thing either. Consider the formula \f$ \frac{1+2+3+4+5}{6} \f$, if the caret is + * placed as displayed here: \f$ \frac{1+2+3+4+5}{6 \mid} \f$, up movement should move to right + * after "3": \f$ \frac{1+2+3|+4+5}{6} \f$. However, such a move depends on the sizes and placement + * of all nodes in the fraction. + * + * + * \subsubsection caretpos_graph_example Example of Caret Position Graph + * + * If we consider the formula + * \f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$ from \ref formula_trees. + * It has the following caret positions: + * + * <TABLE> + * <TR> + * <TD><B>Caret position:</B></TD> + * <TD><B>Example:</B></TD> + * </TR><TR> + * <TD>{id: 3, index: 0}</TD> + * <TD>\f$ \mid\mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD> + * </TR><TR> + * <TD>{id: 5, index: 1}</TD> + * <TD>\f$ \mbox{A}\mid + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD> + * </TR><TR> + * <TD>{id: 6, index: 1}</TD> + * <TD>\f$ \mbox{A} + \mid \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD> + * </TR><TR> + * <TD>{id: 8, index: 0}</TD> + * <TD>\f$ \mbox{A} + \frac{ \mid \mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD> + * </TR><TR> + * <TD>{id: 10, index: 1}</TD> + * <TD>\f$ \mbox{A} + \frac{\mbox{B} \mid \cdot \mbox{C}}{\mbox{D}} \f$</TD> + * </TR><TR> + * <TD>{id: 11, index: 1}</TD> + * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mid \mbox{C}}{\mbox{D}} \f$</TD> + * </TR><TR> + * <TD>{id: 12, index: 1}</TD> + * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C} \mid}{\mbox{D}} \f$</TD> + * </TR><TR> + * <TD>{id: 14, index: 0}</TD> + * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mid \mbox{D}} \f$</TD> + * </TR><TR> + * <TD>{id: 14, index: 1}</TD> + * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D} \mid} \f$</TD> + * </TR><TR> + * <TD>{id: 7, index: 1}</TD> + * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \mid \f$</TD> + * </TR> + * </TABLE> + * + * Below is a directed graph over the caret positions and how you can move between them. + * \dot + * digraph { + * labelloc = "t"; + * label= "Caret Position Graph"; + * size = "4,6"; + * p0 [label = "{id: 3, index: 0}"]; + * p0 -> p1 [fontsize = 10.0, label = "right"]; + * p1 [label = "{id: 5, index: 1}"]; + * p1 -> p0 [fontsize = 10.0, label = "left"]; + * p1 -> p2 [fontsize = 10.0, label = "right"]; + * p2 [label = "{id: 6, index: 1}"]; + * p2 -> p1 [fontsize = 10.0, label = "left"]; + * p2 -> p3 [fontsize = 10.0, label = "right"]; + * p3 [label = "{id: 8, index: 0}"]; + * p3 -> p2 [fontsize = 10.0, label = "left"]; + * p3 -> p4 [fontsize = 10.0, label = "right"]; + * p4 [label = "{id: 10, index: 1}"]; + * p4 -> p3 [fontsize = 10.0, label = "left"]; + * p4 -> p5 [fontsize = 10.0, label = "right"]; + * p5 [label = "{id: 11, index: 1}"]; + * p5 -> p4 [fontsize = 10.0, label = "left"]; + * p5 -> p6 [fontsize = 10.0, label = "right"]; + * p6 [label = "{id: 12, index: 1}"]; + * p6 -> p5 [fontsize = 10.0, label = "left"]; + * p6 -> p9 [fontsize = 10.0, label = "right"]; + * p7 [label = "{id: 14, index: 0}"]; + * p7 -> p2 [fontsize = 10.0, label = "left"]; + * p7 -> p8 [fontsize = 10.0, label = "right"]; + * p8 [label = "{id: 14, index: 1}"]; + * p8 -> p7 [fontsize = 10.0, label = "left"]; + * p8 -> p9 [fontsize = 10.0, label = "right"]; + * p9 [label = "{id: 7, index: 1}"]; + * p9 -> p6 [fontsize = 10.0, label = "left"]; + * } + * \enddot + */ + +/* TODO: Write documentation about the following keywords: + * + * Visual Selections: + * - Show images + * - Talk about how the visitor does this + * + * Modifying a Visual Line: + * - Find top most non-compo of the line (e.g. The subtree that constitutes a line) + * - Make the line into a list + * - Edit the list, add/remove/modify nodes + * - Parse the list back into a subtree + * - Insert the new subtree where the old was taken + */ + +#endif // INCLUDED_STARMATH_INC_CARET_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/cursor.hxx b/starmath/inc/cursor.hxx new file mode 100644 index 000000000..236485d5e --- /dev/null +++ b/starmath/inc/cursor.hxx @@ -0,0 +1,427 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_INC_CURSOR_HXX +#define INCLUDED_STARMATH_INC_CURSOR_HXX + +#include "node.hxx" +#include "caret.hxx" + +#include <cassert> +#include <list> +#include <memory> + +/** Factor to multiple the squared horizontal distance with + * Used for Up and Down movement. + */ +#define HORIZONTICAL_DISTANCE_FACTOR 10 + +/** Enum of direction for movement */ +enum SmMovementDirection{ + MoveUp, + MoveDown, + MoveLeft, + MoveRight +}; + +/** Enum of elements that can inserted into a formula */ +enum SmFormulaElement{ + BlankElement, + FactorialElement, + PlusElement, + MinusElement, + CDotElement, + EqualElement, + LessThanElement, + GreaterThanElement, + PercentElement +}; + +/** Bracket types that can be inserted */ +enum class SmBracketType { + /** Round brackets, left command "(" */ + Round, + /**Square brackets, left command "[" */ + Square, + /** Curly brackets, left command "lbrace" */ + Curly, +}; + +/** A list of nodes */ +typedef std::list<SmNode*> SmNodeList; + +typedef std::list<std::unique_ptr<SmNode>> SmClipboard; + +class SmDocShell; + +/** Formula cursor + * + * This class is used to represent a cursor in a formula, which can be used to manipulate + * a formula programmatically. + * @remarks This class is a very intimate friend of SmDocShell. + */ +class SmCursor{ +public: + SmCursor(SmNode* tree, SmDocShell* pShell) + : mpAnchor(nullptr) + , mpPosition(nullptr) + , mpTree(tree) + , mpDocShell(pShell) + , mnEditSections(0) + , mbIsEnabledSetModifiedSmDocShell(false) + { + //Build graph + BuildGraph(); + } + + /** Get position */ + const SmCaretPos& GetPosition() const { return mpPosition->CaretPos; } + + /** True, if the cursor has a selection */ + bool HasSelection() const { return mpAnchor != mpPosition; } + + /** Move the position of this cursor */ + void Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor = true); + + /** Move to the caret position closest to a given point */ + void MoveTo(OutputDevice* pDev, const Point& pos, bool bMoveAnchor); + + /** Delete the current selection or do nothing */ + void Delete(); + + /** Delete selection, previous element or merge lines + * + * This method implements the behaviour of backspace. + */ + void DeletePrev(OutputDevice* pDev); + + /** Insert text at the current position */ + void InsertText(const OUString& aString); + + /** Insert an element into the formula */ + void InsertElement(SmFormulaElement element); + + /** Insert command text translated into line entries at position + * + * Note: This method uses the parser to translate a command text into a + * tree, then it copies line entries from this tree into the current tree. + * Will not work for commands such as newline or ##, if position is in a matrix. + * This will work for stuff like "A intersection B". But stuff spanning multiple lines + * or dependent on the context which position is placed in will not work! + */ + void InsertCommandText(const OUString& aCommandText); + + /** Insert a special node created from aString + * + * Used for handling insert request from the "catalog" dialog. + * The provided string should be formatted as the desired command: %phi + * Note: this method ONLY supports commands defined in Math.xcu + * + * For more complex expressions use InsertCommandText, this method doesn't + * use SmParser, this means that it's faster, but not as strong. + */ + void InsertSpecial(const OUString& aString); + + /** Create sub-/super script + * + * If there's a selection, it will be move into the appropriate sub-/super scription + * of the node in front of it. If there's no node in front of position (or the selection), + * a sub-/super scription of a new SmPlaceNode will be made. + * + * If there's is an existing subscription of the node, the caret will be moved into it, + * and any selection will replace it. + */ + void InsertSubSup(SmSubSup eSubSup); + + /** Insert a new row or newline + * + * Inserts a new row if position is in a matrix or stack command. + * Otherwise a newline is inserted if we're in a toplevel line. + * + * @returns True, if a new row/line could be inserted. + * + * @remarks If the caret is placed in a subline of a command that doesn't support + * this operator the method returns FALSE, and doesn't do anything. + */ + bool InsertRow(); + + /** Insert a fraction, use selection as numerator */ + void InsertFraction(); + + /** Create brackets around current selection, or new SmPlaceNode */ + void InsertBrackets(SmBracketType eBracketType); + + /** Copy the current selection */ + void Copy(); + /** Cut the current selection */ + void Cut(){ + Copy(); + Delete(); + } + /** Paste the clipboard */ + void Paste(); + + /** Returns true if more than one node is selected + * + * This method is used for implementing backspace and delete. + * If one of these causes a complex selection, e.g. a node with + * subnodes or similar, this should not be deleted immediately. + */ + bool HasComplexSelection(); + + /** Finds the topmost node in a visual line + * + * If MoveUpIfSelected is true, this will move up to the parent line + * if the parent of the current line is selected. + */ + static SmNode* FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected = false); + + /** Draw the caret */ + void Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible); + + bool IsAtTailOfBracket(SmBracketType eBracketType, SmBraceNode** ppBraceNode) const; + void MoveAfterBracket(SmBraceNode* pBraceNode); + +private: + friend class SmDocShell; + + SmCaretPosGraphEntry *mpAnchor, + *mpPosition; + /** Formula tree */ + SmNode* mpTree; + /** Owner of the formula tree */ + SmDocShell* mpDocShell; + /** Graph over caret position in the current tree */ + std::unique_ptr<SmCaretPosGraph> mpGraph; + /** Clipboard holder */ + SmClipboard maClipboard; + + /** Returns a node that is selected, if any could be found */ + SmNode* FindSelectedNode(SmNode* pNode); + + /** Is this one of the nodes used to compose a line + * + * These are SmExpression, SmBinHorNode, SmUnHorNode etc. + */ + static bool IsLineCompositionNode(SmNode const * pNode); + + /** Count number of selected nodes, excluding line composition nodes + * + * Note this function doesn't count line composition nodes and it + * does count all subnodes as well as the owner nodes. + * + * Used by SmCursor::HasComplexSelection() + */ + int CountSelectedNodes(SmNode* pNode); + + /** Convert a visual line to a list + * + * Note this method will delete all the nodes that will no longer be needed. + * that includes pLine! + * This method also deletes SmErrorNode's as they're just meta info in the line. + */ + static void LineToList(SmStructureNode* pLine, SmNodeList& rList); + + /** Auxiliary function for calling LineToList on a node + * + * This method sets pNode = NULL and remove it from its parent. + * (Assuming it has a parent, and is a child of it). + */ + static void NodeToList(SmNode*& rpNode, SmNodeList& rList){ + //Remove from parent and NULL rpNode + SmNode* pNode = rpNode; + if(rpNode && rpNode->GetParent()){ //Don't remove this, correctness relies on it + int index = rpNode->GetParent()->IndexOfSubNode(rpNode); + assert(index >= 0); + rpNode->GetParent()->SetSubNode(index, nullptr); + } + rpNode = nullptr; + //Create line from node + if(pNode && IsLineCompositionNode(pNode)){ + LineToList(static_cast<SmStructureNode*>(pNode), rList); + return; + } + if(pNode) + rList.push_front(pNode); + } + + /** Clone a visual line to a clipboard + * + * ... but the selected part only. + * Doesn't clone SmErrorNodes, which are ignored as they are context dependent metadata. + */ + static void CloneLineToClipboard(SmStructureNode* pLine, SmClipboard* pClipboard); + + /** Build pGraph over caret positions */ + void BuildGraph(); + + /** Insert new nodes in the tree after position */ + void InsertNodes(std::unique_ptr<SmNodeList> pNewNodes); + + /** tries to set position to a specific SmCaretPos + * + * @returns false on failure to find the position in pGraph. + */ + bool SetCaretPosition(SmCaretPos pos); + + /** Set selected on nodes of the tree */ + void AnnotateSelection(); + + /** Clone list of nodes in a clipboard (creates a deep clone) */ + static std::unique_ptr<SmNodeList> CloneList(SmClipboard &rClipboard); + + /** Find an iterator pointing to the node in pLineList following rCaretPos + * + * If rCaretPos.pSelectedNode cannot be found it is assumed that it's in front of pLineList, + * thus not an element in pLineList. In this case this method returns an iterator to the + * first element in pLineList. + * + * If the current position is inside an SmTextNode, this node will be split in two, for this + * reason you should beaware that iterators to elements in pLineList may be invalidated, and + * that you should call PatchLineList() with this iterator if no action is taken. + */ + static SmNodeList::iterator FindPositionInLineList(SmNodeList* pLineList, + const SmCaretPos& rCaretPos); + + /** Patch a line list after modification, merge SmTextNode, remove SmPlaceNode etc. + * + * @param pLineList The line list to patch + * @param aIter Iterator pointing to the element that needs to be patched with its previous. + * + * When the list is patched text nodes before and after aIter will be merged. + * If there's an, in the context, inappropriate SmPlaceNode before or after aIter it will also be + * removed. + * + * @returns A caret position equivalent to one selecting the node before aIter, the method returns + * an invalid SmCaretPos to indicate placement in front of the line. + */ + static SmCaretPos PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter); + + /** Take selected nodes from a list + * + * Puts the selected nodes into pSelectedNodes, or if pSelectedNodes is NULL deletes + * the selected nodes. + * Note: If there's a selection inside an SmTextNode this node will be split, and it + * will not be merged when the selection have been taken. Use PatchLineList on the + * iterator returns to fix this. + * + * @returns An iterator pointing to the element following the selection taken. + */ + static SmNodeList::iterator TakeSelectedNodesFromList(SmNodeList *pLineList, + SmNodeList *pSelectedNodes = nullptr); + + /** Create an instance of SmMathSymbolNode usable for brackets */ + static SmNode *CreateBracket(SmBracketType eBracketType, bool bIsLeft); + + /** The number of times BeginEdit have been called + * Used to allow nesting of BeginEdit() and EndEdit() sections + */ + int mnEditSections; + /** Holds data for BeginEdit() and EndEdit() */ + bool mbIsEnabledSetModifiedSmDocShell; + /** Begin edit section where the tree will be modified */ + void BeginEdit(); + /** End edit section where the tree will be modified */ + void EndEdit(); + /** Finish editing + * + * Finishes editing by parsing pLineList and inserting back into pParent at nParentIndex. + * This method also rebuilds the graph, annotates the selection, sets caret position and + * Calls EndEdit. + * + * @remarks Please note that this method will delete pLineList, as the elements are taken. + * + * @param pLineList List the constitutes the edited line. + * @param pParent Parent to which the line should be inserted. + * @param nParentIndex Index in parent where the line should be inserted. + * @param PosAfterEdit Caret position to look for after rebuilding graph. + * @param pStartLine Line to take first position in, if PosAfterEdit cannot be found, + * leave it NULL for pLineList. + */ + void FinishEdit(std::unique_ptr<SmNodeList> pLineList, + SmStructureNode* pParent, + int nParentIndex, + SmCaretPos PosAfterEdit, + SmNode* pStartLine = nullptr); + /** Request the formula is repainted */ + void RequestRepaint(); +}; + +/** Minimalistic recursive decent SmNodeList parser + * + * This parser is used to take a list of nodes that constitutes a line + * and parse them to a tree of SmBinHorNode, SmUnHorNode and SmExpression. + * + * Please note, this will not handle all kinds of nodes, only nodes that + * constitutes and entry in a line. + * + * Below is an EBNF representation of the grammar used for this parser: + * \code + * Expression -> Relation* + * Relation -> Sum [(=|<|>|...) Sum]* + * Sum -> Product [(+|-) Product]* + * Product -> Factor [(*|/) Factor]* + * Factor -> [+|-|-+|...]* Factor | Postfix + * Postfix -> node [!]* + * \endcode + */ +class SmNodeListParser{ +public: + /** Create an instance of SmNodeListParser */ + SmNodeListParser(){ + pList = nullptr; + } + /** Parse a list of nodes to an expression. + * + * Old error nodes will be deleted. + */ + SmNode* Parse(SmNodeList* list); + /** True, if the token is an operator */ + static bool IsOperator(const SmToken &token); + /** True, if the token is a relation operator */ + static bool IsRelationOperator(const SmToken &token); + /** True, if the token is a sum operator */ + static bool IsSumOperator(const SmToken &token); + /** True, if the token is a product operator */ + static bool IsProductOperator(const SmToken &token); + /** True, if the token is a unary operator */ + static bool IsUnaryOperator(const SmToken &token); + /** True, if the token is a postfix operator */ + static bool IsPostfixOperator(const SmToken &token); +private: + SmNodeList* pList; + /** Get the current terminal */ + SmNode* Terminal(){ + if (!pList->empty()) + return pList->front(); + return nullptr; + } + /** Move to next terminal */ + SmNode* Next(){ + pList->pop_front(); + return Terminal(); + } + /** Take the current terminal */ + SmNode* Take(){ + SmNode* pRetVal = Terminal(); + Next(); + return pRetVal; + } + SmNode* Expression(); + SmNode* Relation(); + SmNode* Sum(); + SmNode* Product(); + SmNode* Factor(); + SmNode* Postfix(); + static SmNode* Error(); +}; + + +#endif // INCLUDED_STARMATH_INC_CURSOR_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/dialog.hxx b/starmath/inc/dialog.hxx new file mode 100644 index 000000000..48910cafe --- /dev/null +++ b/starmath/inc/dialog.hxx @@ -0,0 +1,486 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_INC_DIALOG_HXX +#define INCLUDED_STARMATH_INC_DIALOG_HXX + +#include <sfx2/tabdlg.hxx> +#include <vcl/outdev.hxx> +#include <vcl/customweld.hxx> +#include "symbol.hxx" +#include <memory> + +class SubsetMap; +class SmFormat; +class FontList; +class SvxShowCharSet; + +#define CATEGORY_NONE 0xFFFF + +/**************************************************************************/ + +void SetFontStyle(const OUString &rStyleName, vcl::Font &rFont); + +/**************************************************************************/ + +class SmPrintOptionsTabPage final : public SfxTabPage +{ + std::unique_ptr<weld::CheckButton> m_xTitle; + std::unique_ptr<weld::CheckButton> m_xText; + std::unique_ptr<weld::CheckButton> m_xFrame; + std::unique_ptr<weld::RadioButton> m_xSizeNormal; + std::unique_ptr<weld::RadioButton> m_xSizeScaled; + std::unique_ptr<weld::RadioButton> m_xSizeZoomed; + std::unique_ptr<weld::MetricSpinButton> m_xZoom; + std::unique_ptr<weld::CheckButton> m_xNoRightSpaces; + std::unique_ptr<weld::CheckButton> m_xSaveOnlyUsedSymbols; + std::unique_ptr<weld::CheckButton> m_xAutoCloseBrackets; + + DECL_LINK(SizeButtonClickHdl, weld::ToggleButton&, void); + + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; + +public: + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + + SmPrintOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rOptions); + virtual ~SmPrintOptionsTabPage() override; +}; + +/**************************************************************************/ + +class SmShowFont final : public weld::CustomWidgetController +{ + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + + vcl::Font maFont; + +public: + SmShowFont() + { + } + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + void SetFont(const vcl::Font& rFont); +}; + +class SmFontDialog : public weld::GenericDialogController +{ + vcl::Font maFont; + SmShowFont m_aShowFont; + std::unique_ptr<weld::EntryTreeView> m_xFontBox; + std::unique_ptr<weld::Widget> m_xAttrFrame; + std::unique_ptr<weld::CheckButton> m_xBoldCheckBox; + std::unique_ptr<weld::CheckButton> m_xItalicCheckBox; + std::unique_ptr<weld::CustomWeld> m_xShowFont; + + DECL_LINK(FontSelectHdl, weld::ComboBox&, void); + DECL_LINK(AttrChangeHdl, weld::ToggleButton&, void); + +public: + SmFontDialog(weld::Window* pParent, OutputDevice *pFntListDevice, bool bHideCheckboxes); + virtual ~SmFontDialog() override; + + const vcl::Font& GetFont() const + { + return maFont; + } + void SetFont(const vcl::Font &rFont); +}; + +/**************************************************************************/ + +class SmFontSizeDialog final : public weld::GenericDialogController +{ + std::unique_ptr<weld::MetricSpinButton> m_xBaseSize; + std::unique_ptr<weld::MetricSpinButton> m_xTextSize; + std::unique_ptr<weld::MetricSpinButton> m_xIndexSize; + std::unique_ptr<weld::MetricSpinButton> m_xFunctionSize; + std::unique_ptr<weld::MetricSpinButton> m_xOperatorSize; + std::unique_ptr<weld::MetricSpinButton> m_xBorderSize; + std::unique_ptr<weld::Button> m_xDefaultButton; + + DECL_LINK(DefaultButtonClickHdl, weld::Button&, void); + +public: + SmFontSizeDialog(weld::Window *pParent); + virtual ~SmFontSizeDialog() override; + + void ReadFrom(const SmFormat &rFormat); + void WriteTo (SmFormat &rFormat) const; +}; + +/**************************************************************************/ + +class SmFontTypeDialog final : public weld::GenericDialogController +{ + VclPtr<OutputDevice> pFontListDev; + + std::unique_ptr<SmFontPickListBox> m_xVariableFont; + std::unique_ptr<SmFontPickListBox> m_xFunctionFont; + std::unique_ptr<SmFontPickListBox> m_xNumberFont; + std::unique_ptr<SmFontPickListBox> m_xTextFont; + std::unique_ptr<SmFontPickListBox> m_xSerifFont; + std::unique_ptr<SmFontPickListBox> m_xSansFont; + std::unique_ptr<SmFontPickListBox> m_xFixedFont; + std::unique_ptr<weld::MenuButton> m_xMenuButton; + std::unique_ptr<weld::Button> m_xDefaultButton; + + DECL_LINK(MenuSelectHdl, const OString&, void); + DECL_LINK(DefaultButtonClickHdl, weld::Button&, void); + +public: + SmFontTypeDialog(weld::Window* pParent, OutputDevice *pFntListDevice); + virtual ~SmFontTypeDialog() override; + + void ReadFrom(const SmFormat &rFormat); + void WriteTo (SmFormat &rFormat) const; +}; + +/**************************************************************************/ + +#define NOCATEGORIES 10 + +class SmCategoryDesc +{ + OUString Name; + OUString Strings[4]; + std::unique_ptr<weld::Widget> Graphics[4]; /* regular bitmaps */ + sal_uInt16 Minimum[4]; + sal_uInt16 Maximum[4]; + sal_uInt16 Value[4]; + +public: + SmCategoryDesc(weld::Builder& rBuilder, sal_uInt16 nCategoryIdx); + ~SmCategoryDesc(); + + const OUString& GetName() const { return Name; } + const OUString& GetString(sal_uInt16 Index) const { return Strings[Index]; } + sal_uInt16 GetMinimum(sal_uInt16 Index) { return Minimum[Index]; } + sal_uInt16 GetMaximum(sal_uInt16 Index) { return Maximum[Index]; } + sal_uInt16 GetValue(sal_uInt16 Index) const { return Value[Index]; } + void SetValue(sal_uInt16 Index, sal_uInt16 nVal) { Value[Index] = nVal;} + + weld::Widget* GetGraphic(sal_uInt16 Index) const + { + return Graphics[Index].get(); + } +}; + +class SmDistanceDialog final : public weld::GenericDialogController +{ + std::unique_ptr<weld::Frame> m_xFrame; + std::unique_ptr<weld::Label> m_xFixedText1; + std::unique_ptr<weld::MetricSpinButton> m_xMetricField1; + std::unique_ptr<weld::Label> m_xFixedText2; + std::unique_ptr<weld::MetricSpinButton> m_xMetricField2; + std::unique_ptr<weld::Label> m_xFixedText3; + std::unique_ptr<weld::MetricSpinButton> m_xMetricField3; + std::unique_ptr<weld::CheckButton> m_xCheckBox1; + std::unique_ptr<weld::Label> m_xFixedText4; + std::unique_ptr<weld::MetricSpinButton> m_xMetricField4; + std::unique_ptr<weld::MenuButton> m_xMenuButton; + std::unique_ptr<weld::Button> m_xDefaultButton; + std::unique_ptr<weld::Widget> m_xBitmap; + + weld::Widget* m_pCurrentImage; + + std::unique_ptr<SmCategoryDesc> m_xCategories[NOCATEGORIES]; + sal_uInt16 nActiveCategory; + bool bScaleAllBrackets; + + DECL_LINK(GetFocusHdl, weld::Widget&, void); + DECL_LINK(MenuSelectHdl, const OString&, void); + DECL_LINK(DefaultButtonClickHdl, weld::Button&, void); + DECL_LINK(CheckBoxClickHdl, weld::ToggleButton&, void); + + void SetCategory(sal_uInt16 Category); + +public: + SmDistanceDialog(weld::Window *pParent); + virtual ~SmDistanceDialog() override; + + void ReadFrom(const SmFormat &rFormat); + void WriteTo (SmFormat &rFormat); +}; + +/**************************************************************************/ + + +class SmAlignDialog final : public weld::GenericDialogController +{ + std::unique_ptr<weld::RadioButton> m_xLeft; + std::unique_ptr<weld::RadioButton> m_xCenter; + std::unique_ptr<weld::RadioButton> m_xRight; + std::unique_ptr<weld::Button> m_xDefaultButton; + + DECL_LINK(DefaultButtonClickHdl, weld::Button&, void); + +public: + SmAlignDialog(weld::Window *pParent); + virtual ~SmAlignDialog() override; + + void ReadFrom(const SmFormat &rFormat); + void WriteTo (SmFormat &rFormat) const; +}; + +/**************************************************************************/ + +class SmShowSymbolSet final : public weld::CustomWidgetController +{ + Size m_aOldSize; + SymbolPtrVec_t aSymbolSet; + Link<SmShowSymbolSet&,void> aSelectHdlLink; + Link<SmShowSymbolSet&,void> aDblClickHdlLink; + long nLen; + long nRows, nColumns; + long nXOffset, nYOffset; + sal_uInt16 nSelectSymbol; + std::unique_ptr<weld::ScrolledWindow> m_xScrolledWindow; + + void SetScrollBarRange(); + Point OffsetPoint(const Point &rPoint) const; + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual void Resize() override; + + DECL_LINK(ScrollHdl, weld::ScrolledWindow&, void); + +public: + SmShowSymbolSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow); + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override + { + CustomWidgetController::SetDrawingArea(pDrawingArea); + m_aOldSize = Size(pDrawingArea->get_approximate_digit_width() * 27, + pDrawingArea->get_text_height() * 9); + pDrawingArea->set_size_request(m_aOldSize.Width(), m_aOldSize.Height()); + SetOutputSizePixel(m_aOldSize); + calccols(pDrawingArea->get_ref_device()); + } + + void calccols(const vcl::RenderContext& rRenderContext); + void SelectSymbol(sal_uInt16 nSymbol); + sal_uInt16 GetSelectSymbol() const { return nSelectSymbol; } + void SetSymbolSet(const SymbolPtrVec_t& rSymbolSet); + void SetSelectHdl(const Link<SmShowSymbolSet&,void>& rLink) { aSelectHdlLink = rLink; } + void SetDblClickHdl(const Link<SmShowSymbolSet&,void>& rLink) { aDblClickHdlLink = rLink; } +}; + +class SmShowSymbol final : public weld::CustomWidgetController +{ +private: + vcl::Font m_aFont; + OUString m_aText; + + Link<SmShowSymbol&,void> aDblClickHdlLink; + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + void setFontSize(vcl::Font &rFont) const; + +public: + SmShowSymbol(); + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override + { + CustomWidgetController::SetDrawingArea(pDrawingArea); + pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 27, + pDrawingArea->get_text_height() * 9); + } + + void SetText(const OUString& rText) { m_aText = rText; } + const OUString& GetText() const { return m_aText; } + + void SetFont(const vcl::Font& rFont) { m_aFont = rFont; } + const vcl::Font& GetFont() const { return m_aFont; } + + void SetSymbol(const SmSym *pSymbol); + void SetDblClickHdl(const Link<SmShowSymbol&,void> &rLink) { aDblClickHdlLink = rLink; } +}; + +class SmSymbolDialog final : public weld::GenericDialogController +{ + SmViewShell &rViewSh; + SmSymbolManager &rSymbolMgr; + + OUString aSymbolSetName; + SymbolPtrVec_t aSymbolSet; + + VclPtr<OutputDevice> pFontListDev; + + SmShowSymbol m_aSymbolDisplay; + + std::unique_ptr<weld::ComboBox> m_xSymbolSets; + std::unique_ptr<SmShowSymbolSet> m_xSymbolSetDisplay; + std::unique_ptr<weld::CustomWeld> m_xSymbolSetDisplayArea; + std::unique_ptr<weld::Label> m_xSymbolName; + std::unique_ptr<weld::CustomWeld> m_xSymbolDisplay; + std::unique_ptr<weld::Button> m_xGetBtn; + std::unique_ptr<weld::Button> m_xEditBtn; + + DECL_LINK(SymbolSetChangeHdl, weld::ComboBox&, void); + DECL_LINK(SymbolChangeHdl, SmShowSymbolSet&, void); + DECL_LINK(SymbolDblClickHdl, SmShowSymbol&, void); + DECL_LINK(SymbolDblClickHdl2, SmShowSymbolSet&, void); + DECL_LINK(EditClickHdl, weld::Button&, void); + DECL_LINK(GetClickHdl, weld::Button&, void); + void SymbolDblClickHdl(); + + void FillSymbolSets(); + const SmSym *GetSymbol() const; + +public: + SmSymbolDialog(weld::Window* pParent, OutputDevice *pFntListDevice, + SmSymbolManager &rSymbolMgr, SmViewShell &rViewShell); + virtual ~SmSymbolDialog() override; + + bool SelectSymbolSet(const OUString &rSymbolSetName); + void SelectSymbol(sal_uInt16 nSymbolPos); +}; + +class SmShowChar final : public weld::CustomWidgetController +{ +private: + OUString m_aText; + vcl::Font m_aFont; + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + virtual void Resize() override; + +public: + SmShowChar() + { + } + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override + { + CustomWidgetController::SetDrawingArea(pDrawingArea); + pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 7, + pDrawingArea->get_text_height() * 3); + } + + void SetSymbol(const SmSym *pSym); + void SetSymbol(sal_UCS4 cChar, const vcl::Font &rFont); + void SetText(const OUString& rText) { m_aText = rText; } + const OUString& GetText() const { return m_aText; } + void SetFont(const vcl::Font& rFont) { m_aFont = rFont; } + const vcl::Font& GetFont() const { return m_aFont; } +}; + +class SmSymDefineDialog final : public weld::GenericDialogController +{ + VclPtr<VirtualDevice> m_xVirDev; + SmSymbolManager m_aSymbolMgrCopy; + SmSymbolManager& m_rSymbolMgr; + SmShowChar m_aOldSymbolDisplay; + SmShowChar m_aSymbolDisplay; + std::unique_ptr<SmSym> m_xOrigSymbol; + std::unique_ptr<SubsetMap> m_xSubsetMap; + std::unique_ptr<FontList> m_xFontList; + std::unique_ptr<weld::ComboBox> m_xOldSymbols; + std::unique_ptr<weld::ComboBox> m_xOldSymbolSets; + std::unique_ptr<weld::ComboBox> m_xSymbols; + std::unique_ptr<weld::ComboBox> m_xSymbolSets; + std::unique_ptr<weld::ComboBox> m_xFonts; + std::unique_ptr<weld::ComboBox> m_xFontsSubsetLB; + std::unique_ptr<weld::ComboBox> m_xStyles; + std::unique_ptr<weld::Label> m_xOldSymbolName; + std::unique_ptr<weld::Label> m_xOldSymbolSetName; + std::unique_ptr<weld::Label> m_xSymbolName; + std::unique_ptr<weld::Label> m_xSymbolSetName; + std::unique_ptr<weld::Button> m_xAddBtn; + std::unique_ptr<weld::Button> m_xChangeBtn; + std::unique_ptr<weld::Button> m_xDeleteBtn; + std::unique_ptr<weld::CustomWeld> m_xOldSymbolDisplay; + std::unique_ptr<weld::CustomWeld> m_xSymbolDisplay; + std::unique_ptr<SvxShowCharSet> m_xCharsetDisplay; + std::unique_ptr<weld::CustomWeld> m_xCharsetDisplayArea; + + DECL_LINK(OldSymbolChangeHdl, weld::ComboBox&, void); + DECL_LINK(OldSymbolSetChangeHdl, weld::ComboBox&, void); + DECL_LINK(ModifyHdl, weld::ComboBox&, void); + DECL_LINK(FontChangeHdl, weld::ComboBox&, void); + DECL_LINK(SubsetChangeHdl, weld::ComboBox&, void); + DECL_LINK(StyleChangeHdl, weld::ComboBox&, void); + DECL_LINK(CharHighlightHdl, SvxShowCharSet*, void); + DECL_LINK(AddClickHdl, weld::Button&, void); + DECL_LINK(ChangeClickHdl, weld::Button&, void); + DECL_LINK(DeleteClickHdl, weld::Button&, void); + + void FillSymbols(weld::ComboBox& rComboBox, bool bDeleteText = true); + void FillSymbolSets(weld::ComboBox& rComboBox, bool bDeleteText = true); + void FillFonts(); + void FillStyles(); + + void SetSymbolSetManager(const SmSymbolManager &rMgr); + void SetFont(const OUString &rFontName, const OUString &rStyleName); + void SetOrigSymbol(const SmSym *pSymbol, const OUString &rSymbolSetName); + void UpdateButtons(); + + bool SelectSymbolSet(weld::ComboBox &rComboBox, const OUString &rSymbolSetName, + bool bDeleteText); + bool SelectSymbol(weld::ComboBox& rComboBox, const OUString &rSymbolName, + bool bDeleteText); + bool SelectFont(const OUString &rFontName, bool bApplyFont); + bool SelectStyle(const OUString &rStyleName, bool bApplyFont); + + SmSym* GetSymbol(const weld::ComboBox& rComboBox); + const SmSym* GetSymbol(const weld::ComboBox& rComboBox) const + { + return const_cast<SmSymDefineDialog *>(this)->GetSymbol(rComboBox); + } + +public: + SmSymDefineDialog(weld::Window *pParent, OutputDevice *pFntListDevice, SmSymbolManager &rMgr); + virtual ~SmSymDefineDialog() override; + + virtual short run() override; + + void SelectOldSymbolSet(const OUString &rSymbolSetName) + { + SelectSymbolSet(*m_xOldSymbolSets, rSymbolSetName, false); + } + + void SelectOldSymbol(const OUString &rSymbolName) + { + SelectSymbol(*m_xOldSymbols, rSymbolName, false); + } + + bool SelectSymbolSet(const OUString &rSymbolSetName) + { + return SelectSymbolSet(*m_xSymbolSets, rSymbolSetName, false); + } + + bool SelectSymbol(const OUString &rSymbolName) + { + return SelectSymbol(*m_xSymbols, rSymbolName, false); + } + + bool SelectFont(const OUString &rFontName) { return SelectFont(rFontName, true); } + bool SelectStyle(const OUString &rStyleName) { return SelectStyle(rStyleName, true); }; + void SelectChar(sal_Unicode cChar); +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/document.hxx b/starmath/inc/document.hxx new file mode 100644 index 000000000..1b425c91e --- /dev/null +++ b/starmath/inc/document.hxx @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_INC_DOCUMENT_HXX +#define INCLUDED_STARMATH_INC_DOCUMENT_HXX + +#include <rtl/ustring.hxx> +#include <rtl/strbuf.hxx> +#include <sfx2/docfac.hxx> +#include <sfx2/objsh.hxx> +#include <svl/lstner.hxx> +#include <sax/fshelper.hxx> +#include <unotools/lingucfg.hxx> +#include <oox/core/filterbase.hxx> +#include <oox/export/utils.hxx> + +#include <memory> +#include <set> + +#include "format.hxx" +#include "node.hxx" +#include "parse.hxx" +#include "smdllapi.hxx" + +class SfxPrinter; +class Printer; +class SmCursor; + +namespace oox::formulaimport { class XmlStream; } + +#define STAROFFICE_XML "StarOffice XML (Math)" +#define MATHML_XML "MathML XML (Math)" + +/* Access to printer should happen through this class only + * ========================================================================== + * + * The printer can belong to the document or the OLE-Container. If the document + * is an OLE-Document the printer generally belongs to the container too. + * But the container maybe works with a different MapUnit than the server. + * Referring to the MapMode the printer will be accordingly adjusted in the + * constructor and restored in the destructor. This brings that this class + * is always allowed to exists only a short time (e.g. while painting). + * The control whether the printer is self-generated, gotten from the server + * or is NULL then, is taken by the DocShell in the method GetPrt(), for + * which the access is friend of the DocShell too. + */ + +class SmDocShell; +class EditEngine; + +class SmPrinterAccess +{ + VclPtr<Printer> pPrinter; + VclPtr<OutputDevice> pRefDev; +public: + explicit SmPrinterAccess( SmDocShell &rDocShell ); + ~SmPrinterAccess(); + Printer* GetPrinter() { return pPrinter.get(); } + OutputDevice* GetRefDev() { return pRefDev.get(); } +}; + + +class SM_DLLPUBLIC SmDocShell : public SfxObjectShell, public SfxListener +{ + friend class SmPrinterAccess; + friend class SmCursor; + + OUString maText; + SmFormat maFormat; + SmParser maParser; + OUString maAccText; + SvtLinguOptions maLinguOptions; + std::unique_ptr<SmTableNode> mpTree; + SfxItemPool *mpEditEngineItemPool; + std::unique_ptr<EditEngine> mpEditEngine; + VclPtr<SfxPrinter> mpPrinter; //q.v. comment to SmPrinter Access! + VclPtr<Printer> mpTmpPrinter; //ditto + sal_uInt16 mnModifyCount; + bool mbFormulaArranged; + std::unique_ptr<SmCursor> mpCursor; + std::set< OUString > maUsedSymbols; // to export used symbols only when saving + + + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + + bool WriteAsMathType3( SfxMedium& ); + + virtual void Draw(OutputDevice *pDevice, + const JobSetup & rSetup, + sal_uInt16 nAspect) override; + + virtual void FillClass(SvGlobalName* pClassName, + SotClipboardFormatId* pFormat, + OUString* pFullTypeName, + sal_Int32 nFileFormat, + bool bTemplate = false ) const override; + + virtual void OnDocumentPrinterChanged( Printer * ) override; + virtual bool InitNew( const css::uno::Reference< css::embed::XStorage >& xStorage ) override; + virtual bool Load( SfxMedium& rMedium ) override; + virtual bool Save() override; + virtual bool SaveAs( SfxMedium& rMedium ) override; + + Printer *GetPrt(); + OutputDevice* GetRefDev(); + + void SetFormulaArranged(bool bVal) { mbFormulaArranged = bVal; } + + virtual bool ConvertFrom(SfxMedium &rMedium) override; + + /** Called whenever the formula is changed + * Deletes the current cursor + */ + void InvalidateCursor(); + +public: + SFX_DECL_INTERFACE(SFX_INTERFACE_SMA_START+SfxInterfaceId(1)) + + SFX_DECL_OBJECTFACTORY(); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + explicit SmDocShell( SfxModelFlags i_nSfxCreationFlags ); + virtual ~SmDocShell() override; + + virtual bool ConvertTo( SfxMedium &rMedium ) override; + + // For unit tests, not intended to use in other context + void SetGreekCharStyle(sal_Int16 nVal) { maFormat.SetGreekCharStyle(nVal); } + + static void LoadSymbols(); + static void SaveSymbols(); + + void ArrangeFormula(); + + //Access for the View. This access is not for the OLE-case! + //and for the communication with the SFX! + //All internal printer uses should work with the SmPrinterAccess only + bool HasPrinter() const { return mpPrinter != nullptr; } + SfxPrinter *GetPrinter() { GetPrt(); return mpPrinter; } + void SetPrinter( SfxPrinter * ); + + OUString GetComment() const; + + // to replace chars that can not be saved with the document... + void ReplaceBadChars(); + + void UpdateText(); + void SetText(const OUString& rBuffer); + const OUString& GetText() const { return maText; } + void SetFormat(SmFormat const & rFormat); + const SmFormat& GetFormat() const { return maFormat; } + + void Parse(); + SmParser & GetParser() { return maParser; } + const SmTableNode *GetFormulaTree() const { return mpTree.get(); } + void SetFormulaTree(SmTableNode *pTree) { mpTree.reset(pTree); } + + const std::set< OUString > & GetUsedSymbols() const { return maUsedSymbols; } + + OUString const & GetAccessibleText(); + + EditEngine & GetEditEngine(); + + void DrawFormula(OutputDevice &rDev, Point &rPosition, bool bDrawSelection = false); + Size GetSize(); + + void Repaint(); + + virtual SfxUndoManager *GetUndoManager () override; + + static SfxItemPool& GetPool(); + + void Execute( SfxRequest& rReq ); + void GetState(SfxItemSet &); + + virtual void SetVisArea (const tools::Rectangle & rVisArea) override; + virtual void SetModified(bool bModified = true) override; + + /** Get a cursor for modifying this document + * @remarks Don't store this reference, a new cursor may be made... + */ + SmCursor& GetCursor(); + /** True, if cursor have previously been requested and thus + * has some sort of position. + */ + bool HasCursor() const; + + void writeFormulaOoxml(const ::sax_fastparser::FSHelperPtr& pSerializer, + oox::core::OoxmlVersion version, + oox::drawingml::DocumentType documentType, + const sal_Int8 nAlign); + void writeFormulaRtf(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding); + void readFormulaOoxml( oox::formulaimport::XmlStream& stream ); + + void UpdateEditEngineDefaultFonts(const Color& aTextColor); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/edit.hxx b/starmath/inc/edit.hxx new file mode 100644 index 000000000..c1ce769ab --- /dev/null +++ b/starmath/inc/edit.hxx @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_EDIT_HXX +#define INCLUDED_STARMATH_INC_EDIT_HXX + +#include <vcl/window.hxx> +#include <vcl/idle.hxx> +#include <vcl/transfer.hxx> +#include <editeng/editdata.hxx> +#include <memory> + +class SmDocShell; +class SmViewShell; +class EditView; +class EditEngine; +class EditStatus; +class ScrollBar; +class ScrollBarBox; +class DataChangedEvent; +class SmCmdBoxWindow; +class SmEditAccessible; +class CommandEvent; +class Timer; + +namespace svtools { class ColorConfig; } + +void SmGetLeftSelectionPart(const ESelection &rSelection, sal_Int32 &nPara, sal_uInt16 &nPos); + +class SmEditWindow final : public vcl::Window, public DropTargetHelper +{ + rtl::Reference<SmEditAccessible> mxAccessible; + + SmCmdBoxWindow& rCmdBox; + std::unique_ptr<EditView> pEditView; + VclPtr<ScrollBar> pHScrollBar; + VclPtr<ScrollBar> pVScrollBar; + VclPtr<ScrollBarBox> pScrollBox; + Idle aModifyIdle; + Idle aCursorMoveIdle; + ESelection aOldSelection; + + virtual void KeyInput(const KeyEvent& rKEvt) override; + virtual void Command(const CommandEvent& rCEvt) override; + + DECL_LINK(ModifyTimerHdl, Timer *, void); + DECL_LINK(CursorMoveTimerHdl, Timer *, void); + + virtual void ApplySettings(vcl::RenderContext&) override; + virtual void DataChanged( const DataChangedEvent& ) override; + virtual void Resize() override; + virtual void MouseMove(const MouseEvent &rEvt) override; + virtual void MouseButtonUp(const MouseEvent &rEvt) override; + virtual void MouseButtonDown(const MouseEvent &rEvt) override; + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + + DECL_LINK(EditStatusHdl, EditStatus&, void); + DECL_LINK(ScrollHdl, ScrollBar*, void); + + void CreateEditView(); + tools::Rectangle AdjustScrollBars(); + void SetScrollBarRanges(); + void InitScrollBars(); + void InvalidateSlots(); + void UpdateStatus(bool bSetDocModified); + +public: + explicit SmEditWindow(SmCmdBoxWindow& rMyCmdBoxWin); + virtual ~SmEditWindow() override; + virtual void dispose() override; + + SmDocShell* GetDoc(); + SmViewShell* GetView(); + EditView* GetEditView(); + EditEngine* GetEditEngine(); + + // Window + virtual void SetText(const OUString& rText) override; + virtual OUString GetText() const override; + virtual void GetFocus() override; + virtual void LoseFocus() override; + + ESelection GetSelection() const; + void SetSelection(const ESelection& rSel); + + bool IsEmpty() const; + bool IsSelected() const; + bool IsAllSelected() const; + void Cut(); + void Copy(); + void Paste(); + void Delete(); + void SelectAll(); + void InsertText(const OUString& rText); + void MarkError(const Point &rPos); + void SelNextMark(); + void SelPrevMark(); + static bool HasMark(const OUString &rText); + + void Flush() override; + void DeleteEditView(); + + bool HandleWheelCommands(const CommandEvent& rCEvt); + bool IsInlineEditEnabled(); + void StartCursorMove(); + + // for Accessibility + virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override; + + using Window::GetAccessible; +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/error.hxx b/starmath/inc/error.hxx new file mode 100644 index 000000000..a05123d6e --- /dev/null +++ b/starmath/inc/error.hxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_ERROR_HXX +#define INCLUDED_STARMATH_INC_ERROR_HXX + +#include <rtl/ustring.hxx> + +class SmNode; + +enum class SmParseError +{ + None, + UnexpectedChar, + UnexpectedToken, + PoundExpected, + ColorExpected, + LgroupExpected, + RgroupExpected, + LbraceExpected, + RbraceExpected, + ParentMismatch, + RightExpected, + FontExpected, + SizeExpected, + DoubleAlign, + DoubleSubsupscript +}; + + +struct SmErrorDesc +{ + SmParseError m_eType; + SmNode *m_pNode; + OUString m_aText; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/format.hxx b/starmath/inc/format.hxx new file mode 100644 index 000000000..14fbfc2a6 --- /dev/null +++ b/starmath/inc/format.hxx @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_FORMAT_HXX +#define INCLUDED_STARMATH_INC_FORMAT_HXX + + +#include <svl/hint.hxx> +#include <svl/SfxBroadcaster.hxx> +#include "utility.hxx" +#include "types.hxx" + + +#define FNTNAME_TIMES "Times New Roman" +#define FNTNAME_HELV "Helvetica" +#define FNTNAME_COUR "Courier" +#define FNTNAME_MATH FONTNAME_MATH + + +// symbolic names used as array indices +#define SIZ_BEGIN 0 +#define SIZ_TEXT 0 +#define SIZ_INDEX 1 +#define SIZ_FUNCTION 2 +#define SIZ_OPERATOR 3 +#define SIZ_LIMITS 4 +#define SIZ_END 4 + +// symbolic names used as array indices +#define FNT_BEGIN 0 +#define FNT_VARIABLE 0 +#define FNT_FUNCTION 1 +#define FNT_NUMBER 2 +#define FNT_TEXT 3 +#define FNT_SERIF 4 +#define FNT_SANS 5 +#define FNT_FIXED 6 +#define FNT_MATH 7 +#define FNT_END 7 + +// symbolic names used as array indices +#define DIS_BEGIN 0 +#define DIS_HORIZONTAL 0 +#define DIS_VERTICAL 1 +#define DIS_ROOT 2 +#define DIS_SUPERSCRIPT 3 +#define DIS_SUBSCRIPT 4 +#define DIS_NUMERATOR 5 +#define DIS_DENOMINATOR 6 +#define DIS_FRACTION 7 +#define DIS_STROKEWIDTH 8 +#define DIS_UPPERLIMIT 9 +#define DIS_LOWERLIMIT 10 +#define DIS_BRACKETSIZE 11 +#define DIS_BRACKETSPACE 12 +#define DIS_MATRIXROW 13 +#define DIS_MATRIXCOL 14 +#define DIS_ORNAMENTSIZE 15 +#define DIS_ORNAMENTSPACE 16 +#define DIS_OPERATORSIZE 17 +#define DIS_OPERATORSPACE 18 +#define DIS_LEFTSPACE 19 +#define DIS_RIGHTSPACE 20 +#define DIS_TOPSPACE 21 +#define DIS_BOTTOMSPACE 22 +#define DIS_NORMALBRACKETSIZE 23 +#define DIS_END 23 + + +enum class SmHorAlign { + Left, + Center, + Right +}; + +class SmFormat final : public SfxBroadcaster +{ + SmFace vFont[FNT_END + 1]; + bool bDefaultFont[FNT_END + 1]; + Size aBaseSize; + sal_uInt16 vSize[SIZ_END + 1]; + sal_uInt16 vDist[DIS_END + 1]; + SmHorAlign eHorAlign; + sal_Int16 nGreekCharStyle; + bool bIsTextmode, + bScaleNormalBrackets; + +public: + SmFormat(); + SmFormat(const SmFormat &rFormat) : SfxBroadcaster() { *this = rFormat; } + + const Size & GetBaseSize() const { return aBaseSize; } + void SetBaseSize(const Size &rSize) { aBaseSize = rSize; } + + const SmFace & GetFont(sal_uInt16 nIdent) const { return vFont[nIdent]; } + void SetFont(sal_uInt16 nIdent, const SmFace &rFont, bool bDefault = false); + void SetFontSize(sal_uInt16 nIdent, const Size &rSize) { vFont[nIdent].SetSize( rSize ); } + + void SetDefaultFont(sal_uInt16 nIdent, bool bVal) { bDefaultFont[nIdent] = bVal; } + bool IsDefaultFont(sal_uInt16 nIdent) const { return bDefaultFont[nIdent]; } + + sal_uInt16 GetRelSize(sal_uInt16 nIdent) const { return vSize[nIdent]; } + void SetRelSize(sal_uInt16 nIdent, sal_uInt16 nVal) { vSize[nIdent] = nVal;} + + sal_uInt16 GetDistance(sal_uInt16 nIdent) const { return vDist[nIdent]; } + void SetDistance(sal_uInt16 nIdent, sal_uInt16 nVal) { vDist[nIdent] = nVal; } + + SmHorAlign GetHorAlign() const { return eHorAlign; } + void SetHorAlign(SmHorAlign eAlign) { eHorAlign = eAlign; } + + bool IsTextmode() const { return bIsTextmode; } + void SetTextmode(bool bVal) { bIsTextmode = bVal; } + + sal_Int16 GetGreekCharStyle() const { return nGreekCharStyle; } + void SetGreekCharStyle(sal_Int16 nVal) { nGreekCharStyle = nVal; } + + bool IsScaleNormalBrackets() const { return bScaleNormalBrackets; } + void SetScaleNormalBrackets(bool bVal) { bScaleNormalBrackets = bVal; } + + SmFormat & operator = (const SmFormat &rFormat); + + bool operator == (const SmFormat &rFormat) const; + inline bool operator != (const SmFormat &rFormat) const; + + void RequestApplyChanges() + { + Broadcast(SfxHint(SfxHintId::MathFormatChanged)); + } + +}; + +inline bool SmFormat::operator != (const SmFormat &rFormat) const +{ + return !(*this == rFormat); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/helpids.h b/starmath/inc/helpids.h new file mode 100644 index 000000000..7e13c3ac8 --- /dev/null +++ b/starmath/inc/helpids.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_HELPIDS_H +#define INCLUDED_STARMATH_INC_HELPIDS_H + +#define HID_SMA_WIN_DOCUMENT "STARMATH_HID_SMA_WIN_DOCUMENT" +#define HID_SMA_COMMAND_WIN_EDIT "STARMATH_HID_SMA_COMMAND_WIN_EDIT" + +#define HID_SMA_COMMAND_WIN "STARMATH_HID_SMA_COMMAND_WIN" + +#define HID_SMA_DEFAULT_DIST "STARMATH_HID_SMA_DEFAULT_DIST" +#define HID_SMA_LINE_DIST "STARMATH_HID_SMA_LINE_DIST" +#define HID_SMA_ROOT_DIST "STARMATH_HID_SMA_ROOT_DIST" +#define HID_SMA_SUP_DIST "STARMATH_HID_SMA_SUP_DIST" +#define HID_SMA_SUB_DIST "STARMATH_HID_SMA_SUB_DIST" +#define HID_SMA_NUMERATOR_DIST "STARMATH_HID_SMA_NUMERATOR_DIST" +#define HID_SMA_DENOMINATOR_DIST "STARMATH_HID_SMA_DENOMINATOR_DIST" +#define HID_SMA_FRACLINE_EXCWIDTH "STARMATH_HID_SMA_FRACLINE_EXCWIDTH" +#define HID_SMA_FRACLINE_LINEWIDTH "STARMATH_HID_SMA_FRACLINE_LINEWIDTH" +#define HID_SMA_UPPERLIMIT_DIST "STARMATH_HID_SMA_UPPERLIMIT_DIST" +#define HID_SMA_LOWERLIMIT_DIST "STARMATH_HID_SMA_LOWERLIMIT_DIST" +#define HID_SMA_BRACKET_EXCHEIGHT "STARMATH_HID_SMA_BRACKET_EXCHEIGHT" +#define HID_SMA_BRACKET_DIST "STARMATH_HID_SMA_BRACKET_DIST" +#define HID_SMA_MATRIXROW_DIST "STARMATH_HID_SMA_MATRIXROW_DIST" +#define HID_SMA_MATRIXCOL_DIST "STARMATH_HID_SMA_MATRIXCOL_DIST" +#define HID_SMA_ATTRIBUT_DIST "STARMATH_HID_SMA_ATTRIBUT_DIST" +#define HID_SMA_INTERATTRIBUT_DIST "STARMATH_HID_SMA_INTERATTRIBUT_DIST" +#define HID_SMA_OPERATOR_EXCHEIGHT "STARMATH_HID_SMA_OPERATOR_EXCHEIGHT" +#define HID_SMA_OPERATOR_DIST "STARMATH_HID_SMA_OPERATOR_DIST" +#define HID_SMA_LEFTBORDER_DIST "STARMATH_HID_SMA_LEFTBORDER_DIST" +#define HID_SMA_RIGHTBORDER_DIST "STARMATH_HID_SMA_RIGHTBORDER_DIST" +#define HID_SMA_UPPERBORDER_DIST "STARMATH_HID_SMA_UPPERBORDER_DIST" +#define HID_SMA_LOWERBORDER_DIST "STARMATH_HID_SMA_LOWERBORDER_DIST" +#define HID_SMA_BRACKET_EXCHEIGHT2 "STARMATH_HID_SMA_BRACKET_EXCHEIGHT2" + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/node.hxx b/starmath/inc/node.hxx new file mode 100644 index 000000000..1e301369c --- /dev/null +++ b/starmath/inc/node.hxx @@ -0,0 +1,1272 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_INC_NODE_HXX +#define INCLUDED_STARMATH_INC_NODE_HXX + +#include "types.hxx" +#include "token.hxx" +#include "rect.hxx" +#include "format.hxx" + +#include <o3tl/typed_flags_set.hxx> +#include <rtl/ustrbuf.hxx> + +#include <cassert> +#include <vector> + +enum class FontAttribute { + None = 0x0000, + Bold = 0x0001, + Italic = 0x0002 +}; + +namespace o3tl +{ + template<> struct typed_flags<FontAttribute> : is_typed_flags<FontAttribute, 0x0003> {}; +} + + +enum class FontSizeType { + ABSOLUT = 1, + PLUS = 2, + MINUS = 3, + MULTIPLY = 4, + DIVIDE = 5 +}; + +// flags to interdict respective status changes +enum class FontChangeMask { + None = 0x0000, + Face = 0x0001, + Size = 0x0002, + Bold = 0x0004, + Italic = 0x0008, + Color = 0x0010, + Phantom = 0x0020 +}; + +namespace o3tl +{ + template<> struct typed_flags<FontChangeMask> : is_typed_flags<FontChangeMask, 0x003f> {}; +} + + +class SmVisitor; +class SmDocShell; +class SmNode; +class SmStructureNode; + +typedef std::vector< SmNode * > SmNodeArray; + +enum class SmScaleMode +{ + None, + Width, + Height +}; + +enum class SmNodeType +{ +/* 0*/ Table, Brace, Bracebody, Oper, Align, +/* 5*/ Attribut, Font, UnHor, BinHor, BinVer, +/*10*/ BinDiagonal, SubSup, Matrix, Place, Text, +/*15*/ Special, GlyphSpecial, Math, Blank, Error, +/*20*/ Line, Expression, PolyLine, Root, RootSymbol, +/*25*/ Rectangle, VerticalBrace, MathIdent +}; + + +class SmNode : public SmRect +{ + SmFace maFace; + + SmToken maNodeToken; + SmNodeType meType; + SmScaleMode meScaleMode; + RectHorAlign meRectHorAlign; + FontChangeMask mnFlags; + FontAttribute mnAttributes; + bool mbIsPhantom; + bool mbIsSelected; + // index in accessible text; -1 if not (yet) applicable + sal_Int32 mnAccIndex; + +protected: + SmNode(SmNodeType eNodeType, const SmToken &rNodeToken); + +public: + SmNode(const SmNode&) = delete; + SmNode& operator=(const SmNode&) = delete; + + virtual ~SmNode(); + + /** + * Returns true if this is an instance of SmVisibleNode's subclass, false otherwise. + */ + virtual bool IsVisible() const = 0; + + virtual size_t GetNumSubNodes() const = 0; + virtual SmNode * GetSubNode(size_t nIndex) = 0; + const SmNode * GetSubNode(size_t nIndex) const + { + return const_cast<SmNode *>(this)->GetSubNode(nIndex); + } + + virtual const SmNode * GetLeftMost() const; + + FontChangeMask &Flags() { return mnFlags; } + FontAttribute &Attributes() { return mnAttributes; } + + bool IsPhantom() const { return mbIsPhantom; } + void SetPhantom(bool bIsPhantom); + void SetColor(const Color &rColor); + + void SetAttribut(FontAttribute nAttrib); + void ClearAttribut(FontAttribute nAttrib); + + const SmFace & GetFont() const { return maFace; }; + SmFace & GetFont() { return maFace; }; + + void SetFont(const SmFace &rFace); + void SetFontSize(const Fraction &rRelSize, FontSizeType nType); + void SetSize(const Fraction &rScale); + + /** Prepare preliminary settings about font and text + * (e.g. maFace, meRectHorAlign, mnFlags, mnAttributes, etc.) + */ + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth); + void PrepareAttributes(); + + void SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree = true ); + RectHorAlign GetRectHorAlign() const { return meRectHorAlign; } + + const SmRect & GetRect() const { return *this; } + + void Move(const Point &rPosition); + void MoveTo(const Point &rPosition) { Move(rPosition - GetTopLeft()); } + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) = 0; + virtual void CreateTextFromNode(OUStringBuffer &rText); + + virtual void GetAccessibleText( OUStringBuffer &rText ) const = 0; + sal_Int32 GetAccessibleIndex() const { return mnAccIndex; } + void SetAccessibleIndex(sal_Int32 nAccIndex) { mnAccIndex = nAccIndex; } + const SmNode * FindNodeWithAccessibleIndex(sal_Int32 nAccIndex) const; + + sal_uInt16 GetRow() const { return sal::static_int_cast<sal_uInt16>(maNodeToken.nRow); } + sal_uInt16 GetColumn() const { return sal::static_int_cast<sal_uInt16>(maNodeToken.nCol); } + + SmScaleMode GetScaleMode() const { return meScaleMode; } + void SetScaleMode(SmScaleMode eMode) { meScaleMode = eMode; } + + virtual void AdaptToX(OutputDevice &rDev, sal_uLong nWidth); + virtual void AdaptToY(OutputDevice &rDev, sal_uLong nHeight); + + SmNodeType GetType() const { return meType; } + const SmToken & GetToken() const { return maNodeToken; } + + const SmNode * FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const; + const SmNode * FindRectClosestTo(const Point &rPoint) const; + + /** Accept a visitor + * Calls the method for this class on the visitor + */ + virtual void Accept(SmVisitor* pVisitor) = 0; + + /** True if this node is selected */ + bool IsSelected() const {return mbIsSelected;} + void SetSelected(bool Selected) {mbIsSelected = Selected;} + + /** Get the parent node of this node */ + SmStructureNode* GetParent(){ return mpParentNode; } + const SmStructureNode* GetParent() const { return mpParentNode; } + /** Set the parent node */ + void SetParent(SmStructureNode* parent){ + mpParentNode = parent; + } + + /** Set the token for this node */ + void SetToken(SmToken const & token){ + maNodeToken = token; + } + +private: + SmStructureNode* mpParentNode; +}; + + +/** Abstract baseclass for all composite node + * + * Subclasses of this class can have subnodes. Nodes that doesn't derivate from + * this class does not have subnodes. + */ +class SmStructureNode : public SmNode +{ + SmNodeArray maSubNodes; + +protected: + SmStructureNode(SmNodeType eNodeType, const SmToken &rNodeToken, size_t nSize = 0) + : SmNode(eNodeType, rNodeToken) + , maSubNodes(nSize) + {} + +public: + virtual ~SmStructureNode() override; + + virtual bool IsVisible() const override; + + virtual size_t GetNumSubNodes() const override; + + using SmNode::GetSubNode; + virtual SmNode * GetSubNode(size_t nIndex) override; + void ClearSubNodes(); + void SetSubNodes(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird = nullptr); + void SetSubNodes(SmNodeArray&& rNodeArray); + + virtual void GetAccessibleText( OUStringBuffer &rText ) const override; + + SmNodeArray::iterator begin() {return maSubNodes.begin();} + SmNodeArray::iterator end() {return maSubNodes.end();} + SmNodeArray::reverse_iterator rbegin() {return maSubNodes.rbegin();} + SmNodeArray::reverse_iterator rend() {return maSubNodes.rend();} + + /** Get the index of a child node + * + * Returns -1, if pSubNode isn't a subnode of this. + */ + int IndexOfSubNode(SmNode const * pSubNode) + { + size_t nSize = GetNumSubNodes(); + for (size_t i = 0; i < nSize; i++) + if (pSubNode == GetSubNode(i)) + return i; + return -1; + } + + void SetSubNode(size_t nIndex, SmNode* pNode) + { + size_t size = maSubNodes.size(); + if (size <= nIndex) + { + //Resize subnodes array + maSubNodes.resize(nIndex + 1); + //Set new slots to NULL except at nIndex + for (size_t i = size; i < nIndex; i++) + maSubNodes[i] = nullptr; + } + maSubNodes[nIndex] = pNode; + if (pNode) + pNode->SetParent(this); + } + +private: + /** Sets parent on children of this node */ + void ClaimPaternity(); +}; + + +/** Abstract base class for all visible node + * + * Nodes that doesn't derivate from this class doesn't draw anything, but their + * children. + */ +class SmVisibleNode : public SmNode +{ +protected: + SmVisibleNode(SmNodeType eNodeType, const SmToken &rNodeToken) + : SmNode(eNodeType, rNodeToken) + {} + +public: + + virtual bool IsVisible() const override; + virtual size_t GetNumSubNodes() const override; + using SmNode::GetSubNode; + virtual SmNode * GetSubNode(size_t nIndex) override; +}; + + +class SmGraphicNode : public SmVisibleNode +{ +protected: + SmGraphicNode(SmNodeType eNodeType, const SmToken &rNodeToken) + : SmVisibleNode(eNodeType, rNodeToken) + {} + +public: + + virtual void GetAccessibleText( OUStringBuffer &rText ) const override; +}; + + +/** Draws a rectangle + * + * Used for drawing the line in the OVER and OVERSTRIKE commands. + */ +class SmRectangleNode final : public SmGraphicNode +{ + Size maToSize; + +public: + explicit SmRectangleNode(const SmToken &rNodeToken) + : SmGraphicNode(SmNodeType::Rectangle, rNodeToken) + {} + + virtual void AdaptToX(OutputDevice &rDev, sal_uLong nWidth) override; + virtual void AdaptToY(OutputDevice &rDev, sal_uLong nHeight) override; + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + void CreateTextFromNode(OUStringBuffer &rText) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Polygon line node + * + * Used to draw the slash of the WIDESLASH command by SmBinDiagonalNode. + */ +class SmPolyLineNode final : public SmGraphicNode +{ + tools::Polygon maPoly; + Size maToSize; + long mnWidth; + +public: + explicit SmPolyLineNode(const SmToken &rNodeToken); + + long GetWidth() const { return mnWidth; } + tools::Polygon &GetPolygon() { return maPoly; } + + virtual void AdaptToX(OutputDevice &rDev, sal_uLong nWidth) override; + virtual void AdaptToY(OutputDevice &rDev, sal_uLong nHeight) override; + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Text node + * + * @remarks This class also serves as baseclass for all nodes that contains text. + */ +class SmTextNode : public SmVisibleNode +{ + OUString maText; + sal_uInt16 mnFontDesc; + /** Index within text where the selection starts + * @remarks Only valid if SmNode::IsSelected() is true + */ + sal_Int32 mnSelectionStart; + /** Index within text where the selection ends + * @remarks Only valid if SmNode::IsSelected() is true + */ + sal_Int32 mnSelectionEnd; + +protected: + SmTextNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP ); + +public: + SmTextNode(const SmToken &rNodeToken, sal_uInt16 nFontDescP ); + + sal_uInt16 GetFontDesc() const { return mnFontDesc; } + void SetText(const OUString &rText) { maText = rText; } + const OUString & GetText() const { return maText; } + /** Change the text of this node, including the underlying token */ + void ChangeText(const OUString &rText) { + maText = rText; + SmToken token = GetToken(); + token.aText = rText; + SetToken(token); //TODO: Merge this with AdjustFontDesc for better performance + AdjustFontDesc(); + } + /** Try to guess the correct FontDesc, used during visual editing */ + void AdjustFontDesc(); + /** Index within GetText() where the selection starts + * @remarks Only valid of SmNode::IsSelected() is true + */ + sal_Int32 GetSelectionStart() const {return mnSelectionStart;} + /** Index within GetText() where the selection end + * @remarks Only valid of SmNode::IsSelected() is true + */ + sal_Int32 GetSelectionEnd() const {return mnSelectionEnd;} + /** Set the index within GetText() where the selection starts */ + void SetSelectionStart(sal_Int32 index) {mnSelectionStart = index;} + /** Set the index within GetText() where the selection end */ + void SetSelectionEnd(sal_Int32 index) {mnSelectionEnd = index;} + + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth) override; + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + virtual void CreateTextFromNode(OUStringBuffer &rText) override; + + virtual void GetAccessibleText( OUStringBuffer &rText ) const override; + void Accept(SmVisitor* pVisitor) override; + /** + Converts the character from StarMath's private area symbols to a matching Unicode + character, if necessary. To be used when converting GetText() to a normal text. + */ + static sal_Unicode ConvertSymbolToUnicode(sal_Unicode nIn); +}; + + +/** Special node for user defined characters + * + * Node used for pre- and user-defined characters from: + * officecfg/registry/data/org/openoffice/Office/Math.xcu + * + * This is just single characters, I think. + */ +class SmSpecialNode : public SmTextNode +{ + bool mbIsFromGreekSymbolSet; + +protected: + SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc); + +public: + explicit SmSpecialNode(const SmToken &rNodeToken); + + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth) override; + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Glyph node for custom operators + * + * This node is used with commands: oper, uoper and boper. + * E.g. in "A boper op B", "op" will be an instance of SmGlyphSpecialNode. + * "boper" simply interprets "op", the following token, as a binary operator. + * The command "uoper" interprets the following token as unary operator. + * For these commands an instance of SmGlyphSpecialNode is used for the + * operator token, following the command. + */ +class SmGlyphSpecialNode final : public SmSpecialNode +{ +public: + explicit SmGlyphSpecialNode(const SmToken &rNodeToken) + : SmSpecialNode(SmNodeType::GlyphSpecial, rNodeToken, FNT_MATH) + {} + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Math symbol node + * + * Use for math symbols such as plus, minus and integral in the INT command. + */ +class SmMathSymbolNode : public SmSpecialNode +{ +protected: + SmMathSymbolNode(SmNodeType eNodeType, const SmToken &rNodeToken) + : SmSpecialNode(eNodeType, rNodeToken, FNT_MATH) + { + sal_Unicode cChar = GetToken().cMathChar; + if (u'\0' != cChar) + SetText(OUString(cChar)); + } + +public: + explicit SmMathSymbolNode(const SmToken &rNodeToken); + + virtual void AdaptToX(OutputDevice &rDev, sal_uLong nWidth) override; + virtual void AdaptToY(OutputDevice &rDev, sal_uLong nHeight) override; + + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth) override; + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void CreateTextFromNode(OUStringBuffer &rText) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Math Identifier + * + * This behaves essentially the same as SmMathSymbolNode and is only used to + * represent math symbols that should be exported as <mi> elements rather than + * <mo> elements. + */ +class SmMathIdentifierNode final : public SmMathSymbolNode +{ +public: + explicit SmMathIdentifierNode(const SmToken &rNodeToken) + : SmMathSymbolNode(SmNodeType::MathIdent, rNodeToken) {} +}; + + +/** Root symbol node + * + * Root symbol node used by SmRootNode to create the root symbol, in front of + * the line with the line above. I don't think this node should be used for + * anything else. + */ +class SmRootSymbolNode final : public SmMathSymbolNode +{ + sal_uLong mnBodyWidth; // width of body (argument) of root sign + +public: + explicit SmRootSymbolNode(const SmToken &rNodeToken) + : SmMathSymbolNode(SmNodeType::RootSymbol, rNodeToken) + , mnBodyWidth(0) + { + } + + sal_uLong GetBodyWidth() const {return mnBodyWidth;}; + virtual void AdaptToX(OutputDevice &rDev, sal_uLong nHeight) override; + virtual void AdaptToY(OutputDevice &rDev, sal_uLong nHeight) override; + + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Place node + * + * Used to create the <?> command, that denotes place where something can be + * written. + * It is drawn as a square with a shadow. + */ +class SmPlaceNode final : public SmMathSymbolNode +{ +public: + explicit SmPlaceNode(const SmToken &rNodeToken) + : SmMathSymbolNode(SmNodeType::Place, rNodeToken) + { + } + SmPlaceNode() : SmMathSymbolNode(SmNodeType::Place, SmToken(TPLACE, MS_PLACE, "<?>")) {}; + + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth) override; + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Error node, for parsing errors + * + * This node is used for parsing errors and draws a questionmark turned upside + * down (inverted question mark). + */ +class SmErrorNode final : public SmMathSymbolNode +{ +public: + explicit SmErrorNode(const SmToken &rNodeToken) + : SmMathSymbolNode(SmNodeType::Error, rNodeToken) + { + SetText(OUString(MS_ERROR)); + } + + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth) override; + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Table node + * + * This is the root node for the formula tree. This node is also used for the + * STACK and BINOM commands. When used for root node, its + * children are instances of SmLineNode, and in some obscure cases the child + * can be an instance of SmExpressionNode, mainly when errors occur. + */ +class SmTableNode final : public SmStructureNode +{ + long mnFormulaBaseline; +public: + explicit SmTableNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Table, rNodeToken) + , mnFormulaBaseline(0) + { + } + + virtual const SmNode * GetLeftMost() const override; + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + long GetFormulaBaseline() const; + + void Accept(SmVisitor* pVisitor) override; +}; + + +/** A line + * + * Used as child of SmTableNode when the SmTableNode is the root node of the + * formula tree. + */ +class SmLineNode : public SmStructureNode +{ + bool mbUseExtraSpaces; + +protected: + SmLineNode(SmNodeType eNodeType, const SmToken &rNodeToken) + : SmStructureNode(eNodeType, rNodeToken) + , mbUseExtraSpaces(true) + { + } + +public: + explicit SmLineNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Line, rNodeToken) + , mbUseExtraSpaces(true) + { + } + + void SetUseExtraSpaces(bool bVal) { mbUseExtraSpaces = bVal; } + bool IsUseExtraSpaces() const { return mbUseExtraSpaces; }; + + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth) override; + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Expression node + * + * Used whenever you have an expression such as "A OVER {B + C}", here there is + * an expression node that allows "B + C" to be the denominator of the + * SmBinVerNode, that the OVER command creates. + */ +class SmExpressionNode final : public SmLineNode +{ +public: + explicit SmExpressionNode(const SmToken &rNodeToken) + : SmLineNode(SmNodeType::Expression, rNodeToken) + {} + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void CreateTextFromNode(OUStringBuffer &rText) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Unary horizontal node + * + * The same as SmBinHorNode except this is for unary operators. + */ +class SmUnHorNode final : public SmStructureNode +{ +public: + explicit SmUnHorNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::UnHor, rNodeToken, 2) + { + } + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Root node + * + * Used for create square roots and other roots, example: + * \f$ \sqrt[\mbox{[Argument]}]{\mbox{[Body]}} \f$. + * + * Children:<BR> + * 0: Argument (optional)<BR> + * 1: Symbol (instance of SmRootSymbolNode)<BR> + * 2: Body<BR> + * Where argument is optional and may be NULL. + */ +class SmRootNode final : public SmStructureNode +{ +public: + explicit SmRootNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Root, rNodeToken, 3) + { + } + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void CreateTextFromNode(OUStringBuffer &rText) override; + void Accept(SmVisitor* pVisitor) override; + + SmNode* Argument(); + const SmNode* Argument() const; + SmRootSymbolNode* Symbol(); + const SmRootSymbolNode* Symbol() const; + SmNode* Body(); + const SmNode* Body() const; +}; + + +/** Binary horizontal node + * + * This node is used for binary operators. In a formula such as "A + B". + * + * Children:<BR> + * 0: Left operand<BR> + * 1: Binary operator<BR> + * 2: Right operand<BR> + * + * None of the children may be NULL. + */ +class SmBinHorNode final : public SmStructureNode +{ +public: + explicit SmBinHorNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::BinHor, rNodeToken, 3) + { + } + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; + + SmNode* Symbol(); + const SmNode* Symbol() const; + SmNode* LeftOperand(); + const SmNode* LeftOperand() const; + SmNode* RightOperand(); + const SmNode* RightOperand() const; +}; + + +/** Binary horizontal node + * + * This node is used for creating the OVER command, consider the formula: + * "numerator OVER denominator", which looks like + * \f$ \frac{\mbox{numerator}}{\mbox{denominator}} \f$ + * + * Children:<BR> + * 0: Numerator<BR> + * 1: Line (instance of SmRectangleNode)<BR> + * 2: Denominator<BR> + * None of the children may be NULL. + */ +class SmBinVerNode final : public SmStructureNode +{ +public: + explicit SmBinVerNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::BinVer, rNodeToken, 3) + { + } + + virtual const SmNode * GetLeftMost() const override; + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void CreateTextFromNode(OUStringBuffer &rText) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Binary diagonal node + * + * Used for implementing the WIDESLASH command, example: "A WIDESLASH B". + * + * Children:<BR> + * 0: Left operand<BR> + * 1: right operand<BR> + * 2: Line (instance of SmPolyLineNode).<BR> + * None of the children may be NULL. + */ +class SmBinDiagonalNode final : public SmStructureNode +{ + bool mbAscending; + + void GetOperPosSize(Point &rPos, Size &rSize, + const Point &rDiagPoint, double fAngleDeg) const; + +public: + explicit SmBinDiagonalNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::BinDiagonal, rNodeToken, 3) + , mbAscending(false) + { + } + + bool IsAscending() const { return mbAscending; } + void SetAscending(bool bVal) { mbAscending = bVal; } + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Enum used to index sub-/supscripts in the 'maSubNodes' array + * in 'SmSubSupNode' + * + * See graphic for positions at char: + * + * \code + * CSUP + * + * LSUP H H RSUP + * H H + * HHHH + * H H + * LSUB H H RSUB + * + * CSUB + * \endcode + */ +enum SmSubSup +{ CSUB, CSUP, RSUB, RSUP, LSUB, LSUP +}; + +/** numbers of entries in the above enum (that is: the number of possible + * sub-/supscripts) + */ +#define SUBSUP_NUM_ENTRIES 6 + +/** Super- and subscript node + * + * Used for creating super- and subscripts for commands such as: + * "^", "_", "lsup", "lsub", "csup" and "csub". + * Example: "A^2" which looks like: \f$ A^2 \f$ + * + * This node is also used for creating limits on SmOperNode, when + * "FROM" and "TO" commands are used with "INT", "SUM" or similar. + * + * Children of this node can be enumerated using the SmSubSup enum. + * Please note that children may be NULL, except for the body. + * It is recommended that you access children using GetBody() and + * GetSubSup(). + */ +class SmSubSupNode final : public SmStructureNode +{ + bool mbUseLimits; + +public: + explicit SmSubSupNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::SubSup, rNodeToken, 1 + SUBSUP_NUM_ENTRIES) + , mbUseLimits(false) + { + } + + /** Get body (Not NULL) */ + SmNode * GetBody() { return GetSubNode(0); } + /** Get body (Not NULL) */ + const SmNode * GetBody() const + { + return const_cast<SmSubSupNode *>(this)->GetBody(); + } + + void SetUseLimits(bool bVal) { mbUseLimits = bVal; } + bool IsUseLimits() const { return mbUseLimits; }; + + /** Get super- or subscript + * @remarks this method may return NULL. + */ + SmNode * GetSubSup(SmSubSup eSubSup) { return GetSubNode(1 + eSubSup); }; + const SmNode * GetSubSup(SmSubSup eSubSup) const { return const_cast< SmSubSupNode* >( this )->GetSubSup( eSubSup ); } + + /** Set the body */ + void SetBody(SmNode* pBody) { SetSubNode(0, pBody); } + void SetSubSup(SmSubSup eSubSup, SmNode* pScript) { SetSubNode( 1 + eSubSup, pScript); } + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void CreateTextFromNode(OUStringBuffer &rText) override; + void Accept(SmVisitor* pVisitor) override; + +}; + + +/** Node for brace construction + * + * Used for "lbrace [body] rbrace" and similar constructions. + * Should look like \f$ \{\mbox{[body]}\} \f$ + * + * Children:<BR> + * 0: Opening brace<BR> + * 1: Body (usually SmBracebodyNode)<BR> + * 2: Closing brace<BR> + * None of the children can be NULL. + * + * Note that child 1 (Body) is usually SmBracebodyNode, but it can also be e.g. SmExpressionNode. + */ +class SmBraceNode final : public SmStructureNode +{ +public: + explicit SmBraceNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Brace, rNodeToken, 3) + { + } + + SmMathSymbolNode* OpeningBrace(); + const SmMathSymbolNode* OpeningBrace() const; + SmNode* Body(); + const SmNode* Body() const; + SmMathSymbolNode* ClosingBrace(); + const SmMathSymbolNode* ClosingBrace() const; + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void CreateTextFromNode(OUStringBuffer &rText) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Body of an SmBraceNode + * + * This usually only has one child an SmExpressionNode, however, it can also + * have other children. + * Consider the formula "lbrace [body1] mline [body2] rbrace", looks like: + * \f$ \{\mbox{[body1] | [body2]}\} \f$. + * In this case SmBracebodyNode will have three children, "[body1]", "|" and + * [body2]. + */ +class SmBracebodyNode final : public SmStructureNode +{ + long mnBodyHeight; + +public: + explicit SmBracebodyNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Bracebody, rNodeToken) + , mnBodyHeight(0) + { + } + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + long GetBodyHeight() const { return mnBodyHeight; } + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Node for vertical brace construction + * + * Used to implement commands "[body] underbrace [script]" and + * "[body] overbrace [script]". + * Underbrace should look like this \f$ \underbrace{\mbox{body}}_{\mbox{script}}\f$. + * + * Children:<BR> + * 0: body<BR> + * 1: brace<BR> + * 2: script<BR> + * (None of these children are optional, e.g. they must all be not NULL). + */ +class SmVerticalBraceNode final : public SmStructureNode +{ +public: + explicit inline SmVerticalBraceNode(const SmToken &rNodeToken); + + SmNode* Body(); + const SmNode* Body() const; + SmMathSymbolNode* Brace(); + const SmMathSymbolNode* Brace() const; + SmNode* Script(); + const SmNode* Script() const; + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +inline SmVerticalBraceNode::SmVerticalBraceNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::VerticalBrace, rNodeToken, 3) +{ +} + + +/** Operation Node + * + * Used for commands like SUM, INT and similar. + * + * Children:<BR> + * 0: Operation (instance of SmMathSymbolNode or SmSubSupNode)<BR> + * 1: Body<BR> + * None of the children may be NULL. + * + */ +class SmOperNode final : public SmStructureNode +{ +public: + explicit SmOperNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Oper, rNodeToken, 2) + { + } + + SmNode * GetSymbol(); + const SmNode * GetSymbol() const + { + return const_cast<SmOperNode *>(this)->GetSymbol(); + } + + long CalcSymbolHeight(const SmNode &rSymbol, const SmFormat &rFormat) const; + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Node used for alignment + * + * This node has exactly one child at index 0. + */ +class SmAlignNode final : public SmStructureNode +{ +public: + explicit SmAlignNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Align, rNodeToken) + {} + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Attribute node + * + * Used to give an attribute to another node. Used for commands such as: + * UNDERLINE, OVERLINE, OVERSTRIKE, WIDEVEC, WIDEHARPOON, WIDEHAT and WIDETILDE. + * + * Children:<BR> + * 0: Attribute<BR> + * 1: Body<BR> + * None of these may be NULL. + */ +class SmAttributNode final : public SmStructureNode +{ +public: + explicit SmAttributNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Attribut, rNodeToken, 2) + {} + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void CreateTextFromNode(OUStringBuffer &rText) override; + void Accept(SmVisitor* pVisitor) override; + + SmNode* Attribute(); + const SmNode* Attribute() const; + SmNode* Body(); + const SmNode* Body() const; +}; + + +/** Font node + * + * Used to change the font of its children. + */ +class SmFontNode final : public SmStructureNode +{ + FontSizeType meSizeType; + Fraction maFontSize; + +public: + explicit SmFontNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Font, rNodeToken) + , meSizeType(FontSizeType::MULTIPLY) + , maFontSize(1) + { + } + + void SetSizeParameter(const Fraction &rValue, FontSizeType nType); + const Fraction & GetSizeParameter() const {return maFontSize;} + FontSizeType GetSizeType() const {return meSizeType;} + + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth) override; + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void CreateTextFromNode(OUStringBuffer &rText) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Matrix node + * + * Used to implement the MATRIX command, example: + * "matrix{ 1 # 2 ## 3 # 4}". + */ +class SmMatrixNode final : public SmStructureNode +{ + sal_uInt16 mnNumRows, + mnNumCols; + +public: + explicit SmMatrixNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Matrix, rNodeToken) + , mnNumRows(0) + , mnNumCols(0) + { + } + + sal_uInt16 GetNumRows() const {return mnNumRows;} + sal_uInt16 GetNumCols() const {return mnNumCols;} + void SetRowCol(sal_uInt16 nMatrixRows, sal_uInt16 nMatrixCols); + + virtual const SmNode * GetLeftMost() const override; + + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void CreateTextFromNode(OUStringBuffer &rText) override; + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Node for whitespace + * + * Used to implement the commands "~" and "`". This node is just a blank space. + */ +class SmBlankNode final : public SmGraphicNode +{ + sal_uInt16 mnNum; + +public: + explicit SmBlankNode(const SmToken &rNodeToken) + : SmGraphicNode(SmNodeType::Blank, rNodeToken) + , mnNum(0) + { + } + + void IncreaseBy(const SmToken &rToken, sal_uInt32 nMultiplyBy = 1); + void Clear() { mnNum = 0; } + sal_uInt16 GetBlankNum() const { return mnNum; } + void SetBlankNum(sal_uInt16 nNumber) { mnNum = nNumber; } + + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth) override; + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + void Accept(SmVisitor* pVisitor) override; + virtual void CreateTextFromNode(OUStringBuffer &rText) override; +}; + + +inline SmNode* SmRootNode::Argument() +{ + assert( GetNumSubNodes() == 3 ); + return GetSubNode( 0 ); +} +inline const SmNode* SmRootNode::Argument() const +{ + return const_cast< SmRootNode* >( this )->Argument(); +} +inline SmRootSymbolNode* SmRootNode::Symbol() +{ + assert( GetNumSubNodes() == 3 ); + assert( GetSubNode( 1 )->GetType() == SmNodeType::RootSymbol ); + return static_cast< SmRootSymbolNode* >( GetSubNode( 1 )); +} +inline const SmRootSymbolNode* SmRootNode::Symbol() const +{ + return const_cast< SmRootNode* >( this )->Symbol(); +} +inline SmNode* SmRootNode::Body() +{ + assert( GetNumSubNodes() == 3 ); + return GetSubNode( 2 ); +} +inline const SmNode* SmRootNode::Body() const +{ + return const_cast< SmRootNode* >( this )->Body(); +} + + +inline SmNode* SmBinHorNode::Symbol() +{ + assert( GetNumSubNodes() == 3 ); + return GetSubNode( 1 ); +} +inline const SmNode* SmBinHorNode::Symbol() const +{ + return const_cast< SmBinHorNode* >( this )->Symbol(); +} +inline SmNode* SmBinHorNode::LeftOperand() +{ + assert( GetNumSubNodes() == 3 ); + return GetSubNode( 0 ); +} +inline const SmNode* SmBinHorNode::LeftOperand() const +{ + return const_cast< SmBinHorNode* >( this )->LeftOperand(); +} +inline SmNode* SmBinHorNode::RightOperand() +{ + assert( GetNumSubNodes() == 3 ); + return GetSubNode( 2 ); +} +inline const SmNode* SmBinHorNode::RightOperand() const +{ + return const_cast< SmBinHorNode* >( this )->RightOperand(); +} + +inline SmNode* SmAttributNode::Attribute() +{ + assert( GetNumSubNodes() == 2 ); + return GetSubNode( 0 ); +} +inline const SmNode* SmAttributNode::Attribute() const +{ + return const_cast< SmAttributNode* >( this )->Attribute(); +} +inline SmNode* SmAttributNode::Body() +{ + assert( GetNumSubNodes() == 2 ); + return GetSubNode( 1 ); +} +inline const SmNode* SmAttributNode::Body() const +{ + return const_cast< SmAttributNode* >( this )->Body(); +} + +inline SmMathSymbolNode* SmBraceNode::OpeningBrace() +{ + assert( GetNumSubNodes() == 3 ); + assert( GetSubNode( 0 )->GetType() == SmNodeType::Math ); + return static_cast< SmMathSymbolNode* >( GetSubNode( 0 )); +} +inline const SmMathSymbolNode* SmBraceNode::OpeningBrace() const +{ + return const_cast< SmBraceNode* >( this )->OpeningBrace(); +} +inline SmNode* SmBraceNode::Body() +{ + assert( GetNumSubNodes() == 3 ); + return GetSubNode( 1 ); +} +inline const SmNode* SmBraceNode::Body() const +{ + return const_cast< SmBraceNode* >( this )->Body(); +} +inline SmMathSymbolNode* SmBraceNode::ClosingBrace() +{ + assert( GetNumSubNodes() == 3 ); + assert( GetSubNode( 2 )->GetType() == SmNodeType::Math ); + return static_cast< SmMathSymbolNode* >( GetSubNode( 2 )); +} +inline const SmMathSymbolNode* SmBraceNode::ClosingBrace() const +{ + return const_cast< SmBraceNode* >( this )->ClosingBrace(); +} + +inline SmNode* SmVerticalBraceNode::Body() +{ + assert( GetNumSubNodes() == 3 ); + return GetSubNode( 0 ); +} +inline const SmNode* SmVerticalBraceNode::Body() const +{ + return const_cast< SmVerticalBraceNode* >( this )->Body(); +} +inline SmMathSymbolNode* SmVerticalBraceNode::Brace() +{ + assert( GetNumSubNodes() == 3 ); + assert( GetSubNode( 1 )->GetType() == SmNodeType::Math ); + return static_cast< SmMathSymbolNode* >( GetSubNode( 1 )); +} +inline const SmMathSymbolNode* SmVerticalBraceNode::Brace() const +{ + return const_cast< SmVerticalBraceNode* >( this )->Brace(); +} +inline SmNode* SmVerticalBraceNode::Script() +{ + assert( GetNumSubNodes() == 3 ); + return GetSubNode( 2 ); +} +inline const SmNode* SmVerticalBraceNode::Script() const +{ + return const_cast< SmVerticalBraceNode* >( this )->Script(); +} + +#endif + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/parse.hxx b/starmath/inc/parse.hxx new file mode 100644 index 000000000..519a90041 --- /dev/null +++ b/starmath/inc/parse.hxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_PARSE_HXX +#define INCLUDED_STARMATH_INC_PARSE_HXX + +#include <unotools/charclass.hxx> +#include <memory> +#include <set> +#include <vector> + +#include "token.hxx" +#include "error.hxx" + +class SmBlankNode; +class SmBracebodyNode; +class SmExpressionNode; +class SmGlyphSpecialNode; +class SmNode; +class SmOperNode; +class SmSpecialNode; +class SmStructureNode; +class SmTableNode; +class SmTextNode; + +#define DEPTH_LIMIT 1024 + +class SmParser +{ + OUString m_aBufferString; + SmToken m_aCurToken; + std::vector<std::unique_ptr<SmErrorDesc>> m_aErrDescList; + int m_nCurError; + sal_Int32 m_nBufferIndex, + m_nTokenIndex; + sal_Int32 m_nRow, // 1-based + m_nColOff; // 0-based + bool m_bImportSymNames, + m_bExportSymNames; + sal_Int32 m_nParseDepth; + + class DepthProtect + { + private: + sal_Int32& m_rParseDepth; + public: + DepthProtect(sal_Int32& rParseDepth) + : m_rParseDepth(rParseDepth) + { + ++m_rParseDepth; + } + bool TooDeep() const { return m_rParseDepth > DEPTH_LIMIT; } + ~DepthProtect() + { + --m_rParseDepth; + } + }; + + // map of used symbols (used to reduce file size by exporting only actually used symbols) + std::set< OUString > m_aUsedSymbols; + + // CharClass representing a locale for parsing numbers + CharClass m_aNumCC; + // pointer to System locale's CharClass, which is alive inside SM_MOD() + const CharClass* m_pSysCC; + + SmParser(const SmParser&) = delete; + SmParser& operator=(const SmParser&) = delete; + + void NextToken(); + sal_Int32 GetTokenIndex() const { return m_nTokenIndex; } + void Replace( sal_Int32 nPos, sal_Int32 nLen, const OUString &rText ); + + inline bool TokenInGroup( TG nGroup ); + + // grammar + std::unique_ptr<SmTableNode> DoTable(); + std::unique_ptr<SmNode> DoLine(); + std::unique_ptr<SmNode> DoExpression(bool bUseExtraSpaces = true); + std::unique_ptr<SmNode> DoRelation(); + std::unique_ptr<SmNode> DoSum(); + std::unique_ptr<SmNode> DoProduct(); + std::unique_ptr<SmNode> DoSubSup(TG nActiveGroup, SmNode *pGivenNode); + std::unique_ptr<SmNode> DoOpSubSup(); + std::unique_ptr<SmNode> DoPower(); + std::unique_ptr<SmBlankNode> DoBlank(); + std::unique_ptr<SmNode> DoTerm(bool bGroupNumberIdent); + std::unique_ptr<SmNode> DoEscape(); + std::unique_ptr<SmOperNode> DoOperator(); + std::unique_ptr<SmNode> DoOper(); + std::unique_ptr<SmStructureNode> DoUnOper(); + std::unique_ptr<SmNode> DoAlign(bool bUseExtraSpaces = true); + std::unique_ptr<SmStructureNode> DoFontAttribut(); + std::unique_ptr<SmStructureNode> DoAttribut(); + std::unique_ptr<SmStructureNode> DoFont(); + std::unique_ptr<SmStructureNode> DoFontSize(); + std::unique_ptr<SmStructureNode> DoColor(); + std::unique_ptr<SmStructureNode> DoBrace(); + std::unique_ptr<SmBracebodyNode> DoBracebody(bool bIsLeftRight); + std::unique_ptr<SmTextNode> DoFunction(); + std::unique_ptr<SmTableNode> DoBinom(); + std::unique_ptr<SmStructureNode> DoStack(); + std::unique_ptr<SmStructureNode> DoMatrix(); + std::unique_ptr<SmSpecialNode> DoSpecial(); + std::unique_ptr<SmGlyphSpecialNode> DoGlyphSpecial(); + std::unique_ptr<SmExpressionNode> DoError(SmParseError Error); + // end of grammar + +public: + SmParser(); + + /** Parse rBuffer to formula tree */ + std::unique_ptr<SmTableNode> Parse(const OUString &rBuffer); + /** Parse rBuffer to formula subtree that constitutes an expression */ + std::unique_ptr<SmNode> ParseExpression(const OUString &rBuffer); + + const OUString & GetText() const { return m_aBufferString; }; + + bool IsImportSymbolNames() const { return m_bImportSymNames; } + void SetImportSymbolNames(bool bVal) { m_bImportSymNames = bVal; } + bool IsExportSymbolNames() const { return m_bExportSymNames; } + void SetExportSymbolNames(bool bVal) { m_bExportSymNames = bVal; } + + void AddError(SmParseError Type, SmNode *pNode); + const SmErrorDesc* NextError(); + const SmErrorDesc* PrevError(); + const SmErrorDesc* GetError(); + static const SmTokenTableEntry* GetTokenTableEntry( const OUString &rName ); + const std::set< OUString >& GetUsedSymbols() const { return m_aUsedSymbols; } +}; + + +inline bool SmParser::TokenInGroup( TG nGroup) +{ + return bool(m_aCurToken.nGroup & nGroup); +} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/pch/precompiled_sm.cxx b/starmath/inc/pch/precompiled_sm.cxx new file mode 100644 index 000000000..f5d78e96f --- /dev/null +++ b/starmath/inc/pch/precompiled_sm.cxx @@ -0,0 +1,12 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 "precompiled_sm.hxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/pch/precompiled_sm.hxx b/starmath/inc/pch/precompiled_sm.hxx new file mode 100644 index 000000000..f1239ec47 --- /dev/null +++ b/starmath/inc/pch/precompiled_sm.hxx @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + This file has been autogenerated by update_pch.sh. It is possible to edit it + manually (such as when an include file has been moved/renamed/removed). All such + manual changes will be rewritten by the next run of update_pch.sh (which presumably + also fixes all possible problems, so it's usually better to use it). + + Generated on 2020-04-25 20:55:30 using: + ./bin/update_pch starmath sm --cutoff=5 --exclude:system --exclude:module --include:local + + If after updating build fails, use the following command to locate conflicting headers: + ./bin/update_pch_bisect ./starmath/inc/pch/precompiled_sm.hxx "make starmath.build" --find-conflicts +*/ + +#if PCH_LEVEL >= 1 +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstring> +#include <functional> +#include <limits.h> +#include <limits> +#include <list> +#include <map> +#include <memory> +#include <new> +#include <optional> +#include <ostream> +#include <string_view> +#include <type_traits> +#include <utility> +#include <vector> +#include <boost/property_tree/ptree_fwd.hpp> +#endif // PCH_LEVEL >= 1 +#if PCH_LEVEL >= 2 +#include <osl/diagnose.h> +#include <osl/endian.h> +#include <osl/mutex.hxx> +#include <rtl/alloc.h> +#include <rtl/character.hxx> +#include <rtl/instance.hxx> +#include <rtl/locale.h> +#include <rtl/math.hxx> +#include <rtl/ref.hxx> +#include <rtl/string.hxx> +#include <rtl/stringconcat.hxx> +#include <rtl/stringutils.hxx> +#include <rtl/textenc.h> +#include <rtl/ustrbuf.h> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <rtl/uuid.h> +#include <sal/config.h> +#include <sal/log.hxx> +#include <sal/types.h> +#include <vcl/IDialogRenderable.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/cairo.hxx> +#include <vcl/devicecoordinate.hxx> +#include <vcl/dllapi.h> +#include <vcl/errcode.hxx> +#include <vcl/font.hxx> +#include <vcl/keycod.hxx> +#include <vcl/keycodes.hxx> +#include <vcl/mapmod.hxx> +#include <vcl/metaactiontypes.hxx> +#include <vcl/outdev.hxx> +#include <vcl/outdevmap.hxx> +#include <vcl/outdevstate.hxx> +#include <vcl/region.hxx> +#include <vcl/salnativewidgets.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/uitest/factory.hxx> +#include <vcl/vclenum.hxx> +#include <vcl/vclevent.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/vclreferencebase.hxx> +#include <vcl/virdev.hxx> +#include <vcl/wall.hxx> +#endif // PCH_LEVEL >= 2 +#if PCH_LEVEL >= 3 +#include <basegfx/color/bcolor.hxx> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/vector/b2enums.hxx> +#include <com/sun/star/drawing/LineCap.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <comphelper/comphelperdllapi.h> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <editeng/editdata.hxx> +#include <editeng/editengdllapi.h> +#include <editeng/editstat.hxx> +#include <i18nlangtag/lang.h> +#include <o3tl/cow_wrapper.hxx> +#include <o3tl/strong_int.hxx> +#include <o3tl/typed_flags_set.hxx> +#include <salhelper/simplereferenceobject.hxx> +#include <sfx2/dllapi.h> +#include <sfx2/docfile.hxx> +#include <sfx2/shell.hxx> +#include <sot/formats.hxx> +#include <sot/storage.hxx> +#include <svl/SfxBroadcaster.hxx> +#include <svl/hint.hxx> +#include <svl/itemset.hxx> +#include <svl/languageoptions.hxx> +#include <svl/poolitem.hxx> +#include <svl/stritem.hxx> +#include <svl/svldllapi.h> +#include <svl/typedwhich.hxx> +#include <svtools/colorcfg.hxx> +#include <svx/svxdllapi.h> +#include <tools/color.hxx> +#include <tools/gen.hxx> +#include <tools/lineend.hxx> +#include <tools/link.hxx> +#include <tools/mapunit.hxx> +#include <tools/poly.hxx> +#include <tools/ref.hxx> +#include <tools/solar.h> +#include <tools/toolsdllapi.h> +#include <unotools/configitem.hxx> +#include <unotools/fontdefs.hxx> +#include <unotools/options.hxx> +#include <unotools/unotoolsdllapi.h> +#include <xmloff/dllapi.h> +#endif // PCH_LEVEL >= 3 +#if PCH_LEVEL >= 4 +#include <ElementsDockingWindow.hxx> +#include <dialog.hxx> +#include <document.hxx> +#include <node.hxx> +#include <smmod.hxx> +#include <symbol.hxx> +#include <unomodel.hxx> +#include <utility.hxx> +#include <view.hxx> +#include <visitors.hxx> +#endif // PCH_LEVEL >= 4 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/rect.hxx b/starmath/inc/rect.hxx new file mode 100644 index 000000000..ce390b197 --- /dev/null +++ b/starmath/inc/rect.hxx @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_STARMATH_INC_RECT_HXX +#define INCLUDED_STARMATH_INC_RECT_HXX + +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <tools/gen.hxx> +#include <vcl/outdev.hxx> + +class SmFormat; + + +inline long SmFromTo(long nFrom, long nTo, double fRelDist) +{ + return nFrom + static_cast<long>(fRelDist * (nTo - nFrom)); +} + + +// SmRect +// ... (to be done) +// This Implementation assumes that the x-axis points to the right and the +// y-axis to the bottom. +// Note: however, italic spaces can be negative! + + +// possible positions and alignments for the 'AlignTo' function +enum class RectPos +{ + Left, // align the current object to the left of the argument + Right, + Top, + Bottom, + Attribute +}; + +enum class RectHorAlign +{ + Left, + Center, + Right +}; + +enum class RectVerAlign +{ + Top, + Mid, + Bottom, + Baseline, + CenterY, + AttributeHi, + AttributeMid, + AttributeLo +}; + +// different methods of copying baselines and mid's in 'ExtendBy' function +enum class RectCopyMBL +{ + This, // keep baseline of current object even if it has none + Arg, // as above but for the argument + None, // result will have no baseline + Xor // if current object has a baseline keep it else copy + // the arguments baseline (even if it has none) +}; + + +class SmRect +{ + Point aTopLeft; + Size aSize; + long nBaseline, + nAlignT, + nAlignM, + nAlignB, + nGlyphTop, + nGlyphBottom, + nItalicLeftSpace, + nItalicRightSpace, + nLoAttrFence, + nHiAttrFence; + sal_uInt16 nBorderWidth; + bool bHasBaseline, + bHasAlignInfo; + + inline void CopyMBL(const SmRect& rRect); + void CopyAlignInfo(const SmRect& rRect); + + void Union(const SmRect &rRect); + +public: + SmRect(); + SmRect(const OutputDevice &rDev, const SmFormat *pFormat, + const OUString &rText, sal_uInt16 nBorderWidth); + SmRect(long nWidth, long nHeight); + + + sal_uInt16 GetBorderWidth() const { return nBorderWidth; } + + void SetItalicSpaces(long nLeftSpace, long nRightSpace); + + void SetWidth(sal_uLong nWidth) { aSize.setWidth(nWidth); } + + void SetLeft(long nLeft); + void SetRight(long nRight); + void SetBottom(long nBottom); + void SetTop(long nTop); + + const Point & GetTopLeft() const { return aTopLeft; } + + long GetTop() const { return GetTopLeft().Y(); } + long GetLeft() const { return GetTopLeft().X(); } + long GetBottom() const { return GetTop() + GetHeight() - 1; } + long GetRight() const { return GetLeft() + GetWidth() - 1; } + long GetCenterY() const { return (GetTop() + GetBottom()) / 2; } + long GetWidth() const { return GetSize().Width(); } + long GetHeight() const { return GetSize().Height(); } + + long GetItalicLeftSpace() const { return nItalicLeftSpace; } + long GetItalicRightSpace() const { return nItalicRightSpace; } + + long GetHiAttrFence() const { return nHiAttrFence; } + long GetLoAttrFence() const { return nLoAttrFence; } + + long GetItalicLeft() const { return GetLeft() - GetItalicLeftSpace(); } + long GetItalicCenterX() const { return (GetItalicLeft() + GetItalicRight()) / 2; } + long GetItalicRight() const { return GetRight() + GetItalicRightSpace(); } + long GetItalicWidth() const { return GetWidth() + GetItalicLeftSpace() + GetItalicRightSpace(); } + + bool HasBaseline() const { return bHasBaseline; } + inline long GetBaseline() const; + long GetBaselineOffset() const { return GetBaseline() - GetTop(); } + + long GetAlignT() const { return nAlignT; } + long GetAlignM() const { return nAlignM; } + long GetAlignB() const { return nAlignB; } + + const Size & GetSize() const { return aSize; } + + Size GetItalicSize() const + { return Size(GetItalicWidth(), GetHeight()); } + + void Move (const Point &rPosition); + void MoveTo(const Point &rPosition) { Move(rPosition - GetTopLeft()); } + + bool IsEmpty() const + { + return GetWidth() == 0 || GetHeight() == 0; + } + + bool HasAlignInfo() const { return bHasAlignInfo; } + + Point AlignTo(const SmRect &rRect, RectPos ePos, + RectHorAlign eHor, RectVerAlign eVer) const; + + SmRect & ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode); + void ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode, + long nNewAlignM); + SmRect & ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode, + bool bKeepVerAlignParams); + + long OrientedDist(const Point &rPoint) const; + bool IsInsideRect(const Point &rPoint) const; + bool IsInsideItalicRect(const Point &rPoint) const; + + inline tools::Rectangle AsRectangle() const; + SmRect AsGlyphRect() const; +}; + + +inline void SmRect::SetItalicSpaces(long nLeftSpace, long nRightSpace) + // set extra spacing to the left and right for (italic) + // letters/text +{ + nItalicLeftSpace = nLeftSpace; + nItalicRightSpace = nRightSpace; +} + + +inline void SmRect::CopyMBL(const SmRect &rRect) + // copy AlignM baseline and value of 'rRect' +{ + nBaseline = rRect.nBaseline; + bHasBaseline = rRect.bHasBaseline; + nAlignM = rRect.nAlignM; +} + + +inline long SmRect::GetBaseline() const +{ + SAL_WARN_IF( !HasBaseline(), "starmath", "Baseline does not exist" ); + return nBaseline; +} + + +inline tools::Rectangle SmRect::AsRectangle() const +{ + return tools::Rectangle(Point(GetItalicLeft(), GetTop()), GetItalicSize()); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/smdll.hxx b/starmath/inc/smdll.hxx new file mode 100644 index 000000000..3408f2fd4 --- /dev/null +++ b/starmath/inc/smdll.hxx @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_SMDLL_HXX +#define INCLUDED_STARMATH_INC_SMDLL_HXX + +#include "smdllapi.hxx" + +namespace SmGlobals +{ + SM_DLLPUBLIC void ensure(); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/smdllapi.hxx b/starmath/inc/smdllapi.hxx new file mode 100644 index 000000000..ad183991f --- /dev/null +++ b/starmath/inc/smdllapi.hxx @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_INC_SMDLLAPI_HXX +#define INCLUDED_STARMATH_INC_SMDLLAPI_HXX + +#include <sal/types.h> + +#if defined(SM_DLLIMPLEMENTATION) +#define SM_DLLPUBLIC SAL_DLLPUBLIC_EXPORT +#else +#define SM_DLLPUBLIC SAL_DLLPUBLIC_IMPORT +#endif + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/smmod.hrc b/starmath/inc/smmod.hrc new file mode 100644 index 000000000..59a4a7cd8 --- /dev/null +++ b/starmath/inc/smmod.hrc @@ -0,0 +1,92 @@ +/* -*- 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_VCL_INC_STRINGS_HRC +#define INCLUDED_VCL_INC_STRINGS_HRC + +#define NC_(Context, String) reinterpret_cast<char const *>(Context "\004" u8##String) + +const char* RID_UI_SYMBOLSET_NAMES[] = +{ + NC_("RID_UI_SYMBOLSET_NAMES", "Greek"), + NC_("RID_UI_SYMBOLSET_NAMES", "Special") +}; + +const char* RID_UI_SYMBOL_NAMES[] = +{ + NC_("RID_UI_SYMBOL_NAMES", "alpha"), + NC_("RID_UI_SYMBOL_NAMES", "ALPHA"), + NC_("RID_UI_SYMBOL_NAMES", "beta"), + NC_("RID_UI_SYMBOL_NAMES", "BETA"), + NC_("RID_UI_SYMBOL_NAMES", "gamma"), + NC_("RID_UI_SYMBOL_NAMES", "GAMMA"), + NC_("RID_UI_SYMBOL_NAMES", "delta"), + NC_("RID_UI_SYMBOL_NAMES", "DELTA"), + NC_("RID_UI_SYMBOL_NAMES", "epsilon"), + NC_("RID_UI_SYMBOL_NAMES", "EPSILON"), + NC_("RID_UI_SYMBOL_NAMES", "zeta"), + NC_("RID_UI_SYMBOL_NAMES", "ZETA"), + NC_("RID_UI_SYMBOL_NAMES", "eta"), + NC_("RID_UI_SYMBOL_NAMES", "ETA"), + NC_("RID_UI_SYMBOL_NAMES", "theta"), + NC_("RID_UI_SYMBOL_NAMES", "THETA"), + NC_("RID_UI_SYMBOL_NAMES", "iota"), + NC_("RID_UI_SYMBOL_NAMES", "IOTA"), + NC_("RID_UI_SYMBOL_NAMES", "kappa"), + NC_("RID_UI_SYMBOL_NAMES", "KAPPA"), + NC_("RID_UI_SYMBOL_NAMES", "lambda"), + NC_("RID_UI_SYMBOL_NAMES", "LAMBDA"), + NC_("RID_UI_SYMBOL_NAMES", "mu"), + NC_("RID_UI_SYMBOL_NAMES", "MU"), + NC_("RID_UI_SYMBOL_NAMES", "nu"), + NC_("RID_UI_SYMBOL_NAMES", "NU"), + NC_("RID_UI_SYMBOL_NAMES", "xi"), + NC_("RID_UI_SYMBOL_NAMES", "XI"), + NC_("RID_UI_SYMBOL_NAMES", "omicron"), + NC_("RID_UI_SYMBOL_NAMES", "OMICRON"), + NC_("RID_UI_SYMBOL_NAMES", "pi"), + NC_("RID_UI_SYMBOL_NAMES", "PI"), + NC_("RID_UI_SYMBOL_NAMES", "rho"), + NC_("RID_UI_SYMBOL_NAMES", "RHO"), + NC_("RID_UI_SYMBOL_NAMES", "sigma"), + NC_("RID_UI_SYMBOL_NAMES", "SIGMA"), + NC_("RID_UI_SYMBOL_NAMES", "tau"), + NC_("RID_UI_SYMBOL_NAMES", "TAU"), + NC_("RID_UI_SYMBOL_NAMES", "upsilon"), + NC_("RID_UI_SYMBOL_NAMES", "UPSILON"), + NC_("RID_UI_SYMBOL_NAMES", "phi"), + NC_("RID_UI_SYMBOL_NAMES", "PHI"), + NC_("RID_UI_SYMBOL_NAMES", "chi"), + NC_("RID_UI_SYMBOL_NAMES", "CHI"), + NC_("RID_UI_SYMBOL_NAMES", "psi"), + NC_("RID_UI_SYMBOL_NAMES", "PSI"), + NC_("RID_UI_SYMBOL_NAMES", "omega"), + NC_("RID_UI_SYMBOL_NAMES", "OMEGA"), + NC_("RID_UI_SYMBOL_NAMES", "varepsilon"), + NC_("RID_UI_SYMBOL_NAMES", "vartheta"), + NC_("RID_UI_SYMBOL_NAMES", "varpi"), + NC_("RID_UI_SYMBOL_NAMES", "varrho"), + NC_("RID_UI_SYMBOL_NAMES", "varsigma"), + NC_("RID_UI_SYMBOL_NAMES", "varphi"), + NC_("RID_UI_SYMBOL_NAMES", "element"), + NC_("RID_UI_SYMBOL_NAMES", "noelement"), + NC_("RID_UI_SYMBOL_NAMES", "strictlylessthan"), + NC_("RID_UI_SYMBOL_NAMES", "strictlygreaterthan"), + NC_("RID_UI_SYMBOL_NAMES", "notequal"), + NC_("RID_UI_SYMBOL_NAMES", "identical"), + NC_("RID_UI_SYMBOL_NAMES", "tendto"), + NC_("RID_UI_SYMBOL_NAMES", "infinite"), + NC_("RID_UI_SYMBOL_NAMES", "angle"), + NC_("RID_UI_SYMBOL_NAMES", "perthousand"), + NC_("RID_UI_SYMBOL_NAMES", "and"), + NC_("RID_UI_SYMBOL_NAMES", "or") +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/inc/smmod.hxx b/starmath/inc/smmod.hxx new file mode 100644 index 000000000..a8f1ac17c --- /dev/null +++ b/starmath/inc/smmod.hxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_STARMATH_INC_SMMOD_HXX +#define INCLUDED_STARMATH_INC_SMMOD_HXX + +#include <sfx2/module.hxx> +#include <sfx2/app.hxx> +#include <vcl/vclptr.hxx> + +#include <unotools/options.hxx> +#include <memory> + +namespace svtools { class ColorConfig; } + +class SfxObjectFactory; +class SmSymbolManager; +class SmMathConfig; + +/************************************************************************* +|* +|* This subclass of <SfxModule> (which is a subclass of <SfxShell>) is +|* linked to the DLL. One instance of this class exists while the DLL is +|* loaded. +|* +|* SdModule is like to be compared with the <SfxApplication>-subclass. +|* +|* Remember: Don`t export this class! It uses DLL-internal symbols. +|* +\************************************************************************/ + +class SvtSysLocale; +class VirtualDevice; + + +OUString SmResId(const char* pId); + +class SmLocalizedSymbolData +{ +public: + SmLocalizedSymbolData() = delete; + + static OUString GetUiSymbolName( const OUString &rExportName ); + static OUString GetExportSymbolName( const OUString &rUiName ); + + static OUString GetUiSymbolSetName( const OUString &rExportName ); + static OUString GetExportSymbolSetName( const OUString &rUiName ); +}; + +class SmModule final : public SfxModule, public utl::ConfigurationListener +{ + std::unique_ptr<svtools::ColorConfig> mpColorConfig; + std::unique_ptr<SmMathConfig> mpConfig; + std::unique_ptr<SmLocalizedSymbolData> mpLocSymbolData; + std::unique_ptr<SvtSysLocale> mpSysLocale; + VclPtr<VirtualDevice> mpVirtualDev; + +public: + SFX_DECL_INTERFACE(SFX_INTERFACE_SMA_START + SfxInterfaceId(0)) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + explicit SmModule(SfxObjectFactory* pObjFact); + virtual ~SmModule() override; + + virtual void ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints ) override; + + svtools::ColorConfig & GetColorConfig(); + + SmMathConfig * GetConfig(); + SmSymbolManager & GetSymbolManager(); + + static void GetState(SfxItemSet&); + + const SvtSysLocale& GetSysLocale(); + + VirtualDevice & GetDefaultVirtualDev(); + + //virtual methods for options dialog + virtual std::unique_ptr<SfxItemSet> CreateItemSet( sal_uInt16 nId ) override; + virtual void ApplyItemSet( sal_uInt16 nId, const SfxItemSet& rSet ) override; + virtual std::unique_ptr<SfxTabPage> CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet ) override; +}; + +#define SM_MOD() ( static_cast<SmModule*>(SfxApplication::GetModule(SfxToolsModule::Math)) ) + +#endif // INCLUDED_STARMATH_INC_SMMOD_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/starmath.hrc b/starmath/inc/starmath.hrc new file mode 100644 index 000000000..60fd52e7c --- /dev/null +++ b/starmath/inc/starmath.hrc @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_STARMATH_INC_STARMATH_HRC +#define INCLUDED_STARMATH_INC_STARMATH_HRC + +#include <svl/solar.hrc> + +#define SID_NEXTERR (SID_SMA_START + 1) +#define SID_PREVERR (SID_SMA_START + 2) +#define SID_NEXTMARK (SID_SMA_START + 3) +#define SID_PREVMARK (SID_SMA_START + 4) +#define SID_SYMBOLS_CATALOGUE (SID_SMA_START + 5) +#define SID_ZOOMIN (SID_SMA_START + 10) +#define SID_ZOOMOUT (SID_SMA_START + 11) +#define SID_DRAW (SID_SMA_START + 12) +#define SID_FORMULACURSOR (SID_SMA_START + 15) +#define SID_FONT (SID_SMA_START + 50) +#define SID_FONTSIZE (SID_SMA_START + 51) +#define SID_DISTANCE (SID_SMA_START + 52) +#define SID_ALIGN (SID_SMA_START + 53) + +#define SID_AUTO_REDRAW (SID_SMA_START + 55) +#define SID_TEXTMODE (SID_SMA_START + 57) +#define SID_IMPORT_FORMULA (SID_SMA_START + 58) +#define SID_IMPORT_MATHML_CLIPBOARD (SID_SMA_START + 59) +#define SID_TEXT (SID_SMA_START + 100) +#define SID_GAPHIC_SM (SID_SMA_START + 101) +/** Command for inserting a symbol specified by a string (Inserts an SmSpecialNode) */ +#define SID_INSERTSPECIAL (SID_SMA_START + 104) +/** Command for inserting a math construction */ +#define SID_INSERTCOMMANDTEXT (SID_SMA_START + 106) + +#define SID_LOADSYMBOLS (SID_SMA_START + 107) +#define SID_SAVESYMBOLS (SID_SMA_START + 108) +#define SID_MODIFYSTATUS (SID_SMA_START + 110) +#define SID_TEXTSTATUS (SID_SMA_START + 111) + +#define SID_PRINTTITLE (SID_SMA_START + 112) +#define SID_PRINTTEXT (SID_SMA_START + 113) +#define SID_PRINTFRAME (SID_SMA_START + 114) +#define SID_PRINTSIZE (SID_SMA_START + 115) +#define SID_PRINTZOOM (SID_SMA_START + 116) + +#define SID_COPYOBJECT (SID_SMA_START + 117) +#define SID_PASTEOBJECT (SID_SMA_START + 118) +#define SID_AUTOREDRAW (SID_SMA_START + 119) + +#define SID_GETEDITTEXT (SID_SMA_START + 121) +#define SID_CMDBOXWINDOW (SID_SMA_START + 122) +#define SID_NO_RIGHT_SPACES (SID_SMA_START + 124) +#define SID_SAVE_ONLY_USED_SYMBOLS (SID_SMA_START + 125) +#define SID_ELEMENTSDOCKINGWINDOW (SID_SMA_START + 126) +#define SID_AUTO_CLOSE_BRACKETS (SID_SMA_START + 127) + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/strings.hrc b/starmath/inc/strings.hrc new file mode 100644 index 000000000..b4a22c58e --- /dev/null +++ b/starmath/inc/strings.hrc @@ -0,0 +1,351 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SM_INC_STRINGS_HRC +#define INCLUDED_SM_INC_STRINGS_HRC + +#define NC_(Context, String) reinterpret_cast<char const *>(Context "\004" u8##String) + +#define RID_PLUSX_HELP NC_("RID_PLUSX_HELP", "+ Sign" ) +#define RID_MINUSX_HELP NC_("RID_MINUSX_HELP", "- Sign" ) +#define RID_PLUSMINUSX_HELP NC_("RID_PLUSMINUSX_HELP", "+- Sign" ) +#define RID_MINUSPLUSX_HELP NC_("RID_MINUSPLUSX_HELP", "-+ Sign" ) +#define RID_NEGX_HELP NC_("RID_NEGX_HELP", "Boolean NOT" ) +#define RID_XPLUSY_HELP NC_("RID_XPLUSY_HELP", "Addition +" ) +#define RID_XMINUSY_HELP NC_("RID_XMINUSY_HELP", "Subtraction -" ) +#define RID_XCDOTY_HELP NC_("RID_XCDOTY_HELP", "Multiplication (Dot)" ) +#define RID_XTIMESY_HELP NC_("RID_XTIMESY_HELP", "Multiplication (x)" ) +#define RID_XSYMTIMESY_HELP NC_("RID_XSYMTIMESY_HELP", "Multiplication (*)" ) +#define RID_XSYMDIVIDEY_HELP NC_("RID_XSYMDIVIDEY_HELP", "Division (Slash)" ) +#define RID_XDIVY_HELP NC_("RID_XDIVY_HELP", "Division (÷)" ) +#define RID_XOVERY_HELP NC_("RID_XOVERY_HELP", "Division (Fraction)" ) +#define RID_XODIVIDEY_HELP NC_("RID_XODIVIDEY_HELP", "Circled Slash" ) +#define RID_XODOTY_HELP NC_("RID_XODOTY_HELP", "Circled Dot" ) +#define RID_XOMINUSY_HELP NC_("RID_XOMINUSY_HELP", "Circled Minus" ) +#define RID_XOPLUSY_HELP NC_("RID_XOPLUSY_HELP", "Circled Plus" ) +#define RID_XOTIMESY_HELP NC_("RID_XOTIMESY_HELP", "Tensor Product" ) +#define RID_XANDY_HELP NC_("RID_XANDY_HELP", "Boolean AND" ) +#define RID_XORY_HELP NC_("RID_XORY_HELP", "Boolean OR" ) +#define RID_XEQY_HELP NC_("RID_XEQY_HELP", "Is Equal" ) +#define RID_XNEQY_HELP NC_("RID_XNEQY_HELP", "Is Not Equal" ) +#define RID_XLTY_HELP NC_("RID_XLTY_HELP", "Is Less Than" ) +#define RID_XGTY_HELP NC_("RID_XGTY_HELP", "Is Greater Than" ) +#define RID_XLEY_HELP NC_("RID_XLEY_HELP", "Is Less Than Or Equal To" ) +#define RID_XGEY_HELP NC_("RID_XGEY_HELP", "Is Greater Than Or Equal To" ) +#define RID_XLESLANTY_HELP NC_("RID_XLESLANTY_HELP", "Is Less Than Or Equal To" ) +#define RID_XGESLANTY_HELP NC_("RID_XGESLANTY_HELP", "Is Greater Than Or Equal To" ) +#define RID_XLLY_HELP NC_("RID_XLLY_HELP", "Is Much Less Than" ) +#define RID_XGGY_HELP NC_("RID_XGGY_HELP", "Is Much Greater Than" ) +#define RID_XDEFY_HELP NC_("RID_XDEFY_HELP", "Is Defined As" ) +#define RID_XEQUIVY_HELP NC_("RID_XEQUIVY_HELP", "Is Congruent To" ) +#define RID_XAPPROXY_HELP NC_("RID_XAPPROXY_HELP", "Is Approximately Equal" ) +#define RID_XSIMY_HELP NC_("RID_XSIMY_HELP", "Is Similar To" ) +#define RID_XSIMEQY_HELP NC_("RID_XSIMEQY_HELP", "Is Similar Or Equal" ) +#define RID_XPROPY_HELP NC_("RID_XPROPY_HELP", "Is Proportional To" ) +#define RID_XORTHOY_HELP NC_("RID_XORTHOY_HELP", "Is Orthogonal To" ) +#define RID_XPARALLELY_HELP NC_("RID_XPARALLELY_HELP", "Is Parallel To" ) +#define RID_XTOWARDY_HELP NC_("RID_XTOWARDY_HELP", "Toward" ) +#define RID_XTRANSLY_HELP NC_("RID_XTRANSLY_HELP", "Corresponds To (Left)" ) +#define RID_XTRANSRY_HELP NC_("RID_XTRANSRY_HELP", "Corresponds To (Right)" ) +#define RID_XINY_HELP NC_("RID_XINY_HELP", "Is In" ) +#define RID_XNOTINY_HELP NC_("RID_XNOTINY_HELP", "Is Not In" ) +#define RID_XOWNSY_HELP NC_("RID_XOWNSY_HELP", "Owns" ) +#define RID_XUNIONY_HELP NC_("RID_XUNIONY_HELP", "Union" ) +#define RID_XINTERSECTIONY_HELP NC_("RID_XINTERSECTIONY_HELP", "Intersection" ) +#define RID_XSETMINUSY_HELP NC_("RID_XSETMINUSY_HELP", "Difference" ) +#define RID_XSLASHY_HELP NC_("RID_XSLASHY_HELP", "Quotient Set" ) +#define RID_XSUBSETY_HELP NC_("RID_XSUBSETY_HELP", "Subset" ) +#define RID_XSUBSETEQY_HELP NC_("RID_XSUBSETEQY_HELP", "Subset Or Equal To" ) +#define RID_XSUPSETY_HELP NC_("RID_XSUPSETY_HELP", "Superset" ) +#define RID_XSUPSETEQY_HELP NC_("RID_XSUPSETEQY_HELP", "Superset Or Equal To" ) +#define RID_XNSUBSETY_HELP NC_("RID_XNSUBSETY_HELP", "Not Subset" ) +#define RID_XNSUBSETEQY_HELP NC_("RID_XNSUBSETEQY_HELP", "Not Subset Or Equal" ) +#define RID_XNSUPSETY_HELP NC_("RID_XNSUPSETY_HELP", "Not Superset" ) +#define RID_XNSUPSETEQY_HELP NC_("RID_XNSUPSETEQY_HELP", "Not Superset Or Equal" ) +#define RID_ABSX_HELP NC_("RID_ABSX_HELP", "Absolute Value" ) +#define RID_FACTX_HELP NC_("RID_FACTX_HELP", "Factorial" ) +#define RID_SQRTX_HELP NC_("RID_SQRTX_HELP", "Square Root" ) +#define RID_NROOTXY_HELP NC_("RID_NROOTXY_HELP", "N-th Root" ) +#define RID_EX_HELP NC_("RID_EX_HELP", "Exponential Function" ) +#define RID_EXPX_HELP NC_("RID_EXPX_HELP", "Exponential Function" ) +#define RID_LNX_HELP NC_("RID_LNX_HELP", "Natural Logarithm" ) +#define RID_LOGX_HELP NC_("RID_LOGX_HELP", "Logarithm" ) +#define RID_SINX_HELP NC_("RID_SINX_HELP", "Sine" ) +#define RID_COSX_HELP NC_("RID_COSX_HELP", "Cosine" ) +#define RID_TANX_HELP NC_("RID_TANX_HELP", "Tangent" ) +#define RID_COTX_HELP NC_("RID_COTX_HELP", "Cotangent" ) +#define RID_ARCSINX_HELP NC_("RID_ARCSINX_HELP", "Arcsine" ) +#define RID_ARCCOSX_HELP NC_("RID_ARCCOSX_HELP", "Arccosine" ) +#define RID_ARCTANX_HELP NC_("RID_ARCTANX_HELP", "Arctangent" ) +#define RID_ARCCOTX_HELP NC_("RID_ARCCOTX_HELP", "Arccotangent" ) +#define RID_SINHX_HELP NC_("RID_SINHX_HELP", "Hyperbolic Sine" ) +#define RID_COSHX_HELP NC_("RID_COSHX_HELP", "Hyperbolic Cosine" ) +#define RID_TANHX_HELP NC_("RID_TANHX_HELP", "Hyperbolic Tangent" ) +#define RID_COTHX_HELP NC_("RID_COTHX_HELP", "Hyperbolic Cotangent" ) +#define RID_ARSINHX_HELP NC_("RID_ARSINHX_HELP", "Area Hyperbolic Sine" ) +#define RID_ARCOSHX_HELP NC_("RID_ARCOSHX_HELP", "Area Hyperbolic Cosine" ) +#define RID_ARTANHX_HELP NC_("RID_ARTANHX_HELP", "Area Hyperbolic Tangent" ) +#define RID_ARCOTHX_HELP NC_("RID_ARCOTHX_HELP", "Area Hyperbolic Cotangent" ) +#define RID_SUMX_HELP NC_("RID_SUMX_HELP", "Sum" ) +#define RID_SUM_FROMX_HELP NC_("RID_SUM_FROMX_HELP", "Sum Subscript Bottom" ) +#define RID_SUM_TOX_HELP NC_("RID_SUM_TOX_HELP", "Sum Superscript Top" ) +#define RID_SUM_FROMTOX_HELP NC_("RID_SUM_FROMTOX_HELP", "Sum Sup/Sub script" ) +#define RID_PRODX_HELP NC_("RID_PRODX_HELP", "Product" ) +#define RID_PROD_FROMX_HELP NC_("RID_PROD_FROMX_HELP", "Product Subscript Bottom" ) +#define RID_PROD_TOX_HELP NC_("RID_PROD_TOX_HELP", "Product Superscript Top" ) +#define RID_PROD_FROMTOX_HELP NC_("RID_PROD_FROMTOX_HELP", "Product Sup/Sub script" ) +#define RID_COPRODX_HELP NC_("RID_COPRODX_HELP", "Coproduct" ) +#define RID_COPROD_FROMX_HELP NC_("RID_COPROD_FROMX_HELP", "Coproduct Subscript Bottom" ) +#define RID_COPROD_TOX_HELP NC_("RID_COPROD_TOX_HELP", "Coproduct Superscript Top" ) +#define RID_COPROD_FROMTOX_HELP NC_("RID_COPROD_FROMTOX_HELP", "Coproduct Sup/Sub script" ) +#define RID_LIMX_HELP NC_("RID_LIMX_HELP", "Limes" ) +#define RID_LIM_FROMX_HELP NC_("RID_LIM_FROMX_HELP", "Limes Subscript Bottom" ) +#define RID_LIM_TOX_HELP NC_("RID_LIM_TOX_HELP", "Limes Superscript Top" ) +#define RID_LIM_FROMTOX_HELP NC_("RID_LIM_FROMTOX_HELP", "Limes Sup/Sub script" ) +#define RID_LIMINFX_HELP NC_("RID_LIMINFX_HELP", "Limit Inferior" ) +#define RID_LIMINF_FROMX_HELP NC_("RID_LIMINF_FROMX_HELP", "Limit Inferior Subscript Bottom" ) +#define RID_LIMINF_TOX_HELP NC_("RID_LIMINF_TOX_HELP", "Limit Inferior Superscript Top" ) +#define RID_LIMINF_FROMTOX_HELP NC_("RID_LIMINF_FROMTOX_HELP", "Limit Inferior Sup/Sub script" ) +#define RID_LIMSUPX_HELP NC_("RID_LIMSUPX_HELP", "Limit Superior" ) +#define RID_LIMSUP_FROMX_HELP NC_("RID_LIMSUP_FROMX_HELP", "Limit Superior Subscript Bottom" ) +#define RID_LIMSUP_TOX_HELP NC_("RID_LIMSUP_TOX_HELP", "Limit Superior Superscript Top" ) +#define RID_LIMSUP_FROMTOX_HELP NC_("RID_LIMSUP_FROMTOX_HELP", "Limit Superior Sup/Sub script" ) +#define RID_EXISTS_HELP NC_("RID_EXISTS_HELP", "There Exists" ) +#define RID_NOTEXISTS_HELP NC_("RID_NOTEXISTS_HELP", "There Not Exists" ) +#define RID_FORALL_HELP NC_("RID_FORALL_HELP", "For all" ) +#define RID_INTX_HELP NC_("RID_INTX_HELP", "Integral" ) +#define RID_INT_FROMX_HELP NC_("RID_INT_FROMX_HELP", "Integral Subscript Bottom" ) +#define RID_INT_TOX_HELP NC_("RID_INT_TOX_HELP", "Integral Superscript Top" ) +#define RID_INT_FROMTOX_HELP NC_("RID_INT_FROMTOX_HELP", "Integral Sup/Sub script" ) +#define RID_IINTX_HELP NC_("RID_IINTX_HELP", "Double Integral" ) +#define RID_IINT_FROMX_HELP NC_("RID_IINT_FROMX_HELP", "Double Integral Subscript Bottom" ) +#define RID_IINT_TOX_HELP NC_("RID_IINT_TOX_HELP", "Double Integral Superscript Top" ) +#define RID_IINT_FROMTOX_HELP NC_("RID_IINT_FROMTOX_HELP", "Double Integral Sup/Sub script" ) +#define RID_IIINTX_HELP NC_("RID_IIINTX_HELP", "Triple Integral" ) +#define RID_IIINT_FROMX_HELP NC_("RID_IIINT_FROMX_HELP", "Triple Integral Subscript Bottom" ) +#define RID_IIINT_TOX_HELP NC_("RID_IIINT_TOX_HELP", "Triple Integral Superscript Top" ) +#define RID_IIINT_FROMTOX_HELP NC_("RID_IIINT_FROMTOX_HELP", "Triple Integral Sup/Sub script" ) +#define RID_LINTX_HELP NC_("RID_LINTX_HELP", "Curve Integral" ) +#define RID_LINT_FROMX_HELP NC_("RID_LINT_FROMX_HELP", "Curve Integral Subscript Bottom" ) +#define RID_LINT_TOX_HELP NC_("RID_LINT_TOX_HELP", "Curve Integral Superscript Top" ) +#define RID_LINT_FROMTOX_HELP NC_("RID_LINT_FROMTOX_HELP", "Curve Integral Sup/Sub script" ) +#define RID_LLINTX_HELP NC_("RID_LLINTX_HELP", "Double Curve Integral" ) +#define RID_LLINT_FROMX_HELP NC_("RID_LLINT_FROMX_HELP", "Double Curve Integral Subscript Bottom" ) +#define RID_LLINT_TOX_HELP NC_("RID_LLINT_TOX_HELP", "Double Curve Integral Superscript Top" ) +#define RID_LLINT_FROMTOX_HELP NC_("RID_LLINT_FROMTOX_HELP", "Double Curve Integral Sup/Sub script" ) +#define RID_LLLINTX_HELP NC_("RID_LLLINTX_HELP", "Triple Curve Integral" ) +#define RID_LLLINT_FROMX_HELP NC_("RID_LLLINT_FROMX_HELP", "Triple Curve Integral Subscript Bottom" ) +#define RID_LLLINT_TOX_HELP NC_("RID_LLLINT_TOX_HELP", "Triple Curve Integral Superscript Top" ) +#define RID_LLLINT_FROMTOX_HELP NC_("RID_LLLINT_FROMTOX_HELP", "Triple Curve Integral Sup/Sub script" ) +#define RID_ACUTEX_HELP NC_("RID_ACUTEX_HELP", "Acute Accent" ) +#define RID_BARX_HELP NC_("RID_BARX_HELP", "Line Above" ) +#define RID_BREVEX_HELP NC_("RID_BREVEX_HELP", "Breve" ) +#define RID_CHECKX_HELP NC_("RID_CHECKX_HELP", "Reverse Circumflex" ) +#define RID_CIRCLEX_HELP NC_("RID_CIRCLEX_HELP", "Circle" ) +#define RID_DOTX_HELP NC_("RID_DOTX_HELP", "Dot" ) +#define RID_DDOTX_HELP NC_("RID_DDOTX_HELP", "Double Dot" ) +#define RID_DDDOTX_HELP NC_("RID_DDDOTX_HELP", "Triple Dot" ) +#define RID_GRAVEX_HELP NC_("RID_GRAVEX_HELP", "Grave Accent" ) +#define RID_HATX_HELP NC_("RID_HATX_HELP", "Circumflex" ) +#define RID_TILDEX_HELP NC_("RID_TILDEX_HELP", "Tilde" ) +#define RID_VECX_HELP NC_("RID_VECX_HELP", "Vector Arrow" ) +#define RID_HARPOONX_HELP NC_("RID_HARPOONX_HELP", "Harpoon" ) +#define RID_UNDERLINEX_HELP NC_("RID_UNDERLINEX_HELP", "Line Below" ) +#define RID_OVERLINEX_HELP NC_("RID_OVERLINEX_HELP", "Line Over" ) +#define RID_OVERSTRIKEX_HELP NC_("RID_OVERSTRIKEX_HELP", "Line Through" ) +#define RID_PHANTOMX_HELP NC_("RID_PHANTOMX_HELP", "Transparent" ) +#define RID_BOLDX_HELP NC_("RID_BOLDX_HELP", "Bold Font" ) +#define RID_ITALX_HELP NC_("RID_ITALX_HELP", "Italic Font" ) +#define RID_SIZEXY_HELP NC_("RID_SIZEXY_HELP", "Resize" ) +#define RID_FONTXY_HELP NC_("RID_FONTXY_HELP", "Change Font" ) +#define RID_COLORX_BLACK_HELP NC_("RID_COLORX_BLACK_HELP", "Color Black" ) +#define RID_COLORX_BLUE_HELP NC_("RID_COLORX_BLUE_HELP", "Color Blue" ) +#define RID_COLORX_GREEN_HELP NC_("RID_COLORX_GREEN_HELP", "Color Green" ) +#define RID_COLORX_RED_HELP NC_("RID_COLORX_RED_HELP", "Color Red" ) +#define RID_COLORX_CYAN_HELP NC_("RID_COLORX_CYAN_HELP", "Color Cyan" ) +#define RID_COLORX_MAGENTA_HELP NC_("RID_COLORX_MAGENTA_HELP", "Color Magenta" ) +#define RID_COLORX_GRAY_HELP NC_("RID_COLORX_GRAY_HELP", "Color Gray" ) +#define RID_COLORX_LIME_HELP NC_("RID_COLORX_LIME_HELP", "Color Lime" ) +#define RID_COLORX_MAROON_HELP NC_("RID_COLORX_MAROON_HELP", "Color Maroon" ) +#define RID_COLORX_NAVY_HELP NC_("RID_COLORX_NAVY_HELP", "Color Navy" ) +#define RID_COLORX_OLIVE_HELP NC_("RID_COLORX_OLIVE_HELP", "Color Olive" ) +#define RID_COLORX_PURPLE_HELP NC_("RID_COLORX_PURPLE_HELP", "Color Purple" ) +#define RID_COLORX_SILVER_HELP NC_("RID_COLORX_SILVER_HELP", "Color Silver" ) +#define RID_COLORX_TEAL_HELP NC_("RID_COLORX_TEAL_HELP", "Color Teal" ) +#define RID_COLORX_YELLOW_HELP NC_("RID_COLORX_YELLOW_HELP", "Color Yellow" ) +#define RID_COLORX_RGB_HELP NC_("RID_COLORX_RGB_HELP", "Color RGB" ) +#define RID_LRGROUPX_HELP NC_("RID_LRGROUPX_HELP", "Group Brackets" ) +#define RID_LRPARENTX_HELP NC_("RID_LRPARENTX_HELP", "Round Brackets" ) +#define RID_LRBRACKETX_HELP NC_("RID_LRBRACKETX_HELP", "Square Brackets" ) +#define RID_LRDBRACKETX_HELP NC_("RID_LRDBRACKETX_HELP", "Double Square Brackets" ) +#define RID_LRBRACEX_HELP NC_("RID_LRBRACEX_HELP", "Braces" ) +#define RID_LRANGLEX_HELP NC_("RID_LRANGLEX_HELP", "Angle Brackets" ) +#define RID_LRCEILX_HELP NC_("RID_LRCEILX_HELP", "Upper Ceil" ) +#define RID_LRFLOORX_HELP NC_("RID_LRFLOORX_HELP", "Floor" ) +#define RID_LRLINEX_HELP NC_("RID_LRLINEX_HELP", "Single Lines" ) +#define RID_LRDLINEX_HELP NC_("RID_LRDLINEX_HELP", "Double Lines" ) +#define RID_LMRANGLEXY_HELP NC_("RID_LMRANGLEXY_HELP", "Operator Brackets" ) +#define RID_SLRPARENTX_HELP NC_("RID_SLRPARENTX_HELP", "Round Brackets (Scalable)" ) +#define RID_SLRBRACKETX_HELP NC_("RID_SLRBRACKETX_HELP", "Square Brackets (Scalable)" ) +#define RID_SLRDBRACKETX_HELP NC_("RID_SLRDBRACKETX_HELP", "Double Square Brackets (Scalable)" ) +#define RID_SLRBRACEX_HELP NC_("RID_SLRBRACEX_HELP", "Braces (Scalable)" ) +#define RID_SLRANGLEX_HELP NC_("RID_SLRANGLEX_HELP", "Angle Brackets (Scalable)" ) +#define RID_SLRCEILX_HELP NC_("RID_SLRCEILX_HELP", "Ceiling (Scalable)" ) +#define RID_SLRFLOORX_HELP NC_("RID_SLRFLOORX_HELP", "Floor (Scalable)" ) +#define RID_SLRLINEX_HELP NC_("RID_SLRLINEX_HELP", "Single Lines (Scalable)" ) +#define RID_SLRDLINEX_HELP NC_("RID_SLRDLINEX_HELP", "Double Lines (Scalable)" ) +#define RID_SLMRANGLEXY_HELP NC_("RID_SLMRANGLEXY_HELP", "Operator Brackets (Scalable)" ) +#define RID_XEVALUATEDATY_HELP NC_("RID_XEVALUATEDATY_HELP", "Evaluated At" ) +#define RID_XOVERBRACEY_HELP NC_("RID_XOVERBRACEY_HELP", "Braces Top (Scalable)" ) +#define RID_XUNDERBRACEY_HELP NC_("RID_XUNDERBRACEY_HELP", "Braces Bottom (Scalable)" ) +#define RID_RSUBX_HELP NC_("RID_RSUBX_HELP", "Subscript Right" ) +#define RID_RSUPX_HELP NC_("RID_RSUPX_HELP", "Power" ) +#define RID_LSUBX_HELP NC_("RID_LSUBX_HELP", "Subscript Left" ) +#define RID_LSUPX_HELP NC_("RID_LSUPX_HELP", "Superscript Left" ) +#define RID_CSUBX_HELP NC_("RID_CSUBX_HELP", "Subscript Bottom" ) +#define RID_CSUPX_HELP NC_("RID_CSUPX_HELP", "Superscript Top" ) +#define RID_SBLANK_HELP NC_("RID_SBLANK_HELP", "Small Gap" ) +#define RID_BLANK_HELP NC_("RID_BLANK_HELP", "Blank" ) +#define RID_NEWLINE_HELP NC_("RID_NEWLINE_HELP", "New Line" ) +#define RID_BINOMXY_HELP NC_("RID_BINOMXY_HELP", "Vertical Stack (2 Elements)") +#define RID_STACK_HELP NC_("RID_STACK_HELP", "Vertical Stack" ) +#define RID_MATRIX_HELP NC_("RID_MATRIX_HELP", "Matrix Stack" ) +#define RID_ALIGNLX_HELP NC_("RID_ALIGNLX_HELP", "Align Left" ) +#define RID_ALIGNCX_HELP NC_("RID_ALIGNCX_HELP", "Align Center" ) +#define RID_ALIGNRX_HELP NC_("RID_ALIGNRX_HELP", "Align Right" ) +#define RID_ALEPH_HELP NC_("RID_ALEPH_HELP", "Aleph" ) +#define RID_EMPTYSET_HELP NC_("RID_EMPTYSET_HELP", "Empty Set" ) +#define RID_RE_HELP NC_("RID_RE_HELP", "Real Part" ) +#define RID_IM_HELP NC_("RID_IM_HELP", "Imaginary Part" ) +#define RID_INFINITY_HELP NC_("RID_INFINITY_HELP", "Infinity" ) +#define RID_PARTIAL_HELP NC_("RID_PARTIAL_HELP", "Partial" ) +#define RID_NABLA_HELP NC_("RID_NABLA_HELP", "Nabla" ) +#define RID_LAPLACE_HELP NC_("RID_LAPLACE_HELP", "Laplace" ) +#define RID_WP_HELP NC_("RID_WP_HELP", "Weierstrass p" ) +#define RID_DOTSAXIS_HELP NC_("RID_DOTSAXIS_HELP", "Dots In Middle" ) +#define RID_DOTSUP_HELP NC_("RID_DOTSUP_HELP", "Dots To Top" ) +#define RID_DOTSDOWN_HELP NC_("RID_DOTSDOWN_HELP", "Dots To Bottom" ) +#define RID_DOTSLOW_HELP NC_("RID_DOTSLOW_HELP", "Dots At Bottom" ) +#define RID_DOTSVERT_HELP NC_("RID_DOTSVERT_HELP", "Dots Vertically" ) +#define RID_XCIRCY_HELP NC_("RID_XCIRCY_HELP", "Concatenate" ) +#define RID_XWIDESLASHY_HELP NC_("RID_XWIDESLASHY_HELP", "Division (wideslash)" ) +#define RID_XWIDEBSLASHY_HELP NC_("RID_XWIDEBSLASHY_HELP", "Division (counter wideslash)" ) +#define RID_XDIVIDESY_HELP NC_("RID_XDIVIDESY_HELP", "Divides" ) +#define RID_XNDIVIDESY_HELP NC_("RID_XNDIVIDESY_HELP", "Does Not Divide" ) +#define RID_DLARROW_HELP NC_("RID_DLARROW_HELP", "Double Arrow Left" ) +#define RID_DLRARROW_HELP NC_("RID_DLRARROW_HELP", "Double Arrow Left And Right" ) +#define RID_DRARROW_HELP NC_("RID_DRARROW_HELP", "Double Arrow Right" ) +#define RID_SETN_HELP NC_("RID_SETN_HELP", "Natural Numbers Set" ) +#define RID_SETZ_HELP NC_("RID_SETZ_HELP", "Integers Set" ) +#define RID_SETQ_HELP NC_("RID_SETQ_HELP", "Set of Rational Numbers" ) +#define RID_SETR_HELP NC_("RID_SETR_HELP", "Real Numbers Set" ) +#define RID_SETC_HELP NC_("RID_SETC_HELP", "Complex Numbers Set" ) +#define RID_WIDEHATX_HELP NC_("RID_WIDEHATX_HELP", "Large Circumflex" ) +#define RID_WIDETILDEX_HELP NC_("RID_WIDETILDEX_HELP", "Large Tilde" ) +#define RID_WIDEVECX_HELP NC_("RID_WIDEVECX_HELP", "Large Vector Arrow" ) +#define RID_WIDEHARPOONX_HELP NC_("RID_WIDEHARPOONX_HELP", "Large Harpoon" ) +#define RID_HBAR_HELP NC_("RID_HBAR_HELP", "h Bar" ) +#define RID_LAMBDABAR_HELP NC_("RID_LAMBDABAR_HELP", "Lambda Bar" ) +#define RID_LEFTARROW_HELP NC_("RID_LEFTARROW_HELP", "Left Arrow" ) +#define RID_RIGHTARROW_HELP NC_("RID_RIGHTARROW_HELP", "Right Arrow" ) +#define RID_UPARROW_HELP NC_("RID_UPARROW_HELP", "Up Arrow" ) +#define RID_DOWNARROW_HELP NC_("RID_DOWNARROW_HELP", "Down Arrow" ) +#define RID_NOSPACE_HELP NC_("RID_NOSPACE_HELP", "No space" ) +#define RID_XPRECEDESY_HELP NC_("RID_XPRECEDESY_HELP", "Precedes" ) +#define RID_XPRECEDESEQUALY_HELP NC_("RID_XPRECEDESEQUALY_HELP", "Precedes or equal to" ) +#define RID_XPRECEDESEQUIVY_HELP NC_("RID_XPRECEDESEQUIVY_HELP", "Precedes or equivalent to" ) +#define RID_XSUCCEEDSY_HELP NC_("RID_XSUCCEEDSY_HELP", "Succeeds" ) +#define RID_XSUCCEEDSEQUALY_HELP NC_("RID_XSUCCEEDSEQUALY_HELP", "Succeeds or equal to" ) +#define RID_XSUCCEEDSEQUIVY_HELP NC_("RID_XSUCCEEDSEQUIVY_HELP", "Succeeds or equivalent to" ) +#define RID_XNOTPRECEDESY_HELP NC_("RID_XNOTPRECEDESY_HELP", "Not precedes" ) +#define RID_XNOTSUCCEEDSY_HELP NC_("RID_XNOTSUCCEEDSY_HELP", "Not succeeds" ) +#define RID_CATEGORY_UNARY_BINARY_OPERATORS NC_("RID_CATEGORY_UNARY_BINARY_OPERATORS", "Unary/Binary Operators" ) +#define RID_CATEGORY_RELATIONS NC_("RID_CATEGORY_RELATIONS", "Relations" ) +#define RID_CATEGORY_SET_OPERATIONS NC_("RID_CATEGORY_SET_OPERATIONS", "Set Operations" ) +#define RID_CATEGORY_FUNCTIONS NC_("RID_CATEGORY_FUNCTIONS", "Functions" ) +#define RID_CATEGORY_OPERATORS NC_("RID_CATEGORY_OPERATORS", "Operators" ) +#define RID_CATEGORY_ATTRIBUTES NC_("RID_CATEGORY_ATTRIBUTES", "Attributes" ) +#define RID_CATEGORY_BRACKETS NC_("RID_CATEGORY_BRACKETS", "Brackets" ) +#define RID_CATEGORY_FORMATS NC_("RID_CATEGORY_FORMATS", "Formats" ) +#define RID_CATEGORY_OTHERS NC_("RID_CATEGORY_OTHERS", "Others" ) +#define RID_CATEGORY_EXAMPLES NC_("RID_CATEGORY_EXAMPLES", "Examples" ) + +#define RID_EXAMPLE_CIRCUMFERENCE_HELP NC_("RID_EXAMPLE_CIRCUMFERENCE_HELP", "Circumference" ) +#define RID_EXAMPLE_MASS_ENERGY_EQUIV_HELP NC_("RID_EXAMPLE_MASS_ENERGY_EQUIV_HELP", "Mass–energy equivalence" ) +#define RID_EXAMPLE_PYTHAGOREAN_THEO_HELP NC_("RID_EXAMPLE_PYTHAGOREAN_THEO_HELP", "Pythagorean theorem" ) +#define RID_EXAMPLE_A_SIMPLE_SERIES_HELP NC_("RID_EXAMPLE_A_SIMPLE_SERIES_HELP", "A simple series" ) +#define RID_EXAMPLE_GAUSS_DISTRIBUTION_HELP NC_("RID_EXAMPLE_GAUSS_DISTRIBUTION_HELP", "Gauss distribution" ) + +#define RID_FONTREGULAR NC_("RID_FONTREGULAR", "Standard" ) +#define RID_FONTITALIC NC_("RID_FONTITALIC", "Italic" ) +#define RID_FONTBOLD NC_("RID_FONTBOLD", "Bold" ) +#define STR_BLACK NC_("STR_BLACK", "black" ) +#define STR_BLUE NC_("STR_BLUE", "blue" ) +#define STR_GREEN NC_("STR_GREEN", "green" ) +#define STR_RED NC_("STR_RED", "red" ) +#define STR_CYAN NC_("STR_CYAN", "cyan" ) +#define STR_MAGENTA NC_("STR_MAGENTA", "magenta" ) +#define STR_GRAY NC_("STR_GRAY", "gray" ) +#define STR_LIME NC_("STR_LIME", "lime" ) +#define STR_MAROON NC_("STR_MAROON", "maroon" ) +#define STR_NAVY NC_("STR_NAVY", "navy" ) +#define STR_OLIVE NC_("STR_OLIVE", "olive" ) +#define STR_PURPLE NC_("STR_PURPLE", "purple" ) +#define STR_SILVER NC_("STR_SILVER", "silver" ) +#define STR_TEAL NC_("STR_TEAL", "teal" ) +#define STR_YELLOW NC_("STR_YELLOW", "yellow" ) +#define STR_RGB NC_("STR_RGB", "rgb" ) +#define STR_HIDE NC_("STR_HIDE", "hide" ) +#define STR_SIZE NC_("STR_SIZE", "size" ) +#define STR_FONT NC_("STR_FONT", "font" ) +#define STR_ALIGN_LEFT NC_("STR_ALIGN_LEFT", "left" ) +#define STR_ALIGN_CENTER NC_("STR_ALIGN_CENTER", "center" ) +#define STR_ALIGN_RIGHT NC_("STR_ALIGN_RIGHT", "right" ) +#define STR_CMDBOXWINDOW NC_("STR_CMDBOXWINDOW", "Commands" ) +#define RID_DOCUMENTSTR NC_("RID_DOCUMENTSTR", "Formula" ) +#define STR_STATSTR_WRITING NC_("STR_STATSTR_WRITING", "Saving document..." ) +#define STR_MATH_DOCUMENT_FULLTYPE_CURRENT NC_("STR_MATH_DOCUMENT_FULLTYPE_CURRENT", "%PRODUCTNAME %PRODUCTVERSION Formula") +#define RID_ERR_IDENT NC_("RID_ERR_IDENT", "ERROR : " ) +#define RID_ERR_UNEXPECTEDCHARACTER NC_("RID_ERR_UNEXPECTEDCHARACTER", "Unexpected character" ) +#define RID_ERR_UNEXPECTEDTOKEN NC_("RID_ERR_UNEXPECTEDTOKEN", "Unexpected token" ) +#define RID_ERR_LGROUPEXPECTED NC_("RID_ERR_LGROUPEXPECTED", "'{' expected" ) +#define RID_ERR_RGROUPEXPECTED NC_("RID_ERR_RGROUPEXPECTED", "'}' expected" ) +#define RID_ERR_LBRACEEXPECTED NC_("RID_ERR_LBRACEEXPECTED", "'(' expected" ) +#define RID_ERR_RBRACEEXPECTED NC_("RID_ERR_RBRACEEXPECTED", "')' expected" ) +#define RID_ERR_PARENTMISMATCH NC_("RID_ERR_PARENTMISMATCH", "Left and right symbols mismatched" ) +#define RID_ERR_FONTEXPECTED NC_("RID_ERR_FONTEXPECTED", "'fixed', 'sans', or 'serif' expected" ) +#define RID_ERR_SIZEEXPECTED NC_("RID_ERR_SIZEEXPECTED", "'size' followed by an unexpected token" ) +#define RID_ERR_DOUBLEALIGN NC_("RID_ERR_DOUBLEALIGN", "Double aligning is not allowed" ) +#define RID_ERR_DOUBLESUBSUPSCRIPT NC_("RID_ERR_DOUBLESUBSUPSCRIPT", "Double sub/superscripts is not allowed" ) +#define RID_ERR_POUNDEXPECTED NC_("RID_ERR_POUNDEXPECTED", "'#' expected" ) +#define RID_ERR_COLOREXPECTED NC_("RID_ERR_COLOREXPECTED", "Color required" ) +#define RID_ERR_RIGHTEXPECTED NC_("RID_ERR_RIGHTEXPECTED", "'RIGHT' expected" ) +#define RID_PRINTUIOPT_PRODNAME NC_("RID_PRINTUIOPT_PRODNAME", "%PRODUCTNAME %s" ) +#define RID_PRINTUIOPT_CONTENTS NC_("RID_PRINTUIOPT_CONTENTS", "Contents" ) +#define RID_PRINTUIOPT_TITLE NC_("RID_PRINTUIOPT_TITLE", "~Title" ) +#define RID_PRINTUIOPT_FRMLTXT NC_("RID_PRINTUIOPT_FRMLTXT", "~Formula text" ) +#define RID_PRINTUIOPT_BORDERS NC_("RID_PRINTUIOPT_BORDERS", "B~orders" ) +#define RID_PRINTUIOPT_SIZE NC_("RID_PRINTUIOPT_SIZE", "Size" ) +#define RID_PRINTUIOPT_ORIGSIZE NC_("RID_PRINTUIOPT_ORIGSIZE", "O~riginal size" ) +#define RID_PRINTUIOPT_FITTOPAGE NC_("RID_PRINTUIOPT_FITTOPAGE", "Fit to ~page" ) +#define RID_PRINTUIOPT_SCALING NC_("RID_PRINTUIOPT_SCALING", "~Scaling" ) + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/strings.hxx b/starmath/inc/strings.hxx new file mode 100644 index 000000000..bedd95347 --- /dev/null +++ b/starmath/inc/strings.hxx @@ -0,0 +1,274 @@ +/* -*- 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_INC_STRINGS_HXX +#define INCLUDED_STARMATH_INC_STRINGS_HXX + +#define RID_UNDOFORMATNAME "Format" + +#define RID_PLUSX "+<?> " +#define RID_MINUSX "-<?> " +#define RID_PLUSMINUSX "+-<?> " +#define RID_MINUSPLUSX "-+<?> " +#define RID_NEGX "neg <?> " +#define RID_XPLUSY "<?> + <?> " +#define RID_XMINUSY "<?> - <?> " +#define RID_XCDOTY "<?> cdot <?> " +#define RID_XTIMESY "<?> times <?> " +#define RID_XSYMTIMESY "<?> * <?> " +#define RID_XSYMDIVIDEY "<?> / <?> " +#define RID_XDIVY "<?> div <?> " +#define RID_XOVERY "{<?>} over {<?>} " +#define RID_XODIVIDEY "<?> odivide <?> " +#define RID_XODOTY "<?> odot <?> " +#define RID_XOMINUSY "<?> ominus <?> " +#define RID_XOPLUSY "<?> oplus <?> " +#define RID_XOTIMESY "<?> otimes <?> " +#define RID_XANDY "<?> and <?> " +#define RID_XORY "<?> or <?> " +#define RID_XEQY "<?> = <?> " +#define RID_XNEQY "<?> <> <?> " +#define RID_XLTY "<?> < <?> " +#define RID_XGTY "<?> > <?> " +#define RID_XLEY "<?> <= <?> " +#define RID_XGEY "<?> >= <?> " +#define RID_XLESLANTY "<?> leslant <?> " +#define RID_XGESLANTY "<?> geslant <?> " +#define RID_XLLY "<?> << <?> " +#define RID_XGGY "<?> >> <?> " +#define RID_XDEFY "<?> def <?> " +#define RID_XEQUIVY "<?> equiv <?> " +#define RID_XAPPROXY "<?> approx <?> " +#define RID_XSIMY "<?> sim <?> " +#define RID_XSIMEQY "<?> simeq <?> " +#define RID_XPROPY "<?> prop <?> " +#define RID_XORTHOY "<?> ortho <?> " +#define RID_XPARALLELY "<?> parallel <?> " +#define RID_XTOWARDY "<?> toward <?> " +#define RID_XTRANSLY "<?> transl <?> " +#define RID_XTRANSRY "<?> transr <?> " +#define RID_XINY "<?> in <?> " +#define RID_XNOTINY "<?> notin <?> " +#define RID_XOWNSY "<?> owns <?> " +#define RID_XUNIONY "<?> union <?> " +#define RID_XINTERSECTIONY "<?> intersection <?> " +#define RID_XSETMINUSY "<?> setminus <?> " +#define RID_XSLASHY "<?> slash <?> " +#define RID_XSUBSETY "<?> subset <?> " +#define RID_XSUBSETEQY "<?> subseteq <?> " +#define RID_XSUPSETY "<?> supset <?> " +#define RID_XSUPSETEQY "<?> supseteq <?> " +#define RID_XNSUBSETY "<?> nsubset <?> " +#define RID_XNSUBSETEQY "<?> nsubseteq <?> " +#define RID_XNSUPSETY "<?> nsupset <?> " +#define RID_XNSUPSETEQY "<?> nsupseteq <?> " +#define RID_ABSX "abs{<?>} " +#define RID_FACTX "fact{<?>} " +#define RID_SQRTX "sqrt{<?>} " +#define RID_NROOTXY "nroot{<?>}{<?>} " +#define RID_EX "func e^{<?>} " +#define RID_EXPX "exp(<?>) " +#define RID_LNX "ln(<?>) " +#define RID_LOGX "log(<?>) " +#define RID_SINX "sin(<?>) " +#define RID_COSX "cos(<?>) " +#define RID_TANX "tan(<?>) " +#define RID_COTX "cot(<?>) " +#define RID_ARCSINX "arcsin(<?>) " +#define RID_ARCCOSX "arccos(<?>) " +#define RID_ARCTANX "arctan(<?>) " +#define RID_ARCCOTX "arccot(<?>) " +#define RID_SINHX "sinh(<?>) " +#define RID_COSHX "cosh(<?>) " +#define RID_TANHX "tanh(<?>) " +#define RID_COTHX "coth(<?>) " +#define RID_ARSINHX "arsinh(<?>) " +#define RID_ARCOSHX "arcosh(<?>) " +#define RID_ARTANHX "artanh(<?>) " +#define RID_ARCOTHX "arcoth(<?>) " +#define RID_SUMX "sum <?> " +#define RID_SUM_FROMX "sum from{<?>} <?> " +#define RID_SUM_TOX "sum to{<?>} <?> " +#define RID_SUM_FROMTOX "sum from{<?>} to{<?>} <?> " +#define RID_PRODX "prod <?> " +#define RID_PROD_FROMX "prod from{<?>} <?> " +#define RID_PROD_TOX "prod to{<?>} <?> " +#define RID_PROD_FROMTOX "prod from{<?>} to{<?>} <?> " +#define RID_COPRODX "coprod <?> " +#define RID_COPROD_FROMX "coprod from{<?>} <?> " +#define RID_COPROD_TOX "coprod to{<?>} <?> " +#define RID_COPROD_FROMTOX "coprod from{<?>} to{<?>} <?> " +#define RID_LIMX "lim <?> " +#define RID_LIM_FROMX "lim from{<?>} <?> " +#define RID_LIM_TOX "lim to{<?>} <?> " +#define RID_LIM_FROMTOX "lim from{<?>} to{<?>} <?> " +#define RID_LIMINFX "liminf <?> " +#define RID_LIMINF_FROMX "liminf from{<?>} <?> " +#define RID_LIMINF_TOX "liminf to{<?>} <?> " +#define RID_LIMINF_FROMTOX "liminf from{<?>} to{<?>} <?> " +#define RID_LIMSUPX "limsup <?> " +#define RID_LIMSUP_FROMX "limsup from{<?>} <?> " +#define RID_LIMSUP_TOX "limsup to{<?>} <?> " +#define RID_LIMSUP_FROMTOX "limsup from{<?>} to{<?>} <?> " +#define RID_EXISTS "exists " +#define RID_NOTEXISTS "notexists " +#define RID_FORALL "forall " +#define RID_INTX "int <?> " +#define RID_INT_FROMX "int from{<?>} <?> " +#define RID_INT_TOX "int to{<?>} <?> " +#define RID_INT_FROMTOX "int from{<?>} to{<?>} <?> " +#define RID_IINTX "iint <?> " +#define RID_IINT_FROMX "iint from{<?>} <?> " +#define RID_IINT_TOX "iint to{<?>} <?> " +#define RID_IINT_FROMTOX "iint from{<?>} to{<?>} <?> " +#define RID_IIINTX "iiint <?> " +#define RID_IIINT_FROMX "iiint from{<?>} <?> " +#define RID_IIINT_TOX "iiint to{<?>} <?> " +#define RID_IIINT_FROMTOX "iiint from{<?>} to{<?>} <?> " +#define RID_LINTX "lint <?> " +#define RID_LINT_FROMX "lint from{<?>} <?> " +#define RID_LINT_TOX "lint to{<?>} <?> " +#define RID_LINT_FROMTOX "lint from{<?>} to{<?>} <?> " +#define RID_LLINTX "llint <?> " +#define RID_LLINT_FROMX "llint from{<?>} <?> " +#define RID_LLINT_TOX "llint to{<?>} <?> " +#define RID_LLINT_FROMTOX "llint from{<?>} to{<?>} <?> " +#define RID_LLLINTX "lllint <?> " +#define RID_LLLINT_FROMX "lllint from{<?>} <?> " +#define RID_LLLINT_TOX "lllint to{<?>} <?> " +#define RID_LLLINT_FROMTOX "lllint from{<?>} to{<?>} <?> " +#define RID_FROMX "from{<?>} <?> " +#define RID_TOX "to{<?>} <?> " +#define RID_FROMXTOY "from{<?>} to{<?>} <?> " +#define RID_ACUTEX "acute <?> " +#define RID_BARX "bar <?> " +#define RID_BREVEX "breve <?> " +#define RID_CHECKX "check <?> " +#define RID_CIRCLEX "circle <?> " +#define RID_DOTX "dot <?> " +#define RID_DDOTX "ddot <?> " +#define RID_DDDOTX "dddot <?> " +#define RID_GRAVEX "grave <?> " +#define RID_HATX "hat <?> " +#define RID_TILDEX "tilde <?> " +#define RID_VECX "vec <?> " +#define RID_HARPOONX "harpoon <?> " +#define RID_UNDERLINEX "underline {<?>} " +#define RID_OVERLINEX "overline {<?>} " +#define RID_OVERSTRIKEX "overstrike {<?>} " +#define RID_PHANTOMX "phantom {<?>} " +#define RID_BOLDX "bold <?> " +#define RID_ITALX "ital <?> " +#define RID_SIZEXY "size <?> {<?>} " +#define RID_FONTXY "font <?> {<?>} " +#define RID_COLORX_BLACK "color black {<?>} " +#define RID_COLORX_BLUE "color blue {<?>} " +#define RID_COLORX_GREEN "color green {<?>} " +#define RID_COLORX_RED "color red {<?>} " +#define RID_COLORX_CYAN "color cyan {<?>} " +#define RID_COLORX_MAGENTA "color magenta {<?>} " +#define RID_COLORX_GRAY "color gray {<?>} " +#define RID_COLORX_LIME "color lime {<?>} " +#define RID_COLORX_MAROON "color maroon {<?>} " +#define RID_COLORX_NAVY "color navy {<?>} " +#define RID_COLORX_OLIVE "color olive {<?>} " +#define RID_COLORX_PURPLE "color purple {<?>} " +#define RID_COLORX_SILVER "color silver {<?>} " +#define RID_COLORX_TEAL "color teal {<?>} " +#define RID_COLORX_YELLOW "color yellow {<?>} " +#define RID_COLORX_RGB "color rgb 0 0 0 {<?>} " +#define RID_LRGROUPX "{<?>} " +#define RID_LRPARENTX "(<?>) " +#define RID_LRBRACKETX "[<?>] " +#define RID_LRDBRACKETX "ldbracket <?> rdbracket " +#define RID_LRBRACEX "lbrace <?> rbrace " +#define RID_LRANGLEX "langle <?> rangle " +#define RID_LRCEILX "lceil <?> rceil " +#define RID_LRFLOORX "lfloor <?> rfloor " +#define RID_LRLINEX "lline <?> rline " +#define RID_LRDLINEX "ldline <?> rdline " +#define RID_LMRANGLEXY "langle <?> mline <?> rangle " +#define RID_SLRPARENTX "left ( <?> right ) " +#define RID_SLRBRACKETX "left [ <?> right ] " +#define RID_SLRDBRACKETX "left ldbracket <?> right rdbracket " +#define RID_SLRBRACEX "left lbrace <?> right rbrace " +#define RID_SLRANGLEX "left langle <?> right rangle " +#define RID_SLRCEILX "left lceil <?> right rceil " +#define RID_SLRFLOORX "left lfloor <?> right rfloor " +#define RID_SLRLINEX "left lline <?> right rline " +#define RID_SLRDLINEX "left ldline <?> right rdline " +#define RID_SLMRANGLEXY "left langle <?> mline <?> right rangle " +#define RID_XEVALUATEDATY "left none {<?>} right rline_{<?>} " +#define RID_XOVERBRACEY "{<?>} overbrace {<?>} " +#define RID_XUNDERBRACEY "{<?>} underbrace {<?>} " +#define RID_RSUBX "<?>_{<?>}" +#define RID_RSUPX "<?>^{<?>}" +#define RID_LSUBX "<?> lsub{<?>} " +#define RID_LSUPX "<?> lsup{<?>} " +#define RID_CSUBX "<?> csub{<?>} " +#define RID_CSUPX "<?> csup{<?>} " +#define RID_SBLANK "`" +#define RID_BLANK "~" +#define RID_NEWLINE "newline " +#define RID_BINOMXY "binom{<?>}{<?>} " +#define RID_STACK "stack{<?> # <?> # <?>} " +#define RID_MATRIX "matrix{<?> # <?> ## <?> # <?>} " +#define RID_ALIGNLX "alignl <?> " +#define RID_ALIGNCX "alignc <?> " +#define RID_ALIGNRX "alignr <?> " +#define RID_ALEPH "aleph " +#define RID_EMPTYSET "emptyset " +#define RID_RE "Re " +#define RID_IM "Im " +#define RID_INFINITY "infinity " +#define RID_PARTIAL "partial " +#define RID_NABLA "nabla " +#define RID_WP "wp " +#define RID_LAPLACE "laplace " +#define RID_DOTSAXIS "dotsaxis " +#define RID_DOTSUP "dotsup " +#define RID_DOTSDOWN "dotsdown " +#define RID_DOTSLOW "dotslow " +#define RID_DOTSVERT "dotsvert " +#define RID_XCIRCY "<?> circ <?> " +#define RID_XWIDESLASHY "{<?>} wideslash {<?>} " +#define RID_XWIDEBSLASHY "{<?>} widebslash {<?>} " +#define RID_XDIVIDESY "<?> divides <?> " +#define RID_XNDIVIDESY "<?> ndivides <?> " +#define RID_DLARROW "<?> dlarrow <?> " +#define RID_DLRARROW "<?> dlrarrow <?> " +#define RID_DRARROW "<?> drarrow <?> " +#define RID_SETN "setN " +#define RID_SETZ "setZ " +#define RID_SETQ "setQ " +#define RID_SETR "setR " +#define RID_SETC "setC " +#define RID_WIDEHATX "widehat {<?>} " +#define RID_WIDETILDEX "widetilde {<?>} " +#define RID_WIDEVECX "widevec {<?>} " +#define RID_WIDEHARPOONX "wideharpoon {<?>} " +#define RID_HBAR "hbar " +#define RID_LAMBDABAR "lambdabar " +#define RID_LEFTARROW "leftarrow " +#define RID_RIGHTARROW "rightarrow " +#define RID_UPARROW "uparrow " +#define RID_DOWNARROW "downarrow " +#define RID_NOSPACE "nospace {<?>} " +#define RID_XPRECEDESY "<?> prec <?> " +#define RID_XPRECEDESEQUALY "<?> preccurlyeq <?> " +#define RID_XPRECEDESEQUIVY "<?> precsim <?> " +#define RID_XSUCCEEDSY "<?> succ <?> " +#define RID_XSUCCEEDSEQUALY "<?> succcurlyeq <?> " +#define RID_XSUCCEEDSEQUIVY "<?> succsim <?> " +#define RID_XNOTPRECEDESY "<?> nprec <?> " +#define RID_XNOTSUCCEEDSY "<?> nsucc <?> " + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/inc/symbol.hxx b/starmath/inc/symbol.hxx new file mode 100644 index 000000000..9a9a595ed --- /dev/null +++ b/starmath/inc/symbol.hxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_STARMATH_INC_SYMBOL_HXX +#define INCLUDED_STARMATH_INC_SYMBOL_HXX + +#include <map> +#include <vector> +#include <set> + +#include "utility.hxx" + + +#define SYMBOL_NONE 0xFFFF + +class SmSym +{ +private: + SmFace m_aFace; + OUString m_aName; + OUString m_aExportName; + OUString m_aSetName; + sal_UCS4 m_cChar; + bool m_bPredefined; + +public: + SmSym(); + SmSym(const OUString& rName, const vcl::Font& rFont, sal_UCS4 cChar, + const OUString& rSet, bool bIsPredefined = false); + SmSym(const SmSym& rSymbol); + + SmSym& operator = (const SmSym& rSymbol); + + const vcl::Font& GetFace() const { return m_aFace; } + sal_UCS4 GetCharacter() const { return m_cChar; } + const OUString& GetName() const { return m_aName; } + + bool IsPredefined() const { return m_bPredefined; } + const OUString& GetSymbolSetName() const { return m_aSetName; } + const OUString& GetExportName() const { return m_aExportName; } + void SetExportName( const OUString &rName ) { m_aExportName = rName; } + + // true if rSymbol has the same name, font and character + bool IsEqualInUI( const SmSym& rSymbol ) const; +}; + +// type of the actual container to hold the symbols +typedef std::map< OUString, SmSym > SymbolMap_t; + +// vector of pointers to the actual symbols in the above container +typedef std::vector< const SmSym * > SymbolPtrVec_t; + + +class SmSymbolManager +{ +private: + SymbolMap_t m_aSymbols; + bool m_bModified; + +public: + SmSymbolManager(); + SmSymbolManager(const SmSymbolManager& rSymbolSetManager); + ~SmSymbolManager(); + + SmSymbolManager & operator = (const SmSymbolManager& rSymbolSetManager); + + // symbol sets are for UI purpose only, thus we assemble them here + std::set< OUString > GetSymbolSetNames() const; + SymbolPtrVec_t GetSymbolSet( const OUString& rSymbolSetName ); + + SymbolPtrVec_t GetSymbols() const; + bool AddOrReplaceSymbol( const SmSym & rSymbol, bool bForceChange = false ); + void RemoveSymbol( const OUString & rSymbolName ); + + SmSym * GetSymbolByName(const OUString& rSymbolName); + const SmSym * GetSymbolByName(const OUString& rSymbolName) const + { + return const_cast<SmSymbolManager *>(this)->GetSymbolByName(rSymbolName); + } + + bool IsModified() const { return m_bModified; } + void SetModified(bool bModify) { m_bModified = bModify; } + + void Load(); + void Save(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/token.hxx b/starmath/inc/token.hxx new file mode 100644 index 000000000..cdc91ddf4 --- /dev/null +++ b/starmath/inc/token.hxx @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_INC_TOKEN_HXX +#define INCLUDED_STARMATH_INC_TOKEN_HXX + +#include <sal/types.h> +#include <rtl/ustring.hxx> +#include <o3tl/typed_flags_set.hxx> + +// TokenGroups +enum class TG { + NONE = 0x000000, + Oper = 0x000001, + Relation = 0x000002, + Sum = 0x000004, + Product = 0x000008, + UnOper = 0x000010, + Power = 0x000020, + Attribute = 0x000040, + Align = 0x000080, + Function = 0x000100, + Blank = 0x000200, + LBrace = 0x000400, + RBrace = 0x000800, + Color = 0x001000, + Font = 0x002000, + Standalone = 0x004000, + Limit = 0x010000, + FontAttr = 0x020000 +}; + +namespace o3tl { + template<> struct typed_flags<TG> : is_typed_flags<TG, 0x037fff> {}; +} + +enum SmTokenType +{ + TEND, TLGROUP, TRGROUP, TLPARENT, TRPARENT, + TLBRACKET, TRBRACKET, TPLUS, TMINUS, TMULTIPLY, + TDIVIDEBY, TASSIGN, TPOUND, TSPECIAL, TSLASH, + TBACKSLASH, TBLANK, TSBLANK, TRSUB, TRSUP, + TCSUB, TCSUP, TLSUB, TLSUP, TGT, + TLT, TAND, TOR, TINTERSECT, TUNION, + TNEWLINE, TBINOM, TFROM, TTO, TINT, + TSUM, TOPER, TABS, TSQRT, TFACT, + TNROOT, TOVER, TTIMES, TGE, TLE, + TGG, TLL, TDOTSAXIS, TDOTSLOW, TDOTSVERT, + TDOTSDIAG, TDOTSUP, TDOTSDOWN, TACUTE, TBAR, + TBREVE, TCHECK, TCIRCLE, TDOT, TDDOT, + TDDDOT, TGRAVE, THAT, TTILDE, TVEC, + THARPOON, + TUNDERLINE, TOVERLINE, TOVERSTRIKE, TITALIC, TNITALIC, + TBOLD, TNBOLD, TPHANTOM, TFONT, TSIZE, + TCOLOR, TALIGNL, TALIGNC, TALIGNR, TLEFT, + TRIGHT, TLANGLE, TLBRACE, TLLINE, TLDLINE, + TLCEIL, TLFLOOR, TNONE, TMLINE, TRANGLE, + TRBRACE, TRLINE, TRDLINE, TRCEIL, TRFLOOR, + TSIN, TCOS, TTAN, TCOT, TFUNC, + TSTACK, TMATRIX, TDPOUND, TPLACE, + TTEXT, TNUMBER, TCHARACTER, TIDENT, TNEQ, + TEQUIV, TDEF, TPROP, TSIM, TSIMEQ, + TAPPROX, TPARALLEL, TORTHO, TIN, TNOTIN, + TSUBSET, TSUBSETEQ, TSUPSET, TSUPSETEQ, TPLUSMINUS, + TMINUSPLUS, TOPLUS, TOMINUS, TDIV, TOTIMES, + TODIVIDE, TTRANSL, TTRANSR, TIINT, TIIINT, + TLINT, TLLINT, TLLLINT, TPROD, TCOPROD, + TFORALL, TEXISTS, TNOTEXISTS, TLIM, TNABLA, + TTOWARD, TSINH, TCOSH, TTANH, TCOTH, + TASIN, TACOS, TATAN, TLN, TLOG, + TUOPER, TBOPER, TBLACK, TWHITE, TRED, + TGREEN, TBLUE, TCYAN, TMAGENTA, TYELLOW, + TFIXED, TSANS, TSERIF, TASINH, + TACOSH, TATANH, TACOTH, TACOT, TEXP, + TCDOT, TODOT, TLESLANT, TGESLANT, TNSUBSET, + TNSUPSET, TNSUBSETEQ, TNSUPSETEQ, TPARTIAL, TNEG, + TNI, TBACKEPSILON, TALEPH, TIM, TRE, + TWP, TEMPTYSET, TINFINITY, TESCAPE, TLIMSUP, + TLIMINF, TNDIVIDES, TDRARROW, TDLARROW, TDLRARROW, + TUNDERBRACE, TOVERBRACE, TCIRC, THBAR, + TLAMBDABAR, TLEFTARROW, TRIGHTARROW, TUPARROW, TDOWNARROW, + TDIVIDES, TSETN, TSETZ, TSETQ, + TSETR, TSETC, TWIDEVEC, TWIDEHARPOON, TWIDETILDE, + TWIDEHAT, + TWIDESLASH, TWIDEBACKSLASH, TLDBRACKET, TRDBRACKET, TNOSPACE, + TUNKNOWN, TPRECEDES, TSUCCEEDS, TPRECEDESEQUAL, TSUCCEEDSEQUAL, + TPRECEDESEQUIV, TSUCCEEDSEQUIV, TNOTPRECEDES, TNOTSUCCEEDS, TSILVER, + TGRAY, TMAROON, TPURPLE, TLIME, TOLIVE, + TNAVY, TTEAL, TAQUA, TFUCHSIA, TINTD, + TRGB, TLAPLACE +}; + +struct SmToken +{ + + OUString aText; // token text + SmTokenType eType; // token info + sal_Unicode cMathChar; + + // parse-help info + TG nGroup; + sal_uInt16 nLevel; + + // token position + sal_Int32 nRow; // 1-based + sal_Int32 nCol; // 1-based + + SmToken(); + SmToken(SmTokenType eTokenType, + sal_Unicode cMath, + const char* pText, + TG nTokenGroup = TG::NONE, + sal_uInt16 nTokenLevel = 0); +}; + +struct SmTokenTableEntry +{ + const char* pIdent; + SmTokenType eType; + sal_Unicode cMathChar; + TG nGroup; + sal_uInt16 nLevel; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/types.hxx b/starmath/inc/types.hxx new file mode 100644 index 000000000..044b2fab0 --- /dev/null +++ b/starmath/inc/types.hxx @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_STARMATH_INC_TYPES_HXX +#define INCLUDED_STARMATH_INC_TYPES_HXX + +#include <sal/types.h> +#define FONTNAME_MATH "OpenSymbol" + + +enum SmPrintSize { PRINT_SIZE_NORMAL, PRINT_SIZE_SCALED, PRINT_SIZE_ZOOMED }; + + +// definitions for characters from the 'StarSymbol' font +// (some chars have more than one alias!) +//! Note: not listed here does not(!) mean "not used" +//! (see %alpha ... %gamma for example) + +sal_Unicode const MS_FACT = 0x0021; +sal_Unicode const MS_INFINITY = 0x221E; +sal_Unicode const MS_SLASH = 0x002F; + +sal_Unicode const MS_NDIVIDES = 0x2224; +sal_Unicode const MS_DRARROW = 0x21D2; +sal_Unicode const MS_DLARROW = 0x21D0; +sal_Unicode const MS_DLRARROW = 0x21D4; +sal_Unicode const MS_OVERBRACE = 0x23DE; +sal_Unicode const MS_UNDERBRACE = 0x23DF; +sal_Unicode const MS_CIRC = 0x2218; +sal_Unicode const MS_ASSIGN = 0x003D; +sal_Unicode const MS_ERROR = 0x00BF; + +sal_Unicode const MS_NEQ = 0x2260; +sal_Unicode const MS_PLUS = 0x002B; +sal_Unicode const MS_MINUS = 0x2212; +sal_Unicode const MS_MULTIPLY = 0x2217; +sal_Unicode const MS_TIMES = 0x00D7; +sal_Unicode const MS_CDOT = 0x22C5; +sal_Unicode const MS_DIV = 0x00F7; +sal_Unicode const MS_PLUSMINUS = 0x00B1; +sal_Unicode const MS_MINUSPLUS = 0x2213; +sal_Unicode const MS_OPLUS = 0x2295; +sal_Unicode const MS_OMINUS = 0x2296; +sal_Unicode const MS_OTIMES = 0x2297; +sal_Unicode const MS_ODIVIDE = 0x2298; +sal_Unicode const MS_ODOT = 0x2299; +sal_Unicode const MS_UNION = 0x222A; +sal_Unicode const MS_INTERSECT = 0x2229; + +sal_Unicode const MS_LT = 0x003C; +sal_Unicode const MS_GT = 0x003E; +sal_Unicode const MS_LE = 0x2264; +sal_Unicode const MS_GE = 0x2265; +sal_Unicode const MS_LESLANT = 0x2A7D; +sal_Unicode const MS_GESLANT = 0x2A7E; +sal_Unicode const MS_LL = 0x226A; +sal_Unicode const MS_GG = 0x226B; +sal_Unicode const MS_SIM = 0x223C; +sal_Unicode const MS_SIMEQ = 0x2243; +sal_Unicode const MS_APPROX = 0x2248; +sal_Unicode const MS_DEF = 0x225D; +sal_Unicode const MS_EQUIV = 0x2261; +sal_Unicode const MS_PROP = 0x221D; +sal_Unicode const MS_PARTIAL = 0x2202; +sal_Unicode const MS_LAPLACE = 0x2112; + +sal_Unicode const MS_SUBSET = 0x2282; +sal_Unicode const MS_SUPSET = 0x2283; +sal_Unicode const MS_SUBSETEQ = 0x2286; +sal_Unicode const MS_SUPSETEQ = 0x2287; +sal_Unicode const MS_NSUBSET = 0x2284; +sal_Unicode const MS_NSUPSET = 0x2285; +sal_Unicode const MS_NSUBSETEQ = 0x2288; +sal_Unicode const MS_NSUPSETEQ = 0x2289; +sal_Unicode const MS_IN = 0x2208; +sal_Unicode const MS_NOTIN = 0x2209; +sal_Unicode const MS_EXISTS = 0x2203; +sal_Unicode const MS_NOTEXISTS = 0x2204; +sal_Unicode const MS_BACKEPSILON = 0x220D; +sal_Unicode const MS_ALEPH = 0x2135; +sal_Unicode const MS_IM = 0x2111; +sal_Unicode const MS_RE = 0x211C; +sal_Unicode const MS_WP = 0x2118; + +sal_Unicode const MS_LINE = 0x2223; +sal_Unicode const MS_VERTLINE = 0x007C; +sal_Unicode const MS_DLINE = 0x2225; +sal_Unicode const MS_DVERTLINE = 0x2016; +sal_Unicode const MS_ORTHO = 0x22A5; +sal_Unicode const MS_DOTSLOW = 0x2026; +sal_Unicode const MS_DOTSAXIS = 0x22EF; +sal_Unicode const MS_DOTSVERT = 0x22EE; +sal_Unicode const MS_DOTSUP = 0x22F0; +sal_Unicode const MS_DOTSDOWN = 0x22F1; +sal_Unicode const MS_TRANSR = 0x22B6; +sal_Unicode const MS_TRANSL = 0x22B7; +sal_Unicode const MS_BACKSLASH = 0x2216; +sal_Unicode const MS_NEG = 0x00AC; + +sal_Unicode const MS_FORALL = 0x2200; +sal_Unicode const MS_NABLA = 0x2207; +sal_Unicode const MS_PROD = 0x220F; +sal_Unicode const MS_COPROD = 0x2210; +sal_Unicode const MS_SUM = 0x2211; +sal_Unicode const MS_SQRT = 0x221A; +sal_Unicode const MS_INT = 0x222B; +sal_Unicode const MS_IINT = 0x222C; +sal_Unicode const MS_IIINT = 0x222D; +sal_Unicode const MS_LINT = 0x222E; +sal_Unicode const MS_LLINT = 0x222F; +sal_Unicode const MS_LLLINT = 0x2230; + +sal_Unicode const MS_GRAVE = 0x0060; +sal_Unicode const MS_COMBGRAVE = 0x0300; +sal_Unicode const MS_ACUTE = 0x00B4; +sal_Unicode const MS_COMBACUTE = 0x0301; +sal_Unicode const MS_HAT = 0x005E; +sal_Unicode const MS_COMBHAT = 0x0302; +sal_Unicode const MS_TILDE = 0x007E; +sal_Unicode const MS_COMBTILDE = 0x0303; +sal_Unicode const MS_BAR = 0x00AF; +sal_Unicode const MS_COMBBAR = 0x0304; +sal_Unicode const MS_COMBOVERLINE = 0x0305; +sal_Unicode const MS_BREVE = 0x02D8; +sal_Unicode const MS_COMBBREVE = 0x0306; +sal_Unicode const MS_CIRCLE = 0x02DA; +sal_Unicode const MS_COMBCIRCLE = 0x030A; +sal_Unicode const MS_CHECK = 0x02C7; +sal_Unicode const MS_COMBCHECK = 0x030C; +sal_Unicode const MS_HARPOON = 0x20D1; +sal_Unicode const MS_VEC = 0x20D7; +sal_Unicode const MS_DOT = 0x02D9; +sal_Unicode const MS_DDOT = 0x00A8; +sal_Unicode const MS_COMBDOT = 0x0307; +sal_Unicode const MS_COMBDDOT = 0x0308; +sal_Unicode const MS_DDDOT = 0x20DB; +sal_Unicode const MS_AND = 0x2227; +sal_Unicode const MS_OR = 0x2228; +sal_Unicode const MS_NI = 0x220B; +sal_Unicode const MS_EMPTYSET = 0x2205; + +sal_Unicode const MS_LPARENT = 0x0028; +sal_Unicode const MS_RPARENT = 0x0029; +sal_Unicode const MS_LBRACKET = 0x005B; +sal_Unicode const MS_RBRACKET = 0x005D; +sal_Unicode const MS_LBRACE = 0x007B; +sal_Unicode const MS_RBRACE = 0x007D; +sal_Unicode const MS_LCEIL = 0x2308; +sal_Unicode const MS_RCEIL = 0x2309; +sal_Unicode const MS_LFLOOR = 0x230A; +sal_Unicode const MS_RFLOOR = 0x230B; +sal_Unicode const MS_LANGLE = 0x2329; +sal_Unicode const MS_RANGLE = 0x232A; +sal_Unicode const MS_LDBRACKET = 0x27E6; +sal_Unicode const MS_RDBRACKET = 0x27E7; +sal_Unicode const MS_LMATHANGLE = 0x27E8; +sal_Unicode const MS_RMATHANGLE = 0x27E9; + +sal_Unicode const MS_PLACE = 0x2751; + +sal_Unicode const MS_LAMBDABAR = 0x019B; +sal_Unicode const MS_HBAR = 0x210F; +sal_Unicode const MS_LEFTARROW = 0x2190; +sal_Unicode const MS_UPARROW = 0x2191; +sal_Unicode const MS_RIGHTARROW = 0x2192; +sal_Unicode const MS_DOWNARROW = 0x2193; + +sal_Unicode const MS_SETN = 0x2115; +sal_Unicode const MS_SETZ = 0x2124; +sal_Unicode const MS_SETQ = 0x211A; +sal_Unicode const MS_SETR = 0x211D; +sal_Unicode const MS_SETC = 0x2102; + +sal_Unicode const MS_PERCENT = 0x0025; + +sal_Unicode const MS_PRECEDES = 0x227A; +sal_Unicode const MS_PRECEDESEQUAL = 0x227C; +sal_Unicode const MS_PRECEDESEQUIV = 0x227E; +sal_Unicode const MS_SUCCEEDS = 0x227B; +sal_Unicode const MS_SUCCEEDSEQUAL = 0x227D; +sal_Unicode const MS_SUCCEEDSEQUIV = 0x227F; +sal_Unicode const MS_NOTPRECEDES = 0x2280; +sal_Unicode const MS_NOTSUCCEEDS = 0x2281; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/unomodel.hxx b/starmath/inc/unomodel.hxx new file mode 100644 index 000000000..f18b09381 --- /dev/null +++ b/starmath/inc/unomodel.hxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_UNOMODEL_HXX +#define INCLUDED_STARMATH_INC_UNOMODEL_HXX + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/view/XRenderable.hpp> + +#include <sfx2/sfxbasemodel.hxx> +#include <comphelper/propertysethelper.hxx> +#include <vcl/print.hxx> +#include <oox/mathml/export.hxx> +#include <oox/mathml/import.hxx> +#include <memory> + + +#define PRTUIOPT_TITLE_ROW "TitleRow" +#define PRTUIOPT_FORMULA_TEXT "FormulaText" +#define PRTUIOPT_BORDER "Border" +#define PRTUIOPT_PRINT_FORMAT "PrintFormat" +#define PRTUIOPT_PRINT_SCALE "PrintScale" + +class SmPrintUIOptions : public vcl::PrinterOptionsHelper +{ +public: + SmPrintUIOptions(); +}; + + +class SmModel final : public SfxBaseModel, + public comphelper::PropertySetHelper, + public css::lang::XServiceInfo, + public css::view::XRenderable, + public oox::FormulaExportBase, + public oox::FormulaImportBase +{ + std::unique_ptr<SmPrintUIOptions> m_pPrintUIOptions; + + virtual void _setPropertyValues( const comphelper::PropertyMapEntry** ppEntries, const css::uno::Any* pValues ) override; + virtual void _getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, css::uno::Any* pValue ) override; +public: + explicit SmModel( SfxObjectShell *pObjSh ); + virtual ~SmModel() throw () override; + + //XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire( ) throw() override; + virtual void SAL_CALL release( ) throw() override; + + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId(); + + //XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + //XRenderable + virtual sal_Int32 SAL_CALL getRendererCount( const css::uno::Any& rSelection, const css::uno::Sequence< css::beans::PropertyValue >& rxOptions ) override; + virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getRenderer( sal_Int32 nRenderer, const css::uno::Any& rSelection, const css::uno::Sequence< css::beans::PropertyValue >& rxOptions ) override; + virtual void SAL_CALL render( sal_Int32 nRenderer, const css::uno::Any& rSelection, const css::uno::Sequence< css::beans::PropertyValue >& rxOptions ) override; + + //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; + + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& xParent ) override; + + // oox::FormulaExportBase + virtual void writeFormulaOoxml(::sax_fastparser::FSHelperPtr pSerializer, + oox::core::OoxmlVersion version, + oox::drawingml::DocumentType documentType, sal_Int8 nAlign) override; + virtual void writeFormulaRtf(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding) override; + // oox::FormulaImportBase + virtual void readFormulaOoxml( oox::formulaimport::XmlStream& stream ) override; + virtual Size getFormulaSize() const override; +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/utility.hxx b/starmath/inc/utility.hxx new file mode 100644 index 000000000..2a462b98d --- /dev/null +++ b/starmath/inc/utility.hxx @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_UTILITY_HXX +#define INCLUDED_STARMATH_INC_UTILITY_HXX + +#include <sal/config.h> + +#include <sal/log.hxx> +#include <vcl/font.hxx> +#include <vcl/weld.hxx> +#include <tools/fract.hxx> +#include <deque> + +inline long SmPtsTo100th_mm(long nNumPts) + // returns the length (in 100th of mm) that corresponds to the length + // 'nNumPts' (in units points). + // 72.27 [pt] = 1 [inch] = 2,54 [cm] = 2540 [100th of mm]. + // result is being rounded to the nearest integer. +{ + SAL_WARN_IF( nNumPts < 0, "starmath", "Ooops..." ); + // broken into multiple and fraction of 'nNumPts' to reduce chance + // of overflow + // (7227 / 2) is added in order to round to the nearest integer + return 35 * nNumPts + (nNumPts * 1055L + (7227 / 2)) / 7227L; +} + + +inline Fraction Sm100th_mmToPts(long nNum100th_mm) + // returns the length (in points) that corresponds to the length + // 'nNum100th_mm' (in 100th of mm). +{ + SAL_WARN_IF( nNum100th_mm < 0, "starmath", "Ooops..." ); + return Fraction(7227L, 254000L) * Fraction(nNum100th_mm); +} + + +inline long SmRoundFraction(const Fraction &rFrac) +{ + SAL_WARN_IF( rFrac <= Fraction(), "starmath", "Ooops..." ); + return (rFrac.GetNumerator() + rFrac.GetDenominator() / 2) / rFrac.GetDenominator(); +} + + +class SmViewShell; +SmViewShell * SmGetActiveView(); + + +// SmFace + + +bool IsItalic( const vcl::Font &rFont ); +bool IsBold( const vcl::Font &rFont ); + +class SmFace final : public vcl::Font +{ + long nBorderWidth; + + void Impl_Init(); + +public: + SmFace() : + Font(), nBorderWidth(-1) { Impl_Init(); } + SmFace(const Font& rFont) : + Font(rFont), nBorderWidth(-1) { Impl_Init(); } + SmFace(const OUString& rName, const Size& rSize) : + Font(rName, rSize), nBorderWidth(-1) { Impl_Init(); } + + SmFace(const SmFace &rFace) : + Font(rFace), nBorderWidth(-1) { Impl_Init(); } + + // overloaded version in order to supply a min value + // for font size (height). (Also used in ctor's to do so.) + void SetSize(const Size& rSize); + + void SetBorderWidth(long nWidth) { nBorderWidth = nWidth; } + long GetBorderWidth() const; + long GetDefaultBorderWidth() const { return GetFontSize().Height() / 20 ; } + void FreezeBorderWidth() { nBorderWidth = GetDefaultBorderWidth(); } + + SmFace & operator = (const SmFace &rFace); +}; + +SmFace & operator *= (SmFace &rFace, const Fraction &rFrac); + + +// SmFontPickList + + +class SmFontDialog; + +class SmFontPickList +{ +protected: + sal_uInt16 nMaxItems; + std::deque<vcl::Font> aFontVec; + +public: + explicit SmFontPickList(sal_uInt16 nMax = 5) : nMaxItems(nMax) {} + virtual ~SmFontPickList() { Clear(); } + + virtual void Insert(const vcl::Font &rFont); + + void Clear(); + vcl::Font Get(sal_uInt16 nPos = 0) const; + + SmFontPickList& operator = (const SmFontPickList& rList); + + void ReadFrom(const SmFontDialog& rDialog); + void WriteTo(SmFontDialog& rDialog) const; +}; + + +// SmFontPickListBox + + +class SmFontPickListBox final : public SmFontPickList +{ +private: + std::unique_ptr<weld::ComboBox> m_xWidget; + + DECL_LINK(SelectHdl, weld::ComboBox&, void); + +public: + SmFontPickListBox(std::unique_ptr<weld::ComboBox> pWidget); + SmFontPickListBox& operator = (const SmFontPickList& rList); + virtual void Insert(const vcl::Font &rFont) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/view.hxx b/starmath/inc/view.hxx new file mode 100644 index 000000000..b3625b7dc --- /dev/null +++ b/starmath/inc/view.hxx @@ -0,0 +1,319 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STARMATH_INC_VIEW_HXX +#define INCLUDED_STARMATH_INC_VIEW_HXX + +#include <sal/config.h> + +#include <memory> + +#include <rtl/ref.hxx> +#include <sfx2/dockwin.hxx> +#include <sfx2/viewsh.hxx> +#include <svtools/scrwin.hxx> +#include <sfx2/ctrlitem.hxx> +#include <sfx2/shell.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/timer.hxx> +#include "document.hxx" +#include "edit.hxx" + +class SmViewShell; +class SmPrintUIOptions; +class SmGraphicAccessible; +class SmNode; + +namespace svtools { class ColorConfig; } + +class SmGraphicWindow final : public ScrollableWindow +{ +public: + bool IsCursorVisible() const + { + return bIsCursorVisible; + } + void ShowCursor(bool bShow); + bool IsLineVisible() const + { + return bIsLineVisible; + } + void ShowLine(bool bShow); + const SmNode * SetCursorPos(sal_uInt16 nRow, sal_uInt16 nCol); + + explicit SmGraphicWindow(SmViewShell* pShell); + virtual ~SmGraphicWindow() override; + virtual void dispose() override; + + // Window + virtual void ApplySettings(vcl::RenderContext&) override; + virtual void MouseButtonDown(const MouseEvent &rMEvt) override; + virtual void MouseMove(const MouseEvent &rMEvt) override; + virtual void GetFocus() override; + virtual void LoseFocus() override; + + SmViewShell* GetView() + { + return pViewShell; + } + + using Window::SetZoom; + void SetZoom(sal_uInt16 Factor); + using Window::GetZoom; + sal_uInt16 GetZoom() const + { + return nZoom; + } + + const Point& GetFormulaDrawPos() const + { + return aFormulaDrawPos; + } + + void ZoomToFitInWindow(); + using ScrollableWindow::SetTotalSize; + void SetTotalSize(); + + // for Accessibility + virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override; + + using Window::GetAccessible; + SmGraphicAccessible* GetAccessible_Impl() + { + return mxAccessible.get(); + } + +private: + void SetIsCursorVisible(bool bVis) + { + bIsCursorVisible = bVis; + } + using Window::SetCursor; + void SetCursor(const SmNode *pNode); + void SetCursor(const tools::Rectangle &rRect); + bool IsInlineEditEnabled() const; + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + virtual void KeyInput(const KeyEvent& rKEvt) override; + virtual void Command(const CommandEvent& rCEvt) override; + virtual void StateChanged( StateChangedType eChanged ) override; + + void RepaintViewShellDoc(); + DECL_LINK(CaretBlinkTimerHdl, Timer *, void); + void CaretBlinkInit(); + void CaretBlinkStart(); + void CaretBlinkStop(); + + Point aFormulaDrawPos; + // old style editing pieces + tools::Rectangle aCursorRect; + bool bIsCursorVisible; + bool bIsLineVisible; + AutoTimer aCaretBlinkTimer; + rtl::Reference<SmGraphicAccessible> mxAccessible; + SmViewShell* pViewShell; + sal_uInt16 nZoom; +}; + +class SmGraphicController final : public SfxControllerItem +{ + SmGraphicWindow &rGraphic; +public: + SmGraphicController(SmGraphicWindow &, sal_uInt16, SfxBindings & ); + virtual void StateChanged(sal_uInt16 nSID, + SfxItemState eState, + const SfxPoolItem* pState) override; +}; + +class SmEditController final : public SfxControllerItem +{ + SmEditWindow &rEdit; + +public: + SmEditController(SmEditWindow &, sal_uInt16, SfxBindings & ); + + virtual void StateChanged(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState) override; +}; + +class SmCmdBoxWindow : public SfxDockingWindow +{ + VclPtr<SmEditWindow> aEdit; + SmEditController aController; + bool bExiting; + + Timer aInitialFocusTimer; + + DECL_LINK(InitialFocusTimerHdl, Timer *, void); + +protected: + + // Window + virtual void GetFocus() override; + virtual void Resize() override; + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual void StateChanged( StateChangedType nStateChange ) override; + + virtual Size CalcDockingSize(SfxChildAlignment eAlign) override; + virtual SfxChildAlignment CheckAlignment(SfxChildAlignment eActual, + SfxChildAlignment eWish) override; + + virtual void ToggleFloatingMode() override; + +public: + SmCmdBoxWindow(SfxBindings *pBindings, + SfxChildWindow *pChildWindow, + Window *pParent); + + virtual ~SmCmdBoxWindow () override; + virtual void dispose() override; + + void AdjustPosition(); + + SmEditWindow& GetEditWindow() + { + return *aEdit; + } + SmViewShell* GetView(); +}; + +class SmCmdBoxWrapper final : public SfxChildWindow +{ + SFX_DECL_CHILDWINDOW_WITHID(SmCmdBoxWrapper); + + SmCmdBoxWrapper(vcl::Window* pParentWindow, sal_uInt16 nId, SfxBindings* pBindings, SfxChildWinInfo* pInfo); + +public: + + SmEditWindow& GetEditWindow() + { + return static_cast<SmCmdBoxWindow *>(GetWindow())->GetEditWindow(); + } +}; + +namespace sfx2 { class FileDialogHelper; } +struct SmViewShell_Impl; + +class SmViewShell: public SfxViewShell +{ + std::unique_ptr<SmViewShell_Impl> mpImpl; + + VclPtr<SmGraphicWindow> mpGraphic; + SmGraphicController maGraphicController; + OUString maStatusText; + + bool mbPasteState; + + DECL_LINK( DialogClosedHdl, sfx2::FileDialogHelper*, void ); + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + /** Used to determine whether insertions using SID_INSERTSPECIAL and SID_INSERTCOMMANDTEXT + * should be inserted into SmEditWindow or directly into the SmDocShell as done if the + * visual editor was last to have focus. + */ + bool mbInsertIntoEditWindow; +protected: + + static Size GetTextLineSize(OutputDevice const & rDevice, + const OUString& rLine); + static Size GetTextSize(OutputDevice const & rDevice, + const OUString& rText, + long MaxWidth); + static void DrawTextLine(OutputDevice& rDevice, + const Point& rPosition, + const OUString& rLine); + static void DrawText(OutputDevice& rDevice, + const Point& rPosition, + const OUString& rText, + sal_uInt16 MaxWidth); + + virtual SfxPrinter *GetPrinter(bool bCreate = false) override; + virtual sal_uInt16 SetPrinter(SfxPrinter *pNewPrinter, + SfxPrinterChangeFlags nDiffFlags = SFX_PRINTER_ALL) override; + + void Insert( SfxMedium& rMedium ); + void InsertFrom(SfxMedium &rMedium); + + virtual bool HasPrintOptionsPage() const override; + virtual std::unique_ptr<SfxTabPage> CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet &rOptions) override; + virtual void Deactivate(bool IsMDIActivate) override; + virtual void Activate(bool IsMDIActivate) override; + virtual void InnerResizePixel(const Point &rOfs, const Size &rSize, bool inplaceEditModeChange) override; + virtual void OuterResizePixel(const Point &rOfs, const Size &rSize) override; + virtual void QueryObjAreaPixel( tools::Rectangle& rRect ) const override; + virtual void SetZoomFactor( const Fraction &rX, const Fraction &rY ) override; + +public: + + SmViewShell(SfxViewFrame *pFrame, SfxViewShell *pOldSh); + virtual ~SmViewShell() override; + + SmDocShell * GetDoc() + { + return static_cast<SmDocShell *>( GetViewFrame()->GetObjectShell() ); + } + + SmEditWindow * GetEditWindow(); + + SmGraphicWindow& GetGraphicWindow() + { + return *mpGraphic; + } + const SmGraphicWindow& GetGraphicWindow() const + { + return *mpGraphic; + } + + void SetStatusText(const OUString& rText); + + void ShowError( const SmErrorDesc *pErrorDesc ); + void NextError(); + void PrevError(); + + SFX_DECL_INTERFACE(SFX_INTERFACE_SMA_START+SfxInterfaceId(2)) + SFX_DECL_VIEWFACTORY(SmViewShell); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + void Execute( SfxRequest& rReq ); + void GetState(SfxItemSet &); + + void Impl_Print( OutputDevice &rOutDev, const SmPrintUIOptions &rPrintUIOptions, + tools::Rectangle aOutRect ); + + /** Set bInsertIntoEditWindow so we know where to insert + * + * This method is called whenever SmGraphicWindow or SmEditWindow gets focus, + * so that when text is inserted from catalog or elsewhere we know whether to + * insert for the visual editor, or the text editor. + */ + void SetInsertIntoEditWindow(bool bEditWindowHadFocusLast){ + mbInsertIntoEditWindow = bEditWindowHadFocusLast; + } + bool IsInlineEditEnabled() const; + +private: + void ZoomByItemSet(const SfxItemSet *pSet); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/visitors.hxx b/starmath/inc/visitors.hxx new file mode 100644 index 000000000..3f903aba8 --- /dev/null +++ b/starmath/inc/visitors.hxx @@ -0,0 +1,462 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_INC_VISITORS_HXX +#define INCLUDED_STARMATH_INC_VISITORS_HXX + +#include <sal/config.h> + +#include <sal/log.hxx> + +#include "node.hxx" +#include "caret.hxx" +#include <memory> + +/** Base class for visitors that visits a tree of SmNodes + * @remarks all methods have been left abstract to ensure that implementers + * don't forget to implement one. + */ +class SmVisitor +{ +public: + virtual void Visit( SmTableNode* pNode ) = 0; + virtual void Visit( SmBraceNode* pNode ) = 0; + virtual void Visit( SmBracebodyNode* pNode ) = 0; + virtual void Visit( SmOperNode* pNode ) = 0; + virtual void Visit( SmAlignNode* pNode ) = 0; + virtual void Visit( SmAttributNode* pNode ) = 0; + virtual void Visit( SmFontNode* pNode ) = 0; + virtual void Visit( SmUnHorNode* pNode ) = 0; + virtual void Visit( SmBinHorNode* pNode ) = 0; + virtual void Visit( SmBinVerNode* pNode ) = 0; + virtual void Visit( SmBinDiagonalNode* pNode ) = 0; + virtual void Visit( SmSubSupNode* pNode ) = 0; + virtual void Visit( SmMatrixNode* pNode ) = 0; + virtual void Visit( SmPlaceNode* pNode ) = 0; + virtual void Visit( SmTextNode* pNode ) = 0; + virtual void Visit( SmSpecialNode* pNode ) = 0; + virtual void Visit( SmGlyphSpecialNode* pNode ) = 0; + virtual void Visit( SmMathSymbolNode* pNode ) = 0; + virtual void Visit( SmBlankNode* pNode ) = 0; + virtual void Visit( SmErrorNode* pNode ) = 0; + virtual void Visit( SmLineNode* pNode ) = 0; + virtual void Visit( SmExpressionNode* pNode ) = 0; + virtual void Visit( SmPolyLineNode* pNode ) = 0; + virtual void Visit( SmRootNode* pNode ) = 0; + virtual void Visit( SmRootSymbolNode* pNode ) = 0; + virtual void Visit( SmRectangleNode* pNode ) = 0; + virtual void Visit( SmVerticalBraceNode* pNode ) = 0; + +protected: + ~SmVisitor() {} +}; + +// SmDefaultingVisitor + + +/** Visitor that uses DefaultVisit for handling visits by default + * + * This abstract baseclass is useful for visitors where many methods share the same + * implementation. + */ +class SmDefaultingVisitor : public SmVisitor +{ +public: + void Visit( SmTableNode* pNode ) override; + void Visit( SmBraceNode* pNode ) override; + void Visit( SmBracebodyNode* pNode ) override; + void Visit( SmOperNode* pNode ) override; + void Visit( SmAlignNode* pNode ) override; + void Visit( SmAttributNode* pNode ) override; + void Visit( SmFontNode* pNode ) override; + void Visit( SmUnHorNode* pNode ) override; + void Visit( SmBinHorNode* pNode ) override; + void Visit( SmBinVerNode* pNode ) override; + void Visit( SmBinDiagonalNode* pNode ) override; + void Visit( SmSubSupNode* pNode ) override; + void Visit( SmMatrixNode* pNode ) override; + void Visit( SmPlaceNode* pNode ) override; + void Visit( SmTextNode* pNode ) override; + void Visit( SmSpecialNode* pNode ) override; + void Visit( SmGlyphSpecialNode* pNode ) override; + void Visit( SmMathSymbolNode* pNode ) override; + void Visit( SmBlankNode* pNode ) override; + void Visit( SmErrorNode* pNode ) override; + void Visit( SmLineNode* pNode ) override; + void Visit( SmExpressionNode* pNode ) override; + void Visit( SmPolyLineNode* pNode ) override; + void Visit( SmRootNode* pNode ) override; + void Visit( SmRootSymbolNode* pNode ) override; + void Visit( SmRectangleNode* pNode ) override; + void Visit( SmVerticalBraceNode* pNode ) override; +protected: + ~SmDefaultingVisitor() {} + + /** Method invoked by Visit methods by default */ + virtual void DefaultVisit( SmNode* pNode ) = 0; +}; + +// SmCaretDrawingVisitor + +/** Visitor for drawing a caret position */ +class SmCaretDrawingVisitor final : public SmDefaultingVisitor +{ +public: + /** Given position and device this constructor will draw the caret */ + SmCaretDrawingVisitor( OutputDevice& rDevice, SmCaretPos position, Point offset, bool caretVisible ); + virtual ~SmCaretDrawingVisitor() {} + void Visit( SmTextNode* pNode ) override; + using SmDefaultingVisitor::Visit; +private: + OutputDevice &mrDev; + SmCaretPos maPos; + /** Offset to draw from */ + Point maOffset; + bool mbCaretVisible; + + /** Default method for drawing pNodes */ + void DefaultVisit( SmNode* pNode ) override; +}; + +// SmCaretPos2LineVisitor + +/** Visitor getting a line from a caret position */ +class SmCaretPos2LineVisitor final : public SmDefaultingVisitor +{ +public: + /** Given position and device this constructor will compute a line for the caret */ + SmCaretPos2LineVisitor( OutputDevice *pDevice, SmCaretPos position ) + : mpDev( pDevice ) + , maPos( position ) + { + SAL_WARN_IF( !position.IsValid(), "starmath", "Cannot draw invalid position!" ); + + maPos.pSelectedNode->Accept( this ); + } + virtual ~SmCaretPos2LineVisitor() {} + void Visit( SmTextNode* pNode ) override; + using SmDefaultingVisitor::Visit; + const SmCaretLine& GetResult( ) const { + return maLine; + } +private: + SmCaretLine maLine; + VclPtr<OutputDevice> mpDev; + SmCaretPos maPos; + + /** Default method for computing lines for pNodes */ + void DefaultVisit( SmNode* pNode ) override; +}; + +// SmDrawingVisitor + +/** Visitor for drawing SmNodes to OutputDevice */ +class SmDrawingVisitor final : public SmVisitor +{ +public: + /** Create an instance of SmDrawingVisitor, and use it to draw a formula + * @param rDevice Device to draw on + * @param position Offset on device to draw the formula + * @param pTree Formula tree to draw + * @remarks This constructor will do the drawing, no need to anything more. + */ + SmDrawingVisitor( OutputDevice &rDevice, Point position, SmNode* pTree ) + : mrDev( rDevice ) + , maPosition( position ) + { + pTree->Accept( this ); + } + virtual ~SmDrawingVisitor() {} + void Visit( SmTableNode* pNode ) override; + void Visit( SmBraceNode* pNode ) override; + void Visit( SmBracebodyNode* pNode ) override; + void Visit( SmOperNode* pNode ) override; + void Visit( SmAlignNode* pNode ) override; + void Visit( SmAttributNode* pNode ) override; + void Visit( SmFontNode* pNode ) override; + void Visit( SmUnHorNode* pNode ) override; + void Visit( SmBinHorNode* pNode ) override; + void Visit( SmBinVerNode* pNode ) override; + void Visit( SmBinDiagonalNode* pNode ) override; + void Visit( SmSubSupNode* pNode ) override; + void Visit( SmMatrixNode* pNode ) override; + void Visit( SmPlaceNode* pNode ) override; + void Visit( SmTextNode* pNode ) override; + void Visit( SmSpecialNode* pNode ) override; + void Visit( SmGlyphSpecialNode* pNode ) override; + void Visit( SmMathSymbolNode* pNode ) override; + void Visit( SmBlankNode* pNode ) override; + void Visit( SmErrorNode* pNode ) override; + void Visit( SmLineNode* pNode ) override; + void Visit( SmExpressionNode* pNode ) override; + void Visit( SmPolyLineNode* pNode ) override; + void Visit( SmRootNode* pNode ) override; + void Visit( SmRootSymbolNode* pNode ) override; + void Visit( SmRectangleNode* pNode ) override; + void Visit( SmVerticalBraceNode* pNode ) override; +private: + /** Draw the children of a pNode + * This the default method, use by most pNodes + */ + void DrawChildren( SmStructureNode* pNode ); + + /** Draw an SmTextNode or a subclass of this */ + void DrawTextNode( SmTextNode* pNode ); + /** Draw an SmSpecialNode or a subclass of this */ + void DrawSpecialNode( SmSpecialNode* pNode ); + /** OutputDevice to draw on */ + OutputDevice& mrDev; + /** Position to draw on the mrDev + * @remarks This variable is used to pass parameters in DrawChildren( ), this means + that after a call to DrawChildren( ) the contents of this method is undefined + so if needed cache it locally on the stack. + */ + Point maPosition; +}; + +// SmSetSelectionVisitor + +/** Set Selection Visitor + * Sets the IsSelected( ) property on all SmNodes of the tree + */ +class SmSetSelectionVisitor final : public SmDefaultingVisitor +{ +public: + SmSetSelectionVisitor( SmCaretPos startPos, SmCaretPos endPos, SmNode* pNode); + virtual ~SmSetSelectionVisitor() {} + void Visit( SmBinHorNode* pNode ) override; + void Visit( SmUnHorNode* pNode ) override; + void Visit( SmFontNode* pNode ) override; + void Visit( SmTextNode* pNode ) override; + void Visit( SmExpressionNode* pNode ) override; + void Visit( SmLineNode* pNode ) override; + void Visit( SmAlignNode* pNode ) override; + using SmDefaultingVisitor::Visit; + /** Set IsSelected on all pNodes of pSubTree */ + static void SetSelectedOnAll( SmNode* pSubTree, bool IsSelected = true ); +private: + /** Visit a selectable pNode + * Can be used to handle pNodes that can be selected, that doesn't have more SmCaretPos' + * than 0 and 1 inside them. SmTextNode should be handle separately! + * Also note that pNodes such as SmBinVerNode cannot be selected, don't this method for + * it. + */ + void DefaultVisit( SmNode* pNode ) override; + void VisitCompositionNode( SmStructureNode* pNode ); + /** Caret position where the selection starts */ + SmCaretPos maStartPos; + /** Caret position where the selection ends */ + SmCaretPos maEndPos; + /** The current state of this visitor + * This property changes when the visitor meets either maStartPos + * or maEndPos. This means that anything visited in between will be + * selected. + */ + bool mbSelecting; +}; + + +// SmCaretPosGraphBuildingVisitor + + +/** A visitor for building a SmCaretPosGraph + * + * Visit invariant: + * Each pNode, except SmExpressionNode, SmBinHorNode and a few others, constitutes an entry + * in a line. Consider the line entry "H", this entry creates one carat position, here + * denoted by | in "H|". + * + * Parameter variables: + * The following variables are used to transfer parameters into calls and results out + * of calls. + * pRightMost : SmCaretPosGraphEntry* + * + * Prior to a Visit call: + * pRightMost: A pointer to right most position in front of the current line entry. + * + * After a Visit call: + * pRightMost: A pointer to the right most position in the called line entry, if no there's + * no caret positions in called line entry don't change this variable. + */ +class SmCaretPosGraphBuildingVisitor final : public SmVisitor +{ +public: + /** Builds a caret position graph for pRootNode */ + explicit SmCaretPosGraphBuildingVisitor( SmNode* pRootNode ); + virtual ~SmCaretPosGraphBuildingVisitor(); + void Visit( SmTableNode* pNode ) override; + void Visit( SmBraceNode* pNode ) override; + void Visit( SmBracebodyNode* pNode ) override; + void Visit( SmOperNode* pNode ) override; + void Visit( SmAlignNode* pNode ) override; + void Visit( SmAttributNode* pNode ) override; + void Visit( SmFontNode* pNode ) override; + void Visit( SmUnHorNode* pNode ) override; + void Visit( SmBinHorNode* pNode ) override; + void Visit( SmBinVerNode* pNode ) override; + void Visit( SmBinDiagonalNode* pNode ) override; + void Visit( SmSubSupNode* pNode ) override; + void Visit( SmMatrixNode* pNode ) override; + void Visit( SmPlaceNode* pNode ) override; + void Visit( SmTextNode* pNode ) override; + void Visit( SmSpecialNode* pNode ) override; + void Visit( SmGlyphSpecialNode* pNode ) override; + void Visit( SmMathSymbolNode* pNode ) override; + void Visit( SmBlankNode* pNode ) override; + void Visit( SmErrorNode* pNode ) override; + void Visit( SmLineNode* pNode ) override; + void Visit( SmExpressionNode* pNode ) override; + void Visit( SmPolyLineNode* pNode ) override; + void Visit( SmRootNode* pNode ) override; + void Visit( SmRootSymbolNode* pNode ) override; + void Visit( SmRectangleNode* pNode ) override; + void Visit( SmVerticalBraceNode* pNode ) override; + SmCaretPosGraph* takeGraph() + { + return mpGraph.release(); + } +private: + SmCaretPosGraphEntry* mpRightMost; + std::unique_ptr<SmCaretPosGraph> mpGraph; +}; + +// SmCloningVisitor + +/** Visitor for cloning a pNode + * + * This visitor creates deep clones. + */ +class SmCloningVisitor final : public SmVisitor +{ +public: + SmCloningVisitor() + : mpResult(nullptr) + {} + virtual ~SmCloningVisitor() {} + void Visit( SmTableNode* pNode ) override; + void Visit( SmBraceNode* pNode ) override; + void Visit( SmBracebodyNode* pNode ) override; + void Visit( SmOperNode* pNode ) override; + void Visit( SmAlignNode* pNode ) override; + void Visit( SmAttributNode* pNode ) override; + void Visit( SmFontNode* pNode ) override; + void Visit( SmUnHorNode* pNode ) override; + void Visit( SmBinHorNode* pNode ) override; + void Visit( SmBinVerNode* pNode ) override; + void Visit( SmBinDiagonalNode* pNode ) override; + void Visit( SmSubSupNode* pNode ) override; + void Visit( SmMatrixNode* pNode ) override; + void Visit( SmPlaceNode* pNode ) override; + void Visit( SmTextNode* pNode ) override; + void Visit( SmSpecialNode* pNode ) override; + void Visit( SmGlyphSpecialNode* pNode ) override; + void Visit( SmMathSymbolNode* pNode ) override; + void Visit( SmBlankNode* pNode ) override; + void Visit( SmErrorNode* pNode ) override; + void Visit( SmLineNode* pNode ) override; + void Visit( SmExpressionNode* pNode ) override; + void Visit( SmPolyLineNode* pNode ) override; + void Visit( SmRootNode* pNode ) override; + void Visit( SmRootSymbolNode* pNode ) override; + void Visit( SmRectangleNode* pNode ) override; + void Visit( SmVerticalBraceNode* pNode ) override; + /** Clone a pNode */ + SmNode* Clone( SmNode* pNode ); +private: + SmNode* mpResult; + /** Clone children of pSource and give them to pTarget */ + void CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget ); + /** Clone attributes on a pNode */ + static void CloneNodeAttr( SmNode const * pSource, SmNode* pTarget ); +}; + + +// SmSelectionDrawingVisitor + +class SmSelectionDrawingVisitor final : public SmDefaultingVisitor +{ +public: + /** Draws a selection on rDevice for the selection on pTree */ + SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, const Point& rOffset ); + virtual ~SmSelectionDrawingVisitor() {} + void Visit( SmTextNode* pNode ) override; + using SmDefaultingVisitor::Visit; +private: + /** Reference to drawing device */ + OutputDevice& mrDev; + /** True if aSelectionArea have been initialized */ + bool mbHasSelectionArea; + /** The current area that is selected */ + tools::Rectangle maSelectionArea; + /** Extend the area that must be selected */ + void ExtendSelectionArea(const tools::Rectangle& rArea); + /** Default visiting method */ + void DefaultVisit( SmNode* pNode ) override; + /** Visit the children of a given pNode */ + void VisitChildren( SmNode* pNode ); +}; + +// SmNodeToTextVisitor + +/** Extract command text from pNodes */ +class SmNodeToTextVisitor final : public SmVisitor +{ +public: + SmNodeToTextVisitor( SmNode* pNode, OUString &rText ); + virtual ~SmNodeToTextVisitor() {} + + void Visit( SmTableNode* pNode ) override; + void Visit( SmBraceNode* pNode ) override; + void Visit( SmBracebodyNode* pNode ) override; + void Visit( SmOperNode* pNode ) override; + void Visit( SmAlignNode* pNode ) override; + void Visit( SmAttributNode* pNode ) override; + void Visit( SmFontNode* pNode ) override; + void Visit( SmUnHorNode* pNode ) override; + void Visit( SmBinHorNode* pNode ) override; + void Visit( SmBinVerNode* pNode ) override; + void Visit( SmBinDiagonalNode* pNode ) override; + void Visit( SmSubSupNode* pNode ) override; + void Visit( SmMatrixNode* pNode ) override; + void Visit( SmPlaceNode* pNode ) override; + void Visit( SmTextNode* pNode ) override; + void Visit( SmSpecialNode* pNode ) override; + void Visit( SmGlyphSpecialNode* pNode ) override; + void Visit( SmMathSymbolNode* pNode ) override; + void Visit( SmBlankNode* pNode ) override; + void Visit( SmErrorNode* pNode ) override; + void Visit( SmLineNode* pNode ) override; + void Visit( SmExpressionNode* pNode ) override; + void Visit( SmPolyLineNode* pNode ) override; + void Visit( SmRootNode* pNode ) override; + void Visit( SmRootSymbolNode* pNode ) override; + void Visit( SmRectangleNode* pNode ) override; + void Visit( SmVerticalBraceNode* pNode ) override; +private: + /** Extract text from a pNode that constitutes a line */ + void LineToText( SmNode* pNode ) { + Separate( ); + if( pNode ) + pNode->Accept( this ); + Separate( ); + } + void Append( const OUString &rText ) { + maCmdText.append( rText ); + } + /** Append a blank for separation, if needed */ + void Separate( ){ + if( maCmdText.isEmpty() || maCmdText[ maCmdText.getLength() - 1 ] != ' ' ) + maCmdText.append(' '); + } + /** Output text generated from the pNodes */ + OUStringBuffer maCmdText; +}; + +#endif // INCLUDED_STARMATH_INC_VISITORS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/qa/cppunit/mock-visitor.hxx b/starmath/qa/cppunit/mock-visitor.hxx new file mode 100644 index 000000000..8dab85336 --- /dev/null +++ b/starmath/qa/cppunit/mock-visitor.hxx @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_STARMATH_QA_CPPUNIT_MOCK_VISITOR_HXX +#define INCLUDED_STARMATH_QA_CPPUNIT_MOCK_VISITOR_HXX + +#include <cppunit/TestAssert.h> +#include <visitors.hxx> + +/** Simple visitor for testing SmVisitor */ +class MockVisitor : public SmVisitor +{ +public: + virtual ~MockVisitor() {} + + void Visit( SmTableNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmTableNode should have type SmNodeType::Table", + SmNodeType::Table, pNode->GetType()); + auto eTT = pNode->GetToken().eType; + CPPUNIT_ASSERT_MESSAGE("The type of SmTableNode's token should be either TEND, TBINOM, or TSTACK", + eTT == TEND || eTT == TBINOM || eTT == TSTACK); + VisitChildren( pNode ); + } + + void Visit( SmBraceNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmBraceNode should have type SmNodeType::Brace", + SmNodeType::Brace, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmBracebodyNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmBracebodyNode should have type SmNodeType::Bracebody", + SmNodeType::Bracebody, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmOperNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmOperNode should have type SmNodeType::Oper", + SmNodeType::Oper, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmAlignNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmAlignNode should have type SmNodeType::Align", + SmNodeType::Align, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmAttributNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmAttributNode should have type SmNodeType::Attribut", + SmNodeType::Attribut, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmFontNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmFontNode should have type SmNodeType::Font", + SmNodeType::Font, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmUnHorNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmUnHorNode should have type SmNodeType::UnHor", + SmNodeType::UnHor, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmBinHorNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmBinHorNode should have type SmNodeType::BinHor", + SmNodeType::BinHor, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmBinVerNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmBinVerNode should have type SmNodeType::BinVer", + SmNodeType::BinVer, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmBinDiagonalNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmBinDiagonalNode should have type SmNodeType::BinDiagonal", + SmNodeType::BinDiagonal, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmSubSupNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmSubSupNode should have type SmNodeType::SubSup", + SmNodeType::SubSup, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmMatrixNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmMatrixNode should have type SmNodeType::Matrix", + SmNodeType::Matrix, pNode->GetType()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmMatrixNode's token should be of type TMATRIX", + TMATRIX, pNode->GetToken().eType); + VisitChildren( pNode ); + } + + void Visit( SmPlaceNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmPlaceNode should have type SmNodeType::Place", + SmNodeType::Place, pNode->GetType()); + } + + void Visit( SmTextNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmTextNode should have type SmNodeType::Text", + SmNodeType::Text, pNode->GetType()); + } + + void Visit( SmSpecialNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmSpecialNode should have type SmNodeType::Special", + SmNodeType::Special, pNode->GetType()); + } + + void Visit( SmGlyphSpecialNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmGlyphSpecialNode should have type SmNodeType::GlyphSpecial", + SmNodeType::GlyphSpecial, pNode->GetType()); + } + + void Visit( SmMathSymbolNode* pNode ) override { + CPPUNIT_ASSERT_MESSAGE("SmMathSymbolNode should have type SmNodeType::Math or SmNodeType::MathIdent", + pNode->GetType() == SmNodeType::Math || pNode->GetType() == SmNodeType::MathIdent); + } + + void Visit( SmBlankNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmBlankNode should have type SmNodeType::Blank", + SmNodeType::Blank, pNode->GetType()); + } + + void Visit( SmErrorNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmErrorNode should have type SmNodeType::Error", + SmNodeType::Error, pNode->GetType()); + } + + void Visit( SmLineNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmLineNode should have type SmNodeType::Line", + SmNodeType::Line, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmExpressionNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmExpressionNode should have type SmNodeType::Expression", + SmNodeType::Expression, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmPolyLineNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmPolyLineNode should have type SmNodeType::PolyLine", + SmNodeType::PolyLine, pNode->GetType()); + } + + void Visit( SmRootNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmRootNode should have type SmNodeType::Root", + SmNodeType::Root, pNode->GetType()); + VisitChildren( pNode ); + } + + void Visit( SmRootSymbolNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmRootSymbolNode should have type SmNodeType::RootSymbol", + SmNodeType::RootSymbol, pNode->GetType()); + } + + void Visit( SmRectangleNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmRectangleNode should have type SmNodeType::Rectangle", + SmNodeType::Rectangle, pNode->GetType()); + } + + void Visit( SmVerticalBraceNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmVerticalBraceNode should have type SmNodeType::VerticalBrace", + SmNodeType::VerticalBrace, pNode->GetType()); + VisitChildren( pNode ); + } + +private: + /** Auxiliary method for visiting the children of a pNode */ + void VisitChildren( SmStructureNode* pNode ) { + for (auto pChild : *pNode) + { + if (pChild) + pChild->Accept(this); + } + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/qa/cppunit/test_cursor.cxx b/starmath/qa/cppunit/test_cursor.cxx new file mode 100644 index 000000000..080e7a274 --- /dev/null +++ b/starmath/qa/cppunit/test_cursor.cxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> +#include <test/bootstrapfixture.hxx> + +#include <vcl/virdev.hxx> +#include <sfx2/sfxmodelfactory.hxx> +#include <smdll.hxx> + +#include <document.hxx> +#include <node.hxx> +#include <cursor.hxx> + +#include <memory> + +typedef tools::SvRef<SmDocShell> SmDocShellRef; + +using namespace ::com::sun::star; + +namespace { + +class Test : public test::BootstrapFixture { + +public: + // init + virtual void setUp() override; + virtual void tearDown() override; + + // tests + void testCopyPaste(); + void testCopySelectPaste(); + void testCutPaste(); + void testCutSelectPaste(); + + CPPUNIT_TEST_SUITE(Test); + CPPUNIT_TEST(testCopyPaste); + CPPUNIT_TEST(testCopySelectPaste); + CPPUNIT_TEST(testCutPaste); + CPPUNIT_TEST(testCutSelectPaste); + CPPUNIT_TEST_SUITE_END(); + +private: + SmDocShellRef xDocShRef; +}; + +void Test::setUp() +{ + BootstrapFixture::setUp(); + + SmGlobals::ensure(); + + xDocShRef = new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT); +} + +void Test::tearDown() +{ + xDocShRef.clear(); + BootstrapFixture::tearDown(); +} + +void Test::testCopyPaste() +{ + OUString const sInput("a * b + c"); + auto xTree = SmParser().Parse(sInput); + xTree->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + + SmCursor aCursor(xTree.get(), xDocShRef.get()); + ScopedVclPtrInstance<VirtualDevice> pOutputDevice; + + // go to the position at "*" + aCursor.Move(pOutputDevice, MoveRight); + // select "* b" and then copy + aCursor.Move(pOutputDevice, MoveRight, false); + aCursor.Move(pOutputDevice, MoveRight, false); + aCursor.Copy(); + // go to the right end and then paste + aCursor.Move(pOutputDevice, MoveRight); + aCursor.Move(pOutputDevice, MoveRight); + aCursor.Paste(); + + CPPUNIT_ASSERT_EQUAL(OUString(" { a * b + c * b } "), xDocShRef->GetText()); +} + +void Test::testCopySelectPaste() +{ + OUString const sInput("a * b + c"); + auto xTree = SmParser().Parse(sInput); + xTree->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + + SmCursor aCursor(xTree.get(), xDocShRef.get()); + ScopedVclPtrInstance<VirtualDevice> pOutputDevice; + + // go to the right end + for (int i=0;i<5;i++) + aCursor.Move(pOutputDevice, MoveRight); + // select "b + c" and then copy + aCursor.Move(pOutputDevice, MoveLeft, false); + aCursor.Move(pOutputDevice, MoveLeft, false); + aCursor.Move(pOutputDevice, MoveLeft, false); + aCursor.Copy(); + // go to the left end + aCursor.Move(pOutputDevice, MoveLeft); + aCursor.Move(pOutputDevice, MoveLeft); + // select "a" and then paste + aCursor.Move(pOutputDevice, MoveRight, false); + aCursor.Paste(); + + CPPUNIT_ASSERT_EQUAL(OUString(" { b + c * b + c } "), xDocShRef->GetText()); +} + +void Test::testCutPaste() +{ + OUString const sInput("a * b + c"); + auto xTree = SmParser().Parse(sInput); + xTree->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + + SmCursor aCursor(xTree.get(), xDocShRef.get()); + ScopedVclPtrInstance<VirtualDevice> pOutputDevice; + + // go to the position at "*" + aCursor.Move(pOutputDevice, MoveRight); + // select "* b" and then cut + aCursor.Move(pOutputDevice, MoveRight, false); + aCursor.Move(pOutputDevice, MoveRight, false); + aCursor.Cut(); + // go to the left end and then paste + aCursor.Move(pOutputDevice, MoveRight); + aCursor.Move(pOutputDevice, MoveRight); + aCursor.Paste(); + + CPPUNIT_ASSERT_EQUAL(OUString(" { a + c * b } "), xDocShRef->GetText()); +} + +void Test::testCutSelectPaste() +{ + OUString const sInput("a * b + c"); + auto xTree = SmParser().Parse(sInput); + xTree->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + + SmCursor aCursor(xTree.get(), xDocShRef.get()); + ScopedVclPtrInstance<VirtualDevice> pOutputDevice; + + // go to the right end + for (int i=0;i<5;i++) + aCursor.Move(pOutputDevice, MoveRight); + // select "b + c" and then cut + aCursor.Move(pOutputDevice, MoveLeft, false); + aCursor.Move(pOutputDevice, MoveLeft, false); + aCursor.Move(pOutputDevice, MoveLeft, false); + aCursor.Cut(); + // go to the left end + aCursor.Move(pOutputDevice, MoveLeft); + aCursor.Move(pOutputDevice, MoveLeft); + // select "a" and then paste + aCursor.Move(pOutputDevice, MoveRight, false); + aCursor.Paste(); + + CPPUNIT_ASSERT_EQUAL(OUString(" { b + c * } "), xDocShRef->GetText()); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(Test); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/qa/cppunit/test_node.cxx b/starmath/qa/cppunit/test_node.cxx new file mode 100644 index 000000000..dba0b9ff3 --- /dev/null +++ b/starmath/qa/cppunit/test_node.cxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> +#include <test/bootstrapfixture.hxx> + +#include <o3tl/cppunittraitshelper.hxx> +#include <sfx2/sfxmodelfactory.hxx> +#include <vcl/virdev.hxx> + +#include <document.hxx> +#include <smdll.hxx> +#include <node.hxx> +#include <parse.hxx> +#include <utility.hxx> + +#include <memory> + +namespace { + +using namespace ::com::sun::star; + +typedef tools::SvRef<SmDocShell> SmDocShellRef; + +class NodeTest : public test::BootstrapFixture +{ +public: + virtual void setUp() override; + virtual void tearDown() override; + +private: + void testTdf47813(); + void testTdf52225(); + + CPPUNIT_TEST_SUITE(NodeTest); + CPPUNIT_TEST(testTdf47813); + CPPUNIT_TEST(testTdf52225); + CPPUNIT_TEST_SUITE_END(); + + SmDocShellRef mxDocShell; +}; + +void NodeTest::setUp() +{ + BootstrapFixture::setUp(); + SmGlobals::ensure(); + mxDocShell = new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT | + SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS | + SfxModelFlags::DISABLE_DOCUMENT_RECOVERY); +} + +void NodeTest::tearDown() +{ + if (mxDocShell.is()) + mxDocShell->DoClose(); + BootstrapFixture::tearDown(); +} + +void NodeTest::testTdf47813() +{ + SmParser aParser; +#define MATRIX "matrix {-2#33##4#-5##6,0#7}" + auto pNodeA = aParser.Parse(MATRIX); + auto pNodeC = aParser.Parse("alignc " MATRIX); + auto pNodeL = aParser.Parse("alignl " MATRIX); + auto pNodeR = aParser.Parse("alignr " MATRIX); +#undef MATRIX + ScopedVclPtrInstance<VirtualDevice> pOutputDevice; + SmFormat aFmt; + pNodeA->Prepare(aFmt, *mxDocShell, 0); + pNodeA->Arrange(*pOutputDevice, aFmt); + pNodeC->Prepare(aFmt, *mxDocShell, 0); + pNodeC->Arrange(*pOutputDevice, aFmt); + pNodeL->Prepare(aFmt, *mxDocShell, 0); + pNodeL->Arrange(*pOutputDevice, aFmt); + pNodeR->Prepare(aFmt, *mxDocShell, 0); + pNodeR->Arrange(*pOutputDevice, aFmt); + long nWidthA = pNodeA->GetRect().GetWidth(); + long nWidthC = pNodeC->GetRect().GetWidth(); + long nWidthL = pNodeL->GetRect().GetWidth(); + long nWidthR = pNodeR->GetRect().GetWidth(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, nWidthC/static_cast<double>(nWidthA), 0.01); + // these values appear to change slightly with display scaling + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, nWidthL/static_cast<double>(nWidthA), 0.03); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, nWidthR/static_cast<double>(nWidthA), 0.03); +} + +void NodeTest::testTdf52225() +{ +#define CHECK_GREEK_SYMBOL(text, code, bItalic) do { \ + mxDocShell->SetText(text); \ + const SmTableNode *pTree= mxDocShell->GetFormulaTree(); \ + CPPUNIT_ASSERT_EQUAL(size_t(1), pTree->GetNumSubNodes()); \ + const SmNode *pLine = pTree->GetSubNode(0); \ + CPPUNIT_ASSERT(pLine); \ + CPPUNIT_ASSERT_EQUAL(SmNodeType::Line, pLine->GetType()); \ + CPPUNIT_ASSERT_EQUAL(size_t(1), pLine->GetNumSubNodes()); \ + const SmNode *pNode = pLine->GetSubNode(0); \ + CPPUNIT_ASSERT(pNode); \ + CPPUNIT_ASSERT_EQUAL(SmNodeType::Special, pNode->GetType()); \ + const SmSpecialNode *pSn = static_cast<const SmSpecialNode *>(pNode); \ + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pSn->GetText().getLength()); \ + CPPUNIT_ASSERT_EQUAL(code, pSn->GetText()[0]); \ + CPPUNIT_ASSERT_EQUAL(OUString(text), pSn->GetToken().aText); \ + CPPUNIT_ASSERT_EQUAL(bItalic, IsItalic(pSn->GetFont())); \ + } while (false) + + SmFormat aFormat = mxDocShell->GetFormat(); + CPPUNIT_ASSERT_EQUAL(sal_Int16(2), aFormat.GetGreekCharStyle()); // default format = 2 + CHECK_GREEK_SYMBOL("%ALPHA", u'\x0391', false); + CHECK_GREEK_SYMBOL("%iALPHA", u'\x0391', true); + CHECK_GREEK_SYMBOL("%alpha", u'\x03b1', true); + CHECK_GREEK_SYMBOL("%ialpha", u'\x03b1', true); + + // mode 1 + aFormat.SetGreekCharStyle(1); + mxDocShell->SetFormat(aFormat); + CHECK_GREEK_SYMBOL("%BETA", u'\x0392', true); + CHECK_GREEK_SYMBOL("%iBETA", u'\x0392', true); + CHECK_GREEK_SYMBOL("%beta", u'\x03b2', true); + CHECK_GREEK_SYMBOL("%ibeta", u'\x03b2', true); + + // mode 0 + aFormat.SetGreekCharStyle(0); + mxDocShell->SetFormat(aFormat); + CHECK_GREEK_SYMBOL("%GAMMA", u'\x0393', false); + CHECK_GREEK_SYMBOL("%iGAMMA", u'\x0393', true); + CHECK_GREEK_SYMBOL("%gamma", u'\x03b3', false); + CHECK_GREEK_SYMBOL("%igamma", u'\x03b3', true); + +#undef CHECK_GREEK_SYMBOL +} + +CPPUNIT_TEST_SUITE_REGISTRATION(NodeTest); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/qa/cppunit/test_nodetotextvisitors.cxx b/starmath/qa/cppunit/test_nodetotextvisitors.cxx new file mode 100644 index 000000000..c3172ab60 --- /dev/null +++ b/starmath/qa/cppunit/test_nodetotextvisitors.cxx @@ -0,0 +1,665 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> +#include <test/bootstrapfixture.hxx> + +#include <vcl/virdev.hxx> +#include <sfx2/sfxmodelfactory.hxx> +#include <smdll.hxx> + +#include <document.hxx> +#include <node.hxx> +#include <visitors.hxx> +#include <cursor.hxx> + +#include "mock-visitor.hxx" +#include <memory> + +typedef tools::SvRef<SmDocShell> SmDocShellRef; + +using namespace ::com::sun::star; + +namespace { + +class Test : public test::BootstrapFixture { + +public: + // init + virtual void setUp() override; + virtual void tearDown() override; + + // tests + void SimpleUnaryOp(); + void SimpleBinaryOp(); + void SimpleRelationalOp(); + void SimpleSetOp(); + void SimpleFunctions(); + void SimpleOperators(); + void SimpleAttributes(); + void SimpleMisc(); + void SimpleBrackets(); + void SimpleFormats(); + void SimpleGreekChars(); + void SimpleSpecialChars(); + void testBinomInBinHor(); + void testBinVerInUnary(); + void testBinHorInSubSup(); + void testUnaryInMixedNumberAsNumerator(); + void testMiscEquivalent(); + void testParser(); + + CPPUNIT_TEST_SUITE(Test); + CPPUNIT_TEST(SimpleUnaryOp); + CPPUNIT_TEST(SimpleBinaryOp); + CPPUNIT_TEST(SimpleRelationalOp); + CPPUNIT_TEST(SimpleSetOp); + CPPUNIT_TEST(SimpleFunctions); + CPPUNIT_TEST(SimpleOperators); + CPPUNIT_TEST(SimpleAttributes); + CPPUNIT_TEST(SimpleMisc); + CPPUNIT_TEST(SimpleBrackets); + CPPUNIT_TEST(SimpleFormats); + CPPUNIT_TEST(SimpleGreekChars); + CPPUNIT_TEST(SimpleSpecialChars); + CPPUNIT_TEST(testBinomInBinHor); + CPPUNIT_TEST(testBinVerInUnary); + CPPUNIT_TEST(testBinHorInSubSup); + CPPUNIT_TEST(testUnaryInMixedNumberAsNumerator); + CPPUNIT_TEST(testMiscEquivalent); + CPPUNIT_TEST(testParser); + CPPUNIT_TEST_SUITE_END(); + +private: + SmDocShellRef xDocShRef; + void parseandparseagain(const char *input, const char *test_name); + void ParseAndCheck(const char *input, const char *expected, const char *test_name); + void ParseAndCompare(const char *formula1, const char *formula2, const char *test_name); +}; + +void Test::setUp() +{ + BootstrapFixture::setUp(); + + SmGlobals::ensure(); + + xDocShRef = new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT); +} + +void Test::tearDown() +{ + xDocShRef.clear(); + BootstrapFixture::tearDown(); +} + +/* + * Most of the formula commands in this file came from: + * http://wiki.openoffice.org/wiki/Template:Math_commands_reference + * which was licensed with a + * Creative Common Attribution 3.0 license and written by: + * Jeanweber, Weegreenblobbie, Jdpipe, TJFrazier, Ysangkok, B michaelsen, Spellbreaker + */ + +void Test::SimpleUnaryOp() +{ + parseandparseagain("+1", "Positive (plus)"); + parseandparseagain("-2", "Negative (minus)"); + parseandparseagain("+-3", "Plus/minus"); + parseandparseagain("-+4", "Minus/plus"); + parseandparseagain("neg a", "Boolean 'not'"); + parseandparseagain("fact a", "Factorial"); + parseandparseagain(" - { 1 over 2 } ", "BinVer in Unary 1"); + ParseAndCheck(" - { 1 over 2 } ", " - { 1 over 2 } ", "BinVer in Unary 1"); + parseandparseagain(" { - { 1 over 2 } } ", "BinVer in Unary 2"); + parseandparseagain(" - 1 over 2 ", "Unary in BinVer as numerator 1"); + parseandparseagain(" { - 1 } over 2 ", "Unary in BinVer as numerator 2"); + parseandparseagain(" 1 over - 2 ", "Unary in BinVer as denominator 1"); + parseandparseagain(" 1 over { - 2 } ", "Unary in BinVer as denominator 2"); + parseandparseagain(" 2 { - 1 over 2 } ", "Mixed number with Unary in denominator 1"); + parseandparseagain(" 2 { - 1 } over 2 ", "Mixed number with Unary in denominator 2"); + parseandparseagain(" - 1 + 2 ", "Unary in BinHor"); +} + +void Test::SimpleBinaryOp() +{ + parseandparseagain("a + b", "Addition"); + parseandparseagain("a cdot b", "Dot product"); + parseandparseagain("a times b", "Cross product"); + parseandparseagain("a * b", "Multiplication (asterisk)"); + parseandparseagain("a and b", "Boolean 'and'"); + parseandparseagain("a - b", "Subtraction"); + parseandparseagain("a over b", "Division (as a fraction)"); + parseandparseagain("a div b", "Division (as an operator)"); + parseandparseagain("a / b", "Division (with a slash)"); + parseandparseagain("a or b", "Boolean 'or'"); + parseandparseagain("a circ b", "Concatenation"); +} + +void Test::SimpleRelationalOp() +{ + parseandparseagain("a = b", "Is equal"); + parseandparseagain("a <> b", "Is not equal"); + parseandparseagain("a approx 2", "Approximately"); + parseandparseagain("a divides b", "Divides"); + parseandparseagain("a ndivides b", "Does not divide"); + parseandparseagain("a < 2", "Less than"); + parseandparseagain("a > 2", "Greater than"); + parseandparseagain("a simeq b", "Similar to or equal"); + parseandparseagain("a parallel b", "Parallel"); + parseandparseagain("a ortho b", "Orthogonal to"); + parseandparseagain("a leslant b", "Less than or equal to"); + parseandparseagain("a geslant b", "Greater than or equal to"); + parseandparseagain("a sim b", "Similar to"); + parseandparseagain("a equiv b", "Congruent"); + parseandparseagain("a <= b", "Less than or equal to"); + parseandparseagain("a >= b", "Greater than or equal to"); + parseandparseagain("a prop b", "Proportional"); + parseandparseagain("a toward b", "Toward"); + parseandparseagain("a dlarrow b", "Arrow left"); + parseandparseagain("a dlrarrow b", "Double arrow left and right"); + parseandparseagain("drarrow b", "Arrow right"); +} + +void Test::SimpleSetOp() +{ + parseandparseagain("a in B", "Is in"); + parseandparseagain("a notin B", "Is not in"); + parseandparseagain("A owns b", "Owns"); + parseandparseagain("emptyset", "Empty set"); + parseandparseagain("A intersection B", "Intersection"); + parseandparseagain("A union B", "Union"); + parseandparseagain("A setminus B", "Difference"); + parseandparseagain("A slash B", "Quotient"); + parseandparseagain("aleph", "Aleph"); + parseandparseagain("A subset B", "Subset"); + parseandparseagain("A subseteq B", "Subset or equal to"); + parseandparseagain("A supset B", "Superset"); + parseandparseagain("A supseteq B", "Superset or equal to"); + parseandparseagain("A nsubset B", "Not subset"); + parseandparseagain("A nsubseteq B", "Not subset or equal"); + parseandparseagain("A nsupset B", "Not superset"); + parseandparseagain("A nsupseteq B", "Not superset or equal"); + parseandparseagain("setN", "Set of natural numbers"); + parseandparseagain("setZ", "Set of integers"); + parseandparseagain("setQ", "Set of rational numbers"); + parseandparseagain("setR", "Set of real numbers"); + parseandparseagain("setC", "Set of complex numbers"); +} + +void Test::SimpleFunctions() +{ + parseandparseagain("func e^{a}", "Exponential"); + parseandparseagain("ln(a)", "Natural logarithm"); + parseandparseagain("exp(a)", "Exponential function"); + parseandparseagain("log(a)", "Logarithm"); + parseandparseagain("a^{b}", "Power"); + parseandparseagain("sin(a)", "Sine"); + parseandparseagain("cos(a)", "Cosine"); + parseandparseagain("tan(a)", "Tangent"); + parseandparseagain("cot(a)", "Cotangent"); + parseandparseagain("sqrt{a}", "Square root"); + parseandparseagain("arcsin(a)", "Arcsine"); + parseandparseagain("arccos(a)", "Arccosine"); + parseandparseagain("arctan(a)", "Arctangent"); + parseandparseagain("arccot(a)", "Arc cotangent"); + parseandparseagain("nroot{a}{b}", "nth root"); + parseandparseagain("sinh(a)", "Hyperbolic sine"); + parseandparseagain("cosh(a)", "Hyperbolic cosine"); + parseandparseagain("tanh(a)", "Hyperbolic tangent"); + parseandparseagain("coth(a)", "Hyperbolic cotangent"); + parseandparseagain("abs{a}", "Absolute value"); + parseandparseagain("arsinh(a)", "Arc hyperbolic sine"); + parseandparseagain("arcosh(a)", "Arc hyperbolic cosine"); + parseandparseagain("artanh(a)", "Arc hyperbolic tangent"); + parseandparseagain("arcoth(a)", "Arc hyperbolic cotangent"); +} + +void Test::SimpleOperators() +{ + parseandparseagain("lim{a}", "Limit"); + parseandparseagain("sum{a}", "Sum"); + parseandparseagain("prod{a}", "Product"); + parseandparseagain("coprod{a}", "Coproduct"); + parseandparseagain("int from {r_0} to {r_t} a", "Upper and lower bounds shown with integral (from & to)"); + ParseAndCheck("int csup {r_0} csub {r_t} a", "int csup { r _ 0 } csub { r _ t } a ", "Upper and lower bounds shown with integral (csub & csup)"); + ParseAndCheck("sum csup { size 8 { x - 1 } } csub { size 8 a } b ", "sum csup { size 8 { x - 1 } } csub { size 8 a } b ", "Sum with sized upper and lower bounds"); + parseandparseagain("int{a}", "Integral"); + parseandparseagain("intd_{1}^{2}{x dx}", "Dynamically-sized integral"); + parseandparseagain("iint{a}", "Double integral"); + parseandparseagain("iiint{a}", "Triple integral"); + parseandparseagain("sum from{3}b", "Lower bound shown with summation symbol"); + parseandparseagain("lint a", "Contour integral"); + parseandparseagain("llint a", "Double curved integral"); + parseandparseagain("lllint a", "Triple curved integral"); + parseandparseagain("prod from {i=1} to {n} {(i+1)}", "Product with range"); + ParseAndCheck("%Ux2135", "%Ux2135", "fdo#77831"); +} + +void Test::SimpleAttributes() +{ + parseandparseagain("acute a", "Acute accent"); + parseandparseagain("grave a", "Grave accent"); + parseandparseagain("check a", "Reverse circumflex"); + parseandparseagain("breve a", "Breve"); + parseandparseagain("circle a", "Circle"); + parseandparseagain("vec a", "Vector arrow"); + parseandparseagain("harpoon a", "Harpoon"); + parseandparseagain("tilde a", "Tilde"); + parseandparseagain("hat a", "Circumflex"); + parseandparseagain("bar a", "Line above"); + parseandparseagain("dot a", "Dot"); + parseandparseagain("widevec abc", "Wide vector arrow"); + parseandparseagain("wideharpoon abc", "Wide harpoon"); + parseandparseagain("widetilde abc", "Wide tilde"); + parseandparseagain("widehat abc", "Wide circumflex"); + parseandparseagain("ddot a", "Double dot"); + parseandparseagain("overline abc", "Line over"); + parseandparseagain("underline abc", "Line under"); + parseandparseagain("overstrike abc", "Line through"); + parseandparseagain("dddot a", "Triple dot"); + parseandparseagain("phantom a", "Transparent (useful to get a placeholder of a given size)"); + parseandparseagain("bold a", "Bold font"); + parseandparseagain("ital a", "Italic font"); + parseandparseagain("nitalic a", "Roman (non-italic) font 1"); + parseandparseagain("\"a\"", "Roman (non-italic) font 2"); + parseandparseagain("size 16 qv", "Resize font"); + parseandparseagain("font sans qv", "Sans serif font"); + parseandparseagain("font serif qv", "Serif font"); + parseandparseagain("font fixed qv", "Fixed font"); + parseandparseagain("color cyan qv", "Cyan color"); + parseandparseagain("color yellow qv", "Yellow color"); + parseandparseagain("color white qv", "White color"); + parseandparseagain("color green qv", "Green color"); + parseandparseagain("color blue qv", "Blue color"); + parseandparseagain("color red qv", "Red color"); + parseandparseagain("color green X qv", "Green color changes back"); + parseandparseagain("color green {X qv}", "Green color, more than one item"); +} + +void Test::SimpleMisc() +{ + parseandparseagain("infinity", "Infinity"); + parseandparseagain("partial", "Partial"); + parseandparseagain("nabla", "Nabla"); + parseandparseagain("exists", "There exists"); + parseandparseagain("notexists", "There not exists"); + parseandparseagain("forall", "For all"); + parseandparseagain("hbar", "H bar"); + parseandparseagain("lambdabar", "Lambda bar"); + parseandparseagain("re", "Real part"); + parseandparseagain("im", "Imaginary part"); + parseandparseagain("wp", "Weierstrass p"); + parseandparseagain("leftarrow", "Left arrow"); + parseandparseagain("rightarrow", "Right arrow"); + parseandparseagain("uparrow", "Up arrow"); + parseandparseagain("downarrow", "Down arrow"); + parseandparseagain("dotslow", "Dots at bottom"); + parseandparseagain("dotsaxis", "Dots at middle"); + parseandparseagain("dotsvert", "Dots vertical"); + parseandparseagain("dotsup", "Dots diagonal upward"); + parseandparseagain("dotsdown", "Dots diagonal downward"); +} + +void Test::SimpleBrackets() +{ + parseandparseagain("(a)", "Round Brackets"); + parseandparseagain("[b]", "Square Brackets"); + parseandparseagain("ldbracket c rdbracket", "Double Square Brackets"); + parseandparseagain("lline a rline", "Single line or absolute"); + parseandparseagain("abs a", "Single line or absolute 2"); + parseandparseagain("ldline a rdline", "Double line"); + parseandparseagain("lbrace w rbrace", "Braces"); + parseandparseagain("left lbrace stack{0, n <> 0 # 1, n = 1} right none", "Single left brace"); + parseandparseagain("langle d rangle", "Angle Brackets"); + parseandparseagain("langle a mline b rangle", "Operator Brackets"); + parseandparseagain("{a}", "Group brackets (used for program control)"); + parseandparseagain("left ( stack{a # b # z} right )", "Round brackets scalable"); + parseandparseagain("left [ stack{x # y} right ]", "Square brackets scalable"); + parseandparseagain("left ldbracket c right rdbracket", "Double square brackets scalable"); + parseandparseagain("left lline a right rline", "Line scalable"); + parseandparseagain("left ldline d right rdline", "Double line scalable"); + parseandparseagain("left lbrace e right rbrace", "Brace scalable"); + parseandparseagain("left langle f right rangle", "Angle bracket scalable"); + parseandparseagain("left langle g mline h right rangle", "Operator brackets scalable"); + parseandparseagain("{a} overbrace b", "Over brace scalable"); + parseandparseagain("{b} underbrace a", "Under brace scalable"); +} + +void Test::SimpleFormats() +{ + parseandparseagain("a lsup{b}", "Left superscript"); + parseandparseagain("a csup{b}", "Center superscript"); + parseandparseagain("a^{b}", "Right superscript"); + parseandparseagain("a lsub{b}", "Left subscript"); + parseandparseagain("a csub{b}", "Center subscript"); + parseandparseagain("a_{b}", "Right subscript"); + parseandparseagain("stack { Hello world # alignl (a) }", "Align character to left"); + parseandparseagain("stack{Hello world # alignc(a)}", "Align character to center"); + parseandparseagain("stack { Hello world # alignr(a)}", "Align character to right"); + parseandparseagain("binom{a}{b}", "Vertical stack of 2"); + parseandparseagain("stack{a # b # z}", "Vertical stack, more than 2"); + parseandparseagain("matrix{a # b ## c # d}", "Matrix"); + parseandparseagain("matrix{a # \"=\" # alignl{b} ## {} # \"=\" # alignl{c+1}}", "Equations aligned at '=' (using 'matrix') "); + parseandparseagain("stack{alignl{a} = b # alignl{phantom{a} = c+1}}", "Equations aligned at '=' (using 'phantom') "); + parseandparseagain("asldkfjo newline sadkfj", "New line"); + parseandparseagain("stuff `stuff", "Small gap (grave)"); + parseandparseagain("stuff~stuff", "Large gap (tilde)"); +} + +void Test::SimpleGreekChars() +{ + parseandparseagain("%ALPHA", "Capital alpha"); + parseandparseagain("%BETA", "Capital beta"); + parseandparseagain("%CHI", "Capital chi"); + parseandparseagain("%DELTA", "Capital delta"); + parseandparseagain("%EPSILON", "Capital epsilon"); + parseandparseagain("%ETA", "Capital eta"); + parseandparseagain("%GAMMA", "Capital gamma"); + parseandparseagain("%IOTA", "Capital iota"); + parseandparseagain("%LAMBDA", "Capital lambda"); + parseandparseagain("%MU", "Capital mu"); + parseandparseagain("%NU", "Capital nu"); + parseandparseagain("%OMEGA", "Capital omega"); + parseandparseagain("%OMICRON", "Capital omicron"); + parseandparseagain("%PHI", "Capital phi"); + parseandparseagain("%PI", "Capital pi"); + parseandparseagain("%PSI", "Capital psi"); + parseandparseagain("%RHO", "Capital rho"); + parseandparseagain("%SIGMA", "Capital sigma"); + parseandparseagain("%TAU", "Capital tau"); + parseandparseagain("%THETA", "Capital theta"); + parseandparseagain("%UPSILON", "Capital upsilon"); + parseandparseagain("%XI", "Capital xi"); + parseandparseagain("%ZETA", "Capital zeta"); + parseandparseagain("%alpha", "lowercase alpha"); + parseandparseagain("%beta", "lowercase beta"); + parseandparseagain("%chi", "lowercase chi"); + parseandparseagain("%delta", "lowercase delta"); + parseandparseagain("%epsilon", "lowercase epsilon"); + parseandparseagain("%eta", "lowercase eta"); + parseandparseagain("%gamma", "lowercase gamma"); + parseandparseagain("%iota", "lowercase iota"); + parseandparseagain("%kappa", "lowercase kappa"); + parseandparseagain("%lambda", "lowercase lambda"); + parseandparseagain("%mu", "lowercase mu"); + parseandparseagain("%nu", "lowercase nu"); + parseandparseagain("%omega", "lowercase omega"); + parseandparseagain("%omicron", "lowercase omicron"); + parseandparseagain("%phi", "lowercase phi"); + parseandparseagain("%pi", "lowercase pi"); + parseandparseagain("%psi", "lowercase psi"); + parseandparseagain("%rho", "lowercase rho"); + parseandparseagain("%sigma", "lowercase sigma"); + parseandparseagain("%tau", "lowercase tau"); + parseandparseagain("%theta", "lowercase theta"); + parseandparseagain("%upsilon", "lowercase upsilon"); + parseandparseagain("%varepsilon", "Varepsilon"); + parseandparseagain("%varphi", "Varphi"); + parseandparseagain("%varpi", "Varpi"); + parseandparseagain("%varrho", "Varrho"); + parseandparseagain("%varsigma", "Varsigma"); + parseandparseagain("%vartheta", "Vartheta"); + parseandparseagain("%xi", "lowercase xi"); + parseandparseagain("%zeta", "lowercase zeta"); +} + +void Test::SimpleSpecialChars() +{ + parseandparseagain("%and", "And"); + parseandparseagain("%angle", "Angle"); + parseandparseagain("%element", "Element"); + parseandparseagain("%identical", "Identical"); + parseandparseagain("%infinite", "Infinite"); + parseandparseagain("%noelement", "No element"); + parseandparseagain("%notequal", "Not equal"); + parseandparseagain("%or", "Or"); + parseandparseagain("%perthousand", "Per thousand"); + parseandparseagain("%strictlygreaterthan", "Strictly greater than"); + parseandparseagain("%strictlylessthan", "Strictly less than"); + parseandparseagain("%tendto", "Tend to"); +} + +/* This test takes a formula command, parses it, converts the node to text, + * parses it again, converts it to text again, and compares the values. + * Doing this doesn't prove that it is correct, but it should prove that the + * meaning of the original command is not being changed. + */ +void Test::parseandparseagain(const char *formula, const char *test_name) +{ + OUString output1, output2; + + // parse 1 + OUString input = OUString::createFromAscii(formula); + auto pNode1 = SmParser().ParseExpression(input); + pNode1->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + SmNodeToTextVisitor(pNode1.get(), output1); + + // parse 2 + auto pNode2 = SmParser().ParseExpression(output1); + pNode2->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + SmNodeToTextVisitor(pNode2.get(), output2); + + // compare + CPPUNIT_ASSERT_EQUAL_MESSAGE(test_name, + output1, + output2); + + // auxiliary test for Accept() + std::unique_ptr<MockVisitor> mv(new MockVisitor); + pNode1->Accept(mv.get()); + pNode2->Accept(mv.get()); +} + +void Test::ParseAndCheck(const char *formula, const char * expected, const char *test_name) +{ + OUString sOutput; + + // parse + OUString sInput = OUString::createFromAscii(formula); + auto pNode = SmParser().ParseExpression(sInput); + pNode->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + SmNodeToTextVisitor(pNode.get(), sOutput); + + // compare + OUString sExpected = OUString::createFromAscii(expected); + CPPUNIT_ASSERT_EQUAL_MESSAGE(test_name, + sExpected, + sOutput); + + // auxiliary test for Accept() + std::unique_ptr<MockVisitor> mv(new MockVisitor); + pNode->Accept(mv.get()); +} + +// Parse two formula commands and verify that they give the same output +void Test::ParseAndCompare(const char *formula1, const char *formula2, const char *test_name) +{ + OUString sOutput1, sOutput2; + + // parse formula1 + OUString sInput1(formula1, strlen(formula1), RTL_TEXTENCODING_UTF8); + auto pNode1 = SmParser().ParseExpression(sInput1); + pNode1->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + SmNodeToTextVisitor(pNode1.get(), sOutput1); + + // parse formula2 + OUString sInput2(formula2, strlen(formula2), RTL_TEXTENCODING_UTF8); + auto pNode2 = SmParser().ParseExpression(sInput2); + pNode2->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + SmNodeToTextVisitor(pNode2.get(), sOutput2); + + CPPUNIT_ASSERT_EQUAL_MESSAGE(test_name, sOutput1, sOutput2); + + // auxiliary test for Accept() + std::unique_ptr<MockVisitor> mv(new MockVisitor); + pNode1->Accept(mv.get()); + pNode2->Accept(mv.get()); +} + +void Test::testBinomInBinHor() +{ + OUString sInput, sExpected; + + // set up a binom (table) node + sInput += "binom a b + c"; + auto pTree = SmParser().Parse(sInput); + pTree->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + + SmCursor aCursor(pTree.get(), xDocShRef.get()); + ScopedVclPtrInstance< VirtualDevice > pOutputDevice; + + // move forward (more than) enough places to be at the end + int i; + for (i = 0; i < 8; ++i) + aCursor.Move(pOutputDevice, MoveRight); + + // tack +d on the end, which will put the binom into an SmBinHorNode + aCursor.InsertElement(PlusElement); + aCursor.InsertText("d"); + + sExpected += " { { binom a b + c } + d } "; + CPPUNIT_ASSERT_EQUAL_MESSAGE("Binom Node in BinHor Node", sExpected, xDocShRef->GetText()); +} + +void Test::testBinVerInUnary() +{ + OUString sInput, sExpected; + + // set up a unary operator with operand + sInput += "- 1"; + auto pTree = SmParser().Parse(sInput); + pTree->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + + SmCursor aCursor(pTree.get(), xDocShRef.get()); + ScopedVclPtrInstance< VirtualDevice > pOutputDevice; + + // move forward (more than) enough places to be at the end + int i; + for (i = 0; i < 3; ++i) + aCursor.Move(pOutputDevice, MoveRight); + + // select the operand + aCursor.Move(pOutputDevice, MoveLeft, false); + // set up a fraction + aCursor.InsertFraction(); + aCursor.Move(pOutputDevice, MoveDown); + aCursor.InsertText("2"); + + sExpected += " - { 1 over 2 } "; + CPPUNIT_ASSERT_EQUAL_MESSAGE("Binary Vertical in Unary Operator", sExpected, xDocShRef->GetText()); +} + +void Test::testBinHorInSubSup() +{ + // set up a blank formula + auto pTree = SmParser().Parse(OUString()); + pTree->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + + SmCursor aCursor(pTree.get(), xDocShRef.get()); + ScopedVclPtrInstance< VirtualDevice > pOutputDevice; + + // Insert an RSup expression with a BinHor for the exponent + aCursor.InsertText("a"); + aCursor.InsertSubSup(RSUP); + aCursor.InsertText("b"); + aCursor.InsertElement(PlusElement); + aCursor.InsertText("c"); + + // Move to the end and add d to the expression + aCursor.Move(pOutputDevice, MoveRight); + aCursor.InsertElement(PlusElement); + aCursor.InsertText("d"); + + OUString sExpected = " { a ^ { b + c } + d } "; + CPPUNIT_ASSERT_EQUAL_MESSAGE("BinHor in SubSup", sExpected, xDocShRef->GetText()); +} + +void Test::testUnaryInMixedNumberAsNumerator() +{ + // set up a unary operator + OUString sInput = "- 1"; + auto pTree = SmParser().Parse(sInput); + pTree->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + + SmCursor aCursor(pTree.get(), xDocShRef.get()); + ScopedVclPtrInstance< VirtualDevice > pOutputDevice; + + // move forward (more than) enough places to be at the end + for (size_t i = 0; i < 3; ++i) + aCursor.Move(pOutputDevice, MoveRight); + + // Select the whole Unary Horizontal Node + aCursor.Move(pOutputDevice, MoveLeft, false); + aCursor.Move(pOutputDevice, MoveLeft, false); + + // Set up a fraction + aCursor.InsertFraction(); + aCursor.Move(pOutputDevice, MoveDown); + aCursor.InsertText("2"); + + // Move left and turn this into a mixed number + // (bad form, but this could happen right?) + aCursor.Move(pOutputDevice, MoveLeft); + aCursor.Move(pOutputDevice, MoveLeft); + aCursor.InsertText("2"); + + // move forward (more than) enough places to be at the end + for (size_t i = 0; i < 8; ++i) + aCursor.Move(pOutputDevice, MoveRight); + + // add 4 to the end + aCursor.InsertElement(PlusElement); + aCursor.InsertText("4"); + + OUString sExpected = " { 2 { - 1 over 2 } + 4 } "; + CPPUNIT_ASSERT_EQUAL_MESSAGE("Unary in mixed number as Numerator", sExpected, xDocShRef->GetText()); +} + +void Test::testMiscEquivalent() +{ + // fdo#55853 + ParseAndCompare("2x", "2 x", "Number times variable"); + ParseAndCompare("3x^2", "3 x^2", "Number times power"); + + // i#11752 and fdo#55853 + ParseAndCompare("x_2n", "x_{2 n}", "Number times variable in subscript"); + ParseAndCompare("x^2n", "x^{2 n}", "Number times variable in supscript"); + + // fdo#66081 + ParseAndCompare("{x}", "x", "Variable in brace"); + ParseAndCompare("{{x+{{y}}}}", "x+y", "Nested braces"); + + // check non-BMP Unicode char + ParseAndCompare("{\xf0\x9d\x91\x8e}", "\xf0\x9d\x91\x8e", "non-BMP variable in brace"); + ParseAndCompare("{ \xf0\x9d\x91\x8e }", "\xf0\x9d\x91\x8e", "non-BMP variable in brace"); + + // tdf#88320 + ParseAndCompare("A_1,B_2", "A_{1},B_2", "Comma between a digit and non-digit delimits subscript"); + + //tdf#97164 + ParseAndCompare("100 %", "100\"%\"", "Percent symbol at the end"); +} + +void Test::testParser() +{ + OUString sOutput; + OUString sInput(u"{ \U0001D44E }"); // non-BMP Unicode + OUString sExpected(u"\U0001D44E"); + auto pNode = SmParser().ParseExpression(sInput); + pNode->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + SmNodeToTextVisitor(pNode.get(), sOutput); + CPPUNIT_ASSERT_EQUAL(sExpected, sOutput); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(Test); + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/qa/cppunit/test_parse.cxx b/starmath/qa/cppunit/test_parse.cxx new file mode 100644 index 000000000..ccc0f5478 --- /dev/null +++ b/starmath/qa/cppunit/test_parse.cxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> +#include <test/bootstrapfixture.hxx> + +#include <sfx2/sfxmodelfactory.hxx> + +#include <document.hxx> +#include <smdll.hxx> +#include <node.hxx> +#include <parse.hxx> + +#include <memory> + +namespace { + +using namespace ::com::sun::star; + +typedef tools::SvRef<SmDocShell> SmDocShellRef; + +class ParseTest : public test::BootstrapFixture +{ +public: + virtual void setUp() override; + virtual void tearDown() override; + +private: + void testMinus(); + void testNospace(); + + CPPUNIT_TEST_SUITE(ParseTest); + CPPUNIT_TEST(testMinus); + CPPUNIT_TEST(testNospace); + CPPUNIT_TEST_SUITE_END(); + + SmDocShellRef mxDocShell; +}; + +void ParseTest::setUp() +{ + BootstrapFixture::setUp(); + SmGlobals::ensure(); + mxDocShell = new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT | + SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS | + SfxModelFlags::DISABLE_DOCUMENT_RECOVERY); +} + +void ParseTest::tearDown() +{ + if (mxDocShell.is()) + mxDocShell->DoClose(); + BootstrapFixture::tearDown(); +} + +/* + * This shows that input "-" is recognized as a separate token even when + * it is immediately followed by a number. + */ +void ParseTest::testMinus() +{ + auto pNode = SmParser().Parse("-1.2"); + CPPUNIT_ASSERT_EQUAL(size_t(1), pNode->GetNumSubNodes()); + const SmNode *pNode0 = pNode->GetSubNode(0); + CPPUNIT_ASSERT(pNode0); + CPPUNIT_ASSERT_EQUAL(SmNodeType::Line, pNode0->GetType()); + CPPUNIT_ASSERT_EQUAL(size_t(1), pNode0->GetNumSubNodes()); + const SmNode *pNode00 = pNode0->GetSubNode(0); + CPPUNIT_ASSERT(pNode00); + CPPUNIT_ASSERT_EQUAL(SmNodeType::UnHor, pNode00->GetType()); + CPPUNIT_ASSERT_EQUAL(size_t(2), pNode00->GetNumSubNodes()); + const SmNode *pNode000 = pNode00->GetSubNode(0); + CPPUNIT_ASSERT(pNode000); + CPPUNIT_ASSERT_EQUAL(SmNodeType::Math, pNode000->GetType()); + // GetText() vs GetToken().aText + CPPUNIT_ASSERT_EQUAL(OUString(MS_MINUS), + static_cast<const SmMathSymbolNode *>(pNode000)->GetText()); + CPPUNIT_ASSERT_EQUAL(OUString("-"), + static_cast<const SmMathSymbolNode *>(pNode000)->GetToken().aText); + const SmNode *pNode001 = pNode00->GetSubNode(1); + CPPUNIT_ASSERT(pNode001); + CPPUNIT_ASSERT_EQUAL(SmNodeType::Text, pNode001->GetType()); + // GetText() vs GetToken().aText + CPPUNIT_ASSERT(static_cast<const SmTextNode *>(pNode001)->GetText().isEmpty()); + CPPUNIT_ASSERT_EQUAL(OUString("1.2"), + static_cast<const SmTextNode *>(pNode001)->GetToken().aText); +} + +/* + * This shows that "nospace" turns off the expression's IsUseExtraSpaces(), + * but leaves its descendants' flag on. + */ +void ParseTest::testNospace() +{ + auto pNode = SmParser().Parse("nospace{ nitalic d {F(x) G(x)} }"); + CPPUNIT_ASSERT_EQUAL(size_t(1), pNode->GetNumSubNodes()); + const SmNode *pNode0 = pNode->GetSubNode(0); + CPPUNIT_ASSERT(pNode0); + CPPUNIT_ASSERT_EQUAL(SmNodeType::Line, pNode0->GetType()); + CPPUNIT_ASSERT_EQUAL(size_t(1), pNode0->GetNumSubNodes()); + const SmNode *pNode00 = pNode0->GetSubNode(0); + CPPUNIT_ASSERT(pNode00); + CPPUNIT_ASSERT_EQUAL(SmNodeType::Expression, pNode00->GetType()); + CPPUNIT_ASSERT(!static_cast<const SmExpressionNode *>(pNode00)->IsUseExtraSpaces()); + CPPUNIT_ASSERT_EQUAL(size_t(2), pNode00->GetNumSubNodes()); + const SmNode *pNode000 = pNode00->GetSubNode(0); + CPPUNIT_ASSERT(pNode000); + CPPUNIT_ASSERT_EQUAL(SmNodeType::Font, pNode000->GetType()); + CPPUNIT_ASSERT_EQUAL(OUString("nitalic"), + static_cast<const SmFontNode *>(pNode000)->GetToken().aText); + const SmNode *pNode001 = pNode00->GetSubNode(1); + CPPUNIT_ASSERT(pNode001); + CPPUNIT_ASSERT_EQUAL(SmNodeType::Expression, pNode001->GetType()); + CPPUNIT_ASSERT(static_cast<const SmExpressionNode *>(pNode001)->IsUseExtraSpaces()); + CPPUNIT_ASSERT_EQUAL(size_t(2), pNode00->GetNumSubNodes()); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(ParseTest); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/qa/cppunit/test_starmath.cxx b/starmath/qa/cppunit/test_starmath.cxx new file mode 100644 index 000000000..e26bcd31b --- /dev/null +++ b/starmath/qa/cppunit/test_starmath.cxx @@ -0,0 +1,563 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <config_features.h> +#include <vcl/print.hxx> + +#include <test/bootstrapfixture.hxx> + +#include <smdll.hxx> +#include <document.hxx> +#include <view.hxx> + +#include <tmpdevice.hxx> + +#include <sfx2/sfxmodelfactory.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> + +#include <editeng/editeng.hxx> + +#include <sfx2/zoomitem.hxx> +#include <starmath.hrc> +#include <memory> + +typedef tools::SvRef<SmDocShell> SmDocShellRef; + +using namespace ::com::sun::star; + +namespace { + +class Test : public test::BootstrapFixture +{ +public: + Test(); + + // init + virtual void setUp() override; + virtual void tearDown() override; + + // tests + void editUndoRedo(); + void editMarker(); + void editFailure(); + void ParseErrorUnexpectedToken(); + void ParseErrorPoundExpected(); + void ParseErrorColorExpected(); + void ParseErrorLgroupExpected(); + void ParseErrorRgroupExpected(); + void ParseErrorLbraceExpected(); + void ParseErrorRbraceExpected(); + void ParseErrorParentMismatch(); + void ParseErrorRightExpected(); + void ParseErrorFontExpected(); + void ParseErrorSizeExpected(); + void ParseErrorDoubleAlign(); + void ParseErrorDoubleSubsupscript(); + + void replacePlaceholder(); + void viewZoom(); + +#if HAVE_MORE_FONTS + void testSmTmpDeviceRestoreFont(); +#endif + + CPPUNIT_TEST_SUITE(Test); + CPPUNIT_TEST(editUndoRedo); + CPPUNIT_TEST(editMarker); + CPPUNIT_TEST(editFailure); + CPPUNIT_TEST(ParseErrorUnexpectedToken); + CPPUNIT_TEST(ParseErrorPoundExpected); + CPPUNIT_TEST(ParseErrorColorExpected); + CPPUNIT_TEST(ParseErrorLgroupExpected); + CPPUNIT_TEST(ParseErrorRgroupExpected); + CPPUNIT_TEST(ParseErrorLbraceExpected); + CPPUNIT_TEST(ParseErrorRbraceExpected); + CPPUNIT_TEST(ParseErrorParentMismatch); + CPPUNIT_TEST(ParseErrorRightExpected); + CPPUNIT_TEST(ParseErrorFontExpected); + CPPUNIT_TEST(ParseErrorSizeExpected); + CPPUNIT_TEST(ParseErrorDoubleAlign); + CPPUNIT_TEST(ParseErrorDoubleSubsupscript); + CPPUNIT_TEST(replacePlaceholder); + CPPUNIT_TEST(viewZoom); +#if HAVE_MORE_FONTS + CPPUNIT_TEST(testSmTmpDeviceRestoreFont); +#endif + CPPUNIT_TEST_SUITE_END(); + +private: + SfxBindings m_aBindings; + std::unique_ptr<SfxDispatcher> m_pDispatcher; + VclPtr<SmCmdBoxWindow> m_pSmCmdBoxWindow; + VclPtr<SmEditWindow> m_pEditWindow; + SmDocShellRef m_xDocShRef; + SmViewShell *m_pViewShell; +}; + +Test::Test() + : m_pViewShell(nullptr) +{ +} + +void Test::setUp() +{ + BootstrapFixture::setUp(); + + SmGlobals::ensure(); + + m_xDocShRef = new SmDocShell( + SfxModelFlags::EMBEDDED_OBJECT | + SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS | + SfxModelFlags::DISABLE_DOCUMENT_RECOVERY); + m_xDocShRef->DoInitNew(); + + SfxViewFrame *pViewFrame = SfxViewFrame::LoadHiddenDocument(*m_xDocShRef, SFX_INTERFACE_NONE); + + CPPUNIT_ASSERT_MESSAGE("Should have a SfxViewFrame", pViewFrame); + + m_pDispatcher.reset(new SfxDispatcher(pViewFrame)); + m_aBindings.SetDispatcher(m_pDispatcher.get()); + m_aBindings.EnterRegistrations(); + m_pSmCmdBoxWindow.reset(VclPtr<SmCmdBoxWindow>::Create(&m_aBindings, nullptr, nullptr)); + m_aBindings.LeaveRegistrations(); + m_pEditWindow = VclPtr<SmEditWindow>::Create(*m_pSmCmdBoxWindow); + m_pViewShell = m_pEditWindow->GetView(); + CPPUNIT_ASSERT_MESSAGE("Should have a SmViewShell", m_pViewShell); +} + +void Test::tearDown() +{ + m_pEditWindow.disposeAndClear(); + m_pSmCmdBoxWindow.disposeAndClear(); + m_pDispatcher.reset(); + m_xDocShRef->DoClose(); + m_xDocShRef.clear(); + + BootstrapFixture::tearDown(); +} + +#if HAVE_MORE_FONTS +void Test::testSmTmpDeviceRestoreFont() +{ + ScopedVclPtrInstance<Printer> pPrinter; + bool bUseMap100th_mm = true; + + OUString aFontName("Linux Libertine G"); + CPPUNIT_ASSERT(pPrinter->IsFontAvailable(aFontName)); + + vcl::Font aOriginalFont = pPrinter->GetFont(); + aOriginalFont.SetColor(COL_RED); + pPrinter->SetTextColor(COL_RED); + + vcl::Font aNewFont; + + { + SmTmpDevice aTmpDev(*pPrinter, bUseMap100th_mm); + + aNewFont = pPrinter->GetFont(); + aNewFont.SetFamilyName(aFontName); + aTmpDev.SetFont(aNewFont); + + CPPUNIT_ASSERT_EQUAL(aFontName, pPrinter->GetFont().GetFamilyName()); + CPPUNIT_ASSERT_EQUAL(COL_BLACK, pPrinter->GetTextColor()); + } + + CPPUNIT_ASSERT(aNewFont != pPrinter->GetFont()); + CPPUNIT_ASSERT_EQUAL(COL_RED, pPrinter->GetTextColor()); +} +#endif + +void Test::editMarker() +{ + { + OUString sMarkedText("<?> under <?> under <?>"); + m_pEditWindow->SetText(sMarkedText); + m_pEditWindow->Flush(); + OUString sFinalText = m_pEditWindow->GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be equal text", sMarkedText, sFinalText); + } + + { + OUString const sTargetText("a under b under c"); + ESelection aSelection; + + m_pEditWindow->SelNextMark(); + m_pEditWindow->Delete(); + m_pEditWindow->InsertText("a"); + + m_pEditWindow->SelNextMark(); + m_pEditWindow->SelNextMark(); + m_pEditWindow->Delete(); + m_pEditWindow->InsertText("c"); + + // should be safe i.e. do nothing + m_pEditWindow->SelNextMark(); + aSelection = m_pEditWindow->GetSelection(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aSelection.nStartPara); + CPPUNIT_ASSERT_EQUAL(sal_Int32(19), aSelection.nStartPos); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aSelection.nEndPara); + CPPUNIT_ASSERT_EQUAL(sal_Int32(19), aSelection.nEndPos); + + m_pEditWindow->SelPrevMark(); + m_pEditWindow->Delete(); + m_pEditWindow->InsertText("b"); + + // tdf#106116: should be safe i.e. do nothing + m_pEditWindow->SelPrevMark(); + aSelection = m_pEditWindow->GetSelection(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aSelection.nStartPara); + CPPUNIT_ASSERT_EQUAL(sal_Int32(9), aSelection.nStartPos); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aSelection.nEndPara); + CPPUNIT_ASSERT_EQUAL(sal_Int32(9), aSelection.nEndPos); + + m_pEditWindow->Flush(); + OUString sFinalText = m_pEditWindow->GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be a under b under c", sTargetText, sFinalText); + } + + { + m_pEditWindow->SetText(OUString()); + m_pEditWindow->Flush(); + } +} + +void Test::editFailure() +{ + m_xDocShRef->SetText("color a b over {a/}"); + + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + + CPPUNIT_ASSERT_MESSAGE("Should be a SmParseError::ColorExpected", + pErrorDesc && pErrorDesc->m_eType == SmParseError::ColorExpected); + + pErrorDesc = m_xDocShRef->GetParser().PrevError(); + + CPPUNIT_ASSERT_MESSAGE("Should be a SmParseError::UnexpectedChar", + pErrorDesc && pErrorDesc->m_eType == SmParseError::UnexpectedChar); + + pErrorDesc = m_xDocShRef->GetParser().PrevError(); + + CPPUNIT_ASSERT_MESSAGE("Should be a SmParseError::RgroupExpected", + pErrorDesc && pErrorDesc->m_eType == SmParseError::RgroupExpected); + + const SmErrorDesc *pLastErrorDesc = m_xDocShRef->GetParser().PrevError(); + + CPPUNIT_ASSERT_MESSAGE("Should be three syntax errors", + pLastErrorDesc && pLastErrorDesc == pErrorDesc); +} + +void Test::ParseErrorUnexpectedToken() +{ + m_xDocShRef->SetText("\\foo"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::UnexpectedToken expected", + SmParseError::UnexpectedToken, pErrorDesc->m_eType); +} + +void Test::ParseErrorPoundExpected() +{ + m_xDocShRef->SetText("matrix {1#2##a##b#c}"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::PoundExpected expected", + SmParseError::PoundExpected, pErrorDesc->m_eType); +} + +void Test::ParseErrorColorExpected() +{ + m_xDocShRef->SetText("color 42 x"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::ColorExpected expected", + SmParseError::ColorExpected, pErrorDesc->m_eType); +} + +void Test::ParseErrorLgroupExpected() +{ + m_xDocShRef->SetText("stack 42"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::LgroupExpected expected", + SmParseError::LgroupExpected, pErrorDesc->m_eType); +} + +void Test::ParseErrorRgroupExpected() +{ + m_xDocShRef->SetText("stack {a#b#c)"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::RgroupExpected expected", + SmParseError::RgroupExpected, pErrorDesc->m_eType); +} + +void Test::ParseErrorLbraceExpected() +{ + m_xDocShRef->SetText("left 42"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::LbraceExpected expected", + SmParseError::LbraceExpected, pErrorDesc->m_eType); +} + +void Test::ParseErrorRbraceExpected() +{ + m_xDocShRef->SetText("left ( foo right x"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::RbraceExpected expected", + SmParseError::RbraceExpected, pErrorDesc->m_eType); +} + +void Test::ParseErrorParentMismatch() +{ + m_xDocShRef->SetText("lbrace foo rceil"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::ParentMismatch expected", + SmParseError::ParentMismatch, pErrorDesc->m_eType); +} + +void Test::ParseErrorRightExpected() +{ + m_xDocShRef->SetText("left ( x mline y )"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::RightExpected expected", + SmParseError::RightExpected, pErrorDesc->m_eType); +} + +void Test::ParseErrorFontExpected() +{ + m_xDocShRef->SetText("font small bar"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::FontExpected expected", + SmParseError::FontExpected, pErrorDesc->m_eType); +} + +void Test::ParseErrorSizeExpected() +{ + m_xDocShRef->SetText("size small baz"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::SizeExpected expected", + SmParseError::SizeExpected, pErrorDesc->m_eType); +} + +void Test::ParseErrorDoubleAlign() +{ + m_xDocShRef->SetText("alignl alignc x"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::DoubleAlign expected", + SmParseError::DoubleAlign, pErrorDesc->m_eType); +} + +void Test::ParseErrorDoubleSubsupscript() +{ + m_xDocShRef->SetText("x_y_z"); + const SmErrorDesc *pErrorDesc = m_xDocShRef->GetParser().NextError(); + CPPUNIT_ASSERT(pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmParseError::DoubleSubsupscript expected", + SmParseError::DoubleSubsupscript, pErrorDesc->m_eType); +} + +void Test::editUndoRedo() +{ + EditEngine &rEditEngine = m_xDocShRef->GetEditEngine(); + + OUString sStringOne("a under b"); + { + rEditEngine.SetText(0, sStringOne); + m_xDocShRef->UpdateText(); + OUString sFinalText = m_xDocShRef->GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Strings must match", sFinalText, sStringOne); + } + + OUString sStringTwo("a over b"); + { + rEditEngine.SetText(0, sStringTwo); + m_xDocShRef->UpdateText(); + OUString sFinalText = m_xDocShRef->GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Strings must match", sFinalText, sStringTwo); + } + + SfxRequest aUndo(SID_UNDO, SfxCallMode::SYNCHRON, SmDocShell::GetPool()); + + { + m_xDocShRef->Execute(aUndo); + m_xDocShRef->UpdateText(); + OUString sFinalText = m_xDocShRef->GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Strings much match", sFinalText, sStringOne); + } + + { + m_xDocShRef->Execute(aUndo); + m_xDocShRef->UpdateText(); + OUString sFinalText = m_xDocShRef->GetText(); + CPPUNIT_ASSERT_MESSAGE("Must now be empty", sFinalText.isEmpty()); + } + + SfxRequest aRedo(SID_REDO, SfxCallMode::SYNCHRON, SmDocShell::GetPool()); + { + m_xDocShRef->Execute(aRedo); + m_xDocShRef->UpdateText(); + OUString sFinalText = m_xDocShRef->GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Strings much match", sFinalText, sStringOne); + } + + { + rEditEngine.SetText(0, OUString()); + m_xDocShRef->UpdateText(); + rEditEngine.ClearModifyFlag(); + OUString sFinalText = m_xDocShRef->GetText(); + CPPUNIT_ASSERT_MESSAGE("Must be empty", sFinalText.isEmpty()); + } + +} + +void Test::replacePlaceholder() +{ + // Test the placeholder replacement. In this case, testing 'a + b', it + // should return '+a + b' when selecting '+<?>' in ElementsDock + m_pEditWindow->SetText("a + b"); + m_pEditWindow->SelectAll(); + m_pEditWindow->InsertText("+<?>"); + OUString sFinalText = m_pEditWindow->GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be '+a + b'", OUString("+a + b"), sFinalText); +} + +void Test::viewZoom() +{ + sal_uInt16 nOrigZoom, nNextZoom, nFinalZoom; + + EditEngine &rEditEngine = m_xDocShRef->GetEditEngine(); + + OUString sStringOne("a under b"); + { + rEditEngine.SetText(0, sStringOne); + m_xDocShRef->UpdateText(); + OUString sFinalText = m_xDocShRef->GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Strings must match", sFinalText, sStringOne); + } + + SmGraphicWindow &rGraphicWindow = m_pViewShell->GetGraphicWindow(); + rGraphicWindow.SetSizePixel(Size(1024, 800)); + nOrigZoom = rGraphicWindow.GetZoom(); + + { + SfxRequest aZoomIn(SID_ZOOMIN, SfxCallMode::SYNCHRON, m_pViewShell->GetPool()); + m_pViewShell->Execute(aZoomIn); + nNextZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_MESSAGE("Should be bigger", nNextZoom > nOrigZoom); + } + + { + SfxRequest aZoomOut(SID_ZOOMOUT, SfxCallMode::SYNCHRON, m_pViewShell->GetPool()); + m_pViewShell->Execute(aZoomOut); + nFinalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be equal", nOrigZoom, nFinalZoom); + } + + sal_uInt16 nOptimalZoom=0; + + { + SfxRequest aZoom(SID_ZOOM_OPTIMAL, SfxCallMode::SYNCHRON, m_pViewShell->GetPool()); + m_pViewShell->Execute(aZoom); + nOptimalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_MESSAGE("Should be about 800%", nOptimalZoom > nOrigZoom); + } + + { + SfxItemSet aSet(SmDocShell::GetPool(), svl::Items<SID_ATTR_ZOOM, SID_ATTR_ZOOM>{}); + aSet.Put(SvxZoomItem(SvxZoomType::OPTIMAL, 0)); + SfxRequest aZoom(SID_ATTR_ZOOM, SfxCallMode::SYNCHRON, aSet); + m_pViewShell->Execute(aZoom); + nFinalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be optimal zoom", nOptimalZoom, nFinalZoom); + } + +//To-Do: investigate GetPrinter logic of SvxZoomType::PAGEWIDTH/SvxZoomType::WHOLEPAGE to ensure +//consistent value regardless of +#if 0 + { + SfxRequest aZoomOut(SID_ZOOMOUT, SfxCallMode::SYNCHRON, m_pViewShell->GetPool()); + m_pViewShell->Execute(aZoomOut); + nFinalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_MESSAGE("Should not be optimal zoom", nFinalZoom != nOptimalZoom); + + SfxItemSet aSet(m_xDocShRef->GetPool(), SID_ATTR_ZOOM, SID_ATTR_ZOOM); + aSet.Put(SvxZoomItem(SvxZoomType::PAGEWIDTH, 0)); + SfxRequest aZoom(SID_ATTR_ZOOM, SfxCallMode::SYNCHRON, aSet); + m_pViewShell->Execute(aZoom); + nFinalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_MESSAGE("Should be same as optimal zoom", nFinalZoom == nOptimalZoom); + } + + { + SfxRequest aZoomOut(SID_ZOOMOUT, SfxCallMode::SYNCHRON, m_pViewShell->GetPool()); + m_pViewShell->Execute(aZoomOut); + nFinalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_MESSAGE("Should not be optimal zoom", nFinalZoom != nOptimalZoom); + + SfxItemSet aSet(m_xDocShRef->GetPool(), SID_ATTR_ZOOM, SID_ATTR_ZOOM); + aSet.Put(SvxZoomItem(SvxZoomType::WHOLEPAGE, 0)); + SfxRequest aZoom(SID_ATTR_ZOOM, SfxCallMode::SYNCHRON, aSet); + m_pViewShell->Execute(aZoom); + nFinalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_MESSAGE("Should be same as optimal zoom", nFinalZoom == nOptimalZoom); + } +#endif + + { + SfxRequest aZoomOut(SID_ZOOMOUT, SfxCallMode::SYNCHRON, m_pViewShell->GetPool()); + m_pViewShell->Execute(aZoomOut); + nFinalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_MESSAGE("Should not be optimal zoom", nFinalZoom != nOptimalZoom); + + SfxItemSet aSet(SmDocShell::GetPool(), svl::Items<SID_ATTR_ZOOM, SID_ATTR_ZOOM>{}); + aSet.Put(SvxZoomItem(SvxZoomType::PERCENT, 50)); + SfxRequest aZoom(SID_ATTR_ZOOM, SfxCallMode::SYNCHRON, aSet); + m_pViewShell->Execute(aZoom); + nFinalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be 50%", static_cast<sal_uInt16>(50), nFinalZoom); + } + + { + SfxItemSet aSet(SmDocShell::GetPool(), svl::Items<SID_ATTR_ZOOM, SID_ATTR_ZOOM>{}); + aSet.Put(SvxZoomItem(SvxZoomType::PERCENT, 5)); + SfxRequest aZoom(SID_ATTR_ZOOM, SfxCallMode::SYNCHRON, aSet); + m_pViewShell->Execute(aZoom); + nFinalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be Clipped to 25%", static_cast<sal_uInt16>(25), nFinalZoom); + } + + { + SfxItemSet aSet(SmDocShell::GetPool(), svl::Items<SID_ATTR_ZOOM, SID_ATTR_ZOOM>{}); + aSet.Put(SvxZoomItem(SvxZoomType::PERCENT, 1000)); + SfxRequest aZoom(SID_ATTR_ZOOM, SfxCallMode::SYNCHRON, aSet); + m_pViewShell->Execute(aZoom); + nFinalZoom = rGraphicWindow.GetZoom(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be Clipped to 800%", static_cast<sal_uInt16>(800), nFinalZoom); + } + +} + +CPPUNIT_TEST_SUITE_REGISTRATION(Test); + +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/qa/extras/data/color.mml b/starmath/qa/extras/data/color.mml new file mode 100644 index 000000000..6aad87b21 --- /dev/null +++ b/starmath/qa/extras/data/color.mml @@ -0,0 +1,20 @@ +<math xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mi mathcolor="black">b</mi> + <mi mathcolor="white">w</mi> + <mi mathcolor="red">r</mi> + <mi mathcolor="green">g</mi> + <mi mathcolor="blue">b</mi> + <mi mathcolor="yellow">y</mi> + <mi mathcolor="silver">s</mi> + <mi mathcolor="gray">g</mi> + <mi mathcolor="maroon">m</mi> + <mi mathcolor="purple">p</mi> + <mi mathcolor="lime">l</mi> + <mi mathcolor="olive">o</mi> + <mi mathcolor="navy">n</mi> + <mi mathcolor="teal">t</mi> + <mi mathcolor="aqua">a</mi> + <mi mathcolor="fuchsia">f</mi> + </mrow> +</math> diff --git a/starmath/qa/extras/data/maction.mml b/starmath/qa/extras/data/maction.mml new file mode 100644 index 000000000..365008799 --- /dev/null +++ b/starmath/qa/extras/data/maction.mml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<math xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mtable> + <mtr><maction actiontype="toggle"><mn>1</mn><mn>0</mn><mn>0</mn></maction></mtr> + <mtr><maction actiontype="toggle" selection="2"><mn>0</mn><mn>2</mn><mn>0</mn></maction></mtr> + <mtr><maction actiontype="toggle" selection="3"><mn>0</mn><mn>0</mn><mn>3</mn></maction></mtr> + </mtable> + </mrow> +</math> diff --git a/starmath/qa/extras/data/mspace.mml b/starmath/qa/extras/data/mspace.mml new file mode 100644 index 000000000..190651094 --- /dev/null +++ b/starmath/qa/extras/data/mspace.mml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<math xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mi>a</mi> + <mspace /> + <mi>b</mi> + <mspace width="2em" /> + <mi>c</mi> + <mspace width="5.5em" /> + <mi>d</mi> + </mrow> +</math> diff --git a/starmath/qa/extras/data/ns-prefix-math.mml b/starmath/qa/extras/data/ns-prefix-math.mml new file mode 100644 index 000000000..c4c961223 --- /dev/null +++ b/starmath/qa/extras/data/ns-prefix-math.mml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<math:math xmlns:math="http://www.w3.org/1998/Math/MathML"> + <math:msup> + <math:mfenced> + <math:mrow> + <math:mi>a</math:mi> + <math:mo>+</math:mo> + <math:mi>b</math:mi> + </math:mrow> + </math:mfenced> + <math:mn>2</math:mn> + </math:msup> +</math:math> diff --git a/starmath/qa/extras/data/simple.mml b/starmath/qa/extras/data/simple.mml new file mode 100644 index 000000000..822d1a709 --- /dev/null +++ b/starmath/qa/extras/data/simple.mml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<math xmlns="http://www.w3.org/1998/Math/MathML"> + <msup> + <mfenced> + <mrow> + <mi>a</mi> + <mo>+</mo> + <mi>b</mi> + </mrow> + </mfenced> + <mn>2</mn> + </msup> +</math> diff --git a/starmath/qa/extras/data/tdf103430.mml b/starmath/qa/extras/data/tdf103430.mml new file mode 100644 index 000000000..92fba05dc --- /dev/null +++ b/starmath/qa/extras/data/tdf103430.mml @@ -0,0 +1,15 @@ +<math xmlns="http://www.w3.org/1998/Math/MathML"> + <mfrac> + <mrow> + <msup> + <mo form="prefix" rspace="0">d</mo> + <mn>2</mn> + </msup> + <mi mathvariant="normal" mathcolor="blue">y</mi> + </mrow> + <mrow> + <mo fontstyle="italic" fontweight="bold" mathvariant="normal" form="prefix" rspace="0">d</mo> + <mi fontfamily="serif" mathvariant="sans-serif-bold-italic" mathcolor="red">x</mi> + </mrow> + </mfrac> +</math> diff --git a/starmath/qa/extras/data/tdf103500.mml b/starmath/qa/extras/data/tdf103500.mml new file mode 100644 index 000000000..7c4966985 --- /dev/null +++ b/starmath/qa/extras/data/tdf103500.mml @@ -0,0 +1,41 @@ +<math xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mrow> + <munderover> + <mo stretchy="false">∫</mo> + <mi>a</mi> + <mi>b</mi> + </munderover> + <mrow> + <mfrac> + <mn>1</mn> + <mi>x</mi> + </mfrac> + <mspace width="0.5em"/> + <mstyle mathvariant="normal"> + <mi mathvariant="normal">d</mi> + </mstyle> + <mi>x</mi> + </mrow> + </mrow> + <mo stretchy="false">=</mo> + <mrow> + <munderover> + <mo stretchy="true">∫</mo> + <mi>a</mi> + <mi>b</mi> + </munderover> + <mrow> + <mfrac> + <mn>1</mn> + <mi>y</mi> + </mfrac> + <mspace width="0.5em"/> + <mstyle mathvariant="normal"> + <mi mathvariant="normal">d</mi> + </mstyle> + <mi>y</mi> + </mrow> + </mrow> + </mrow> +</math> diff --git a/starmath/qa/extras/data/tdf99556-1.mml b/starmath/qa/extras/data/tdf99556-1.mml new file mode 100644 index 000000000..0eae8b2df --- /dev/null +++ b/starmath/qa/extras/data/tdf99556-1.mml @@ -0,0 +1,3 @@ +<math xmlns="http://www.w3.org/1998/Math/MathML"> +<msqrt/> +</math> diff --git a/starmath/qa/extras/mmlexport-test.cxx b/starmath/qa/extras/mmlexport-test.cxx new file mode 100644 index 000000000..18732e09a --- /dev/null +++ b/starmath/qa/extras/mmlexport-test.cxx @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <o3tl/cppunittraitshelper.hxx> +#include <test/bootstrapfixture.hxx> +#include <test/xmltesttools.hxx> +#include <unotools/tempfile.hxx> + +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/sfxmodelfactory.hxx> + +#include <document.hxx> +#include <smdll.hxx> + +#include <memory> + +namespace { + +using namespace ::com::sun::star; + +typedef tools::SvRef<SmDocShell> SmDocShellRef; + +class MathMLExportTest : public test::BootstrapFixture, public XmlTestTools +{ +public: + virtual void setUp() override; + virtual void tearDown() override; + + void testBlank(); + void testTdf97049(); + void testTdf101022(); + + CPPUNIT_TEST_SUITE(MathMLExportTest); + CPPUNIT_TEST(testBlank); + CPPUNIT_TEST(testTdf97049); + CPPUNIT_TEST(testTdf101022); + CPPUNIT_TEST_SUITE_END(); + +protected: + virtual void registerNamespaces(xmlXPathContextPtr &pXmlXPathCtx) override; + +private: + xmlDocUniquePtr exportAndParse(); + + SmDocShellRef mxDocShell; +}; + +void MathMLExportTest::setUp() +{ + BootstrapFixture::setUp(); + SmGlobals::ensure(); + mxDocShell = new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT | + SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS | + SfxModelFlags::DISABLE_DOCUMENT_RECOVERY); +} + +void MathMLExportTest::tearDown() +{ + if (mxDocShell.is()) + mxDocShell->DoClose(); + BootstrapFixture::tearDown(); +} + +void MathMLExportTest::registerNamespaces(xmlXPathContextPtr &pXmlXPathCtx) +{ + xmlXPathRegisterNs(pXmlXPathCtx, BAD_CAST("m"), BAD_CAST("http://www.w3.org/1998/Math/MathML")); +} + +xmlDocUniquePtr MathMLExportTest::exportAndParse() +{ + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + SfxMedium aStoreMedium(aTempFile.GetURL(), StreamMode::STD_WRITE); + std::shared_ptr<const SfxFilter> pExportFilter = SfxFilter::GetFilterByName(MATHML_XML); + aStoreMedium.SetFilter(pExportFilter); + CPPUNIT_ASSERT(mxDocShell->ConvertTo(aStoreMedium)); + aStoreMedium.Commit(); + xmlDocUniquePtr pDoc = parseXml(aTempFile); + CPPUNIT_ASSERT(pDoc); + return pDoc; +} + +void MathMLExportTest::testBlank() +{ + mxDocShell->SetText("x`y~~z"); + xmlDocUniquePtr pDoc = exportAndParse(); + assertXPath(pDoc, "/m:math/m:semantics/m:mrow/m:mspace[1]", "width", "0.5em"); + assertXPath(pDoc, "/m:math/m:semantics/m:mrow/m:mspace[2]", "width", "4em"); +} + +void MathMLExportTest::testTdf97049() +{ + mxDocShell->SetText("intd {{1 over x} dx}"); + xmlDocUniquePtr pDoc = exportAndParse(); + assertXPath(pDoc, "/m:math/m:semantics/m:mrow/m:mo[1]", "stretchy", "true"); + auto aContent = getXPathContent(pDoc, "/m:math/m:semantics/m:mrow/m:mo[1]"); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aContent.getLength()); + CPPUNIT_ASSERT_EQUAL(u'\x222B', aContent[0]); +} + +void MathMLExportTest::testTdf101022() +{ +#define CHECK_MATHVARIANT(capital, small) do \ + { \ + mxDocShell->SetText("%GAMMA %iGAMMA {ital %GAMMA} {nitalic %iGAMMA} " \ + "%gamma %igamma {ital %gamma} {nitalic %igamma}"); \ + xmlDocUniquePtr pDoc = exportAndParse(); \ + if (capital) \ + assertXPathNoAttribute(pDoc, "/m:math/m:semantics/m:mrow/m:mi[1]", "mathvariant"); \ + else \ + assertXPath(pDoc, "/m:math/m:semantics/m:mrow/m:mi[1]", "mathvariant", "normal"); \ + assertXPathNoAttribute(pDoc, "/m:math/m:semantics/m:mrow/m:mstyle[1]/m:mi[1]", "mathvariant"); \ + assertXPathNoAttribute(pDoc, "/m:math/m:semantics/m:mrow/m:mi[2]", "mathvariant"); \ + assertXPath(pDoc, "/m:math/m:semantics/m:mrow/m:mstyle[2]/m:mi[1]", "mathvariant", "normal"); \ + if (small) \ + assertXPathNoAttribute(pDoc, "/m:math/m:semantics/m:mrow/m:mi[3]", "mathvariant"); \ + else \ + assertXPath(pDoc, "/m:math/m:semantics/m:mrow/m:mi[3]", "mathvariant", "normal"); \ + assertXPathNoAttribute(pDoc, "/m:math/m:semantics/m:mrow/m:mstyle[3]/m:mi[1]", "mathvariant"); \ + assertXPathNoAttribute(pDoc, "/m:math/m:semantics/m:mrow/m:mi[4]", "mathvariant"); \ + assertXPath(pDoc, "/m:math/m:semantics/m:mrow/m:mstyle[4]/m:mi[1]", "mathvariant", "normal"); \ + mxDocShell->SetText(""); \ + } \ + while (false) + + CHECK_MATHVARIANT(false, true); // default mode 2 + + mxDocShell->SetGreekCharStyle(1); // mode 1 + CHECK_MATHVARIANT(true, true); + + mxDocShell->SetGreekCharStyle(0); // mode 0 + CHECK_MATHVARIANT(false, false); + +#undef CHECK_MATHVARIANT +} + +CPPUNIT_TEST_SUITE_REGISTRATION(MathMLExportTest); + +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/qa/extras/mmlimport-test.cxx b/starmath/qa/extras/mmlimport-test.cxx new file mode 100644 index 000000000..e455e60d8 --- /dev/null +++ b/starmath/qa/extras/mmlimport-test.cxx @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> +#include <test/bootstrapfixture.hxx> + +#include <comphelper/fileformat.h> + +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/sfxmodelfactory.hxx> + +#include <document.hxx> +#include <smdll.hxx> + +namespace { + +using namespace ::com::sun::star; + +typedef tools::SvRef<SmDocShell> SmDocShellRef; + +class Test : public test::BootstrapFixture +{ +public: + virtual void setUp() override; + virtual void tearDown() override; + + void testColor(); + void testSimple(); + void testNsPrefixMath(); + void testMaction(); + void testMspace(); + void testtdf99556(); + void testTdf103430(); + void testTdf103500(); + + CPPUNIT_TEST_SUITE(Test); + CPPUNIT_TEST(testColor); + CPPUNIT_TEST(testSimple); + CPPUNIT_TEST(testNsPrefixMath); + CPPUNIT_TEST(testMaction); + CPPUNIT_TEST(testMspace); + CPPUNIT_TEST(testtdf99556); + CPPUNIT_TEST(testTdf103430); + CPPUNIT_TEST(testTdf103500); + CPPUNIT_TEST_SUITE_END(); + +private: + void loadURL(const OUString &rURL) + { + // Cf. + // filter/source/config/fragments/filters/MathML_XML__Math_.xcu + auto pFilter = std::make_shared<SfxFilter>(MATHML_XML, + OUString(), + SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::TEMPLATE, + SotClipboardFormatId::STARCALC_8, + "MathML 2.0", + OUString(), + OUString(), + "private:factory/smath*"); + pFilter->SetVersion(SOFFICE_FILEFORMAT_60); + + mxDocShell = new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT | + SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS | + SfxModelFlags::DISABLE_DOCUMENT_RECOVERY); + + SfxMedium* pSrcMed = new SfxMedium(rURL, StreamMode::STD_READ); + pSrcMed->SetFilter(pFilter); + pSrcMed->UseInteractionHandler(false); + bool bLoaded = mxDocShell->DoLoad(pSrcMed); + CPPUNIT_ASSERT_MESSAGE(OUStringToOString("failed to load " + rURL, RTL_TEXTENCODING_UTF8).getStr(), + bLoaded); + } + + SmDocShellRef mxDocShell; +}; + +void Test::setUp() +{ + BootstrapFixture::setUp(); + SmGlobals::ensure(); +} + +void Test::tearDown() +{ + if (mxDocShell.is()) mxDocShell->DoClose(); + BootstrapFixture::tearDown(); +} + +void Test::testColor() +{ + loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/color.mml")); + CPPUNIT_ASSERT_EQUAL(OUString("{{color black b}" + " {color white w}" + " {color red r}" + " {color green g}" + " {color blue b}" + " {color yellow y}" + " {color silver s}" + " {color gray g}" + " {color maroon m}" + " {color purple p}" + " {color lime l}" + " {color olive o}" + " {color navy n}" + " {color teal t}" + " {color aqua a}" + " {color fuchsia f}}"), + mxDocShell->GetText()); +} + +void Test::testSimple() +{ + loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/simple.mml")); + OUString const sExpected("left ( {a + b} right )^2"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("loaded text", sExpected, mxDocShell->GetText()); +} + +void Test::testNsPrefixMath() +{ + loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/ns-prefix-math.mml")); + OUString const sExpected("left ( {a + b} right )^2"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("loaded text", sExpected, mxDocShell->GetText()); +} + +void Test::testMaction() +{ + loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/maction.mml")); + OUString const sExpected("matrix {1 ## 2 ## 3}"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("loaded text", sExpected, mxDocShell->GetText()); +} + +void Test::testMspace() +{ + loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/mspace.mml")); + CPPUNIT_ASSERT_EQUAL(OUString("{a b ~ c ~~``` d}"), mxDocShell->GetText()); +} + +void Test::testtdf99556() +{ + loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/tdf99556-1.mml")); + OUString const sExpected("sqrt { {} }"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("loaded text", sExpected, mxDocShell->GetText()); +} + +void Test::testTdf103430() +{ + loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/tdf103430.mml")); + CPPUNIT_ASSERT_EQUAL(OUString("{{nitalic d}^2 {nitalic {color blue y}}} over {{nitalic d} {font sans {bold {italic {color red x}}}}}"), + mxDocShell->GetText()); +} + +void Test::testTdf103500() +{ + loadURL(m_directories.getURLFromSrc("starmath/qa/extras/data/tdf103500.mml")); + CPPUNIT_ASSERT_EQUAL(OUString("{{ int csub a csup b {1 over x ` {nitalic d} x}} = {intd csub a csup b {1 over y ` {nitalic d} y}}}"), + mxDocShell->GetText()); +} + + +CPPUNIT_TEST_SUITE_REGISTRATION(Test); + +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/qa/unit/data/starmath-dialogs-test.txt b/starmath/qa/unit/data/starmath-dialogs-test.txt new file mode 100644 index 000000000..7847bc71f --- /dev/null +++ b/starmath/qa/unit/data/starmath-dialogs-test.txt @@ -0,0 +1,47 @@ +# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# This file contains all dialogs that the unit tests in the module +# will work on if it is in script mode. It will read one-by-one, +# try to open it and create a screenshot that will be saved in +# workdir/screenshots using the pattern of the ui-file name. +# +# Syntax: +# - empty lines are allowed +# - lines starting with '#' are treated as comment +# - all other lines should contain a *.ui filename in the same +# notation as in the dialog constructors (see code) + +# +# The 'known' dialogs which have a hard-coded representation +# in registerKnownDialogsByID/createDialogByID +# + +# No known dialogs in starmath for now + +# +# Dialogs without a hard-coded representation. These will +# be visualized using a fallback based on VclBuilder +# + +# currently deactivated, leads to problems and the test to not work +# This is typically a hint that these should be hard-coded in the +# test case since they need some document and model data to work + +modules/smath/ui/printeroptions.ui +modules/smath/ui/smathsettings.ui +modules/smath/ui/fontdialog.ui +modules/smath/ui/fontsizedialog.ui +modules/smath/ui/fonttypedialog.ui +modules/smath/ui/spacingdialog.ui +modules/smath/ui/alignmentdialog.ui +modules/smath/ui/catalogdialog.ui +modules/smath/ui/symdefinedialog.ui +modules/smath/ui/savedefaultsdialog.ui +modules/smath/ui/dockingelements.ui diff --git a/starmath/qa/unit/starmath-dialogs-test.cxx b/starmath/qa/unit/starmath-dialogs-test.cxx new file mode 100644 index 000000000..42e6ef45c --- /dev/null +++ b/starmath/qa/unit/starmath-dialogs-test.cxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> +#include <test/screenshot_test.hxx> +#include <vcl/abstdlg.hxx> + +using namespace ::com::sun::star; + +/// Test opening a dialog in starmath +class StarmathDialogsTest : public ScreenshotTest +{ +private: + /// helper method to populate KnownDialogs, called in setUp(). Needs to be + /// written and has to add entries to KnownDialogs + virtual void registerKnownDialogsByID(mapType& rKnownDialogs) override; + + /// dialog creation for known dialogs by ID. Has to be implemented for + /// each registered known dialog + virtual VclPtr<VclAbstractDialog> createDialogByID(sal_uInt32 nID) override; + +public: + StarmathDialogsTest(); + + // try to open a dialog + void openAnyDialog(); + + CPPUNIT_TEST_SUITE(StarmathDialogsTest); + CPPUNIT_TEST(openAnyDialog); + CPPUNIT_TEST_SUITE_END(); +}; + +StarmathDialogsTest::StarmathDialogsTest() +{ +} + +void StarmathDialogsTest::registerKnownDialogsByID(mapType& /*rKnownDialogs*/) +{ + // fill map of known dialogs +} + +VclPtr<VclAbstractDialog> StarmathDialogsTest::createDialogByID(sal_uInt32 /*nID*/) +{ + return nullptr; +} + +void StarmathDialogsTest::openAnyDialog() +{ + /// process input file containing the UXMLDescriptions of the dialogs to dump + processDialogBatchFile("starmath/qa/unit/data/starmath-dialogs-test.txt"); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(StarmathDialogsTest); + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/qa/unoapi/knownissues.xcl b/starmath/qa/unoapi/knownissues.xcl new file mode 100644 index 000000000..2fedc6e55 --- /dev/null +++ b/starmath/qa/unoapi/knownissues.xcl @@ -0,0 +1,39 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +### i23394 ### +sm.XMLImporter::com::sun::star::xml::sax::XDocumentHandler +sm.XMLMetaImporter::com::sun::star::xml::sax::XDocumentHandler + +### i88645 ### +sm.XMLExporter::com::sun::star::document::XFilter + +### i94322 ### +sm.SmEditAccessible::com::sun::star::accessibility::XAccessibleEventBroadcaster + +### i94320 ### +sm.SmGraphicAccessible::com::sun::star::accessibility::XAccessibleEventBroadcaster + +### i94275 ### +sm.SmGraphicAccessible::com::sun::star::accessibility::XAccessibleText + +### i111220 ### +sm.XMLMetaExporter::com::sun::star::document::XFilter + +### i112743 ### +sm.XMLSettingsExporter::com::sun::star::document::XFilter diff --git a/starmath/qa/unoapi/sm.sce b/starmath/qa/unoapi/sm.sce new file mode 100644 index 000000000..4e7b278a7 --- /dev/null +++ b/starmath/qa/unoapi/sm.sce @@ -0,0 +1,26 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +-o sm.SmEditAccessible +-o sm.SmGraphicAccessible +-o sm.SmModel +-o sm.XMLExporter +-o sm.XMLImporter +-o sm.XMLMetaExporter +-o sm.XMLMetaImporter +-o sm.XMLSettingsExporter +-o sm.XMLSettingsImporter diff --git a/starmath/sdi/smath.sdi b/starmath/sdi/smath.sdi new file mode 100644 index 000000000..575e464fa --- /dev/null +++ b/starmath/sdi/smath.sdi @@ -0,0 +1,564 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +SfxVoidItem ChangeAlignment SID_ALIGN +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Format; +] + + +SfxVoidItem ChangeDistance SID_DISTANCE +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Format; +] + + +SfxVoidItem ChangeFont SID_FONT +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Format; +] + + +SfxVoidItem ChangeFontSize SID_FONTSIZE +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Format; +] + + +SfxVoidItem CommandWindow SID_CMDBOXWINDOW +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::View; +] + + +SfxVoidItem ElementsDockingWindow SID_ELEMENTSDOCKINGWINDOW +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::View; +] + + +SfxStringItem ConfigName SID_TEXT + +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::View; +] + + +SfxVoidItem CopyObject SID_COPYOBJECT +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::Edit; +] + + +SfxVoidItem Draw SID_DRAW +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::View; +] + + +SfxVoidItem FormelCursor SID_FORMULACURSOR +() +[ + AutoUpdate = TRUE, + FastCall = TRUE, + ReadOnlyDoc = TRUE, + Toggle = TRUE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Math; +] + + +SfxInt16Item Graphic SID_GAPHIC_SM + +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::Math; +] + + +SfxVoidItem InsertCommandText SID_INSERTCOMMANDTEXT +(SfxStringItem Text SID_INSERTCOMMANDTEXT) +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + Asynchron; + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::Insert; +] + + +SfxVoidItem InsertConfigName SID_INSERTSPECIAL +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + Asynchron; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Insert; +] + + +SfxVoidItem ImportFormula SID_IMPORT_FORMULA +(SfxStringItem Name SID_IMPORT_FORMULA,SfxStringItem Filter FN_PARAM_1) +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + Asynchron; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Insert; +] + +SfxVoidItem ImportMathMLClipboard SID_IMPORT_MATHML_CLIPBOARD +() +[ + AutoUpdate = FALSE, + FastCall = TRUE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, /*obsolete */ + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Insert; +] + +SfxVoidItem LoadSymbols SID_LOADSYMBOLS +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + Asynchron; + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::Options; +] + + +SfxStringItem ModifyStatus SID_MODIFYSTATUS + +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::View; +] + + +SfxVoidItem NextError SID_NEXTERR +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Navigator; +] + + +SfxVoidItem NextMark SID_NEXTMARK +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Navigator; +] + + +SfxVoidItem PasteObject SID_PASTEOBJECT +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::Edit; +] + + +SfxVoidItem PrevError SID_PREVERR +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Navigator; +] + + +SfxVoidItem PrevMark SID_PREVMARK +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Navigator; +] + + +SfxBoolItem RedrawAutomatic SID_AUTO_REDRAW + +[ + AutoUpdate = TRUE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::View; +] + + +SfxVoidItem SaveSymbols SID_SAVESYMBOLS +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::Options; +] + + +SfxVoidItem SetPaperSize SID_GETEDITTEXT +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::Math; +] + + +SfxVoidItem SymbolCatalogue SID_SYMBOLS_CATALOGUE +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Options; +] + + +SfxBoolItem Textmode SID_TEXTMODE + +[ + AutoUpdate = TRUE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::View; +] + + +SfxStringItem TextStatus SID_TEXTSTATUS + +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::View; +] + + +SfxVoidItem ZoomIn SID_ZOOMIN +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::View; +] + + +SfxVoidItem ZoomOut SID_ZOOMOUT +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::View; +] diff --git a/starmath/sdi/smslots.sdi b/starmath/sdi/smslots.sdi new file mode 100644 index 000000000..0697dd865 --- /dev/null +++ b/starmath/sdi/smslots.sdi @@ -0,0 +1,294 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module +StarMath +[ + SlotIdFile ( "starmath.hrc" ) + SlotIdFile ( "editeng/memberids.h" ) + SlotIdFile ( "editeng/editids.hrc" ) + SlotIdFile ( "sfx2/sfxsids.hrc" ) + SlotIdFile ( "svx/svxids.hrc" ) + SlotIdFile ( "svx/unomid.hxx" ) +] +{ + include "sfxitems.sdi" + include "sfx.sdi" + include "svxitems.sdi" + include "xoitems.sdi" + include "svx.sdi" + include "smath.sdi" + interface StarMath + { + SID_CONFIGEVENT //idlpp ole : no , status : no + [ + StateMethod = GetState ; + ] +} + +shell SmModule +{ + import StarMath; +} + +interface FormulaDocument +{ + SID_DOCTEMPLATE //idlpp ole : no , status : no + [ + StateMethod = GetState ; + ] + SID_AUTO_REDRAW //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_LOADSYMBOLS //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_SAVESYMBOLS //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_FONT //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_FONTSIZE //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_DISTANCE //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_ALIGN //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_TEXTMODE //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_MODIFYSTATUS //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_TEXT //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_GAPHIC_SM //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + + SID_UNDO //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_REDO //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_GETUNDOSTRINGS //idlpp ole : no , status : no + [ + StateMethod = GetState ; + ] + SID_GETREDOSTRINGS //idlpp ole : no , status : no + [ + StateMethod = GetState ; + ] +} + +shell SmDocShell +{ + import FormulaDocument; +} + + +interface FormulaView +{ + SID_FORMULACURSOR + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_NEXTERR //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_PREVERR //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_NEXTMARK //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_PREVMARK //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_ZOOMIN //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_ZOOMOUT //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_DRAW //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_COPYOBJECT //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_PASTEOBJECT //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_CUT //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_COPY //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_PASTE //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_DELETE //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_SELECT //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_INSERTCOMMANDTEXT + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_INSERTSPECIAL //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_IMPORT_FORMULA //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + Export = FALSE ; + ] + SID_IMPORT_MATHML_CLIPBOARD //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + Export = FALSE ; + ] + //idlpp no menu entry, so no texts + SID_ATTR_ZOOM //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_ATTR_ZOOMSLIDER + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_TEXTSTATUS //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_GETEDITTEXT //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //idlpp no menu entry, so no texts + SID_CMDBOXWINDOW //idlpp ole : no , status : no + [ + ExecMethod = NoExec ; + StateMethod = NoState ; + ] + SID_ELEMENTSDOCKINGWINDOW + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_UNICODE_NOTATION_TOGGLE + [ + ExecMethod = Execute; + ] + SID_SYMBOLS_CATALOGUE //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] +} + +shell SmViewShell +{ + import FormulaView; +} + +} 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: |"]; // 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: ∈"]; // Unicode "ELEMENT OF" + * n10 -> n13 [label="2"]; + * n13 [label="SmMathSymbolNode: ℤ"]; // 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: */ diff --git a/starmath/uiconfig/smath/menubar/menubar.xml b/starmath/uiconfig/smath/menubar/menubar.xml new file mode 100644 index 000000000..04ca77df8 --- /dev/null +++ b/starmath/uiconfig/smath/menubar/menubar.xml @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> +<menu:menubar xmlns:menu="http://openoffice.org/2001/menu" menu:id="menubar"> + <menu:menu menu:id=".uno:PickList"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:AddDirect"/> + <menu:menuitem menu:id=".uno:Open"/> + <menu:menuitem menu:id=".uno:OpenRemote" menu:style="text"/> + <menu:menuitem menu:id=".uno:RecentFileList"/> + <menu:menuitem menu:id=".uno:CloseDoc"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:AutoPilotMenu" menu:style="text"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:Reload" menu:style="text"/> + <menu:menuitem menu:id=".uno:VersionDialog" menu:style="text"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:Save"/> + <menu:menuitem menu:id=".uno:SaveAsRemote" menu:style="text"/> + <menu:menuitem menu:id=".uno:SaveAs"/> + <menu:menuitem menu:id=".uno:SaveAll" menu:style="text"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:CheckOut"/> + <menu:menuitem menu:id=".uno:CancelCheckOut"/> + <menu:menuitem menu:id=".uno:CheckIn"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:ExportToPDF"/> + <menu:menu menu:id=".uno:SendToMenu"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:SendMail"/> + <menu:menuitem menu:id=".uno:SendMailDocAsPDF"/> + </menu:menupopup> + </menu:menu> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:Print"/> + <menu:menuitem menu:id=".uno:PrinterSetup"/> + <menu:menuseparator/> + <menu:menu menu:id=".uno:SignaturesMenu"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:Signature"/> + <menu:menuitem menu:id=".uno:SignPDF"/> + </menu:menupopup> + </menu:menu> + <menu:menuitem menu:id=".uno:SetDocumentProperties"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:Quit"/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:EditMenu"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:Undo"/> + <menu:menuitem menu:id=".uno:Redo"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:Cut"/> + <menu:menuitem menu:id=".uno:Copy"/> + <menu:menuitem menu:id=".uno:Paste"/> + <menu:menuitem menu:id=".uno:Select"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:NextMark"/> + <menu:menuitem menu:id=".uno:PrevMark"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:NextError"/> + <menu:menuitem menu:id=".uno:PrevError"/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:ViewMenu"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:Draw"/> + <menu:menuitem menu:id=".uno:RedrawAutomatic"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:ToolbarMode"/> + <menu:menuitem menu:id=".uno:AvailableToolbars"/> + <menu:menuitem menu:id=".uno:StatusBarVisible"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:ElementsDockingWindow"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:FullScreen"/> + <menu:menuitem menu:id=".uno:ZoomIn"/> + <menu:menuitem menu:id=".uno:ZoomOut"/> + <menu:menuitem menu:id=".uno:ZoomOptimal"/> + <menu:menu menu:id=".uno:ZoomMenu"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:Zoom50Percent"/> + <menu:menuitem menu:id=".uno:Zoom75Percent"/> + <menu:menuitem menu:id=".uno:Zoom100Percent"/> + <menu:menuitem menu:id=".uno:Zoom150Percent"/> + <menu:menuitem menu:id=".uno:Zoom200Percent"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:Zoom"/> + </menu:menupopup> + </menu:menu> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:FormatMenu"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:ChangeFont"/> + <menu:menuitem menu:id=".uno:ChangeFontSize"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:ChangeDistance"/> + <menu:menuitem menu:id=".uno:ChangeAlignment"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:Textmode"/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:ToolsMenu"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:SymbolCatalogue"/> + <menu:menuitem menu:id=".uno:ImportFormula"/> + <menu:menuitem menu:id=".uno:ImportMathMLClipboard"/> + <menu:menuseparator/> + <menu:menu menu:id=".uno:MacrosMenu"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:MacroRecorder"/> + <menu:menuitem menu:id=".uno:RunMacro"/> + <menu:menuitem menu:id=".uno:BasicIDEAppear"/> + <menu:menuitem menu:id=".uno:ScriptOrganizer"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:MacroSignature"/> + <menu:menuitem menu:id=".uno:MacroOrganizer?TabId:short=1"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:OpenXMLFilterSettings"/> + </menu:menupopup> + </menu:menu> + <menu:menuitem menu:id="service:com.sun.star.deployment.ui.PackageManagerDialog" menu:style="text"/> + <menu:menuitem menu:id=".uno:ConfigureDialog" menu:style="text"/> + <menu:menuitem menu:id=".uno:OptionsTreeDialog"/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:WindowList"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:NewWindow"/> + <menu:menuitem menu:id=".uno:CloseWin"/> + <menu:menuseparator/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:HelpMenu"> + <menu:menupopup> + <menu:menuitem menu:id=".uno:HelpIndex"/> + <menu:menuitem menu:id=".uno:ExtendedHelp"/> + <menu:menuitem menu:id=".uno:Documentation"/> + <menu:menuitem menu:id=".uno:TipOfTheDay"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:QuestionAnswers"/> + <menu:menuitem menu:id=".uno:SendFeedback"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:SafeMode"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:GetInvolved"/> + <menu:menuitem menu:id=".uno:Donation"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:ShowLicense"/> + <menu:menuitem menu:id=".uno:About"/> + </menu:menupopup> + </menu:menu> +</menu:menubar> diff --git a/starmath/uiconfig/smath/popupmenu/edit.xml b/starmath/uiconfig/smath/popupmenu/edit.xml new file mode 100644 index 000000000..f5a7f0680 --- /dev/null +++ b/starmath/uiconfig/smath/popupmenu/edit.xml @@ -0,0 +1,269 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * +--> +<!-- BEWARE: We store most labels here because they are not translatable. + Labels that are supposed to be translatable should be added to + officecfg/.../MathCommands.xcu instead. +--> +<menu:menupopup xmlns:menu="http://openoffice.org/2001/menu"> + <menu:menuitem menu:id=".uno:Cut"/> + <menu:menuitem menu:id=".uno:Copy"/> + <menu:menuitem menu:id=".uno:Paste"/> + <menu:menuseparator/> + <menu:menu menu:id=".uno:UnaryBinaryMenu"> + <menu:menupopup> + <menu:menuitem menu:label="+a" menu:id=".uno:InsertCommandText?Text:string=+<?> "/> + <menu:menuitem menu:label="-a" menu:id=".uno:InsertCommandText?Text:string=-<?> "/> + <menu:menuitem menu:label="+-a" menu:id=".uno:InsertCommandText?Text:string=+-<?> "/> + <menu:menuitem menu:label="-+a" menu:id=".uno:InsertCommandText?Text:string=-+<?> "/> + <menu:menuseparator/> + <menu:menuitem menu:label="a + b" menu:id=".uno:InsertCommandText?Text:string=<?> + <?> "/> + <menu:menuitem menu:label="a - b" menu:id=".uno:InsertCommandText?Text:string=<?> - <?> "/> + <menu:menuitem menu:label="a cdot b" menu:id=".uno:InsertCommandText?Text:string=<?> cdot <?> "/> + <menu:menuitem menu:label="a times b" menu:id=".uno:InsertCommandText?Text:string=<?> times <?> "/> + <menu:menuitem menu:label="a * b" menu:id=".uno:InsertCommandText?Text:string=<?> * <?> "/> + <menu:menuitem menu:label="a over b" menu:id=".uno:InsertCommandText?Text:string={<?>} over {<?>} "/> + <menu:menuitem menu:label="a div b" menu:id=".uno:InsertCommandText?Text:string=<?> div <?> "/> + <menu:menuitem menu:label="a / b" menu:id=".uno:InsertCommandText?Text:string=<?> / <?> "/> + <menu:menuitem menu:label="a circ b" menu:id=".uno:InsertCommandText?Text:string=<?> circ <?> "/> + <menu:menuseparator/> + <menu:menuitem menu:label="a wideslash b" menu:id=".uno:InsertCommandText?Text:string={<?>} wideslash {<?>} "/> + <menu:menuitem menu:label="a widebslash b" menu:id=".uno:InsertCommandText?Text:string={<?>} widebslash {<?>} "/> + <menu:menuseparator/> + <menu:menuitem menu:label="neg a" menu:id=".uno:InsertCommandText?Text:string=neg <?> "/> + <menu:menuitem menu:label="a and b" menu:id=".uno:InsertCommandText?Text:string=<?> and <?> "/> + <menu:menuitem menu:label="a or b" menu:id=".uno:InsertCommandText?Text:string=<?> or <?> "/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:RelationsMenu"> + <menu:menupopup> + <menu:menuitem menu:label="a = b" menu:id=".uno:InsertCommandText?Text:string=<?> %3D <?> "/> + <menu:menuitem menu:label="a <> b" menu:id=".uno:InsertCommandText?Text:string=<?> <> <?> "/> + <menu:menuitem menu:label="a < b" menu:id=".uno:InsertCommandText?Text:string=<?> < <?> "/> + <menu:menuitem menu:label="a <= b" menu:id=".uno:InsertCommandText?Text:string=<?> <%3D <?> "/> + <menu:menuitem menu:label="a leslant b" menu:id=".uno:InsertCommandText?Text:string=<?> leslant <?> "/> + <menu:menuitem menu:label="a > b" menu:id=".uno:InsertCommandText?Text:string=<?> > <?> "/> + <menu:menuitem menu:label="a >= b" menu:id=".uno:InsertCommandText?Text:string=<?> >%3D <?> "/> + <menu:menuitem menu:label="a geslant b" menu:id=".uno:InsertCommandText?Text:string=<?> geslant <?> "/> + <menu:menuseparator/> + <menu:menuitem menu:label="a approx b" menu:id=".uno:InsertCommandText?Text:string=<?> approx <?> "/> + <menu:menuitem menu:label="a sim b" menu:id=".uno:InsertCommandText?Text:string=<?> sim <?> "/> + <menu:menuitem menu:label="a simeq b" menu:id=".uno:InsertCommandText?Text:string=<?> simeq <?> "/> + <menu:menuitem menu:label="a equiv b" menu:id=".uno:InsertCommandText?Text:string=<?> equiv <?> "/> + <menu:menuitem menu:label="a prop b" menu:id=".uno:InsertCommandText?Text:string=<?> prop <?> "/> + <menu:menuitem menu:label="a parallel b" menu:id=".uno:InsertCommandText?Text:string=<?> parallel <?> "/> + <menu:menuitem menu:label="a ortho b" menu:id=".uno:InsertCommandText?Text:string=<?> ortho <?> "/> + <menu:menuitem menu:label="a divides b" menu:id=".uno:InsertCommandText?Text:string=<?> divides <?> "/> + <menu:menuitem menu:label="a ndivides b" menu:id=".uno:InsertCommandText?Text:string=<?> ndivides <?> "/> + <menu:menuitem menu:label="a toward b" menu:id=".uno:InsertCommandText?Text:string=<?> toward <?> "/> + <menu:menuseparator/> + <menu:menuitem menu:label="a dlarrow b" menu:id=".uno:InsertCommandText?Text:string=<?> dlarrow <?> "/> + <menu:menuitem menu:label="a dlrarrow b" menu:id=".uno:InsertCommandText?Text:string=<?> dlrarrow <?> "/> + <menu:menuitem menu:label="a drarrow b" menu:id=".uno:InsertCommandText?Text:string=<?> drarrow <?> "/> + <menu:menuseparator/> + <menu:menuitem menu:label="a prec b" menu:id=".uno:InsertCommandText?Text:string=<?> prec <?> "/> + <menu:menuitem menu:label="a succ b" menu:id=".uno:InsertCommandText?Text:string=<?> succ <?> "/> + <menu:menuitem menu:label="a preccurlyeq b" menu:id=".uno:InsertCommandText?Text:string=<?> preccurlyeq <?> "/> + <menu:menuitem menu:label="a succcurlyeq b" menu:id=".uno:InsertCommandText?Text:string=<?> succcurlyeq <?> "/> + <menu:menuitem menu:label="a precsim b" menu:id=".uno:InsertCommandText?Text:string=<?> precsim <?> "/> + <menu:menuitem menu:label="a succsim b" menu:id=".uno:InsertCommandText?Text:string=<?> succsim <?> "/> + <menu:menuitem menu:label="a nprec b" menu:id=".uno:InsertCommandText?Text:string=<?> nprec <?> "/> + <menu:menuitem menu:label="a nsucc b" menu:id=".uno:InsertCommandText?Text:string=<?> nsucc <?> "/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:SetOperationsMenu"> + <menu:menupopup> + <menu:menuitem menu:label="a in A" menu:id=".uno:InsertCommandText?Text:string=<?> in <?> "/> + <menu:menuitem menu:label="a notin A" menu:id=".uno:InsertCommandText?Text:string=<?> notin <?> "/> + <menu:menuitem menu:label="A owns a" menu:id=".uno:InsertCommandText?Text:string=<?> owns <?> "/> + <menu:menuseparator/> + <menu:menuitem menu:label="A intersection B" menu:id=".uno:InsertCommandText?Text:string=<?> intersection <?> "/> + <menu:menuitem menu:label="A union B" menu:id=".uno:InsertCommandText?Text:string=<?> union <?> "/> + <menu:menuitem menu:label="A \ B" menu:id=".uno:InsertCommandText?Text:string=<?> setminus <?> "/> + <menu:menuitem menu:label="A / B" menu:id=".uno:InsertCommandText?Text:string=<?> slash <?> "/> + <menu:menuitem menu:label="A subset B" menu:id=".uno:InsertCommandText?Text:string=<?> subset <?> "/> + <menu:menuitem menu:label="A subseteq B" menu:id=".uno:InsertCommandText?Text:string=<?> subseteq <?> "/> + <menu:menuitem menu:label="A supset B" menu:id=".uno:InsertCommandText?Text:string=<?> supset <?> "/> + <menu:menuitem menu:label="A supseteq B" menu:id=".uno:InsertCommandText?Text:string=<?> supseteq <?> "/> + <menu:menuitem menu:label="A nsubset B" menu:id=".uno:InsertCommandText?Text:string=<?> nsubset <?> "/> + <menu:menuitem menu:label="A nsubseteq B" menu:id=".uno:InsertCommandText?Text:string=<?> nsubseteq <?> "/> + <menu:menuitem menu:label="A nsupset B" menu:id=".uno:InsertCommandText?Text:string=<?> nsupset <?> "/> + <menu:menuitem menu:label="A nsupseteq B" menu:id=".uno:InsertCommandText?Text:string=<?> nsupseteq <?> "/> + <menu:menuseparator/> + <menu:menuitem menu:label="emptyset" menu:id=".uno:InsertCommandText?Text:string=emptyset "/> + <menu:menuitem menu:label="aleph" menu:id=".uno:InsertCommandText?Text:string=aleph "/> + <menu:menuitem menu:label="setN" menu:id=".uno:InsertCommandText?Text:string=setN "/> + <menu:menuitem menu:label="setZ" menu:id=".uno:InsertCommandText?Text:string=setZ "/> + <menu:menuitem menu:label="setQ" menu:id=".uno:InsertCommandText?Text:string=setQ "/> + <menu:menuitem menu:label="setR" menu:id=".uno:InsertCommandText?Text:string=setR "/> + <menu:menuitem menu:label="setC" menu:id=".uno:InsertCommandText?Text:string=setC "/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:FunctionsMenu"> + <menu:menupopup> + <menu:menuitem menu:label="|x|" menu:id=".uno:InsertCommandText?Text:string=abs{<?>} "/> + <menu:menuitem menu:label="x!" menu:id=".uno:InsertCommandText?Text:string=fact{<?>} "/> + <menu:menuitem menu:label="sqrt x" menu:id=".uno:InsertCommandText?Text:string=sqrt{<?>} "/> + <menu:menuitem menu:label="nroot x y" menu:id=".uno:InsertCommandText?Text:string=nroot{<?>}{<?>} "/> + <menu:menuitem menu:label="x^y" menu:id=".uno:InsertCommandText?Text:string=<?>^{<?>}"/> + <menu:menuitem menu:label="e^x" menu:id=".uno:InsertCommandText?Text:string=func e^{<?>} "/> + <menu:menuitem menu:label="ln(x)" menu:id=".uno:InsertCommandText?Text:string=ln(<?>) "/> + <menu:menuitem menu:label="exp(x)" menu:id=".uno:InsertCommandText?Text:string=exp(<?>) "/> + <menu:menuitem menu:label="log(x)" menu:id=".uno:InsertCommandText?Text:string=log(<?>) "/> + <menu:menuseparator/> + <menu:menuitem menu:label="sin(x)" menu:id=".uno:InsertCommandText?Text:string=sin(<?>) "/> + <menu:menuitem menu:label="cos(x)" menu:id=".uno:InsertCommandText?Text:string=cos(<?>) "/> + <menu:menuitem menu:label="tan(x)" menu:id=".uno:InsertCommandText?Text:string=tan(<?>) "/> + <menu:menuitem menu:label="cot(x)" menu:id=".uno:InsertCommandText?Text:string=cot(<?>) "/> + <menu:menuitem menu:label="sinh(x)" menu:id=".uno:InsertCommandText?Text:string=sinh(<?>) "/> + <menu:menuitem menu:label="cosh(x)" menu:id=".uno:InsertCommandText?Text:string=cosh(<?>) "/> + <menu:menuitem menu:label="tanh(x)" menu:id=".uno:InsertCommandText?Text:string=tanh(<?>) "/> + <menu:menuitem menu:label="coth(x)" menu:id=".uno:InsertCommandText?Text:string=coth(<?>) "/> + <menu:menuseparator/> + <menu:menuitem menu:label="arcsin(x)" menu:id=".uno:InsertCommandText?Text:string=arcsin(<?>) "/> + <menu:menuitem menu:label="arccos(x)" menu:id=".uno:InsertCommandText?Text:string=arccos(<?>) "/> + <menu:menuitem menu:label="arctan(x)" menu:id=".uno:InsertCommandText?Text:string=arctan(<?>) "/> + <menu:menuitem menu:label="arccot(x)" menu:id=".uno:InsertCommandText?Text:string=arccot(<?>) "/> + <menu:menuitem menu:label="arsinh(x)" menu:id=".uno:InsertCommandText?Text:string=arsinh(<?>) "/> + <menu:menuitem menu:label="arcosh(x)" menu:id=".uno:InsertCommandText?Text:string=arcosh(<?>) "/> + <menu:menuitem menu:label="artanh(x)" menu:id=".uno:InsertCommandText?Text:string=artanh(<?>) "/> + <menu:menuitem menu:label="arcoth(x)" menu:id=".uno:InsertCommandText?Text:string=arcoth(<?>) "/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:OperatorsMenu"> + <menu:menupopup> + <menu:menuitem menu:label="lim x" menu:id=".uno:InsertCommandText?Text:string=lim <?> "/> + <menu:menuitem menu:label="sum x" menu:id=".uno:InsertCommandText?Text:string=sum <?> "/> + <menu:menuitem menu:label="prod x" menu:id=".uno:InsertCommandText?Text:string=prod <?> "/> + <menu:menuitem menu:label="coprod x" menu:id=".uno:InsertCommandText?Text:string=coprod <?> "/> + <menu:menuitem menu:label="int x" menu:id=".uno:InsertCommandText?Text:string=int <?> "/> + <menu:menuitem menu:label="iint x" menu:id=".uno:InsertCommandText?Text:string=iint <?> "/> + <menu:menuitem menu:label="iiint x" menu:id=".uno:InsertCommandText?Text:string=iiint <?> "/> + <menu:menuitem menu:label="lint x" menu:id=".uno:InsertCommandText?Text:string=lint <?> "/> + <menu:menuitem menu:label="llint x" menu:id=".uno:InsertCommandText?Text:string=llint <?> "/> + <menu:menuitem menu:label="lllint x" menu:id=".uno:InsertCommandText?Text:string=lllint <?> "/> + <menu:menuseparator/> + <menu:menuitem menu:label="... from a to b" menu:id=".uno:InsertCommandText?Text:string=from{<?>} to{<?>} <?> "/> + <menu:menuitem menu:label="... from a" menu:id=".uno:InsertCommandText?Text:string=from{<?>} <?> "/> + <menu:menuitem menu:label="... to b" menu:id=".uno:InsertCommandText?Text:string=to{<?>} <?> "/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:AttributesMenu"> + <menu:menupopup> + <menu:menuitem menu:label="acute a" menu:id=".uno:InsertCommandText?Text:string=acute <?> "/> + <menu:menuitem menu:label="grave a" menu:id=".uno:InsertCommandText?Text:string=grave <?> "/> + <menu:menuitem menu:label="check a" menu:id=".uno:InsertCommandText?Text:string=check <?> "/> + <menu:menuitem menu:label="breve a" menu:id=".uno:InsertCommandText?Text:string=breve <?> "/> + <menu:menuitem menu:label="circle a" menu:id=".uno:InsertCommandText?Text:string=circle <?> "/> + <menu:menuitem menu:label="dot a" menu:id=".uno:InsertCommandText?Text:string=dot <?> "/> + <menu:menuitem menu:label="ddot a" menu:id=".uno:InsertCommandText?Text:string=ddot <?> "/> + <menu:menuitem menu:label="dddot a" menu:id=".uno:InsertCommandText?Text:string=dddot <?> "/> + <menu:menuitem menu:label="bar a" menu:id=".uno:InsertCommandText?Text:string=bar <?> "/> + <menu:menuitem menu:label="vec a" menu:id=".uno:InsertCommandText?Text:string=vec <?> "/> + <menu:menuitem menu:label="harpoon a" menu:id=".uno:InsertCommandText?Text:string=harpoon <?> "/> + <menu:menuitem menu:label="tilde a" menu:id=".uno:InsertCommandText?Text:string=tilde <?> "/> + <menu:menuitem menu:label="hat a" menu:id=".uno:InsertCommandText?Text:string=hat <?> "/> + <menu:menuseparator/> + <menu:menuitem menu:label="widevec abc" menu:id=".uno:InsertCommandText?Text:string=widevec {<?>} "/> + <menu:menuitem menu:label="wideharpoon abc" menu:id=".uno:InsertCommandText?Text:string=wideharpoon {<?>} "/> + <menu:menuitem menu:label="widetilde abc" menu:id=".uno:InsertCommandText?Text:string=widetilde {<?>} "/> + <menu:menuitem menu:label="widehat abc" menu:id=".uno:InsertCommandText?Text:string=widehat {<?>} "/> + <menu:menuitem menu:label="overline abc" menu:id=".uno:InsertCommandText?Text:string=overline {<?>} "/> + <menu:menuitem menu:label="underline abc" menu:id=".uno:InsertCommandText?Text:string=underline {<?>} "/> + <menu:menuitem menu:label="overstrike abc" menu:id=".uno:InsertCommandText?Text:string=overstrike {<?>} "/> + <menu:menuseparator/> + <menu:menuitem menu:label="phantom b" menu:id=".uno:InsertCommandText?Text:string=phantom {<?>} "/> + <menu:menuitem menu:label="bold b" menu:id=".uno:InsertCommandText?Text:string=bold <?> "/> + <menu:menuitem menu:label="ital b" menu:id=".uno:InsertCommandText?Text:string=ital <?> "/> + <menu:menuitem menu:label="size s b" menu:id=".uno:InsertCommandText?Text:string=size <?> {<?>} "/> + <menu:menuitem menu:label="font f b" menu:id=".uno:InsertCommandText?Text:string=font <?> {<?>} "/> + <menu:menuitem menu:label="color rgb" menu:id=".uno:InsertCommandText?Text:string=color rgb 0 0 0 "/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:BracketsMenu"> + <menu:menupopup> + <menu:menuitem menu:label="{...}" menu:id=".uno:InsertCommandText?Text:string={<?>} "/> + <menu:menuseparator/> + <menu:menuitem menu:label="(x)" menu:id=".uno:InsertCommandText?Text:string=(<?>) "/> + <menu:menuitem menu:label="[x]" menu:id=".uno:InsertCommandText?Text:string=[<?>] "/> + <menu:menuitem menu:label="ldbracket x rdbracket " menu:id=".uno:InsertCommandText?Text:string=ldbracket <?> rdbracket "/> + <menu:menuitem menu:label="{x}" menu:id=".uno:InsertCommandText?Text:string=lbrace <?> rbrace "/> + <menu:menuitem menu:label="langle x rangle" menu:id=".uno:InsertCommandText?Text:string=langle <?> rangle "/> + <menu:menuitem menu:label="langle x mline y rangle" menu:id=".uno:InsertCommandText?Text:string=langle <?> mline <?> rangle "/> + <menu:menuitem menu:label="lceil x rceil" menu:id=".uno:InsertCommandText?Text:string=lceil <?> rceil "/> + <menu:menuitem menu:label="lfloor x rfloor" menu:id=".uno:InsertCommandText?Text:string=lfloor <?> rfloor "/> + <menu:menuitem menu:label="lline x rline" menu:id=".uno:InsertCommandText?Text:string=lline <?> rline "/> + <menu:menuitem menu:label="ldline x rdline" menu:id=".uno:InsertCommandText?Text:string=ldline <?> rdline "/> + <menu:menuseparator/> + <menu:menuitem menu:label="left ( x right )" menu:id=".uno:InsertCommandText?Text:string=left ( <?> right ) "/> + <menu:menuitem menu:label="left [ x right ]" menu:id=".uno:InsertCommandText?Text:string=left [ <?> right ] "/> + <menu:menuitem menu:label="left ldbracket x right rdbracket " menu:id=".uno:InsertCommandText?Text:string=left ldbracket <?> right rdbracket "/> + <menu:menuitem menu:label="left { x right }" menu:id=".uno:InsertCommandText?Text:string=left lbrace <?> right rbrace "/> + <menu:menuitem menu:label="left langle x right rangle" menu:id=".uno:InsertCommandText?Text:string=left langle <?> right rangle "/> + <menu:menuitem menu:label="left langle x mline y right rangle" menu:id=".uno:InsertCommandText?Text:string=left langle <?> mline <?> right rangle "/> + <menu:menuitem menu:label="left lceil x right rceil" menu:id=".uno:InsertCommandText?Text:string=left lceil <?> right rceil "/> + <menu:menuitem menu:label="left lfloor x right rfloor" menu:id=".uno:InsertCommandText?Text:string=left lfloor <?> right rfloor "/> + <menu:menuitem menu:label="left lline x right rline" menu:id=".uno:InsertCommandText?Text:string=left lline <?> right rline "/> + <menu:menuitem menu:label="left ldline x right rdline" menu:id=".uno:InsertCommandText?Text:string=left ldline <?> right rdline "/> + <menu:menuitem menu:label="left none x right rline_y" menu:id=".uno:InsertCommandText?Text:string=left none {<?>} right rline_{<?>} "/> + <menu:menuseparator/> + <menu:menuitem menu:label="x overbrace y" menu:id=".uno:InsertCommandText?Text:string={<?>} overbrace {<?>} "/> + <menu:menuitem menu:label="x underbrace y" menu:id=".uno:InsertCommandText?Text:string={<?>} underbrace {<?>} "/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:FormatsMenu"> + <menu:menupopup> + <menu:menuitem menu:label="^x" menu:id=".uno:InsertCommandText?Text:string=<?>^{<?>} "/> + <menu:menuitem menu:label="_x" menu:id=".uno:InsertCommandText?Text:string=<?>_{<?>} "/> + <menu:menuitem menu:label="lsup x" menu:id=".uno:InsertCommandText?Text:string=<?> lsup{<?>} "/> + <menu:menuitem menu:label="lsub x" menu:id=".uno:InsertCommandText?Text:string=<?> lsub{<?>} "/> + <menu:menuitem menu:label="csup x" menu:id=".uno:InsertCommandText?Text:string=<?> csup{<?>} "/> + <menu:menuitem menu:label="csub x" menu:id=".uno:InsertCommandText?Text:string=<?> csub{<?>} "/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:InsertCommandText?Text:string=newline "/> + <menu:menuitem menu:id=".uno:InsertCommandText?Text:string=`"/> + <menu:menuitem menu:id=".uno:InsertCommandText?Text:string=~"/> + <menu:menuitem menu:label="nospace {...}" menu:id=".uno:InsertCommandText?Text:string=nospace {<?>} "/> + <menu:menuitem menu:label="binom x y" menu:id=".uno:InsertCommandText?Text:string=binom{<?>}{<?>} "/> + <menu:menuitem menu:label="stack {...}" menu:id=".uno:InsertCommandText?Text:string=stack{<?> # <?> # <?>} "/> + <menu:menuitem menu:label="matrix {...}" menu:id=".uno:InsertCommandText?Text:string=matrix{<?> # <?> ## <?> # <?>} "/> + <menu:menuseparator/> + <menu:menuitem menu:label="alignl x" menu:id=".uno:InsertCommandText?Text:string=alignl <?> "/> + <menu:menuitem menu:label="alignc x" menu:id=".uno:InsertCommandText?Text:string=alignc <?> "/> + <menu:menuitem menu:label="alignr x" menu:id=".uno:InsertCommandText?Text:string=alignr <?> "/> + </menu:menupopup> + </menu:menu> + <menu:menu menu:id=".uno:OthersMenu"> + <menu:menupopup> + <menu:menuitem menu:label="infinity" menu:id=".uno:InsertCommandText?Text:string=infinity "/> + <menu:menuitem menu:label="partial" menu:id=".uno:InsertCommandText?Text:string=partial "/> + <menu:menuitem menu:label="nabla" menu:id=".uno:InsertCommandText?Text:string=nabla "/> + <menu:menuitem menu:label="exists" menu:id=".uno:InsertCommandText?Text:string=exists "/> + <menu:menuitem menu:label="not exists" menu:id=".uno:InsertCommandText?Text:string=notexists "/> + <menu:menuitem menu:label="forall" menu:id=".uno:InsertCommandText?Text:string=forall "/> + <menu:menuitem menu:label="hbar" menu:id=".uno:InsertCommandText?Text:string=hbar "/> + <menu:menuitem menu:label="lambdabar" menu:id=".uno:InsertCommandText?Text:string=lambdabar "/> + <menu:menuitem menu:label="Re" menu:id=".uno:InsertCommandText?Text:string=Re "/> + <menu:menuitem menu:label="Im" menu:id=".uno:InsertCommandText?Text:string=Im "/> + <menu:menuitem menu:label="wp" menu:id=".uno:InsertCommandText?Text:string=wp "/> + <menu:menuitem menu:label="laplace" menu:id=".uno:InsertCommandText?Text:string=laplace "/> + <menu:menuseparator/> + <menu:menuitem menu:label="leftarrow" menu:id=".uno:InsertCommandText?Text:string=leftarrow "/> + <menu:menuitem menu:label="rightarrow" menu:id=".uno:InsertCommandText?Text:string=rightarrow "/> + <menu:menuitem menu:label="uparrow" menu:id=".uno:InsertCommandText?Text:string=uparrow "/> + <menu:menuitem menu:label="downarrow" menu:id=".uno:InsertCommandText?Text:string=downarrow "/> + <menu:menuseparator/> + <menu:menuitem menu:label="dotslow" menu:id=".uno:InsertCommandText?Text:string=dotslow "/> + <menu:menuitem menu:label="dotsaxis" menu:id=".uno:InsertCommandText?Text:string=dotsaxis "/> + <menu:menuitem menu:label="dotsvert" menu:id=".uno:InsertCommandText?Text:string=dotsvert "/> + <menu:menuitem menu:label="dotsup" menu:id=".uno:InsertCommandText?Text:string=dotsup "/> + <menu:menuitem menu:label="dotsdown" menu:id=".uno:InsertCommandText?Text:string=dotsdown "/> + </menu:menupopup> + </menu:menu> +</menu:menupopup> diff --git a/starmath/uiconfig/smath/popupmenu/view.xml b/starmath/uiconfig/smath/popupmenu/view.xml new file mode 100644 index 000000000..16233ce37 --- /dev/null +++ b/starmath/uiconfig/smath/popupmenu/view.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * +--> +<menu:menupopup xmlns:menu="http://openoffice.org/2001/menu"> + <menu:menuitem menu:id=".uno:Zoom50Percent"/> + <menu:menuitem menu:id=".uno:Zoom100Percent"/> + <menu:menuitem menu:id=".uno:Zoom200Percent"/> + <menu:menuitem menu:id=".uno:ZoomIn"/> + <menu:menuitem menu:id=".uno:ZoomOut"/> + <menu:menuitem menu:id=".uno:ZoomOptimal"/> + <menu:menuseparator/> + <menu:menuitem menu:id=".uno:Draw"/> +</menu:menupopup> diff --git a/starmath/uiconfig/smath/statusbar/statusbar.xml b/starmath/uiconfig/smath/statusbar/statusbar.xml new file mode 100644 index 000000000..ca57ace7e --- /dev/null +++ b/starmath/uiconfig/smath/statusbar/statusbar.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE statusbar:statusbar PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "statusbar.dtd"> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> +<statusbar:statusbar xmlns:statusbar="http://openoffice.org/2001/statusbar" xmlns:xlink="http://www.w3.org/1999/xlink"> + <statusbar:statusbaritem xlink:href=".uno:TextStatus" statusbar:align="left" statusbar:autosize="true" statusbar:width="300"/> + <statusbar:statusbaritem xlink:href=".uno:ModifiedStatus" statusbar:align="center" statusbar:ownerdraw="true" statusbar:width="9"/> + <statusbar:statusbaritem xlink:href=".uno:Signature" statusbar:align="center" statusbar:ownerdraw="true" statusbar:width="16"/> + <statusbar:statusbaritem xlink:href=".uno:ZoomSlider" statusbar:align="right" statusbar:ownerdraw="true" statusbar:width="130"/> + <statusbar:statusbaritem xlink:href=".uno:Zoom" statusbar:align="center" statusbar:width="54"/> +</statusbar:statusbar> diff --git a/starmath/uiconfig/smath/toolbar/fullscreenbar.xml b/starmath/uiconfig/smath/toolbar/fullscreenbar.xml new file mode 100644 index 000000000..4162e396e --- /dev/null +++ b/starmath/uiconfig/smath/toolbar/fullscreenbar.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE toolbar:toolbar PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "toolbar.dtd"> +<toolbar:toolbar xmlns:toolbar="http://openoffice.org/2001/toolbar" xmlns:xlink="http://www.w3.org/1999/xlink"> + <toolbar:toolbaritem xlink:href=".uno:FullScreen"/> +</toolbar:toolbar>
\ No newline at end of file diff --git a/starmath/uiconfig/smath/toolbar/standardbar.xml b/starmath/uiconfig/smath/toolbar/standardbar.xml new file mode 100644 index 000000000..8d0065f2a --- /dev/null +++ b/starmath/uiconfig/smath/toolbar/standardbar.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE toolbar:toolbar PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "toolbar.dtd"> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> +<toolbar:toolbar xmlns:toolbar="http://openoffice.org/2001/toolbar" xmlns:xlink="http://www.w3.org/1999/xlink"> + <toolbar:toolbaritem xlink:href=".uno:OpenUrl" toolbar:visible="false"/> + <toolbar:toolbaritem xlink:href=".uno:AddDirect"/> + <toolbar:toolbaritem xlink:href=".uno:NewDoc" toolbar:visible="false"/> + <toolbar:toolbaritem xlink:href=".uno:Open"/> + <toolbar:toolbaritem xlink:href=".uno:Save"/> + <toolbar:toolbaritem xlink:href=".uno:SaveAs" toolbar:visible="false"/> + <toolbar:toolbaritem xlink:href=".uno:SendMail"/> + <toolbar:toolbarseparator/> + <toolbar:toolbaritem xlink:href=".uno:ExportDirectToPDF"/> + <toolbar:toolbaritem xlink:href=".uno:PrintDefault" toolbar:visible="false"/> + <toolbar:toolbaritem xlink:href=".uno:Print"/> + <toolbar:toolbaritem xlink:href=".uno:SendFax" toolbar:visible="false"/> + <toolbar:toolbarseparator/> + <toolbar:toolbaritem xlink:href=".uno:Cut"/> + <toolbar:toolbaritem xlink:href=".uno:Copy"/> + <toolbar:toolbaritem xlink:href=".uno:Paste"/> + <toolbar:toolbarseparator/> + <toolbar:toolbaritem xlink:href=".uno:Undo"/> + <toolbar:toolbaritem xlink:href=".uno:Redo"/> + <toolbar:toolbarseparator/> + <toolbar:toolbaritem xlink:href=".uno:HelpIndex"/> + <toolbar:toolbaritem xlink:href=".uno:ExtendedHelp" toolbar:visible="false"/> + +</toolbar:toolbar> diff --git a/starmath/uiconfig/smath/toolbar/toolbar.xml b/starmath/uiconfig/smath/toolbar/toolbar.xml new file mode 100644 index 000000000..7501380c4 --- /dev/null +++ b/starmath/uiconfig/smath/toolbar/toolbar.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE toolbar:toolbar PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "toolbar.dtd"> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> +<toolbar:toolbar xmlns:toolbar="http://openoffice.org/2001/toolbar" xmlns:xlink="http://www.w3.org/1999/xlink"> + <toolbar:toolbaritem xlink:href=".uno:ZoomIn"/> + <toolbar:toolbaritem xlink:href=".uno:ZoomOut"/> + <toolbar:toolbaritem xlink:href=".uno:Zoom100Percent"/> + <toolbar:toolbaritem xlink:href=".uno:ZoomOptimal"/> + <toolbar:toolbarseparator/> + <toolbar:toolbaritem xlink:href=".uno:Draw"/> + <toolbar:toolbaritem xlink:href=".uno:FormelCursor"/> + <toolbar:toolbaritem xlink:href=".uno:SymbolCatalogue"/> +</toolbar:toolbar> diff --git a/starmath/uiconfig/smath/ui/alignmentdialog.ui b/starmath/uiconfig/smath/ui/alignmentdialog.ui new file mode 100644 index 000000000..de1e9c462 --- /dev/null +++ b/starmath/uiconfig/smath/ui/alignmentdialog.ui @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.20.4 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkDialog" id="AlignmentDialog"> + <property name="can_focus">False</property> + <property name="border_width">6</property> + <property name="title" translatable="yes" context="alignmentdialog|AlignmentDialog">Alignment</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="type_hint">dialog</property> + <child internal-child="vbox"> + <object class="GtkBox" id="dialog-vbox3"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialog-action_area3"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="default"> + <property name="label" translatable="yes" context="alignmentdialog|default">_Default</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="ok"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="cancel"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="help"> + <property name="label">gtk-help</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + <property name="secondary">True</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkRadioButton" id="left"> + <property name="label" translatable="yes" context="alignmentdialog|left">_Left</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="center"> + <property name="label" translatable="yes" context="alignmentdialog|center">_Centered</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + <property name="group">left</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="right"> + <property name="label" translatable="yes" context="alignmentdialog|right">_Right</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + <property name="group">left</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="alignmentdialog|label1">Horizontal</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-5">ok</action-widget> + <action-widget response="-6">cancel</action-widget> + <action-widget response="-11">help</action-widget> + </action-widgets> + <child> + <placeholder/> + </child> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/catalogdialog.ui b/starmath/uiconfig/smath/ui/catalogdialog.ui new file mode 100644 index 000000000..21524bccc --- /dev/null +++ b/starmath/uiconfig/smath/ui/catalogdialog.ui @@ -0,0 +1,225 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.20.4 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkDialog" id="CatalogDialog"> + <property name="can_focus">False</property> + <property name="border_width">6</property> + <property name="title" translatable="yes" context="catalogdialog|CatalogDialog">Symbols</property> + <property name="modal">True</property> + <property name="default_width">0</property> + <property name="default_height">0</property> + <property name="type_hint">dialog</property> + <child internal-child="vbox"> + <object class="GtkBox" id="dialog-vbox5"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialog-action_area5"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="edit"> + <property name="label" translatable="yes" context="catalogdialog|edit">_Edit...</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="ok"> + <property name="label" translatable="yes" context="catalogdialog|insert">_Insert</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="close"> + <property name="label">gtk-close</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="help"> + <property name="label">gtk-help</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + <property name="secondary">True</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkGrid" id="grid1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <property name="column_homogeneous">True</property> + <child> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="catalogdialog|label1">_Symbol set:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="symbolset"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_bottom">6</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="symbolname"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="catalogdialog|symbolname">Unknown</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="vscrollbar_policy">always</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <child> + <object class="GtkDrawingArea" id="symbolsetdisplay"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="hscrollbar_policy">never</property> + <property name="vscrollbar_policy">never</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <child> + <object class="GtkDrawingArea" id="preview"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-5">ok</action-widget> + <action-widget response="-7">close</action-widget> + <action-widget response="-11">help</action-widget> + </action-widgets> + <child> + <placeholder/> + </child> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/dockingelements.ui b/starmath/uiconfig/smath/ui/dockingelements.ui new file mode 100644 index 000000000..182117da3 --- /dev/null +++ b/starmath/uiconfig/smath/ui/dockingelements.ui @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.1 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkBox" id="DockingElements"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkComboBoxText" id="listbox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="tooltip_text" translatable="yes" context="dockingelements|ElementCategories|tooltip_text">Element categories</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkDrawingArea" id="element_selector"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/fontdialog.ui b/starmath/uiconfig/smath/ui/fontdialog.ui new file mode 100644 index 000000000..ba58c6250 --- /dev/null +++ b/starmath/uiconfig/smath/ui/fontdialog.ui @@ -0,0 +1,300 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.1 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkTreeStore" id="liststore1"> + <columns> + <!-- column-name text --> + <column type="gchararray"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkDialog" id="FontDialog"> + <property name="can_focus">False</property> + <property name="border_width">6</property> + <property name="title" translatable="yes" context="fontdialog|FontDialog">Fonts</property> + <property name="modal">True</property> + <property name="type_hint">dialog</property> + <child> + <placeholder/> + </child> + <child internal-child="vbox"> + <object class="GtkBox" id="dialog-vbox1"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialog-action_area1"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="ok"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="cancel"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="help"> + <property name="label">gtk-help</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + <property name="secondary">True</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkGrid" id="fontgrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="row_spacing">3</property> + <child> + <object class="GtkScrolledWindow"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTreeView" id="fonts"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">liststore1</property> + <property name="headers_visible">False</property> + <property name="headers_clickable">False</property> + <property name="search_column">0</property> + <property name="show_expanders">False</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeview-selection1"/> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn1"> + <child> + <object class="GtkCellRendererText" id="cellrenderertext1"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="font"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="activates_default">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="formulaL"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fontdialog|formulaL">Font</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="attrframe"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkGrid" id="grid2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <property name="column_homogeneous">True</property> + <child> + <object class="GtkCheckButton" id="bold"> + <property name="label" translatable="yes" context="fontdialog|bold">_Bold</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="italic"> + <property name="label" translatable="yes" context="fontdialog|italic">_Italic</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="formulaL1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fontdialog|formulaL1">Attributes</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hscrollbar_policy">never</property> + <property name="vscrollbar_policy">never</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkDrawingArea" id="preview"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-5">ok</action-widget> + <action-widget response="-6">cancel</action-widget> + <action-widget response="-11">help</action-widget> + </action-widgets> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/fontsizedialog.ui b/starmath/uiconfig/smath/ui/fontsizedialog.ui new file mode 100644 index 000000000..b3e81ef4c --- /dev/null +++ b/starmath/uiconfig/smath/ui/fontsizedialog.ui @@ -0,0 +1,387 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.20.4 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkAdjustment" id="adjustmentBaseSize"> + <property name="lower">4</property> + <property name="upper">127</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkAdjustment" id="adjustmentRelativeSizes1"> + <property name="lower">5</property> + <property name="upper">200</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkAdjustment" id="adjustmentRelativeSizes2"> + <property name="lower">5</property> + <property name="upper">200</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkAdjustment" id="adjustmentRelativeSizes3"> + <property name="lower">5</property> + <property name="upper">200</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkAdjustment" id="adjustmentRelativeSizes4"> + <property name="lower">5</property> + <property name="upper">200</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkAdjustment" id="adjustmentRelativeSizes5"> + <property name="lower">5</property> + <property name="upper">200</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkDialog" id="FontSizeDialog"> + <property name="can_focus">False</property> + <property name="border_width">6</property> + <property name="title" translatable="yes" context="fontsizedialog|FontSizeDialog">Font Sizes</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="default_width">0</property> + <property name="default_height">0</property> + <property name="type_hint">dialog</property> + <child internal-child="vbox"> + <object class="GtkBox" id="dialog-vbox4"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialog-action_area4"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="default"> + <property name="label" translatable="yes" context="fontsizedialog|default">_Default</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="ok"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="cancel"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="help"> + <property name="label">gtk-help</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + <property name="secondary">True</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fontsizedialog|label4">Base _size:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinB_baseSize</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spinB_baseSize"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustmentBaseSize</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkGrid" id="grid1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkSpinButton" id="spinB_function"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustmentRelativeSizes1</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fontsizedialog|label2">_Operators:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinB_operator</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fontsizedialog|label3">_Limits:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinB_limit</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spinB_operator"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustmentRelativeSizes2</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spinB_limit"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustmentRelativeSizes3</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fontsizedialog|label5">_Text:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinB_text</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label7"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fontsizedialog|label7">_Functions:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinB_function</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fontsizedialog|label6">_Indexes:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinB_index</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spinB_text"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustmentRelativeSizes4</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spinB_index"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustmentRelativeSizes5</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fontsizedialog|label1">Relative Sizes</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-5">ok</action-widget> + <action-widget response="-6">cancel</action-widget> + <action-widget response="-11">help</action-widget> + </action-widgets> + <child> + <placeholder/> + </child> + </object> + <object class="GtkSizeGroup" id="sizegroup1"> + <widgets> + <widget name="label4"/> + <widget name="label2"/> + <widget name="label3"/> + <widget name="label5"/> + <widget name="label7"/> + <widget name="label6"/> + </widgets> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/fonttypedialog.ui b/starmath/uiconfig/smath/ui/fonttypedialog.ui new file mode 100644 index 000000000..6aace9cc6 --- /dev/null +++ b/starmath/uiconfig/smath/ui/fonttypedialog.ui @@ -0,0 +1,483 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.20.4 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkMenu" id="menu1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkMenuItem" id="variables"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|menuitem1">_Variables</property> + <property name="use_underline">True</property> + </object> + </child> + <child> + <object class="GtkMenuItem" id="functions"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|menuitem2">_Functions</property> + <property name="use_underline">True</property> + </object> + </child> + <child> + <object class="GtkMenuItem" id="numbers"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|menuitem3">_Numbers</property> + <property name="use_underline">True</property> + </object> + </child> + <child> + <object class="GtkMenuItem" id="text"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|menuitem4">_Text</property> + <property name="use_underline">True</property> + </object> + </child> + <child> + <object class="GtkMenuItem" id="serif"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|menuitem5">_Serif</property> + <property name="use_underline">True</property> + </object> + </child> + <child> + <object class="GtkMenuItem" id="sansserif"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|menuitem6">S_ans-serif</property> + <property name="use_underline">True</property> + </object> + </child> + <child> + <object class="GtkMenuItem" id="fixedwidth"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|menuitem7">Fixe_d-width</property> + <property name="use_underline">True</property> + </object> + </child> + </object> + <object class="GtkDialog" id="FontsDialog"> + <property name="can_focus">False</property> + <property name="border_width">6</property> + <property name="title" translatable="yes" context="fonttypedialog|FontsDialog">Fonts</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="type_hint">dialog</property> + <child internal-child="vbox"> + <object class="GtkBox" id="dialog-vbox1"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialog-action_area1"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkMenuButton" id="modify"> + <property name="label" translatable="yes" context="fonttypedialog|modify">_Modify</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + <property name="popup">menu1</property> + <property name="use_popover">False</property> + <property name="draw_indicator">True</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="help"> + <property name="label">gtk-help</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + <property name="secondary">True</property> + </packing> + </child> + <child> + <object class="GtkButton" id="default"> + <property name="label" translatable="yes" context="fonttypedialog|default">_Default</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="ok"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkButton" id="cancel"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkGrid" id="grid1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|label1">_Variables:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">variableCB</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="normal"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|label2">_Functions:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">functionCB</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="normal"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|label3">_Numbers:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">numberCB</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="normal"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|label4">_Text:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">textCB</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="normal"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="variableCB"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="functionCB"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="numberCB"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="textCB"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="formulaL"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|formulaL">Formula Fonts</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkGrid" id="grid2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|label5">_Serif:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">serifCB</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="normal"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|label6">S_ans-serif:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">sansCB</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="normal"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label7"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|label7">F_ixed-width:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">fixedCB</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="normal"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="serifCB"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="sansCB"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="fixedCB"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="customL"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="fonttypedialog|customL">Custom Fonts</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-11">help</action-widget> + <action-widget response="-5">ok</action-widget> + <action-widget response="-6">cancel</action-widget> + </action-widgets> + <child> + <placeholder/> + </child> + </object> + <object class="GtkSizeGroup" id="sizegroup1"> + <widgets> + <widget name="label1"/> + <widget name="label2"/> + <widget name="label3"/> + <widget name="label4"/> + <widget name="label5"/> + <widget name="label6"/> + <widget name="label7"/> + </widgets> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/printeroptions.ui b/starmath/uiconfig/smath/ui/printeroptions.ui new file mode 100644 index 000000000..9c500f953 --- /dev/null +++ b/starmath/uiconfig/smath/ui/printeroptions.ui @@ -0,0 +1,221 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.1 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkAdjustment" id="adjustment1"> + <property name="lower">10</property> + <property name="upper">1000</property> + <property name="value">100</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkBox" id="box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkFrame" id="contents"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkCheckButton" id="title"> + <property name="label" translatable="yes" context="printeroptions|title">Title</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="formulatext"> + <property name="label" translatable="yes" context="printeroptions|formulatext">Formula text</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="borders"> + <property name="label" translatable="yes" context="printeroptions|borders">Borders</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="printeroptions|label4">Contents</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="size"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkRadioButton" id="originalsize"> + <property name="label" translatable="yes" context="printeroptions|originalsize">Original size</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="fittopage"> + <property name="label" translatable="yes" context="printeroptions|fittopage">Fit to page</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + <property name="group">originalsize</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="spacing">12</property> + <child> + <object class="GtkRadioButton" id="scaling"> + <property name="label" translatable="yes" context="printeroptions|scaling">Scaling:</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + <property name="group">originalsize</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="scalingspin"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="adjustment">adjustment1</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="printeroptions|label5">Size</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/savedefaultsdialog.ui b/starmath/uiconfig/smath/ui/savedefaultsdialog.ui new file mode 100644 index 000000000..6002e8d58 --- /dev/null +++ b/starmath/uiconfig/smath/ui/savedefaultsdialog.ui @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.18.3 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkMessageDialog" id="SaveDefaultsDialog"> + <property name="can_focus">False</property> + <property name="title" translatable="yes" context="savedefaultsdialog|SaveDefaultsDialog">Save defaults?</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="type_hint">dialog</property> + <property name="skip_taskbar_hint">True</property> + <property name="message_type">question</property> + <property name="buttons">yes-no</property> + <property name="text" translatable="yes" context="savedefaultsdialog|SaveDefaultsDialog">Should the changes be saved as defaults?</property> + <property name="secondary_text" translatable="yes" context="savedefaultsdialog|SaveDefaultsDialog">These changes will apply for all new formulas.</property> + <child internal-child="vbox"> + <object class="GtkBox" id="messagedialog-vbox"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="messagedialog-action_area"> + <property name="can_focus">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/smathsettings.ui b/starmath/uiconfig/smath/ui/smathsettings.ui new file mode 100644 index 000000000..f2b989a1e --- /dev/null +++ b/starmath/uiconfig/smath/ui/smathsettings.ui @@ -0,0 +1,310 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.20.4 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkAdjustment" id="adjustment1"> + <property name="lower">10</property> + <property name="upper">400</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkBox" id="SmathSettings"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="border_width">6</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkFrame" id="contents"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkCheckButton" id="title"> + <property name="label" translatable="yes" context="smathsettings|title">_Title row</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="text"> + <property name="label" translatable="yes" context="smathsettings|text">_Formula text</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="frame"> + <property name="label" translatable="yes" context="smathsettings|frame">B_order</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="smathsettings|label4">Print Options</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="size"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="homogeneous">True</property> + <child> + <object class="GtkRadioButton" id="sizenormal"> + <property name="label" translatable="yes" context="smathsettings|sizenormal">O_riginal size</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="sizescaled"> + <property name="label" translatable="yes" context="smathsettings|sizescaled">Fit to _page</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + <property name="group">sizenormal</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="spacing">12</property> + <child> + <object class="GtkRadioButton" id="sizezoomed"> + <property name="label" translatable="yes" context="smathsettings|sizezoomed">_Scaling:</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + <property name="group">sizenormal</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="zoom"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustment1</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="smathsettings|label5">Print Format</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="contents1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkCheckButton" id="norightspaces"> + <property name="label" translatable="yes" context="smathsettings|norightspaces">Ig_nore ~~ and ' at the end of the line</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="saveonlyusedsymbols"> + <property name="label" translatable="yes" context="smathsettings|saveonlyusedsymbols">Embed only used symbols (smaller file size)</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="autoclosebrackets"> + <property name="label" translatable="yes" context="smathsettings|autoclosebrackets">Auto close brackets, parentheses and braces</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="smathsettings|label1">Miscellaneous Options</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/spacingdialog.ui b/starmath/uiconfig/smath/ui/spacingdialog.ui new file mode 100644 index 000000000..e16c6b58d --- /dev/null +++ b/starmath/uiconfig/smath/ui/spacingdialog.ui @@ -0,0 +1,1860 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.20.4 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkAdjustment" id="adjustment1"> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkAdjustment" id="adjustment2"> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkAdjustment" id="adjustment3"> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkAdjustment" id="adjustment4"> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <object class="GtkMenu" id="menu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkRadioMenuItem" id="menuitem1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|menuitem1">Spacing</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <property name="draw_as_radio">True</property> + </object> + </child> + <child> + <object class="GtkRadioMenuItem" id="menuitem2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|menuitem2">Indexes</property> + <property name="use_underline">True</property> + <property name="draw_as_radio">True</property> + <property name="group">menuitem1</property> + </object> + </child> + <child> + <object class="GtkRadioMenuItem" id="menuitem3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|menuitem3">Fractions</property> + <property name="use_underline">True</property> + <property name="draw_as_radio">True</property> + <property name="group">menuitem1</property> + </object> + </child> + <child> + <object class="GtkRadioMenuItem" id="menuitem4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|menuitem4">Fraction Bars</property> + <property name="use_underline">True</property> + <property name="draw_as_radio">True</property> + <property name="group">menuitem1</property> + </object> + </child> + <child> + <object class="GtkRadioMenuItem" id="menuitem5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|menuitem5">Limits</property> + <property name="use_underline">True</property> + <property name="draw_as_radio">True</property> + <property name="group">menuitem1</property> + </object> + </child> + <child> + <object class="GtkRadioMenuItem" id="menuitem6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|menuitem6">Brackets</property> + <property name="use_underline">True</property> + <property name="draw_as_radio">True</property> + <property name="group">menuitem1</property> + </object> + </child> + <child> + <object class="GtkRadioMenuItem" id="menuitem7"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|menuitem7">Matrices</property> + <property name="use_underline">True</property> + <property name="draw_as_radio">True</property> + <property name="group">menuitem1</property> + </object> + </child> + <child> + <object class="GtkRadioMenuItem" id="menuitem8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|menuitem8">Symbols</property> + <property name="use_underline">True</property> + <property name="draw_as_radio">True</property> + <property name="group">menuitem1</property> + </object> + </child> + <child> + <object class="GtkRadioMenuItem" id="menuitem9"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|menuitem9">Operators</property> + <property name="use_underline">True</property> + <property name="draw_as_radio">True</property> + <property name="group">menuitem1</property> + </object> + </child> + <child> + <object class="GtkRadioMenuItem" id="menuitem10"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|menuitem10">Borders</property> + <property name="use_underline">True</property> + <property name="draw_as_radio">True</property> + <property name="group">menuitem1</property> + </object> + </child> + </object> + <object class="GtkDialog" id="SpacingDialog"> + <property name="can_focus">False</property> + <property name="border_width">6</property> + <property name="title" translatable="yes" context="spacingdialog|SpacingDialog">Spacing</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="default_width">0</property> + <property name="default_height">0</property> + <property name="type_hint">dialog</property> + <child internal-child="vbox"> + <object class="GtkBox" id="dialog-vbox4"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialog-action_area4"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="default"> + <property name="label" translatable="yes" context="spacingdialog|default">_Default</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkMenuButton" id="category"> + <property name="label" translatable="yes" context="spacingdialog|category">_Category</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + <property name="popup">menu</property> + <property name="use_popover">False</property> + <property name="draw_indicator">True</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="cancel"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="ok"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkButton" id="help"> + <property name="label">gtk-help</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + <property name="secondary">True</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkFrame" id="template"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkSpinButton" id="spinbutton2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK</property> + <property name="no_show_all">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustment2</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spinbutton3"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK</property> + <property name="no_show_all">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustment3</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="label" translatable="no">String 1</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinbutton1</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="label" translatable="no">String 2</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinbutton2</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="label" translatable="no">String 3</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinbutton3</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spinbutton4"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK</property> + <property name="no_show_all">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustment4</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="label" translatable="no">String 4</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">spinbutton4</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="checkbutton"> + <property name="label" translatable="yes" context="spacingdialog|checkbutton">Scale all brackets</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="no_show_all">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + <property name="width">2</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spinbutton1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK</property> + <property name="no_show_all">True</property> + <property name="activates_default">True</property> + <property name="adjustment">adjustment1</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="imagebox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">center</property> + <property name="valign">start</property> + <property name="hexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkImage" id="4image1"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist41.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage" id="7image1"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist71.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage" id="10image1"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist101.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage" id="1image1"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist11.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage" id="5image1"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist51.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage" id="3image1"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist31.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage" id="8image1"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist81.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage" id="9image1"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist91.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage" id="2image1"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist21.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage" id="6image1"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist61.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage" id="8image2"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist82.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage" id="3image2"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist32.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage" id="6image2"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist62.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage" id="2image2"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist22.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage" id="9image2"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist92.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage" id="7image2"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist72.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage" id="4image2"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist42.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage" id="5image2"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist52.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage" id="1image2"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist12.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage" id="10image2"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist102.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage" id="1image3"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist13.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkImage" id="6image4"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist61.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkImage" id="10image3"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist103.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkImage" id="10image4"> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist104.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkImage" id="image"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">starmath/res/dist104.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">10</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|title">Title</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame1"> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box9"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="1label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|1label1">_Spacing:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="1label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|1label2">_Line spacing:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="1label3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|1label3">_Root spacing:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="1title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|1title">Spacing</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame2"> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box11"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box12"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="2label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|2label1">_Superscript:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="2label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|2label2">S_ubscript:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="2title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|2title">Indexes</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame3"> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box14"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box15"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="3label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|3label1">_Numerator:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="3label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|3label2">_Denominator:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="3title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|3title">Fractions</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame4"> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box17"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box18"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="4label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|4label1">_Excess length:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="4label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|4label2">_Weight:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="4title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|4title">Fraction Bar</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame5"> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment7"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box20"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box21"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid7"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="5label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|5label1">_Upper limit:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="5label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|5label2">_Lower limit:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="5title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|5title">Limits</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">5</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame6"> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box23"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box24"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="6label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|6label1">_Excess size (left/right):</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="6label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|6label2">_Spacing:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="6label4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|6label4">_Excess size:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="6title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|6title">Brackets</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">6</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame7"> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment9"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box26"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box27"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid9"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="7label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|7label1">_Line spacing:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="7label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|7label2">_Column spacing:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="7title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|7title">Matrix</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">7</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame8"> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment10"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box29"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box30"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid10"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="8label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|8label1">_Primary height:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="8label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|8label2">_Minimum spacing:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="8title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|8title">Symbols</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">8</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame9"> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment11"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box32"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box33"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid11"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="9label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|9label1">_Excess size:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="9label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|9label2">_Spacing:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="9title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|9title">Operators</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">9</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame10"> + <property name="can_focus">False</property> + <property name="no_show_all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkBox" id="box2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="10label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|10label1">_Left:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="10label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|10label2">_Right:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="10label3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|10label3">_Top:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="10label4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|10label4">_Bottom:</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="10title"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="spacingdialog|10title">Borders</property> + <property name="use_underline">True</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">10</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-6">cancel</action-widget> + <action-widget response="-5">ok</action-widget> + <action-widget response="-11">help</action-widget> + </action-widgets> + <child> + <placeholder/> + </child> + </object> + <object class="GtkSizeGroup" id="sizegroup1"> + <property name="mode">both</property> + <widgets> + <widget name="10image1"/> + <widget name="9image1"/> + <widget name="8image1"/> + <widget name="7image1"/> + <widget name="1image1"/> + <widget name="2image1"/> + <widget name="3image1"/> + <widget name="4image1"/> + <widget name="5image1"/> + <widget name="6image1"/> + <widget name="6image2"/> + <widget name="5image2"/> + <widget name="4image2"/> + <widget name="3image2"/> + <widget name="2image2"/> + <widget name="1image2"/> + <widget name="7image2"/> + <widget name="8image2"/> + <widget name="9image2"/> + <widget name="10image2"/> + <widget name="10image3"/> + <widget name="1image3"/> + <widget name="6image4"/> + <widget name="10image4"/> + <widget name="image"/> + </widgets> + </object> + <object class="GtkSizeGroup" id="sizegroup2"> + <widgets> + <widget name="label1"/> + <widget name="label2"/> + <widget name="label3"/> + <widget name="label4"/> + <widget name="1label1"/> + <widget name="1label2"/> + <widget name="1label3"/> + <widget name="2label1"/> + <widget name="2label2"/> + <widget name="3label1"/> + <widget name="3label2"/> + <widget name="4label1"/> + <widget name="4label2"/> + <widget name="5label1"/> + <widget name="5label2"/> + <widget name="6label1"/> + <widget name="6label2"/> + <widget name="6label4"/> + <widget name="7label1"/> + <widget name="7label2"/> + <widget name="8label1"/> + <widget name="8label2"/> + <widget name="9label1"/> + <widget name="9label2"/> + <widget name="10label1"/> + <widget name="10label2"/> + <widget name="10label3"/> + <widget name="10label4"/> + </widgets> + </object> + <object class="GtkSizeGroup" id="sizegroup3"> + <widgets> + <widget name="title"/> + <widget name="1title"/> + <widget name="2title"/> + <widget name="3title"/> + <widget name="4title"/> + <widget name="5title"/> + <widget name="6title"/> + <widget name="7title"/> + <widget name="8title"/> + <widget name="9title"/> + <widget name="10title"/> + </widgets> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/symdefinedialog.ui b/starmath/uiconfig/smath/ui/symdefinedialog.ui new file mode 100644 index 000000000..3ce746abd --- /dev/null +++ b/starmath/uiconfig/smath/ui/symdefinedialog.ui @@ -0,0 +1,594 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.1 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkDialog" id="EditSymbols"> + <property name="can_focus">False</property> + <property name="border_width">6</property> + <property name="title" translatable="yes" context="symdefinedialog|EditSymbols">Edit Symbols</property> + <property name="modal">True</property> + <property name="default_width">0</property> + <property name="default_height">0</property> + <property name="type_hint">dialog</property> + <child> + <placeholder/> + </child> + <child internal-child="vbox"> + <object class="GtkBox" id="dialog-vbox1"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialog-action_area1"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="ok"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="cancel"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="help"> + <property name="label">gtk-help</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + <property name="secondary">True</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkGrid" id="grid1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="column_spacing">18</property> + <child> + <object class="GtkGrid" id="grid4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="orientation">vertical</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="oldSymbolSetText"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="symdefinedialog|oldSymbolSetText">O_ld symbol set:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">oldSymbolSets</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="oldSymbolSets"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="has_entry">True</property> + <child internal-child="entry"> + <object class="GtkEntry" id="combobox-entry1"> + <property name="can_focus">True</property> + <property name="activates_default">True</property> + </object> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkGrid" id="grid5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="orientation">vertical</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="oldSymbolText"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_top">6</property> + <property name="label" translatable="yes" context="symdefinedialog|oldSymbolText">_Old symbol:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">oldSymbols</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="oldSymbols"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="has_entry">True</property> + <child internal-child="entry"> + <object class="GtkEntry" id="combobox-entry5"> + <property name="can_focus">True</property> + <property name="activates_default">True</property> + </object> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkScrolledWindow" id="showscroll"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="hscrollbar_policy">never</property> + <property name="vscrollbar_policy">always</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkDrawingArea" id="charsetDisplay"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkButtonBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="layout_style">start</property> + <child> + <object class="GtkButton" id="add"> + <property name="label">gtk-add</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="modify"> + <property name="label" translatable="yes" context="symdefinedialog|modify">_Modify</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="delete"> + <property name="label">gtk-delete</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkGrid" id="grid2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="column_spacing">18</property> + <child> + <object class="GtkGrid" id="grid3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="symbolText"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="symdefinedialog|symbolText">_Symbol:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">symbols</property> + <property name="xalign">1</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="symbolSetText"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="symdefinedialog|symbolSetText">Symbol s_et:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">symbolSets</property> + <property name="xalign">1</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="fontText"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="symdefinedialog|fontText">_Font:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">fonts</property> + <property name="xalign">1</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="styleText"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="symdefinedialog|styleText">S_tyle:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">styles</property> + <property name="xalign">1</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="fontsSubsetFT"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="symdefinedialog|fontsSubsetFT">S_ubset:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">fontsSubsetLB</property> + <property name="xalign">1</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="fonts"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="valign">center</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="fontsSubsetLB"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="valign">center</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="symbols"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="valign">center</property> + <property name="hexpand">True</property> + <property name="has_entry">True</property> + <child internal-child="entry"> + <object class="GtkEntry" id="combobox-entry2"> + <property name="can_focus">True</property> + <property name="activates_default">True</property> + </object> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="symbolSets"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="valign">center</property> + <property name="hexpand">True</property> + <property name="has_entry">True</property> + <child internal-child="entry"> + <object class="GtkEntry" id="combobox-entry3"> + <property name="can_focus">True</property> + <property name="activates_default">True</property> + </object> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="styles"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="valign">center</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">4</property> + </packing> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkGrid" id="grid7"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="valign">end</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="oldSymbolName"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="ellipsize">middle</property> + <property name="width_chars">10</property> + <property name="single_line_mode">True</property> + <property name="max_width_chars">10</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkDrawingArea" id="oldSymbolDisplay"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="oldSymbolSetName"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="ellipsize">middle</property> + <property name="width_chars">10</property> + <property name="single_line_mode">True</property> + <property name="max_width_chars">10</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="symbolName"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="ellipsize">middle</property> + <property name="width_chars">10</property> + <property name="single_line_mode">True</property> + <property name="max_width_chars">10</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkDrawingArea" id="symbolDisplay"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="symbolSetName"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="ellipsize">middle</property> + <property name="width_chars">10</property> + <property name="single_line_mode">True</property> + <property name="max_width_chars">10</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkImage" id="rightArrow"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="icon_name">starmath/res/ar_right.png</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-5">ok</action-widget> + <action-widget response="-6">cancel</action-widget> + <action-widget response="-11">help</action-widget> + </action-widgets> + </object> + <object class="GtkSizeGroup" id="sizegroup1"> + <property name="mode">vertical</property> + <widgets> + <widget name="fonts"/> + <widget name="fontsSubsetLB"/> + <widget name="symbols"/> + <widget name="symbolSets"/> + <widget name="styles"/> + </widgets> + </object> +</interface> diff --git a/starmath/util/sm.component b/starmath/util/sm.component new file mode 100644 index 000000000..775b41100 --- /dev/null +++ b/starmath/util/sm.component @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="sm" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.Math.FormulaDocument"> + <service name="com.sun.star.formula.FormulaProperties"/> + </implementation> + <implementation name="com.sun.star.comp.Math.XMLContentExporter" + constructor="Math_XMLContentExporter_get_implementation"> + <service name="com.sun.star.xml.XMLExportFilter"/> + </implementation> + <implementation name="com.sun.star.comp.Math.XMLExporter" + constructor="Math_XMLExporter_get_implementation"> + <service name="com.sun.star.xml.XMLExportFilter"/> + </implementation> + <implementation name="com.sun.star.comp.Math.XMLImporter" + constructor="Math_XMLImporter_get_implementation"> + <service name="com.sun.star.xml.XMLImportFilter"/> + </implementation> + <implementation name="com.sun.star.comp.Math.XMLMetaExporter" + constructor="Math_XMLMetaExporter_get_implementation"> + <service name="com.sun.star.xml.XMLExportFilter"/> + </implementation> + <implementation name="com.sun.star.comp.Math.XMLOasisMetaExporter" + constructor="Math_XMLOasisMetaExporter_get_implementation"> + <service name="com.sun.star.xml.XMLExportFilter"/> + </implementation> + <implementation name="com.sun.star.comp.Math.XMLOasisMetaImporter" + constructor="Math_XMLOasisMetaImporter_get_implementation"> + <service name="com.sun.star.xml.XMLImportFilter"/> + </implementation> + <implementation name="com.sun.star.comp.Math.XMLOasisSettingsExporter" + constructor="Math_XMLOasisSettingsExporter_get_implementation"> + <service name="com.sun.star.xml.XMLExportFilter"/> + </implementation> + <implementation name="com.sun.star.comp.Math.XMLOasisSettingsImporter" + constructor="Math_XMLOasisSettingsImporter_get_implementation"> + <service name="com.sun.star.xml.XMLImportFilter"/> + </implementation> + <implementation name="com.sun.star.comp.Math.XMLSettingsExporter" + constructor="Math_XMLSettingsExporter_get_implementation"> + <service name="com.sun.star.xml.XMLExportFilter"/> + </implementation> + <implementation name="com.sun.star.comp.Math.MathTypeFilter" + constructor="com_sun_star_comp_Math_MathTypeFilter_get_implementation"> + <service name="com.sun.star.document.ImportFilter"/> + </implementation> +</component> diff --git a/starmath/util/smd.component b/starmath/util/smd.component new file mode 100644 index 000000000..4230e807b --- /dev/null +++ b/starmath/util/smd.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.math.FormatDetector" + constructor="math_FormatDetector_get_implementation"> + <service name="com.sun.star.frame.ExtendedTypeDetection"/> + </implementation> +</component> diff --git a/starmath/visual-editor-todo b/starmath/visual-editor-todo new file mode 100644 index 000000000..04dedae01 --- /dev/null +++ b/starmath/visual-editor-todo @@ -0,0 +1,43 @@ +Visual Formula Editor for LibreOffice Math +========================================== + +This file contains a list of things to do for the visual formula editor. +The visual formula editor hack was started by Jonas during GSoC... +He is, as of writing this, still working on this features, but do by all +means feel free to help out. + +Here is the list of things to be done. They are organized by complexity and necessity, note that +some of the items are wishful thinking... If you have questions please feel free to ping me (Jonas) +on IRC (jopsen) or e-mail me at jopsen@gmail.com. + +Easy +---- +1. SmGraphicWindow::KeyInput relies on comparison of char, a better way must be available for CTRL+c +2. Code style (missing spaces, linebreaks and a few renames) +3. More documentation +4. Remove the CreateTextFromNode methods and replace calls to it with NodeToTextVisitor +5. Extend NodeToTextVisitor to update token offsets so SmNode::GetRow and SmNode::GetColumn will work. + (These methods can be used to enable synchronization of caret positions between visual and non-visual editor). + +Medium +------ +1. SmCursor::InsertCol() method for added columns to matrices should be implemented. +2. SmCursor` should support deletion of lines, rows, cols and sub-/superscripts. +3. SmCursor::InsertSubSup() should wrap the body in a SmBraceNode if the body is an SmOperNode, SmBinVerNode, etc. +4. When OpenOffice Math runs in standalone mode it centers the current formula, this is not nice for visual editing. + +Complex +------- +1. SmAlignNode and SmFontNode are ignored by visual editor, figure out how these should work. +2. Solve the flickering issue when drawing formulas (See dev@gsl.OpenOffice.org) +3. Make " a shortcut for creating an SmTextNode with FNT_TEXT, also check that SmNodeToTextVisitor supports this. +4. parse.cxx merges multiple blanks into one SmBlankNode, the visual editor doesn't... + +Complex and non-essential +------------------------- +* Global clipboard integration +* Consider improving GUI for "Formula Elements"-dialog, most buttons work with visual editor +* Consider allowing users to enter commands in visual editor, by prefixing the command... +* Optimize things, for instance SmCursor::AnnotateSelection() is called way too many places... +* Improve handling of MoveUp and MoveDown in SmCursor::Move, SmCaretPos2LineVisitor might need improvement. +* Synchronized command text caret and visual editor caret. |