diff options
Diffstat (limited to '')
167 files changed, 60556 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..6a5cf437a --- /dev/null +++ b/starmath/CppunitTest_starmath_qa_cppunit.mk @@ -0,0 +1,86 @@ +# -*- 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 \ + salhelper \ + 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 \ + 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 \ + vcl/vcl.common \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,starmath_qa_cppunit)) + +$(eval $(call gb_CppunitTest_use_more_fonts,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..72047b7c8 --- /dev/null +++ b/starmath/IwyuFilter_starmath.yaml @@ -0,0 +1,54 @@ +--- +assumeFilename: starmath/source/document.cxx +excludelist: + starmath/inc/smmod.hxx: + # Needed for define + - sfx2/app.hxx + starmath/inc/format.hxx: + # Needed for FONTNAME_MATH macro + - types.hxx + starmath/qa/extras/mmlexport-test.cxx: + # Required in C++20 mode. + - o3tl/cppunittraitshelper.hxx + starmath/qa/cppunit/test_node.cxx: + # Required in C++20 mode. + - o3tl/cppunittraitshelper.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/node.cxx: + # Actually used + - vector + 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 + # Needed for template + - com/sun/star/frame/XModel.hpp + starmath/source/mathml/def.cxx: + # Needed for extern + - mathml/attribute.hxx + starmath/source/mathml/mathmlexport.cxx: + # Needed for rtl::math::round + - rtl/math.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..aa824cee5 --- /dev/null +++ b/starmath/Library_sm.mk @@ -0,0 +1,127 @@ +# -*- 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,services)) + +$(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$(SRCDIR)/starmath/inc/mathml \ + -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 \ + salhelper \ + sax \ + sfx \ + sot \ + svl \ + svt \ + svx \ + svxcore \ + tk \ + tl \ + utl \ + vcl \ + xo \ +)) + +$(eval $(call gb_Library_add_exception_objects,sm,\ + starmath/source/ElementsDockingWindow \ + starmath/source/SmElementsPanel \ + starmath/source/SmPanelFactory \ + starmath/source/SmPropertiesPanel \ + 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/smediteng \ + starmath/source/format \ + starmath/source/mathtype \ + starmath/source/node \ + starmath/source/ooxmlexport \ + starmath/source/ooxmlimport \ + starmath/source/rtfexport \ + starmath/source/parsebase \ + starmath/source/parse \ + starmath/source/parse5 \ + starmath/source/rect \ + starmath/source/smdll \ + starmath/source/smmod \ + starmath/source/symbol \ + starmath/source/tmpdevice \ + starmath/source/typemap \ + starmath/source/unodoc \ + starmath/source/unofilter \ + starmath/source/unomodel \ + starmath/source/utility \ + starmath/source/view \ + starmath/source/visitors \ + starmath/source/wordexportbase \ + starmath/source/mathml/xparsmlbase \ + starmath/source/mathml/mathmlattr \ + starmath/source/mathml/mathmlexport \ + starmath/source/mathml/mathmlimport \ + starmath/source/mathml/mathmlMo \ + starmath/source/mathml/export \ + starmath/source/mathml/import \ + starmath/source/mathml/iterator \ + starmath/source/mathml/attribute \ + starmath/source/mathml/element \ + starmath/source/mathml/def \ + starmath/source/mathml/starmathdatabase \ +)) + + +$(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..3bea24b08 --- /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,services)) + +$(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.md b/starmath/README.md new file mode 100644 index 000000000..c2a8fcaf0 --- /dev/null +++ b/starmath/README.md @@ -0,0 +1,5 @@ +# Formula Editor Code for LibreOffice Writer + +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..a54f2e455 --- /dev/null +++ b/starmath/UIConfig_smath.mk @@ -0,0 +1,49 @@ +# -*- 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/editwindow \ + starmath/uiconfig/smath/ui/fontdialog \ + starmath/uiconfig/smath/ui/fontsizedialog \ + starmath/uiconfig/smath/ui/fonttypedialog \ + starmath/uiconfig/smath/ui/mathwindow \ + starmath/uiconfig/smath/ui/printeroptions \ + starmath/uiconfig/smath/ui/savedefaultsdialog \ + starmath/uiconfig/smath/ui/sidebarelements_math \ + starmath/uiconfig/smath/ui/sidebarproperties_math \ + 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/ElementsDockingWindow.hxx b/starmath/inc/ElementsDockingWindow.hxx new file mode 100644 index 000000000..5d6a0bd3d --- /dev/null +++ b/starmath/inc/ElementsDockingWindow.hxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sfx2/dockwin.hxx> +#include <vcl/customweld.hxx> +#include <vcl/weld.hxx> +#include <unotools/resmgr.hxx> + +#include "node.hxx" +#include "parsebase.hxx" + +#include <memory> +#include <vector> + +class SmDocShell; +class SvTreeListBox; +struct ElementData; + +class SmElementsControl +{ + std::unique_ptr<AbstractSmParser> maParser; + + SmDocShell* mpDocShell; + SmFormat maFormat; + TranslateId msCurrentSetId; + sal_uInt16 m_nSmSyntaxVersion; + + bool mbVerticalMode; + std::vector<std::unique_ptr<ElementData>> maItemDatas; + std::unique_ptr<weld::IconView> mpIconView; + + Link<OUString, void> maSelectHdlLink; + + void addElement(const OUString& aElementVisual, const OUString& aElementSource, const OUString& aHelpText); + void addElements(const TranslateId& rCategory); + + void build(); + + DECL_LINK(QueryTooltipHandler, const weld::TreeIter&, OUString); + DECL_LINK(ElementActivatedHandler, weld::IconView&, bool); + + static OUString GetElementSource(const OUString& itemId); + static OUString GetElementHelpText(const OUString& itemId); + +public: + + explicit SmElementsControl(std::unique_ptr<weld::IconView> pIconView); + ~SmElementsControl(); + + static const std::vector<TranslateId>& categories(); + void setElementSetId(TranslateId pSetId); + + void setVerticalMode(bool bVertical); + + void setSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion); + + void SetSelectHdl(const Link<OUString, void>& rLink) { maSelectHdlLink = rLink; } + + static Color GetTextColor(); + static Color GetControlBackground(); +}; + +class SmElementsDockingWindow final : public SfxDockingWindow +{ + std::unique_ptr<SmElementsControl> mxElementsControl; + std::unique_ptr<weld::ComboBox> mxElementListBox; + + virtual void Resize() override; + SmViewShell* GetView(); + + DECL_LINK(SelectClickHandler, OUString, 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; + virtual void GetFocus() override; + + void setSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion); +}; + +class SmElementsDockingWindowWrapper final : public SfxChildWindow +{ + SFX_DECL_CHILDWINDOW_WITHID(SmElementsDockingWindowWrapper); + + SmElementsDockingWindowWrapper( vcl::Window* pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ); + virtual ~SmElementsDockingWindowWrapper() override; +}; + +/* 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..aaec78549 --- /dev/null +++ b/starmath/inc/action.hxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <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; +}; + +/* 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..eff810792 --- /dev/null +++ b/starmath/inc/caret.hxx @@ -0,0 +1,424 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <sal/config.h> +#include "node.hxx" + +/** 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(tools::Long left = 0, tools::Long top = 0, tools::Long height = 0) + { + _top = top; + _left = left; + _height = height; + } + tools::Long GetTop() const { return _top; } + tools::Long GetLeft() const { return _left; } + tools::Long GetHeight() const { return _height; } + tools::Long SquaredDistanceX(const SmCaretLine& line) const + { + return (GetLeft() - line.GetLeft()) * (GetLeft() - line.GetLeft()); + } + tools::Long SquaredDistanceX(const Point& pos) const + { + return (GetLeft() - pos.X()) * (GetLeft() - pos.X()); + } + tools::Long SquaredDistanceY(const SmCaretLine& line) const + { + tools::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; + } + tools::Long SquaredDistanceY(const Point& pos) const + { + tools::Long d = GetTop() - pos.Y(); + if (d < 0) + d = (d * -1) - GetHeight(); + if (d < 0) + return 0; + return d * d; + } + +private: + tools::Long _top; + tools::Long _left; + tools::Long _height; +}; + +// SmCaretPosGraph + +/** An entry in SmCaretPosGraph */ +struct SmCaretPosGraphEntry +{ + SmCaretPosGraphEntry(SmCaretPos pos, SmCaretPosGraphEntry* left, SmCaretPosGraphEntry* right) + : CaretPos{ pos } + , Left{ left } + , Right{ right } + { + } + /** Caret position */ + const 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 + */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/cfgitem.hxx b/starmath/inc/cfgitem.hxx new file mode 100644 index 000000000..16c65fd7f --- /dev/null +++ b/starmath/inc/cfgitem.hxx @@ -0,0 +1,205 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "utility.hxx" + +#include <string_view> +#include <rtl/ustring.hxx> +#include <svl/SfxBroadcaster.hxx> +#include <unotools/configitem.hxx> + +#include "types.hxx" + +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(std::u16string_view rFntFmtId); + + const SmFontFormat* GetFontFormat(std::u16string_view 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]; + sal_Int32 m_nCommitLock = 0; + + 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, + std::u16string_view rBaseNode) const; + void ReadFontFormat(SmFontFormat& rFontFormat, std::u16string_view rSymbolName, + std::u16string_view rBaseNode) const; + + bool 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; + void LockCommit() { ++m_nCommitLock; } + void UnlockCommit(); + // Used to avoid tens of atomic commits in e.g. ItemSetToConfig that calls individual setters + friend struct CommitLocker; + struct CommitLocker + { + SmMathConfig& m_rConfig; + CommitLocker(SmMathConfig& rConfig) + : m_rConfig(rConfig) + { + m_rConfig.LockCommit(); + } + ~CommitLocker() { m_rConfig.UnlockCommit(); } + }; + + void Clear(); + +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); + sal_uInt16 GetSmEditWindowZoomFactor() const; + void SetSmEditWindowZoomFactor(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); + + sal_uInt16 GetDefaultSmSyntaxVersion() const; + void SetDefaultSmSyntaxVersion(sal_uInt16 nVal); + + SmFontPickList& GetFontPickList(sal_uInt16 nIdent) { return vFontPickList[nIdent]; } + + void ItemSetToConfig(const SfxItemSet& rSet); + void ConfigToItemSet(SfxItemSet& rSet) const; +}; + +/* 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..5886ffaf2 --- /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/. + */ + +#pragma once + +#include "caret.hxx" + +#include <list> + +/** 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(std::u16string_view 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) const; + +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(); +}; + +/* 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..fee12d9a5 --- /dev/null +++ b/starmath/inc/dialog.hxx @@ -0,0 +1,483 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sfx2/tabdlg.hxx> +#include <vcl/outdev.hxx> +#include <vcl/customweld.hxx> +#include "symbol.hxx" + +class SubsetMap; +class SmFormat; +class FontList; +class SvxShowCharSet; + +#define CATEGORY_NONE 0xFFFF + +/**************************************************************************/ + +void SetFontStyle(std::u16string_view 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; + std::unique_ptr<weld::MetricSpinButton> m_xSmZoom; + + DECL_LINK(SizeButtonClickHdl, weld::Toggleable&, 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::Toggleable&, 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::Toggleable&, 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; + tools::Long nLen; + sal_Int32 nRows, nColumns; + tools::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, std::u16string_view rStyleName); + void SetOrigSymbol(const SmSym *pSymbol, const OUString &rSymbolSetName); + void UpdateButtons(); + + bool SelectSymbolSet(weld::ComboBox &rComboBox, std::u16string_view 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(std::u16string_view rSymbolSetName) + { + SelectSymbolSet(*m_xOldSymbolSets, rSymbolSetName, false); + } + + void SelectOldSymbol(const OUString &rSymbolName) + { + SelectSymbol(*m_xOldSymbols, rSymbolName, false); + } + + bool SelectSymbolSet(std::u16string_view 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); +}; + +/* 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..214d9a9ce --- /dev/null +++ b/starmath/inc/document.hxx @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <rtl/strbuf.hxx> +#include <sfx2/docfac.hxx> +#include <sfx2/objsh.hxx> +#include <svl/lstner.hxx> +#include <svl/itempool.hxx> +#include <sax/fshelper.hxx> +#include <unotools/lingucfg.hxx> +#include <oox/core/filterbase.hxx> +#include <oox/export/utils.hxx> + +#include "format.hxx" +#include "node.hxx" +#include "parsebase.hxx" +#include "smdllapi.hxx" +#include "mathml/iterator.hxx" + +class SfxPrinter; +class Printer; +class SmCursor; + +namespace oox::formulaimport { class XmlStream; } + +#define STAROFFICE_XML "StarOffice XML (Math)" +inline constexpr OUStringLiteral MATHML_XML = u"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 SmEditEngine; + +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 final : public SfxObjectShell, public SfxListener +{ + friend class SmPrinterAccess; + friend class SmCursor; + + OUString maText; + SmFormat maFormat; + OUString maAccText; + SvtLinguOptions maLinguOptions; + std::unique_ptr<SmTableNode> mpTree; + SmMlElement* m_pMlElementTree; + rtl::Reference<SfxItemPool> mpEditEngineItemPool; + std::unique_ptr<SmEditEngine> mpEditEngine; + VclPtr<SfxPrinter> mpPrinter; //q.v. comment to SmPrinter Access! + VclPtr<Printer> mpTmpPrinter; //ditto + sal_uInt16 mnModifyCount; + bool mbFormulaArranged; + sal_uInt16 mnSmSyntaxVersion; + std::unique_ptr<AbstractSmParser> maParser; + 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(); + AbstractSmParser* GetParser() { return maParser.get(); } + const SmTableNode *GetFormulaTree() const { return mpTree.get(); } + void SetFormulaTree(SmTableNode *pTree) { mpTree.reset(pTree); } + sal_uInt16 GetSmSyntaxVersion() const { return mnSmSyntaxVersion; } + void SetSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion); + + 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(); + + SmMlElement* GetMlElementTree() { return m_pMlElementTree; } + void SetMlElementTree(SmMlElement* pMlElementTree) { + mathml::SmMlIteratorFree(m_pMlElementTree); + m_pMlElementTree = pMlElementTree; + } +}; + +/* 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..2fd6fac63 --- /dev/null +++ b/starmath/inc/edit.hxx @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <svx/weldeditview.hxx> +#include <vcl/idle.hxx> + +class SmDocShell; +class SmViewShell; +class EditView; +class EditEngine; +class EditStatus; +class DataChangedEvent; +class SmCmdBoxWindow; +class CommandEvent; +class Timer; + +void SmGetLeftSelectionPart(const ESelection& rSelection, sal_Int32& nPara, sal_uInt16& nPos); + +class SmEditWindow; + +class SmEditTextWindow : public WeldEditView +{ +private: + SmEditWindow& mrEditWindow; + + Idle aModifyIdle; + Idle aCursorMoveIdle; + + ESelection aOldSelection; + + DECL_LINK(ModifyTimerHdl, Timer*, void); + DECL_LINK(CursorMoveTimerHdl, Timer*, void); + DECL_LINK(EditStatusHdl, EditStatus&, void); + +public: + SmEditTextWindow(SmEditWindow& rEditWindow); + virtual ~SmEditTextWindow() override; + + virtual EditEngine* GetEditEngine() const override; + + virtual void EditViewScrollStateChange() override; + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + + virtual bool KeyInput(const KeyEvent& rKeyEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rEvt) override; + virtual bool Command(const CommandEvent& rCEvt) override; + virtual void GetFocus() override; + virtual void LoseFocus() override; + virtual void StyleUpdated() override; + + void SetText(const OUString& rText); + void InsertText(const OUString& rText); + void SelNextMark(); + ESelection GetSelection() const; + void UserPossiblyChangedText(); + void Flush(); + void UpdateStatus(bool bSetDocModified); + void StartCursorMove(); +}; + +class SmEditWindow final +{ + SmCmdBoxWindow& rCmdBox; + std::unique_ptr<weld::ScrolledWindow> mxScrolledWindow; + std::unique_ptr<SmEditTextWindow> mxTextControl; + std::unique_ptr<weld::CustomWeld> mxTextControlWin; + + DECL_LINK(ScrollHdl, weld::ScrolledWindow&, void); + + void CreateEditView(weld::Builder& rBuilder); + +public: + SmEditWindow(SmCmdBoxWindow& rMyCmdBoxWin, weld::Builder& rBuilder); + ~SmEditWindow() COVERITY_NOEXCEPT_FALSE; + + weld::Window* GetFrameWeld() const; + + SmDocShell* GetDoc(); + SmViewShell* GetView(); + EditView* GetEditView() const; + EditEngine* GetEditEngine(); + SmCmdBoxWindow& GetCmdBox() const { return rCmdBox; } + + void SetText(const OUString& rText); + OUString GetText() const; + void Flush(); + void GrabFocus(); + + css::uno::Reference<css::datatransfer::clipboard::XClipboard> GetClipboard() const + { + return mxTextControl->GetClipboard(); + } + + ESelection GetSelection() const; + void SetSelection(const ESelection& rSel); + void UpdateStatus(); + + bool IsEmpty() const; + bool IsSelected() const; + bool IsAllSelected() const; + void SetScrollBarRanges(); + tools::Rectangle AdjustScrollBars(); + void InvalidateSlots(); + void Cut(); + void Copy(); + void Paste(); + void Delete(); + void SelectAll(); + void InsertText(const OUString& rText); + void MarkError(const Point& rPos); + void SelNextMark(); + void SelPrevMark(); + + void DeleteEditView(); +}; + +/* 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..61fc8b047 --- /dev/null +++ b/starmath/inc/format.hxx @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <svl/hint.hxx> +#include <svl/SfxBroadcaster.hxx> +#include "utility.hxx" +#include "types.hxx" + + +inline constexpr OUStringLiteral FNTNAME_TIMES = u"Times New Roman"; +inline constexpr OUStringLiteral FNTNAME_HELV = u"Helvetica"; +inline constexpr OUStringLiteral FNTNAME_COUR = u"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); +} + +/* 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..59489e57a --- /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 . + */ + +#pragma once + +#include <rtl/string.hxx> + +inline constexpr OStringLiteral HID_SMA_WIN_DOCUMENT = "STARMATH_HID_SMA_WIN_DOCUMENT"; +inline constexpr OStringLiteral HID_SMA_COMMAND_WIN_EDIT = "STARMATH_HID_SMA_COMMAND_WIN_EDIT"; + +inline constexpr OStringLiteral 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" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/mathml/attribute.hxx b/starmath/inc/mathml/attribute.hxx new file mode 100644 index 000000000..946e9f463 --- /dev/null +++ b/starmath/inc/mathml/attribute.hxx @@ -0,0 +1,203 @@ +/* -*- 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/. + */ + +#pragma once + +#include "def.hxx" + +/* All possible data needed to do the job outside mathml limits */ +// Ml prefix means it is part of mathml standard +// NMl means it is not part of mathml standard but needed info to work + +/* Union for storing the mathml attribute value */ +/*************************************************************************************************/ + +union SmMlAttributeValue { + SmMlAttributeValue(){}; + + struct SmMlAccent m_aAccent; + struct SmMlDir m_aDir; + struct SmMlDisplaystyle m_aDisplaystyle; + struct SmMlFence m_aFence; + struct SmMlForm m_aForm; + struct SmMlHref m_aHref; + struct SmMlLspace m_aLspace; + struct SmMlMathbackground m_aMathbackground; + struct SmMlMathcolor m_aMathcolor; + struct SmMlMathsize m_aMathsize; + struct SmMlMathvariant m_aMathvariant; + struct SmMlMaxsize m_aMaxsize; + struct SmMlMinsize m_aMinsize; + struct SmMlMovablelimits m_aMovablelimits; + struct SmMlRspace m_aRspace; + struct SmMlSeparator m_aSeparator; + struct SmMlStretchy m_aStretchy; + struct SmMlSymmetric m_aSymmetric; +}; + +/* Class managing the attribute value */ +/*************************************************************************************************/ + +class SmMlAttribute +{ +private: + SmMlAttributeValueType m_aSmMlAttributeValueType; + SmMlAttributeValue m_aAttributeValue; + bool m_bSet; + +private: + void clearPreviousAttributeValue(); + void setDefaultAttributeValue(); + void setAttributeValue(const SmMlAttribute* aMlAttribute); + +public: + SmMlAttribute() + : m_aSmMlAttributeValueType(SmMlAttributeValueType::NMlEmpty) + , m_bSet(false){}; + + ~SmMlAttribute() { clearPreviousAttributeValue(); }; + + SmMlAttribute(SmMlAttributeValueType) + : m_aSmMlAttributeValueType(SmMlAttributeValueType::NMlEmpty) + , m_bSet(false) + { + setDefaultAttributeValue(); + }; + + SmMlAttribute(const SmMlAttribute& aMlAttribute) + : m_aSmMlAttributeValueType(SmMlAttributeValueType::NMlEmpty) + , m_bSet(aMlAttribute.isSet()) + { + setAttributeValue(&aMlAttribute); + } + + SmMlAttribute(const SmMlAttribute* aMlAttribute) + : m_aSmMlAttributeValueType(SmMlAttributeValueType::NMlEmpty) + , m_bSet(aMlAttribute->isSet()) + { + setAttributeValue(aMlAttribute); + } + +public: + /** Check if the attribute has been set + */ + bool isSet() const { return m_bSet; } + + /** Set if the attribute has been set + */ + void setSet(bool bSet) { m_bSet = bSet; } + +public: + /** + * Returns the type of attribute we are dealing with. + * Attribute Value Type + */ + SmMlAttributeValueType getMlAttributeValueType() const { return m_aSmMlAttributeValueType; }; + + /** + * Checks if the attribute contains information. + * Attribute Value Type + */ + bool isNullAttribute() const + { + return m_aSmMlAttributeValueType == SmMlAttributeValueType::NMlEmpty; + }; + + /** + * Compares the type of attribute with a given one. + * Attribute Value Type + */ + bool isMlAttributeValueType(SmMlAttributeValueType aAttributeValueType) const + { + return m_aSmMlAttributeValueType == aAttributeValueType; + }; + + /** + * Set the type of attribute we are dealing with. + * @param Attribute Value Type + */ + void setMlAttributeValueType(SmMlAttributeValueType aAttributeValueType) + { + clearPreviousAttributeValue(); + m_aSmMlAttributeValueType = aAttributeValueType; + setDefaultAttributeValue(); + } + + void setMlAttributeValue(const SmMlAttribute& aMlAttribute) + { + m_bSet = true; + setAttributeValue(&aMlAttribute); + } + + void setMlAttributeValue(const SmMlAttribute* aMlAttribute) + { + m_bSet = true; + setAttributeValue(aMlAttribute); + } + +public: + // Get values + const struct SmMlAccent* getMlAccent() const; + const struct SmMlDir* getMlDir() const; + const struct SmMlDisplaystyle* getMlDisplaystyle() const; + const struct SmMlFence* getMlFence() const; + const struct SmMlForm* getMlForm() const; + const struct SmMlHref* getMlHref() const; + const struct SmMlLspace* getMlLspace() const; + const struct SmMlMathbackground* getMlMathbackground() const; + const struct SmMlMathcolor* getMlMathcolor() const; + const struct SmMlMathsize* getMlMathsize() const; + const struct SmMlMathvariant* getMlMathvariant() const; + const struct SmMlMaxsize* getMlMaxsize() const; + const struct SmMlMinsize* getMlMinsize() const; + const struct SmMlMovablelimits* getMlMovablelimits() const; + const struct SmMlRspace* getMlRspace() const; + const struct SmMlSeparator* getMlSeparator() const; + const struct SmMlStretchy* getMlStretchy() const; + const struct SmMlSymmetric* getMlSymmetric() const; + + // Set values + // Note that content is copied. + void setMlAccent(const SmMlAccent* aAccent); + void setMlDir(const SmMlDir* aDir); + void setMlDisplaystyle(const SmMlDisplaystyle* aDisplaystyle); + void setMlFence(const SmMlFence* aFence); + void setMlForm(const SmMlForm* aForm); + void setMlHref(const SmMlHref* aHref); + void setMlLspace(const SmMlLspace* aLspace); + void setMlMathbackground(const SmMlMathbackground* aMathbackground); + void setMlMathcolor(const SmMlMathcolor* aMathcolor); + void setMlMathsize(const SmMlMathsize* aMathsize); + void setMlMathvariant(const SmMlMathvariant* aMathvariant); + void setMlMaxsize(const SmMlMaxsize* aMaxsize); + void setMlMinsize(const SmMlMinsize* aMinSize); + void setMlMovablelimits(const SmMlMovablelimits* aMovablelimits); + void setMlRspace(const SmMlRspace* aRspace); + void setMlSeparator(const SmMlSeparator* aSeparator); + void setMlStretchy(const SmMlStretchy* aStretchy); + void setMlSymmetric(const SmMlSymmetric* aSymmetric); +}; + +/* element's attributes */ +/*************************************************************************************************/ + +namespace starmathdatabase +{ +extern SmMlAttributePos MlAttributeListEmpty[1]; +extern SmMlAttributePos MlAttributeListMath[1]; +extern SmMlAttributePos MlAttributeListMi[7]; +extern SmMlAttributePos MlAttributeListMerror[4]; +extern SmMlAttributePos MlAttributeListMn[7]; +extern SmMlAttributePos MlAttributeListMo[18]; +extern SmMlAttributePos MlAttributeListMrow[4]; +extern SmMlAttributePos MlAttributeListMtext[7]; +extern SmMlAttributePos MlAttributeListMstyle[18]; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/inc/mathml/def.hxx b/starmath/inc/mathml/def.hxx new file mode 100644 index 000000000..380736711 --- /dev/null +++ b/starmath/inc/mathml/def.hxx @@ -0,0 +1,330 @@ +/* -*- 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/. + */ + +#pragma once + +#include <tools/color.hxx> + +/* All possible data needed to do the job outside mathml limits */ +// Ml prefix means it is part of mathml standard +// NMl means it is not part of mathml standard but needed info to work + +/* For now empty, don't know yet what's needed besides default font size. */ +struct SmGlobalData +{ +}; + +/* Mhtml length tools */ +/*************************************************************************************************/ + +enum class SmLengthUnit : uint_fast8_t +{ + MlEm, + MlEx, + MlPx, + MlIn, + MlCm, + MlMm, + MlPt, + MlPc, + MlP, // Percent + MlM // Multiplier +}; + +struct SmLengthValue +{ + SmLengthUnit m_aLengthUnit; + double m_aLengthValue; + // Keeps original text value to avoid numerical error data loss + OUString* m_aOriginalText; +}; + +/* Possible mathml elements */ +/*************************************************************************************************/ + +enum class SmMlElementType : uint_fast8_t +{ + // Used for base element. Means no information contained. + NMlEmpty, + // Used for structural dependencies. Means no information contained. + NMlStructural, + NMlSmNode, + // Mathml real elements + MlMath, + MlMi, + MlMerror, + MlMn, + MlMo, + MlMrow, + MlMtext, + MlMstyle +}; + +/* Possible mathml attributes */ +/*************************************************************************************************/ + +enum class SmMlAttributeValueType : uint_fast8_t +{ + NMlEmpty, + MlAccent, + MlDir, + MlDisplaystyle, + MlFence, + MlForm, + MlHref, + MlLspace, + MlMathbackground, + MlMathcolor, + MlMathsize, + MlMathvariant, + MlMaxsize, + MlMinsize, + MlMovablelimits, + MlRspace, + MlSeparator, + MlStretchy, + MlSymmetric +}; + +/* Possible values of mathml attributes */ +/*************************************************************************************************/ + +enum class SmMlAttributeValueEmpty : uint_fast8_t +{ + MlEmpty = 0x00 +}; + +enum class SmMlAttributeValueAccent : uint_fast8_t +{ + MlFalse = 0x00, + MlTrue = 0x01 +}; + +enum class SmMlAttributeValueDir : uint_fast8_t +{ + MlLtr = 0x00, + MlRtl = 0x01 +}; + +enum class SmMlAttributeValueDisplaystyle : uint_fast8_t +{ + MlFalse = 0x00, + MlTrue = 0x01 +}; + +enum class SmMlAttributeValueFence : uint_fast8_t +{ + MlFalse = 0x00, + MlTrue = 0x01 +}; + +enum class SmMlAttributeValueForm : uint_fast8_t +{ + MlPrefix = 0x01, + MlInfix = 0x02, + MlPosfix = 0x04 +}; + +enum class SmMlAttributeValueHref : uint_fast8_t +{ + NMlEmpty = 0x00, + NMlValid = 0x01 +}; + +enum class SmMlAttributeValueLspace : uint_fast8_t +{ + NMlEmpty = 0x00 +}; + +enum class SmMlAttributeValueMathbackground : uint_fast32_t +{ + MlTransparent = 0x00, + MlRgb = 0x01 +}; + +enum class SmMlAttributeValueMathcolor : uint_fast8_t +{ + MlDefault = 0x00, + MlRgb = 0x01 +}; + +enum class SmMlAttributeValueMathsize : uint_fast8_t +{ + NMlEmpty = 0x00, +}; + +enum class SmMlAttributeValueMathvariant : uint_fast16_t +{ + normal = 0x000, + bold = 0x001, + italic = 0x002, + double_struck = 0x004, + script = 0x008, + fraktur = 0x010, + sans_serif = 0x020, + monospace = 0x040, + bold_italic = 0x001 | 0x002, + bold_fraktur = 0x001 | 0x010, + bold_script = 0x001 | 0x008, + bold_sans_serif = 0x001 | 0x020, + sans_serif_italic = 0x002 | 0x20, + sans_serif_bold_italic = 0x001 | 0x002 | 0x020, + // Non english + initial = 0x080, + tailed = 0x100, + looped = 0x200, + stretched = 0x400 +}; + +enum class SmMlAttributeValueMaxsize : uint_fast8_t +{ + MlInfinity = 0x00, + MlFinite = 0x01 +}; + +/* + * Specifies whether attached under- and overscripts move to sub- and superscript positions when displaystyle is false. + * Source: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo + */ +enum class SmMlAttributeValueMovablelimits : uint_fast8_t +{ + MlFalse = 0x00, + MlTrue = 0x01 +}; + +enum class SmMlAttributeValueRspace : uint_fast8_t +{ + NMlEmpty = 0x00 +}; + +enum class SmMlAttributeValueSeparator : uint_fast8_t +{ + MlFalse = 0x00, + MlTrue = 0x01 +}; + +enum class SmMlAttributeValueStretchy : uint_fast8_t +{ + MlFalse = 0x00, + MlTrue = 0x01 +}; + +enum class SmMlAttributeValueSymmetric : uint_fast8_t +{ + MlFalse = 0x00, + MlTrue = 0x01 +}; + +/* Structures for all possible attributes */ +/*************************************************************************************************/ + +struct SmMlAccent +{ + SmMlAttributeValueAccent m_aAccent; +}; + +struct SmMlDir +{ + SmMlAttributeValueDir m_aDir; +}; + +struct SmMlDisplaystyle +{ + SmMlAttributeValueDisplaystyle m_aDisplaystyle; +}; + +struct SmMlFence +{ + SmMlAttributeValueFence m_aFence; +}; + +struct SmMlForm +{ + SmMlAttributeValueForm m_aForm; +}; + +struct SmMlHref +{ + SmMlAttributeValueHref m_aHref; + OUString* m_aLnk; +}; + +struct SmMlLspace +{ + SmLengthValue m_aLengthValue; +}; + +struct SmMlMathbackground +{ + SmMlAttributeValueMathbackground m_aMathbackground; + Color m_aCol; +}; + +struct SmMlMathcolor +{ + SmMlAttributeValueMathcolor m_aMathcolor; + Color m_aCol; +}; + +struct SmMlMathsize +{ + SmLengthValue m_aLengthValue; +}; + +struct SmMlMathvariant +{ + SmMlAttributeValueMathvariant m_aMathvariant; +}; + +struct SmMlMaxsize +{ + SmMlAttributeValueMaxsize m_aMaxsize; + SmLengthValue m_aLengthValue; +}; + +struct SmMlMinsize +{ + SmLengthValue m_aLengthValue; +}; + +struct SmMlMovablelimits +{ + SmMlAttributeValueMovablelimits m_aMovablelimits; +}; + +struct SmMlRspace +{ + SmLengthValue m_aLengthValue; +}; + +struct SmMlSeparator +{ + SmMlAttributeValueSeparator m_aSeparator; +}; + +struct SmMlStretchy +{ + SmMlAttributeValueStretchy m_aStretchy; +}; + +struct SmMlSymmetric +{ + SmMlAttributeValueSymmetric m_aSymmetric; +}; + +/* order attributes */ +/*************************************************************************************************/ + +struct SmMlAttributePos +{ + SmMlAttributeValueType m_aAttributeValueType; + uint_fast8_t m_nPos; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/inc/mathml/element.hxx b/starmath/inc/mathml/element.hxx new file mode 100644 index 000000000..ce5d7073b --- /dev/null +++ b/starmath/inc/mathml/element.hxx @@ -0,0 +1,300 @@ +/* -*- 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/. + */ + +#pragma once + +#include "attribute.hxx" +#include <rect.hxx> + +#include <editeng/editdata.hxx> + +class SmMlElement final : public SmRect +{ + /* Technical stuff */ + +public: + SmMlElement() + : m_aElementType(SmMlElementType::NMlEmpty) + , m_aText(u"") + , m_aESelection(0, 0, 0, 0) + , m_aAttributeList(0) + , m_aAttributePosList(0) + , m_aSubElements(0) + , m_aParentElement(nullptr) + , m_nSubElementId(0) + { + SmImplAttributeType(); + }; + /* Mathml stuff */ + +public: + SmMlElement(SmMlElementType aElementType) + : m_aElementType(aElementType) + , m_aText(u"\u00B6") + , m_aESelection(0, 0, 0, 0) + , m_aSubElements(0) + , m_aParentElement(nullptr) + , m_nSubElementId(0) + { + SmImplAttributeType(); + }; + +public: + SmMlElement(const SmMlElement& aElement) + : SmRect(static_cast<SmRect>(aElement)) + , m_aElementType(aElement.getMlElementType()) + , m_aText(aElement.getText()) + , m_aESelection(aElement.getESelectionReference()) + , m_aSubElements(0) + , m_aParentElement(nullptr) + , m_nSubElementId(aElement.getSubElementId()) + { + m_aAttributePosList = std::vector<SmMlAttributePos>(aElement.getAttributeCount()); + for (size_t i = 0; i < aElement.getAttributeCount(); ++i) + setAttributeForce(i, aElement.getAttributePointer(i)); + }; + +private: + // Type of element + SmMlElementType m_aElementType; + + // Element text + OUString m_aText; + + // Location in source code + ESelection m_aESelection; + + // Attribute list + std::vector<SmMlAttribute> m_aAttributeList; + + // Attribute position list + std::vector<SmMlAttributePos> m_aAttributePosList; + + // Sub elements + std::vector<SmMlElement*> m_aSubElements; + + // Parent element + SmMlElement* m_aParentElement; + + // Child id, so it is possible to iterate + size_t m_nSubElementId; + +private: + void SmImplAttributeType(); + +public: // Element type + /** + * Returns the mathml element type + * @return mathml element type + */ + SmMlElementType getMlElementType() const { return m_aElementType; }; + + /** + * Check if the mathml element is of a given type + * @param aElementType + * @return is mathml element type + */ + bool isMlElementType(SmMlElementType aElementType) const + { + return m_aElementType == aElementType; + }; + +public: // location in the source + /** + * Returns the location in the source code of the node type + * @return selection + */ + const ESelection& getESelection() const { return m_aESelection; }; + + /** + * Returns the location in the source code of the node type + * @return selection + */ + const ESelection& getESelectionReference() const { return m_aESelection; }; + + /** + * Sets the location in the source code of the node type + * @param aESelection + */ + void setESelection(ESelection aESelection) { m_aESelection = aESelection; }; + + /** + * Gets the line in the text where the node is located. + * It is used to do the visual <-> text correspondence. + * @return line + */ + sal_Int32 GetSourceCodeRow() const { return m_aESelection.nStartPara; } + + /** + * Gets the column of the line in the text where the node is located. + * It is used to do the visual <-> text correspondence. + * @return column + */ + sal_Int32 GetSourceCodeColumn() const { return m_aESelection.nStartPos; } + +public: // attributes + /** + * Returns the amount of available attributes + * @return attribute count + */ + size_t getAttributeCount() const { return m_aAttributeList.size(); }; + + /** + * Gets a given attribute. + * If no available returns empty attribute. + * @param nAttributePos + * @return given attribute. + */ + SmMlAttribute getAttribute(size_t nAttributePos) const + { + return nAttributePos < m_aAttributeList.size() ? m_aAttributeList[nAttributePos] + : SmMlAttribute(); + } + + /** + * Gets a given attribute. + * If no available returns empty attribute. + * @param nAttributePos + * @return given attribute. + */ + SmMlAttribute getAttribute(SmMlAttributeValueType aAttributeType) const; + + /** + * Sets a given attribute. + * If no available does nothing. + * @param nAttributePos + * @return given attribute. + */ + void setAttribute(const SmMlAttribute* aAttribute); + + /** + * Set's a given attribute. + * If no available does nothing. + * @param nAttributePos + * @return given attribute. + */ + void setAttribute(const SmMlAttribute& aAttribute) { setAttribute(&aAttribute); } + + /** Checks if an attribute has been manually set + * @param aElementType + */ + bool isAttributeSet(SmMlAttributeValueType aAttributeType) const; + +private: // attributes + /** + * Gets a given attribute. + * If no available returns empty attribute. + * @param nAttributePos + * @return given attribute. + */ + const SmMlAttribute* getAttributePointer(size_t nAttributePos) const + { + return nAttributePos < m_aAttributeList.size() ? &m_aAttributeList[nAttributePos] : nullptr; + } + + /** + * Sets a given attribute. + * If no available undefined behaviour. + * @param nAttributePos + * @param aAttribute + * @return given attribute. + */ + void setAttributeForce(size_t nAttributePos, const SmMlAttribute* aAttribute) + { + m_aAttributeList[nAttributePos].setMlAttributeValue(aAttribute); + } + +public: // sub elements + /** + * Returns the sub elements count + * @return sub elements count + */ + size_t getSubElementsCount() const { return m_aSubElements.size(); }; + + /** + * Returns a given sub element + * @param nPos + * @return sub elements + */ + SmMlElement* getSubElement(size_t nPos) + { + return nPos < m_aSubElements.size() ? m_aSubElements[nPos] : nullptr; + }; + + /** + * Returns a given sub element + * @param nPos + * @return sub elements + */ + const SmMlElement* getSubElement(size_t nPos) const + { + return nPos < m_aSubElements.size() ? m_aSubElements[nPos] : nullptr; + }; + + /** + * Sets a given sub element + * @param nPos + * @param aElement + */ + void setSubElement(size_t nPos, SmMlElement* aElement); + + /** + * Gets subelement id + */ + size_t getSubElementId() const { return m_nSubElementId; } + + /** + * Sets subelement id + * @param nSubElementId + */ + void setSubElementId(size_t nSubElementId) { m_nSubElementId = nSubElementId; } + +public: // parent elements + /** + * Returns the parent element + * @return parent element + */ + SmMlElement* getParentElement() { return m_aParentElement; }; + + /** + * Returns the parent element + * @return parent element + */ + const SmMlElement* getParentElement() const { return m_aParentElement; }; + + /** + * Sets the parent element + * No allocation / free is done. + * @param aParentElement + */ + void setParentElement(SmMlElement* aParentElement) { m_aParentElement = aParentElement; }; + +public: // text elements + /** + * Returns the element text + */ + const OUString& getText() const { return m_aText; }; + + /** + * Returns the element text + */ + void setText(OUString aText) { m_aText = aText; }; +}; + +namespace starmathdatabase +{ +/** + * Generates an attribute vector of default values from an attribute position list. + * @param aAttributePosList + * @return attribute vector + */ +std::vector<SmMlAttribute> makeMlAttributeList(std::vector<SmMlAttributePos> aAttributePosList); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/inc/mathml/export.hxx b/starmath/inc/mathml/export.hxx new file mode 100644 index 000000000..0bd83e6ef --- /dev/null +++ b/starmath/inc/mathml/export.hxx @@ -0,0 +1,241 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +// Our mathml +#include "element.hxx" + +// Xml tools +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmltoken.hxx> + +// Extras +#include <com/sun/star/io/XOutputStream.hpp> + +class SfxMedium; +class SmDocShell; + +class SmMLExportWrapper +{ +private: + // Model + css::uno::Reference<css::frame::XModel> m_xModel; + // Save as a flat document ( mml, fodf ... ) + bool m_bFlat; + // Use html / mathml entities + bool m_bUseHTMLMLEntities; + // Mathmml tree to parse + SmMlElement* m_pElementTree; + // Export xmlns tag + bool m_bUseExportTag; + +public: + /** Set's the writer to export to flat document + */ + void setFlat(bool bFlat) { m_bFlat = bFlat; } + + /** Checks if the writer is set to export to flat document + */ + bool getFlat() const { return m_bFlat; } + + /** Checks the use of HTML / MathML entities such as &infinity; + */ + void setUseHTMLMLEntities(bool bUseHTMLMLEntities) + { + m_bUseHTMLMLEntities = bUseHTMLMLEntities; + } + + /** Activates the use of HTML / MathML entities such as &infinity; + */ + bool getUseHTMLMLEntities() const { return m_bUseHTMLMLEntities; } + + /** Get's if xmlns field is added + */ + bool getUseExportTag() const { return m_bUseExportTag; } + + /** Set's if xmlns field is added + */ + void setUseExportTag(bool bUseExportTag) { m_bUseExportTag = bUseExportTag; } + +public: + explicit SmMLExportWrapper(css::uno::Reference<css::frame::XModel> const& rRef) + : m_xModel(rRef) + , m_bFlat(true) + , m_bUseHTMLMLEntities(false) + , m_pElementTree(nullptr) + , m_bUseExportTag(false) + { + } + + /** Export to an archive + */ + bool Export(SfxMedium& rMedium); + + /** Just export a mathml tree + */ + OUString Export(SmMlElement* pElementTree); + + // Making this protected we can keep it from getting trash as input +protected: + /** export through an XML exporter component (output stream version) + */ + bool WriteThroughComponentOS(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 char16_t* pComponentName, int_fast16_t nSyntaxVersion); + + /** export through an XML exporter component (storage version) + */ + bool WriteThroughComponentS(const css::uno::Reference<css::embed::XStorage>& xStor, + const css::uno::Reference<css::lang::XComponent>& xComponent, + const char16_t* pStreamName, + css::uno::Reference<css::uno::XComponentContext> const& rxContext, + css::uno::Reference<css::beans::XPropertySet> const& rPropSet, + const char16_t* pComponentName, int_fast16_t nSyntaxVersion); + + /** export through an XML exporter component (memory stream version) + */ + OUString + WriteThroughComponentMS(const css::uno::Reference<css::lang::XComponent>& xComponent, + css::uno::Reference<css::uno::XComponentContext> const& rxContext, + css::uno::Reference<css::beans::XPropertySet> const& rPropSet); +}; + +class SmMLExport final : public SvXMLExport +{ +private: + SmMlElement* m_pElementTree; + bool m_bSuccess; + bool m_bUseExportTag; + +public: + /** Everything was allright + */ + bool getSuccess() const { return m_bSuccess; } + + /** Get's if xmlns field is added + */ + bool getUseExportTag() const { return m_bUseExportTag; } + + /** Set's if xmlns field is added + */ + void setUseExportTag(bool bUseExportTag) { m_bUseExportTag = bUseExportTag; } + + /** Set's the element tree to be exported. + * If it isn't nullptr the this will be exported instead of the document + */ + void setElementTree(SmMlElement* pElementTree) { m_pElementTree = pElementTree; } + +private: + /** Adds an element + */ + SvXMLElementExport* createElementExport(xmloff::token::XMLTokenEnum nElement) + { + // We can't afford to ignore white spaces. They are part of the code. + return new SvXMLElementExport(*this, XML_NAMESPACE_MATH, nElement, false, false); + } + + /** Adds an attribute + */ + void addAttribute(xmloff::token::XMLTokenEnum pAttribute, + xmloff::token::XMLTokenEnum pAttributeValue) + { + AddAttribute(XML_NAMESPACE_MATH, pAttribute, pAttributeValue); + } + + /** Adds an attribute + */ + void addAttribute(xmloff::token::XMLTokenEnum pAttribute, const OUString& pAttributeValue) + { + AddAttribute(XML_NAMESPACE_MATH, pAttribute, pAttributeValue); + } + +public: + /** Exports an attribute of type "length" + */ + void exportMlAttributeLength(xmloff::token::XMLTokenEnum pAttribute, + const SmLengthValue& aLengthValue); + + /** Exports attributes of an element + */ + void exportMlAttributes(const SmMlElement* pMlElement); + + /** Exports an element and all it's attributes + */ + SvXMLElementExport* exportMlElement(const SmMlElement* pMlElement); + + /** Exports an element tree + */ + void exportMlElementTree(); + + /** Handles an error on the mathml structure + */ + void declareMlError(); + +public: + /** Constructor + */ + SmMLExport(const css::uno::Reference<css::uno::XComponentContext>& rContext, + OUString const& implementationName, SvXMLExportFlags nExportFlags); + +private: + /** Get's document shell + */ + SmDocShell* getSmDocShell(); + +public: + // XUnoTunnel + sal_Int64 SAL_CALL getSomething(const css::uno::Sequence<sal_Int8>& rId) override; + static const css::uno::Sequence<sal_Int8>& getUnoTunnelId() noexcept; + + /** Exports auto styles + * However math doesn't have any + */ + void ExportAutoStyles_() override {} + + /** Exports master styles + * However math doesn't have any + */ + void ExportMasterStyles_() override {} + + /** Exports formula + * Handler used from exportDoc + */ + void ExportContent_() override { exportMlElementTree(); }; + + /** Exports the document + * If m_pElementTree isn't null then exports m_pElementTree + */ + ErrCode exportDoc(enum ::xmloff::token::XMLTokenEnum eClass + = ::xmloff::token::XML_TOKEN_INVALID) override; + + /** Get's view settings and prepares them to export + */ + virtual void GetViewSettings(css::uno::Sequence<css::beans::PropertyValue>& aProps) override; + + /** Get's configuration settings and prepares them to export + */ + virtual void + GetConfigurationSettings(css::uno::Sequence<css::beans::PropertyValue>& aProps) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/mathml/import.hxx b/starmath/inc/mathml/import.hxx new file mode 100644 index 000000000..d98cc8cf4 --- /dev/null +++ b/starmath/inc/mathml/import.hxx @@ -0,0 +1,150 @@ +/* -*- 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/. + */ + +#pragma once + +// Our mathml +#include "element.hxx" + +// XML tools +#include <vcl/errcode.hxx> +#include <xmloff/xmlimp.hxx> + +// Extras + +class SfxMedium; +class SmDocShell; +class SmMLImport; + +class SmMLImportWrapper +{ + css::uno::Reference<css::frame::XModel> m_xModel; + SmDocShell* m_pDocShell; + SmMLImport* m_pMlImport; + +private: + // Use customized entities + +public: + /** Get the element tree when parsed from text + */ + SmMlElement* getElementTree(); + +public: + /** Constructor + */ + explicit SmMLImportWrapper(css::uno::Reference<css::frame::XModel> const& rRef) + : m_xModel(rRef) + , m_pDocShell(nullptr) + , m_pMlImport(nullptr) + { + } + + /** Imports the mathml + */ + ErrCode Import(SfxMedium& rMedium); + + /** Imports the mathml + */ + ErrCode Import(std::u16string_view aSource); + + /** read a component from input stream + */ + ErrCode + ReadThroughComponentIS(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 char16_t* pFilterName, bool bEncrypted, + int_fast16_t nSyntaxVersion); + + /** read a component from storage + */ + ErrCode ReadThroughComponentS(const css::uno::Reference<css::embed::XStorage>& xStorage, + const css::uno::Reference<css::lang::XComponent>& xModelComponent, + const char16_t* pStreamName, + css::uno::Reference<css::uno::XComponentContext> const& rxContext, + css::uno::Reference<css::beans::XPropertySet> const& rPropSet, + const char16_t* pFilterName, int_fast16_t nSyntaxVersion); + + /** read a component from text + */ + ErrCode + ReadThroughComponentMS(std::u16string_view aText, + const css::uno::Reference<css::lang::XComponent>& xModelComponent, + css::uno::Reference<css::uno::XComponentContext> const& rxContext, + css::uno::Reference<css::beans::XPropertySet> const& rPropSet); +}; + +class SmMLImport final : public SvXMLImport +{ +private: + SmMlElement* m_pElementTree = new SmMlElement(SmMlElementType::NMlEmpty); + bool m_bSuccess; + size_t m_nSmSyntaxVersion; + +public: + /** Gets parsed element tree + */ + SmMlElement* getElementTree() { return m_pElementTree; } + + /** Checks out if parse was a success + */ + bool getSuccess() const { return m_bSuccess; } + +public: + /** Handles an error on the mathml structure + */ + void declareMlError(); + +public: + /** Constructor + */ + SmMLImport(const css::uno::Reference<css::uno::XComponentContext>& rContext, + OUString const& implementationName, SvXMLImportFlags nImportFlags); + + /** Destructor + */ + virtual ~SmMLImport() noexcept override { cleanup(); }; + +public: + // XUnoTunnel + sal_Int64 SAL_CALL getSomething(const css::uno::Sequence<sal_Int8>& rId) override; + static const css::uno::Sequence<sal_Int8>& getUnoTunnelId() noexcept; + + /** End the document + */ + void SAL_CALL endDocument() override; + + /** Create a fast context + */ + SvXMLImportContext* CreateFastContext( + sal_Int32 nElement, + const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override; + + /** Imports view settings formula + */ + virtual void + SetViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& aViewProps) override; + + /** Imports configurations settings formula + */ + virtual void SetConfigurationSettings( + const css::uno::Sequence<css::beans::PropertyValue>& aViewProps) override; + + /** Set syntax version + */ + void SetSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion) { m_nSmSyntaxVersion = nSmSyntaxVersion; } + + /** Get syntax version + */ + sal_uInt16 GetSmSyntaxVersion() const { return m_nSmSyntaxVersion; } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/inc/mathml/iterator.hxx b/starmath/inc/mathml/iterator.hxx new file mode 100644 index 000000000..559829ce8 --- /dev/null +++ b/starmath/inc/mathml/iterator.hxx @@ -0,0 +1,125 @@ +/* -*- 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/. + */ + +#pragma once + +#include "element.hxx" + +/** The purpose of this iterator is to be able to iterate threw an infinite element tree + * infinite -> as much as your memory can hold + * No call-backs that will end up in out of stack + */ + +namespace mathml +{ +template <typename runType> +void SmMlIteratorBottomToTop(SmMlElement* pMlElementTree, runType aRunType, void* aData) +{ + if (pMlElementTree == nullptr) + return; + + SmMlElement* pCurrent; + + // Fetch the deepest element + pCurrent = pMlElementTree; + while (pCurrent->getSubElementsCount() != 0) + { + if (pCurrent->getSubElement(0) == nullptr) + break; + pCurrent = pCurrent->getSubElement(0); + } + + do + { + // Fetch next element + size_t nId = pCurrent->getSubElementId(); + // We are back to the top. + if (pCurrent->getParentElement() == nullptr) + break; + // If this was the last, then turn back to the parent + if (nId + 1 == pCurrent->getParentElement()->getSubElementsCount()) + pCurrent = pCurrent->getParentElement(); + else // If not, next is the one near it + { + // It could have sub elements + if (pCurrent->getParentElement()->getSubElement(nId + 1) == nullptr) + break; + pCurrent = pCurrent->getParentElement()->getSubElement(nId + 1); + // Fetch the deepest element + while (pCurrent->getSubElementsCount() != 0) + { + if (pCurrent->getSubElement(0) == nullptr) + break; + pCurrent = pCurrent->getSubElement(0); + } + } + + // Just in case of, but should be forbidden + if (pCurrent != nullptr) + aRunType(pCurrent, aData); + + } while (pCurrent != nullptr); +} + +template <typename runType> +void SmMlIteratorTopToBottom(SmMlElement* pMlElementTree, runType aRunType, void* aData) +{ + if (pMlElementTree == nullptr) + return; + + SmMlElement* pCurrent; + + // Fetch the deepest element + pCurrent = pMlElementTree; + aRunType(pCurrent, aData); + while (pCurrent->getSubElementsCount() != 0) + { + if (pCurrent->getSubElement(0) == nullptr) + break; + pCurrent = pCurrent->getSubElement(0); + aRunType(pCurrent, aData); + } + + do + { + // Fetch next element + size_t nId = pCurrent->getSubElementId(); + // We are back to the top. + if (pCurrent->getParentElement() == nullptr) + break; + // If this was the last, then turn back to the parent + if (nId + 1 == pCurrent->getParentElement()->getSubElementsCount()) + pCurrent = pCurrent->getParentElement(); + else // If not, next is the one near it + { + // It could have sub elements + if (pCurrent->getParentElement()->getSubElement(nId + 1) == nullptr) + break; + pCurrent = pCurrent->getParentElement()->getSubElement(nId + 1); + aRunType(pCurrent, aData); + // Fetch the deepest element + while (pCurrent->getSubElementsCount() != 0) + { + if (pCurrent->getSubElement(0) == nullptr) + break; + pCurrent = pCurrent->getSubElement(0); + aRunType(pCurrent, aData); + } + } + + } while (pCurrent != nullptr); +} + +void SmMlIteratorFree(SmMlElement* pMlElementTree); + +SmMlElement* SmMlIteratorCopy(SmMlElement* pMlElementTree); + +} // end namespace mathml + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/inc/mathml/mathmlMo.hxx b/starmath/inc/mathml/mathmlMo.hxx new file mode 100644 index 000000000..10a8b0001 --- /dev/null +++ b/starmath/inc/mathml/mathmlMo.hxx @@ -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/. + * + * 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 +*/ + +#pragma once + +#include <sal/types.h> +#include <rtl/ustring.hxx> +#include <vector> + +// https://www.w3.org/TR/MathML3/appendixc.html + +enum class moOpDP : sal_uInt16 +{ // moOperatorDataProperty + nonedp = 0x0000, + accent = 0x0001, + fence = 0x0002, + stretchy = 0x0004, + symmetric = 0x0008, + separator = 0x0010, + linebreakstyleAfter = 0x0020, + largeop = 0x0080, + movablelimits = 0x0100, + starmathCustom = 0x0200, + starmathCustomMo = 0x0400, + stretchyfence = 0x0006, + movablelargeop = 0x0180 +}; + +enum class moOpDF : sal_uInt16 +{ // moOperatorDataForm + nonedf = 0x0000, + prefix = 0x0001, + infix = 0x0002, + postfix = 0x0004, + prepostfix = 0x0005 +}; + +struct moOperatorData +{ + OUString m_motxt; + moOpDF m_form; + sal_uInt16 m_priority; + sal_uInt16 m_lspace; + sal_uInt16 m_rspace; + moOpDP m_properties; + + moOperatorData(OUString motxt, moOpDF form, sal_uInt16 priority, sal_uInt16 lspace, + sal_uInt16 rspace, moOpDP properties) + : m_motxt(motxt) + , m_form(form) + , m_priority(priority) + , m_lspace(lspace) + , m_rspace(rspace) + , m_properties(properties) + { + } +}; + +inline moOpDF operator|(moOpDF a, moOpDF b) +{ + return static_cast<moOpDF>(static_cast<sal_uInt16>(a) | static_cast<sal_uInt16>(b)); +} + +inline moOpDF operator&(moOpDF a, moOpDF b) +{ + return static_cast<moOpDF>(static_cast<sal_uInt16>(a) & static_cast<sal_uInt16>(b)); +} + +inline moOpDP operator|(moOpDP a, moOpDP b) +{ + return static_cast<moOpDP>(static_cast<sal_uInt16>(a) | static_cast<sal_uInt16>(b)); +} + +inline moOpDP operator&(moOpDP a, moOpDP b) +{ + return static_cast<moOpDP>(static_cast<sal_uInt16>(a) & static_cast<sal_uInt16>(b)); +} + +namespace starmathdatabase +{ +constexpr size_t MATHML_MO_COUNT = 1100; + +extern std::vector<moOperatorData> moOperatorDataDictionary; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/mathml/mathmlattr.hxx b/starmath/inc/mathml/mathmlattr.hxx new file mode 100644 index 000000000..b7d1160bb --- /dev/null +++ b/starmath/inc/mathml/mathmlattr.hxx @@ -0,0 +1,79 @@ +/* -*- 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/. + */ + +#pragma once + +#include <sal/config.h> + +#include <string_view> + +#include <rtl/ustring.hxx> +#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) + { + } +}; + +bool ParseMathMLAttributeLengthValue(std::u16string_view 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); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/inc/mathml/mathmlexport.hxx b/starmath/inc/mathml/mathmlexport.hxx new file mode 100644 index 000000000..b7c054440 --- /dev/null +++ b/starmath/inc/mathml/mathmlexport.hxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <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 + +private: + // Use customized entities + bool m_bUseHTMLMLEntities; + +public: + explicit SmXMLExportWrapper(css::uno::Reference<css::frame::XModel> const& rRef) + : xModel(rRef) + , bFlat(true) + , m_bUseHTMLMLEntities(false) + { + } + + bool Export(SfxMedium& rMedium); + void SetFlat(bool bIn) { bFlat = bIn; } + + bool IsUseHTMLMLEntities() const { return m_bUseHTMLMLEntities; } + void SetUseHTMLMLEntities(bool bUseHTMLMLEntities) + { + m_bUseHTMLMLEntities = bUseHTMLMLEntities; + } + + 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); + + 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() noexcept; + + 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; } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/mathml/mathmlimport.hxx b/starmath/inc/mathml/mathmlimport.hxx new file mode 100644 index 000000000..0a963a1b0 --- /dev/null +++ b/starmath/inc/mathml/mathmlimport.hxx @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <xmloff/xmlimp.hxx> +#include <vcl/errcode.hxx> + +#include <deque> + +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; + +private: + // Use customized entities + bool m_bUseHTMLMLEntities; + +public: + explicit SmXMLImportWrapper(css::uno::Reference<css::frame::XModel> const& rRef) + : xModel(rRef) + , m_bUseHTMLMLEntities(false) + { + } + + ErrCode Import(SfxMedium& rMedium); + void useHTMLMLEntities(bool bUseHTMLMLEntities) { m_bUseHTMLMLEntities = bUseHTMLMLEntities; } + + 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, bool bUseHTMLMLEntities); + + 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, bool bUseHTMLMLEntities); +}; + +class SmXMLImport final : public SvXMLImport +{ + SmNodeStack aNodeStack; + bool bSuccess; + int nParseDepth; + OUString aText; + sal_uInt16 mnSmSyntaxVersion; + +public: + SmXMLImport(const css::uno::Reference<css::uno::XComponentContext>& rContext, + OUString const& implementationName, SvXMLImportFlags nImportFlags); + virtual ~SmXMLImport() noexcept override; + + // XUnoTunnel + sal_Int64 SAL_CALL getSomething(const css::uno::Sequence<sal_Int8>& rId) override; + static const css::uno::Sequence<sal_Int8>& getUnoTunnelId() noexcept; + + void SAL_CALL endDocument() override; + + SvXMLImportContext* CreateFastContext( + sal_Int32 nElement, + const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override; + + 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; } + void SetSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion) { mnSmSyntaxVersion = nSmSyntaxVersion; } + sal_uInt16 GetSmSyntaxVersion() const { return mnSmSyntaxVersion; } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/mathml/starmathdatabase.hxx b/starmath/inc/mathml/starmathdatabase.hxx new file mode 100644 index 000000000..b4d3d6d3f --- /dev/null +++ b/starmath/inc/mathml/starmathdatabase.hxx @@ -0,0 +1,332 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <token.hxx> + +// Starmath color types +// In order to add them to starmath, edit the SmColorTokenTableEntry lists on +// /core/starmath/source/starmathdatabase.css . + +// HTML +// https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_Keywords +/* CSS Level 1 */ +constexpr Color COL_SM_BLACK(0x000000); +constexpr Color COL_SM_SILVER(0xC0C0C0); +constexpr Color COL_SM_GRAY(0x808080); +constexpr Color COL_SM_WHITE(0xFFFFFF); +constexpr Color COL_SM_MAROON(0x800000); +constexpr Color COL_SM_RED(0xFF0000); +constexpr Color COL_SM_PURPLE(0x800080); +constexpr Color COL_SM_FUCHSIA(0xFF00FF); +constexpr Color COL_SM_GREEN(0x008000); +constexpr Color COL_SM_LIME(0x00FF00); +constexpr Color COL_SM_OLIVE(0x808000); +constexpr Color COL_SM_YELLOW(0xFFFF00); +constexpr Color COL_SM_NAVY(0x000080); +constexpr Color COL_SM_BLUE(0x0000FF); +constexpr Color COL_SM_TEAL(0x008080); +constexpr Color COL_SM_AQUA(0x00FFFF); +/* CSS Level 2 */ +constexpr Color COL_SM_ORANGE(0xFFA500); +/* CSS Level 3 */ +constexpr Color COL_SM_ALICEBLUE(0xF0F8FF); +constexpr Color COL_SM_ANTIQUEWHITE(0xFAEBD7); +constexpr Color COL_SM_AQUAMARINE(0x7FFFD4); +constexpr Color COL_SM_AZURE(0xF0FFFF); +constexpr Color COL_SM_BEIGE(0xF5F5DC); +constexpr Color COL_SM_BISQUE(0xFFE4C4); +constexpr Color COL_SM_BLANCHEDALMOND(0xFFEBCD); +constexpr Color COL_SM_BLUEVIOLET(0x8A2BE2); +constexpr Color COL_SM_BROWN(0xA52A2A); +constexpr Color COL_SM_BURLYWOOD(0xDEB887); +constexpr Color COL_SM_CADETBLUE(0x5F9EA0); +constexpr Color COL_SM_CHARTREUSE(0x7FFF00); +constexpr Color COL_SM_CHOCOLATE(0xD2691E); +constexpr Color COL_SM_CORAL(0xFF7F50); +constexpr Color COL_SM_CORNFLOWERBLUE(0x6495ED); +constexpr Color COL_SM_CORNSILK(0xFFF8DC); +constexpr Color COL_SM_CRIMSON(0xDC143C); +constexpr Color COL_SM_CYAN(0x00FFFF); +constexpr Color COL_SM_DARKBLUE(0x00008B); +constexpr Color COL_SM_DARKCYAN(0x008B8B); +constexpr Color COL_SM_DARKGOLDENROD(0xB8860B); +constexpr Color COL_SM_DARKGRAY(0xA9A9A9); +constexpr Color COL_SM_DARKGREEN(0x006400); +constexpr Color COL_SM_DARKGREY(0xA9A9A9); +constexpr Color COL_SM_DARKKHAKI(0xBDB76B); +constexpr Color COL_SM_DARKMAGENTA(0x8B008B); +constexpr Color COL_SM_DARKOLIVEGREEN(0x556B2F); +constexpr Color COL_SM_DARKORANGE(0xFF8C00); +constexpr Color COL_SM_DARKORCHID(0x9932CC); +constexpr Color COL_SM_DARKRED(0x8B0000); +constexpr Color COL_SM_DARKSALMON(0xE9967A); +constexpr Color COL_SM_DARKSEAGREEN(0x8FBC8F); +constexpr Color COL_SM_DARKSLATEBLUE(0x483D8B); +constexpr Color COL_SM_DARKSLATEGRAY(0x2F4F4F); +constexpr Color COL_SM_DARKSLATEGREY(0x2F4F4F); +constexpr Color COL_SM_DARKTURQUOISE(0x00CED1); +constexpr Color COL_SM_DARKVIOLET(0x9400D3); +constexpr Color COL_SM_DEEPPINK(0xFF1493); +constexpr Color COL_SM_DEEPSKYBLUE(0x00BFFF); +constexpr Color COL_SM_DIMGRAY(0x696969); +constexpr Color COL_SM_DIMGREY(0x696969); +constexpr Color COL_SM_DODGERBLUE(0x1E90FF); +constexpr Color COL_SM_FIREBRICK(0xB22222); +constexpr Color COL_SM_FLORALWHITE(0xFFFAF0); +constexpr Color COL_SM_FORESTGREEN(0x228B22); +constexpr Color COL_SM_GAINSBORO(0xDCDCDC); +constexpr Color COL_SM_GHOSTWHITE(0xF8F8FF); +constexpr Color COL_SM_GOLD(0xFFD700); +constexpr Color COL_SM_GOLDENROD(0xDAA520); +constexpr Color COL_SM_GREENYELLOW(0xADFF2F); +constexpr Color COL_SM_GREY(0x808080); +constexpr Color COL_SM_HONEYDEW(0xF0FFF0); +constexpr Color COL_SM_HOTPINK(0xFF69B4); +constexpr Color COL_SM_INDIANRED(0xCD5C5C); +constexpr Color COL_SM_INDIGO(0x4B0082); +constexpr Color COL_SM_IVORY(0xFFFFF0); +constexpr Color COL_SM_KHAKI(0xF0E68C); +constexpr Color COL_SM_LAVENDER(0xE6E6FA); +constexpr Color COL_SM_LAVENDERBLUSH(0xFFF0F5); +constexpr Color COL_SM_LAWNGREEN(0x7CFC00); +constexpr Color COL_SM_LEMONCHIFFON(0xFFFACD); +constexpr Color COL_SM_LIGHTBLUE(0xADD8E6); +constexpr Color COL_SM_LIGHTCORAL(0xF08080); +constexpr Color COL_SM_LIGHTCYAN(0xE0FFFF); +constexpr Color COL_SM_LIGHTGOLDENRODYELLOW(0xFAFAD2); +constexpr Color COL_SM_LIGHTGRAY(0xD3D3D3); +constexpr Color COL_SM_LIGHTGREEN(0x90EE90); +constexpr Color COL_SM_LIGHTGREY(0xD3D3D3); +constexpr Color COL_SM_LIGHTPINK(0xFFB6C1); +constexpr Color COL_SM_LIGHTSALMON(0xFFA07A); +constexpr Color COL_SM_LIGHTSEAGREEN(0x20B2AA); +constexpr Color COL_SM_LIGHTSKYBLUE(0x87CEFA); +constexpr Color COL_SM_LIGHTSLATEGRAY(0x778899); +constexpr Color COL_SM_LIGHTSLATEGREY(0x778899); +constexpr Color COL_SM_LIGHTSTEELBLUE(0xB0C4DE); +constexpr Color COL_SM_LIGHTYELLOW(0xFFFFE0); +constexpr Color COL_SM_LIMEGREEN(0x32CD32); +constexpr Color COL_SM_LINEN(0xFAF0E6); +constexpr Color COL_SM_MAGENTA(0xFF00FF); +constexpr Color COL_SM_MEDIUMAQUAMARINE(0x66CDAA); +constexpr Color COL_SM_MEDIUMBLUE(0x0000CD); +constexpr Color COL_SM_MEDIUMORCHID(0xBA55D3); +constexpr Color COL_SM_MEDIUMPURPLE(0x9370DB); +constexpr Color COL_SM_MEDIUMSEAGREEN(0x3CB371); +constexpr Color COL_SM_MEDIUMSLATEBLUE(0x7B68EE); +constexpr Color COL_SM_MEDIUMSPRINGGREEN(0x00FA9A); +constexpr Color COL_SM_MEDIUMTURQUOISE(0x48D1CC); +constexpr Color COL_SM_MEDIUMVIOLETRED(0xC71585); +constexpr Color COL_SM_MIDNIGHTBLUE(0x191970); +constexpr Color COL_SM_MINTCREAM(0xF5FFFA); +constexpr Color COL_SM_MISTYROSE(0xFFE4E1); +constexpr Color COL_SM_MOCCASIN(0xFFE4B5); +constexpr Color COL_SM_NAVAJOWHITE(0xFFDEAD); +constexpr Color COL_SM_OLDLACE(0xFDF5E6); +constexpr Color COL_SM_OLIVEDRAB(0x6B8E23); +constexpr Color COL_SM_ORANGERED(0xFF4500); +constexpr Color COL_SM_ORCHID(0xDA70D6); +constexpr Color COL_SM_PALEGOLDENROD(0xEEE8AA); +constexpr Color COL_SM_PALEGREEN(0x98FB98); +constexpr Color COL_SM_PALETURQUOISE(0xAFEEEE); +constexpr Color COL_SM_PALEVIOLETRED(0xDB7093); +constexpr Color COL_SM_PAPAYAWHIP(0xFFEFD5); +constexpr Color COL_SM_PEACHPUFF(0xFFDAB9); +constexpr Color COL_SM_PERU(0xCD853F); +constexpr Color COL_SM_PINK(0xFFC0CB); +constexpr Color COL_SM_PLUM(0xDDA0DD); +constexpr Color COL_SM_POWDERBLUE(0xB0E0E6); +constexpr Color COL_SM_ROSYBROWN(0xBC8F8F); +constexpr Color COL_SM_ROYALBLUE(0x4169E1); +constexpr Color COL_SM_SADDLEBROWN(0x8B4513); +constexpr Color COL_SM_SALMON(0xFA8072); +constexpr Color COL_SM_SANDYBROWN(0xF4A460); +constexpr Color COL_SM_SEAGREEN(0x2E8B57); +constexpr Color COL_SM_SEASHELL(0xFFF5EE); +constexpr Color COL_SM_SIENNA(0xA0522D); +constexpr Color COL_SM_SKYBLUE(0x87CEEB); +constexpr Color COL_SM_SLATEBLUE(0x6A5ACD); +constexpr Color COL_SM_SLATEGRAY(0x708090); +constexpr Color COL_SM_SLATEGREY(0x708090); +constexpr Color COL_SM_SNOW(0xFFFAFA); +constexpr Color COL_SM_SPRINGGREEN(0x00FF7F); +constexpr Color COL_SM_STEELBLUE(0x4682B4); +constexpr Color COL_SM_TAN(0xD2B48C); +constexpr Color COL_SM_THISTLE(0xD8BFD8); +constexpr Color COL_SM_TOMATO(0xFF6347); +constexpr Color COL_SM_TURQUOISE(0x40E0D0); +constexpr Color COL_SM_VIOLET(0xEE82EE); +constexpr Color COL_SM_WHEAT(0xF5DEB3); +constexpr Color COL_SM_WHITESMOKE(0xF5F5F5); +constexpr Color COL_SM_YELLOWGREEN(0x9ACD32); +/* CSS Level 4 */ +constexpr Color COL_SM_REBECCAPURPLE(0x663399); +/* dvipsnames */ +// For now only five colors. +// In a future all of them. +// https://www.overleaf.com/learn/latex/Using_colours_in_LaTeX +constexpr Color COL_SM_DIV_APRICOT(0xFFB781); +constexpr Color COL_SM_DIV_AQUAMARINE(0x1BBEC1); +constexpr Color COL_SM_DIV_BITTERSWEET(0xCF4B16); +constexpr Color COL_SM_DIV_BLACK(0xCF4B16); +constexpr Color COL_SM_DIV_BLUE(0x102694); +/* Iconic colors */ +// https://design.ubuntu.com/brand/colour-palette/ +constexpr Color COL_SM_UBUNTU_ORANGE(0xE95420); +// https://www.debian.org/logos/ Picked from SVG logo +constexpr Color COL_SM_DEBIAN_MAGENTA(0xA80030); +// https://libreoffice.org/ +constexpr Color COL_SM_LO_GREEN(0x00A500); + +namespace starmathdatabase +{ +// Variables containing color information. +extern const SmColorTokenTableEntry aColorTokenTableParse[159]; +extern const SmColorTokenTableEntry aColorTokenTableHTML[148]; +extern const SmColorTokenTableEntry aColorTokenTableMATHML[16]; +extern const SmColorTokenTableEntry aColorTokenTableDVIPS[5]; +extern const SmColorTokenTableEntry aColorTokenTableERROR[1]; + +/** + * Identifies operator chars tokens for importing mathml. + * Identifies from char cChar + * + * While loading MO or MI elements might find an unicode16 symbol. + * This code allows to generate appropriate token for them. + * + * @param cChar + * @return closing fences' token + */ +SmToken Identify_SmXMLOperatorContext_Impl(sal_Unicode cChar, bool bIsStretchy = true); + +/** + * Identifies opening / closing brace tokens for importing mathml. + * Identifies from char cChar + * + * While loading MO fenced elements might find braces symbols. + * This code allows to generate appropriate token for them. + * + * @param cChar + * @return closing fences' token + */ +SmToken Identify_PrefixPostfix_SmXMLOperatorContext_Impl(sal_Unicode cChar); + +/** + * Identifies opening brace tokens for importing mathml. + * Identifies from char cChar + * + * While loading MO elements ( with prefix value for form attribute ) might find braces symbols. + * This code allows to generate appropriate token for them. + * + * @param cChar + * @return closing fences' token + */ +SmToken Identify_Prefix_SmXMLOperatorContext_Impl(sal_Unicode cChar); + +/** + * Identifies closing brace tokens for importing mathml. + * Identifies from char cChar + * + * While loading MO elements ( with postfix value for form attribute ) might find braces symbols. + * This code allows to generate appropriate token for them. + * + * @param cChar + * @return closing fences' token + */ +SmToken Identify_Postfix_SmXMLOperatorContext_Impl(sal_Unicode cChar); + +/** + * Identifies color from color code cColor. + * It will be returned with the parser syntax. + * + * For a given color returns the way it would be in the parser. + * Used for nodes to text visitors. + * + * @param cColor + * @param parser color + */ +SmColorTokenTableEntry Identify_Color_Parser(sal_uInt32 cColor); + +/** + * Identifies color from color code cColor. + * It will be returned with the HTML syntax. + * @param cColor + * @param parser color + */ +SmColorTokenTableEntry Identify_Color_HTML(sal_uInt32 cColor); + +/** + * Identifies color from color code cColor. + * It will be returned with the MATHML syntax. + * + * This is used to export mathml. + * Identifies the color and allows it to export it in proper mathml code. + * + * @param cColor + * @param parser color + */ +SmColorTokenTableEntry Identify_Color_MATHML(sal_uInt32 cColor); + +/** + * Identifies color from color code cColor. + * It will be returned with the dvipsnames syntax. + * @param cColor + * @param parser color + */ +SmColorTokenTableEntry Identify_Color_DVIPSNAMES(sal_uInt32 cColor); + +/** + * Identifies color from color name. + * It will be returned with the parser syntax. + * + * This finds color values for the color names loaded by the parser. + * + * @param cColor + * @param parser color + */ +const SmColorTokenTableEntry* Identify_ColorName_Parser(std::u16string_view colorname); + +/** + * Identifies color from color name. + * It will be returned with the HTML syntax. + * + * This finds color values for the color names loaded by mathmlimport. + * In theory mathml only supports HTML4 colors, but most browsers support all HTML5 colors. + * That's why there is an high risk of finding them inside mathml and have to give support. + * + * @param cColor + * @param parser color + */ +SmColorTokenTableEntry Identify_ColorName_HTML(std::u16string_view colorname); + +/** + * Identifies color from color name. + * It will be returned with the dvipsnames syntax. + * + * This code has been implemented to add a compatibility layer to import / export latex. + * + * @param cColor + * @param parser color + */ +const SmColorTokenTableEntry* Identify_ColorName_DVIPSNAMES(std::u16string_view colorname); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/mathml/xparsmlbase.hxx b/starmath/inc/mathml/xparsmlbase.hxx new file mode 100644 index 000000000..6c645a55e --- /dev/null +++ b/starmath/inc/mathml/xparsmlbase.hxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/beans/Pair.hpp> +namespace starmathdatabase +{ +/** + * w3 documentation has been used for this. + * See: https://www.w3.org/2003/entities/2007/htmlmathml-f.ent + * Copyright 1998 - 2011 W3C. + * We allow the import of HTML5 entities because are compatible with mathml + * and ill formatted are expected. + * On export only mathml entities are allowed. + * Some documentation: https://www.w3.org/TR/MathML3/chapter7.html + */ + +constexpr sal_Int32 STARMATH_MATHMLHTML_ENTITY_NUMBER = 2125; + +/** + * Entity names for mathml. Example: &infin -> \u221E; + * These ones are to be used on import. + */ +const extern ::css::uno::Sequence<::css::beans::Pair<::rtl::OUString, ::rtl::OUString>> + icustomMathmlHtmlEntities; + +/** + * Entity names for mathml. Example: "\u221E"; -> ∞ + * These ones are to be used on file export. + */ +const extern ::css::uno::Sequence<::css::beans::Pair<::rtl::OUString, ::rtl::OUString>> + icustomMathmlHtmlEntitiesExport; +}; + +/* 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..0a8a6c369 --- /dev/null +++ b/starmath/inc/node.hxx @@ -0,0 +1,2110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 . + */ + +/** The SmNode is the basic structure of formula data. + * + * Each token is stored in one node of specific kind. + * They can have SmNodeType. It allows to identify node type after abstraction. + * Here goes the subclasses tree: + * + * SmRect + * SmNode + * SmStructureNode Head of tree diagram + * SmTableNode binom + * SmLineNode A line + * SmExpressionNode { content } + * SmUnHorNode unary operators +-; -+; +x; -x; ... + * SmRootNode Root structure + * SmBinHorNode binary operators A + B + * SmBinVerNode over; frac; ... + * SmBinDiagonalNode wideslash + * SmSubSupNode csub, csup, lsub, from, to, ... + * SmBraceNode (); []; left lbrace right rbrace; ... + * SmBracebodyNode ( content ); [ content ]; ... + * SmVerticalBraceNode overbrace; underbrace; + * SmOperNode sum from to; int from to; + * SmAlignNode text alignment + * SmAttributeNode font attributes; bold; + * SmFontNode font serif; ... + * SmMatrixNode matrix + * SmVisibleNode drawable node + * SmGraphicNode graphics display + * SmRectangleNode + * SmPolyLineNode overline; underline; widehat; ... + * SmBlankNode blank space; ~; ... + * SmTextNode "text"; func functname; ... + * SmSpecialNode + * SmGlyphSpecialNode %symbolname + * SmMathSymbolNode math symbols + * SmMathIdentifierNode variable + * SmRootSymbolNode root symbol + * SmPlaceNode <?> + * SmErrorNode red ? for errors + * + */ + +#pragma once + +#include "types.hxx" +#include "token.hxx" +#include "rect.hxx" +#include "format.hxx" +#include "nodetype.hxx" + +#include <editeng/editdata.hxx> +#include <rtl/ustrbuf.hxx> + +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 +}; + +class SmNode : public SmRect +{ + // Rendering info for SmRect + SmFace maFace; + // Anclage to the code + SmToken maNodeToken; + ESelection m_aESelection; + // Node information + 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(); + + /** + * Checks node visibility. + * Returns true if this is an instance of SmVisibleNode's subclass, false otherwise. + * @return node visibility + */ + virtual bool IsVisible() const = 0; + + /** + * Gets the number of subnodes. + * @return number of subnodes + */ + virtual size_t GetNumSubNodes() const = 0; + + /** + * Gets the subnode of index nIndex. + * @param nIndex + * @return subnode of index nIndex + */ + 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; + + /** + * Gets the FontChangeMask flags. + * @return FontChangeMask flags + */ + FontChangeMask &Flags() { return mnFlags; } + + /** + * Gets the font attributes. + * @return font attributes + */ + FontAttribute &Attributes() { return mnAttributes; } + + /** + * Checks if it is a visible node rendered invisible. + * @return rendered visibility + */ + bool IsPhantom() const { return mbIsPhantom; } + + /** + * Sets the render visibility of a visible node to bIsPhantom. + * @param bIsPhantom + * @return + */ + void SetPhantom(bool bIsPhantom); + + /** + * Sets the font color. + * @param rColor + * @return + */ + void SetColor(const Color &rColor); + + /** + * Sets the font attribute nAttrib. + * Check FontAttribute class. + * @param nAttrib + * @return + */ + void SetAttribute(FontAttribute nAttrib); + + /** + * Clears the font attribute nAttrib. + * Check FontAttribute class. + * @param nAttrib + * @return + */ + void ClearAttribute(FontAttribute nAttrib); + + /** + * Gets the font. + * @return font + */ + const SmFace & GetFont() const { return maFace; }; + SmFace & GetFont() { return maFace; }; + + /** + * Sets the font to rFace. + * @param rFace + * @return + */ + void SetFont(const SmFace &rFace); + + /** + * Sets the font size to rRelSize with type nType. + * Check FontSizeType for details. + * @param rRelSize + * @param nType + * @return + */ + void SetFontSize(const Fraction &rRelSize, FontSizeType nType); + + /** + * Sets the font size to rRelSize with type FontSizeType::ABSOLUT. + * @param rScale + * @return + */ + void SetSize(const Fraction &rScale); + + /** + * Prepare preliminary settings about font and text + * (e.g. maFace, meRectHorAlign, mnFlags, mnAttributes, etc.) + * @param rFormat + * @param rDocShell + * @param nDepth + * @return + */ + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth); + + /** + * Prepare preliminary font attributes + * Called on Prepare(...). + * @return + */ + void PrepareAttributes(); + + /** + * Sets the alignment of the text. + * Check RectHorAlign class for details. + * The subtrees will be affected if bApplyToSubTree. + * @param eHorAlign + * @param bApplyToSubTree + * @return + */ + void SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree = true ); + + /** + * Gets the alignment of the text. + * @return alignment of the text + */ + RectHorAlign GetRectHorAlign() const { return meRectHorAlign; } + + /** + * Parses itself to SmRect. + * @return this + */ + const SmRect & GetRect() const { return *this; } + + /** + * Moves the rectangle by rVector. + * @param rVector + * @return + */ + void Move(const Point &rVector); + + /** + * Moves the rectangle to rPoint, being the top left corner the origin. + * @param rPoint + * @return + */ + void MoveTo(const Point &rPoint) { Move(rPoint - GetTopLeft()); } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) = 0; + + /** + * Appends to rText the node text. + * @param rText + * @return + */ + virtual void GetAccessibleText( OUStringBuffer &rText ) const = 0; + + /** + * Gets the node accessible index. + * Used for visual editing. + * @return node accessible index + */ + sal_Int32 GetAccessibleIndex() const { return mnAccIndex; } + + /** + * Sets the node accessible index to nAccIndex. + * Used for visual editing. + * @param nAccIndex + * @return + */ + void SetAccessibleIndex(sal_Int32 nAccIndex) { mnAccIndex = nAccIndex; } + + /** + * Finds the node with accessible index nAccIndex. + * Used for visual editing. + * @param nAccIndex + * @return node with accessible index nAccIndex + */ + const SmNode * FindNodeWithAccessibleIndex(sal_Int32 nAccIndex) const; + + /** + * Gets the line in the text where the node is located. + * It is used to do the visual <-> text correspondence. + * @return line + */ + sal_uInt16 GetRow() const { return sal::static_int_cast<sal_uInt16>(m_aESelection.nStartPara); } + + /** + * Gets the column of the line in the text where the node is located. + * It is used to do the visual <-> text correspondence. + * @return column + */ + sal_uInt16 GetColumn() const { return sal::static_int_cast<sal_uInt16>(m_aESelection.nStartPos); } + + /** + * Gets the scale mode. + * @return scale mode + */ + SmScaleMode GetScaleMode() const { return meScaleMode; } + + /** + * Sets the scale mode to eMode. + * @param eMode + * @return + */ + void SetScaleMode(SmScaleMode eMode) { meScaleMode = eMode; } + + //visual stuff TODO comment + virtual void AdaptToX(OutputDevice &rDev, sal_uLong nWidth); + virtual void AdaptToY(OutputDevice &rDev, sal_uLong nHeight); + + /** + * Gets the node type. + * @return node type + */ + SmNodeType GetType() const { return meType; } + + /** + * Gets the token. + * The token contains the data extracted from the text mode. + * Ej: text, type (sub, sup, int,...), row and column,... + * @return node type + */ + const SmToken & GetToken() const { return maNodeToken; } + SmToken & GetToken() { return maNodeToken; } + + /** + * Gets node position in input text. + * @return node position in input text + */ + const ESelection& GetSelection() const { return m_aESelection; } + + /** + * Gets node position in input text. + * @param aESelection + */ + void SetSelection(ESelection aESelection) { m_aESelection = aESelection; } + + /** + * Finds the node from the position in the text. + * It is used to do the visual <-> text correspondence. + * @param nRow + * @param nCol + * @return the given node + */ + const SmNode * FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const; + + /** + * Finds the closest rectangle in the screen. + * @param rPoint + * @return the given node + */ + const SmNode * FindRectClosestTo(const Point &rPoint) const; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + virtual void Accept(SmVisitor* pVisitor) = 0; + + /** + * Checks if the node is selected. + * @return the node is selected + */ + bool IsSelected() const {return mbIsSelected;} + + /** + * Sets the node to Selected. + * @param Selected + * @return + */ + void SetSelected(bool Selected) {mbIsSelected = Selected;} + + /** + * Gets the parent node of this node. + * @return parent node + */ + const SmStructureNode* GetParent() const { return mpParentNode; } + SmStructureNode* GetParent() { return mpParentNode; } + + /** + * Sets the parent node. + * @param parent + * @return + */ + void SetParent(SmStructureNode* parent){ mpParentNode = parent; } + + /** + * Sets the token for this node. + * @param token + * @return + */ + 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; + + /** + * Checks node visibility. + * Returns true if this is an instance of SmVisibleNode's subclass, false otherwise. + * @return node visibility + */ + virtual bool IsVisible() const override; + + /** + * Gets the number of subnodes. + * @return number of subnodes + */ + virtual size_t GetNumSubNodes() const override; + + /** + * Gets the subnode of index nIndex. + * @param nIndex + * @return subnode of index nIndex + */ + using SmNode::GetSubNode; + virtual SmNode * GetSubNode(size_t nIndex) override; + + /** + * Gets the subnode of index nIndex, used for operators. + * @param nIndex + * @return subnode of index nIndex + */ + SmNode * GetSubNodeBinMo(size_t nIndex) const; + + /** + * Does the cleaning of the subnodes. + * @return + */ + void ClearSubNodes(); + + /** + * Sets subnodes, used for operators. + * @param pFirst + * @param pSecond + * @param pThird + * @return + */ + void SetSubNodes(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, + std::unique_ptr<SmNode> pThird = nullptr); + + /** + * Sets subnodes. + * @param pFirst + * @param pSecond + * @param pThird + * @return + */ + void SetSubNodes(SmNode* pFirst, SmNode* pSecond, SmNode* pThird); + + /** + * Sets subnodes, used for operators. + * The data is reordered so the items are correctly ordered. + * @param pFirst + * @param pSecond + * @param pThird + * @return + */ + void SetSubNodesBinMo(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, + std::unique_ptr<SmNode> pThird = nullptr); + + /** + * Sets subnodes, used for operators. + * The data is reordered so the items are correctly ordered. + * @param pFirst + * @param pSecond + * @param pThird + * @return + */ + void SetSubNodesBinMo(SmNode* pFirst, SmNode* pSecond, SmNode* pThird); + + /** + * Sets subnodes. + * @param rNodeArray + * @return + */ + void SetSubNodes(SmNodeArray&& rNodeArray); + + /** + * Appends to rText the node text. + * @param rText + * @return + */ + virtual void GetAccessibleText( OUStringBuffer &rText ) const override; + + /** + * Gets the first subnode. + * @return first subnode + */ + SmNodeArray::iterator begin() {return maSubNodes.begin();} + + /** + * Gets the last subnode. + * @return last subnode + */ + SmNodeArray::iterator end() {return maSubNodes.end();} + + /** + * Gets the last subnode. + * @return last subnode + */ + SmNodeArray::reverse_iterator rbegin() {return maSubNodes.rbegin();} + + /** + * Gets the first subnode. + * @return first subnode + */ + SmNodeArray::reverse_iterator rend() {return maSubNodes.rend();} + + /** + * Get the index of the child node pSubNode. + * Returns -1, if pSubNode isn't a subnode of this. + * @param pSubNode + * @return index of the child node + */ + int IndexOfSubNode(SmNode const * pSubNode); + + /** + * Sets the subnode pNode at nIndex. + * If necessary increases the subnodes length. + * @param nIndex + * @param pNode + * @return + */ + void SetSubNode(size_t nIndex, SmNode* pNode); + +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: + + /** + * Checks node visibility. + * Returns true if this is an instance of SmVisibleNode's subclass, false otherwise. + * @return node visibility + */ + virtual bool IsVisible() const override; + + /** + * Gets the number of subnodes. + * @return number of subnodes + */ + virtual size_t GetNumSubNodes() const override; + + /** + * Gets the subnode of index nIndex. + * @param nIndex + * @return subnode of index nIndex + */ + 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: + + /** + * Appends to rText the node text. + * @param rText + * @return + */ + 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) + {} + + //visual stuff TODO comment + virtual void AdaptToX(OutputDevice &rDev, sal_uLong nWidth) override; + virtual void AdaptToY(OutputDevice &rDev, sal_uLong nHeight) override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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; + tools::Long mnWidth; + +public: + explicit SmPolyLineNode(const SmToken &rNodeToken); + + /** + * Gets the width of the rect. + * @return width + */ + tools::Long GetWidth() const { return mnWidth; } + + /** + * Gets the polygon to draw the node. + * @return polygon + */ + tools::Polygon &GetPolygon() { return maPoly; } + + //visual stuff TODO comment + virtual void AdaptToX(OutputDevice &rDev, sal_uLong nWidth) override; + virtual void AdaptToY(OutputDevice &rDev, sal_uLong nHeight) override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + void Accept(SmVisitor* pVisitor) override; +}; + + +/** Text node + * + * @remarks This class also serves as baseclass for all nodes that contains text. + */ +class SmTextNode : public SmVisibleNode +{ + +protected: + 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 ); + + /** + * Returns the font type being used (text, variable, symbol, ...). + * @return font type + */ + sal_uInt16 GetFontDesc() const { return mnFontDesc; } + + /** + * Sets the node text to rText. + * @param rText + * @return + */ + void SetText(const OUString &rText) { maText = rText; } + + /** + * Gets the node text. + * @return node text + */ + const OUString & GetText() const { return maText; } + OUString & GetText() { return maText; } + + /** + * Change the text of this node, including the underlying token to rText. + * @param rText + * @return + */ + void ChangeText(const OUString &rText); + + /** + * Try to guess the correct FontDesc, used during visual editing + * @return + */ + void AdjustFontDesc(); + + /** + * Index within GetText() where the selection starts. + * @remarks Only valid of SmNode::IsSelected() is true. + * @return index. + */ + sal_Int32 GetSelectionStart() const { return mnSelectionStart; } + + /** + * Index within GetText() where the selection ends. + * @remarks Only valid of SmNode::IsSelected() is true. + * @return index. + */ + sal_Int32 GetSelectionEnd() const {return mnSelectionEnd; } + + /** + * Sets the index within GetText() where the selection starts to index. + * @param index + * @return + */ + void SetSelectionStart(sal_Int32 index) {mnSelectionStart = index;} + + /** + * Sets the index within GetText() where the selection ends to index. + * @param index + * @return + */ + void SetSelectionEnd(sal_Int32 index) {mnSelectionEnd = index;} + + /** + * Prepare preliminary settings about font and text + * (e.g. maFace, meRectHorAlign, mnFlags, mnAttributes, etc.) + * @param rFormat + * @param rDocShell + * @param nDepth + * @return + */ + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, + int nDepth) override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Appends to rText the node text. + * @param rText + * @return + */ + virtual void GetAccessibleText( OUStringBuffer &rText ) const override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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. + * @param nIn + * @return unicode char + */ + 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); + + /** + * Prepare preliminary settings about font and text + * (e.g. maFace, meRectHorAlign, mnFlags, mnAttributes, etc.) + * @param rFormat + * @param rDocShell + * @param nDepth + * @return + */ + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, + int nDepth) override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) + {} + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) + { + SetText(GetToken().cMathChar); + } + +public: + explicit SmMathSymbolNode(const SmToken &rNodeToken); + + //visual stuff TODO comment + virtual void AdaptToX(OutputDevice &rDev, sal_uLong nWidth) override; + virtual void AdaptToY(OutputDevice &rDev, sal_uLong nHeight) override; + + /** + * Prepare preliminary settings about font and text + * (e.g. maFace, meRectHorAlign, mnFlags, mnAttributes, etc.) + * @param rFormat + * @param rDocShell + * @param nDepth + * @return + */ + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, + int nDepth) override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) { } + + /** + * Gets the body width. + * Allows to know how long is the root and paint it. + * @return body width + */ + sal_uLong GetBodyWidth() const {return mnBodyWidth;}; + + //visual stuff TODO comment + virtual void AdaptToX(OutputDevice &rDev, sal_uLong nHeight) override; + virtual void AdaptToY(OutputDevice &rDev, sal_uLong nHeight) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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, "<?>")) { }; + + /** + * Prepare preliminary settings about font and text + * (e.g. maFace, meRectHorAlign, mnFlags, mnAttributes, etc.) + * @param rFormat + * @param rDocShell + * @param nDepth + * @return + */ + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, + int nDepth) override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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)); } + + /** + * Prepare preliminary settings about font and text + * (e.g. maFace, meRectHorAlign, mnFlags, mnAttributes, etc.) + * @param rFormat + * @param rDocShell + * @param nDepth + * @return + */ + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, + int nDepth) override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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 +{ + tools::Long mnFormulaBaseline; +public: + explicit SmTableNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Table, rNodeToken) + , mnFormulaBaseline(0) { } + + virtual const SmNode * GetLeftMost() const override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Gets the formula baseline. + * @return formula baseline + */ + tools::Long GetFormulaBaseline() const; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) { } + + /** + * Sets if it going to use extra spaces. + * It is used to set if there has to be space between node while rendering. + * By default it is true. + * @param bVal + * @return + */ + void SetUseExtraSpaces(bool bVal) { mbUseExtraSpaces = bVal; } + + /** + * Checks if it is using extra spaces. + * It is used for calculating space between nodes when rendering. + * By default it is true. + * @return is using extra spaces + */ + bool IsUseExtraSpaces() const { return mbUseExtraSpaces; }; + + /** + * Prepare preliminary settings about font and text + * (e.g. maFace, meRectHorAlign, mnFlags, mnAttributes, etc.) + * @param rFormat + * @param rDocShell + * @param nDepth + * @return + */ + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, + int nDepth) override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) { } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) { } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) { } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + void Accept(SmVisitor* pVisitor) override; + + /** + * Returns the node containing the data of the order of the root. + * @return order data + */ + const SmNode* Argument() const { return const_cast<SmRootNode *>(this)->Argument(); } + SmNode* Argument() { assert( GetNumSubNodes() == 3 ); return GetSubNode( 0 ); } + + /** + * Returns the node containing the data of the character used for the root. + * @return symbol data + */ + const SmRootSymbolNode* Symbol() const { return const_cast<SmRootNode *>(this)->Symbol(); } + SmRootSymbolNode* Symbol() { assert( GetNumSubNodes() == 3 ); + assert( GetSubNode( 1 )->GetType() + == SmNodeType::RootSymbol ); + return static_cast< SmRootSymbolNode* > + ( GetSubNode( 1 )); } + + /** + * Returns the node containing the data inside the root. + * @return body data + */ + const SmNode* Body() const { return const_cast<SmRootNode *>(this)->Body(); } + SmNode* Body() { assert( GetNumSubNodes() == 3 ); return GetSubNode( 2 ); } + +}; + + +/** 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) { } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + void Accept(SmVisitor* pVisitor) override; + + /** + * Returns the node containing the data of the binary operator. + * @return symbol data + */ + const SmNode* Symbol() const { return const_cast<SmBinHorNode *>(this)->Symbol(); } + SmNode* Symbol() { assert( GetNumSubNodes() == 3 ); return GetSubNode( 1 ); } + + /** + * Returns the node containing the data of the left operand. + * @return left operand data + */ + const SmNode* LeftOperand() const { return const_cast<SmBinHorNode *>(this)->LeftOperand(); } + SmNode* LeftOperand() { assert( GetNumSubNodes() == 3 ); return GetSubNode( 0 ); } + + /** + * Returns the node containing the data of the right operand. + * @return right operand data + */ + const SmNode* RightOperand() const { return const_cast<SmBinHorNode *>(this)->RightOperand(); } + SmNode* RightOperand() { assert( GetNumSubNodes() == 3 ); return GetSubNode( 2 ); } +}; + + +/** 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; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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; + + /** + * Returns the position and size of the diagonal line by reference. + * @param rPos + * @param rSize + * @param rDiagPoint + * @param fAngleDeg + * @return position and size of the diagonal line + */ + 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) { } + + /** + * Checks if it is of ascending type. + * Ascending: + * / b + * / + * a / + * Descending: + * a \ + * \ + * \ b + * @return ascending. + */ + bool IsAscending() const { return mbAscending; } + + /** + * Sets if the wideslash is ascending to bVal. + * @param bVal + * @return + */ + void SetAscending(bool bVal) { mbAscending = bVal; } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) { } + + /** + * Returns the node with the data of what has to be superindex or subindex. + * @return body data + */ + const SmNode * GetBody() const { return const_cast<SmSubSupNode *>(this)->GetBody(); } + SmNode * GetBody() { return GetSubNode(0); } + + /** + * Checks if it is going to be used for a limit. + * Example lim from { x toward 0 } { {sin x}over x } = 1 + * @return is a limit + */ + bool IsUseLimits() const { return mbUseLimits; }; + + /** + * Sets if it is going to be used for a limit to bVal. + * @param bVal + * @return + */ + void SetUseLimits(bool bVal) { mbUseLimits = bVal; } + + /** + * Gets the node with the data of what has to be superindex or subindex. + * The position to check is given by eSubSup. + * @remarks this method may return NULL. + * @param eSubSup + * @return body data + */ + const SmNode * GetSubSup(SmSubSup eSubSup) const { return const_cast< SmSubSupNode* > + ( this )->GetSubSup( eSubSup ); } + SmNode * GetSubSup(SmSubSup eSubSup) { return GetSubNode(1 + eSubSup); }; + + /** + * Sets the node with the data of what has to be superindex or subindex. + * @param pScript + */ + void SetBody(SmNode* pBody) { SetSubNode(0, pBody); } + + /** + * Sets the node with the data of what has to be superindex or subindex. + * The position to check is given by eSubSup. + * @param eSubSup + * @param pScript + */ + void SetSubSup(SmSubSup eSubSup, SmNode* pScript) { SetSubNode( 1 + eSubSup, pScript); } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) { } + + /** + * Returns the node containing the data of the opening brace. + * @return opening brace data + */ + const SmMathSymbolNode* OpeningBrace() const { return const_cast<SmBraceNode *> + (this)->OpeningBrace(); } + SmMathSymbolNode* OpeningBrace() { assert( GetNumSubNodes() == 3 ); + assert( GetSubNode( 0 )->GetType() + == SmNodeType::Math ); + return static_cast< SmMathSymbolNode* > + ( GetSubNode( 0 )); } + + /** + * Returns the node containing the data of what is between braces. + * @return body data + */ + const SmNode* Body() const { return const_cast<SmBraceNode *>(this)->Body(); } + SmNode* Body() { assert( GetNumSubNodes() == 3 ); return GetSubNode( 1 ); } + + /** + * Returns the node containing the data of the closing brace. + * @return closing brace data + */ + const SmMathSymbolNode* ClosingBrace() const { return const_cast<SmBraceNode *> + (this)->ClosingBrace(); } + SmMathSymbolNode* ClosingBrace() { assert( GetNumSubNodes() == 3 ); + assert( GetSubNode( 2 )->GetType() + == SmNodeType::Math ); + return static_cast< SmMathSymbolNode* > + ( GetSubNode( 2 )); } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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 +{ + tools::Long mnBodyHeight; + +public: + explicit SmBracebodyNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Bracebody, rNodeToken) + , mnBodyHeight(0) { } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + tools::Long GetBodyHeight() const { return mnBodyHeight; } + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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 SmVerticalBraceNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::VerticalBrace, rNodeToken, 3) { } + + /** + * Returns the node containing the data of what the brace is pointing for. + * body { script } + * @return body data + */ + const SmNode* Body() const { return const_cast<SmVerticalBraceNode *>(this)->Body(); } + SmNode* Body() { assert( GetNumSubNodes() == 3 ); return GetSubNode( 0 ); } + + /** + * Returns the node containing the data of the brace. + * @return brace data + */ + const SmMathSymbolNode* Brace() const { return const_cast<SmVerticalBraceNode *> + (this)->Brace(); } + SmMathSymbolNode* Brace() { assert( GetNumSubNodes() == 3 ); + assert( GetSubNode( 1 )->GetType() + == SmNodeType::Math ); + return static_cast< SmMathSymbolNode* > + ( GetSubNode( 1 )); } + + /** + * Returns the node containing the data of what is in the brace. + * body { script } + * @return opening brace data + */ + const SmNode* Script() const { return const_cast<SmVerticalBraceNode *>(this)->Script(); } + SmNode* Script() { assert( GetNumSubNodes() == 3 ); return GetSubNode( 2 ); } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + void Accept(SmVisitor* pVisitor) override; +}; + +/** 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) { } + + /** + * Returns the node with the operator data + * @return operator data + */ + const SmNode * GetSymbol() const { return const_cast<SmOperNode *>(this)->GetSymbol(); } + SmNode * GetSymbol(); + + /** + * Returns the height of the node in base to the symbol + * ( rSymbol contains the operator data ) + * and the font format ( rFormat ). + * @param rSymbol + * @param rFormat + * @return node's height + */ + tools::Long CalcSymbolHeight(const SmNode &rSymbol, const SmFormat &rFormat) const; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) { } + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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 SmAttributeNode final : public SmStructureNode +{ +public: + explicit SmAttributeNode(const SmToken &rNodeToken) + : SmStructureNode(SmNodeType::Attribute, rNodeToken, 2) {} + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + void Accept(SmVisitor* pVisitor) override; + + /** + * Gets the attribute data. + * @return attribute data + */ + const SmNode* Attribute() const { return const_cast<SmAttributeNode *>(this)->Attribute(); } + SmNode* Attribute() { assert( GetNumSubNodes() == 2 ); return GetSubNode( 0 ); } + + /** + * Gets the body data ( the nodes affected by the attribute ). + * @return body data + */ + const SmNode* Body() const { return const_cast<SmAttributeNode *>(this)->Body(); } + SmNode* Body() { assert( GetNumSubNodes() == 2 ); return GetSubNode( 1 ); } +}; + + +/** 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) { } + + /** + * Sets font size to rValue in nType mode. + * Check FontSizeType for details. + * @param rValue + * @param nType + * @return + */ + void SetSizeParameter(const Fraction &rValue, FontSizeType nType) + { meSizeType = nType; maFontSize = rValue; } + + /** + * Returns the font size. + * @return font size. + */ + const Fraction & GetSizeParameter() const {return maFontSize;} + + /** + * Returns the font size type. + * Check FontSizeType for details. + * @return font size type. + */ + FontSizeType GetSizeType() const {return meSizeType;} + + /** + * Prepare preliminary settings about font and text + * (e.g. maFace, meRectHorAlign, mnFlags, mnAttributes, etc.) + * @param rFormat + * @param rDocShell + * @param nDepth + * @return + */ + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, + int nDepth) override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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) { } + + /** + * Gets the number of rows of the matrix. + * @return rows number + */ + sal_uInt16 GetNumRows() const {return mnNumRows;} + + /** + * Gets the number of columns of the matrix. + * @return columns number + */ + sal_uInt16 GetNumCols() const {return mnNumCols;} + + /** + * Sets the dimensions of the matrix. + * @param nMatrixRows + * @param nMatrixCols + * @return + */ + void SetRowCol(sal_uInt16 nMatrixRows, sal_uInt16 nMatrixCols) + { mnNumRows = nMatrixRows; mnNumCols = nMatrixCols; } + + virtual const SmNode * GetLeftMost() const override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + 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; } + + /** + * Prepare preliminary settings about font and text + * (e.g. maFace, meRectHorAlign, mnFlags, mnAttributes, etc.) + * @param rFormat + * @param rDocShell + * @param nDepth + * @return + */ + virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, + int nDepth) override; + + /** + * Prepares the SmRect to render. + * @param rDev + * @param rFormat + * @return + */ + virtual void Arrange(OutputDevice &rDev, const SmFormat &rFormat) override; + + /** + * Accept a visitor. + * Calls the method for this class on the visitor. + * @param pVisitor + * @return + */ + void Accept(SmVisitor* pVisitor) override; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/nodetype.hxx b/starmath/inc/nodetype.hxx new file mode 100644 index 000000000..f0bf28cec --- /dev/null +++ b/starmath/inc/nodetype.hxx @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +/** + * This file provides definition for the nodetypes. + * Also provides analysis of the node functions + */ + +enum class SmNodeType : int_fast16_t +{ + Table, // tree header + Brace, // () [] {} + Bracebody, // content of () [] {} + Oper, // largeop + Align, // alignment + Attribute, // attributes + Font, // fonts + UnHor, // unmo + BinHor, // binmo + BinVer, // over frac + BinDiagonal, // slash + SubSup, // lsub lsup rsub rsup csub csup + Matrix, // binom matrix + Place, // <?> + Text, // "text" + Special, // %glyph + GlyphSpecial, // %glyph + Math, // operator value + Blank, // ~ + Error, // Syntax error + Line, // a line of math until newline + Expression, // { content in here } + PolyLine, // ^ + Root, // root node + RootSymbol, // 3 of cubic root + Rectangle, //just structural + VerticalBrace, // vertical {} + MathIdent // identities and variables +}; + +namespace starmathdatabase +{ +inline bool isStructuralNode(SmNodeType aNodeTypeName) +{ + return aNodeTypeName == SmNodeType::Table || aNodeTypeName == SmNodeType::Line + || aNodeTypeName == SmNodeType::UnHor || aNodeTypeName == SmNodeType::BinHor + || aNodeTypeName == SmNodeType::BinVer || aNodeTypeName == SmNodeType::BinDiagonal + || aNodeTypeName == SmNodeType::SubSup || aNodeTypeName == SmNodeType::Matrix + || aNodeTypeName == SmNodeType::Root || aNodeTypeName == SmNodeType::Expression + || aNodeTypeName == SmNodeType::Brace || aNodeTypeName == SmNodeType::Bracebody + || aNodeTypeName == SmNodeType::Oper || aNodeTypeName == SmNodeType::Align + || aNodeTypeName == SmNodeType::Attribute || aNodeTypeName == SmNodeType::Font; +} + +inline bool isBinOperatorNode(SmNodeType aNodeTypeName) +{ + return aNodeTypeName == SmNodeType::BinHor || aNodeTypeName == SmNodeType::BinVer + || aNodeTypeName == SmNodeType::BinDiagonal || aNodeTypeName == SmNodeType::SubSup; +} + +inline bool isUnOperatorNode(SmNodeType aNodeTypeName) +{ + return aNodeTypeName == SmNodeType::UnHor; +} + +inline bool isOperatorNode(SmNodeType aNodeTypeName) +{ + return aNodeTypeName == SmNodeType::BinHor || aNodeTypeName == SmNodeType::BinVer + || aNodeTypeName == SmNodeType::BinDiagonal || aNodeTypeName == SmNodeType::SubSup + || aNodeTypeName == SmNodeType::UnHor || aNodeTypeName == SmNodeType::Oper; +} + +inline bool isStandaloneNode(SmNodeType aNodeTypeName) +{ + return aNodeTypeName == SmNodeType::Text || aNodeTypeName == SmNodeType::Special + || aNodeTypeName == SmNodeType::GlyphSpecial || aNodeTypeName == SmNodeType::Math + || aNodeTypeName == SmNodeType::Blank || aNodeTypeName == SmNodeType::MathIdent; +} +} + +/* 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..a62d515b5 --- /dev/null +++ b/starmath/inc/parse.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 . + */ + +/** Parses the starmath code and creates the nodes. + * + */ + +#pragma once + +#include "parsebase.hxx" + +namespace starmathdatabase +{ + +AbstractSmParser* GetDefaultSmParser(); + +AbstractSmParser* GetVersionSmParser(sal_uInt16 nVersion); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/parse5.hxx b/starmath/inc/parse5.hxx new file mode 100644 index 000000000..5b7237867 --- /dev/null +++ b/starmath/inc/parse5.hxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +/** Parses the starmath code and creates the nodes. + * + */ + +#pragma once + +#include "parsebase.hxx" +#include <unotools/charclass.hxx> + +class SmParser5 final : public AbstractSmParser +{ + OUString m_aBufferString; + SmToken m_aCurToken; + ESelection m_aCurESelection; + std::vector<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; + + // 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; + + SmParser5(const SmParser5&) = delete; + SmParser5& operator=(const SmParser5&) = delete; + + // Moves between tokens inside starmath code. + void NextToken(); + void NextTokenColor(SmTokenType dvipload); + void NextTokenFontSize(); + 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, std::unique_ptr<SmNode> xGivenNode); + std::unique_ptr<SmNode> DoSubSupEvaluate(std::unique_ptr<SmNode> xGivenNode); + 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> DoFontAttribute(); + std::unique_ptr<SmStructureNode> DoAttribute(); + 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<SmNode> DoEvaluate(); + std::unique_ptr<SmTextNode> DoFunction(); + std::unique_ptr<SmTableNode> DoBinom(); + std::unique_ptr<SmBinVerNode> DoFrac(); + 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: + SmParser5(); + virtual ~SmParser5(); + + /** 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; } + + const SmErrorDesc* NextError(); + const SmErrorDesc* PrevError(); + const SmErrorDesc* GetError() const; + const std::set<OUString>& GetUsedSymbols() const { return m_aUsedSymbols; } +}; + +inline bool SmParser5::TokenInGroup(TG nGroup) { return bool(m_aCurToken.nGroup & nGroup); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/parsebase.hxx b/starmath/inc/parsebase.hxx new file mode 100644 index 000000000..ffa9b538a --- /dev/null +++ b/starmath/inc/parsebase.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 . + */ + +/** Parses the starmath code and creates the nodes. + * + */ + +#pragma once + +#include <unotools/resmgr.hxx> + +#include "node.hxx" + +#include <set> + +#define DEPTH_LIMIT 1024 + +// Those are the errors that the parser may encounter. +enum class SmParseError : uint_fast8_t +{ + None = 0, + UnexpectedChar = 1, + UnexpectedToken = 2, + PoundExpected = 3, + ColorExpected = 4, + LgroupExpected = 5, + RgroupExpected = 6, + LbraceExpected = 7, + RbraceExpected = 8, + ParentMismatch = 9, + RightExpected = 10, + FontExpected = 11, + SizeExpected = 12, + DoubleAlign = 13, + DoubleSubsupscript = 14, + NumberExpected = 15 +}; + +struct SmErrorDesc +{ + SmParseError m_eType; + SmNode* m_pNode; + OUString m_aText; + + SmErrorDesc(SmParseError eType, SmNode* pNode, OUString aText) + : m_eType(eType) + , m_pNode(pNode) + , m_aText(aText) + { + } +}; + +class DepthProtect +{ +private: + sal_Int32& m_rParseDepth; + +public: + DepthProtect(sal_Int32& rParseDepth) + : m_rParseDepth(rParseDepth) + { + ++m_rParseDepth; + if (m_rParseDepth > DEPTH_LIMIT) + throw std::range_error("parser depth limit"); + } + ~DepthProtect() { --m_rParseDepth; } +}; + +namespace starmathdatabase +{ +// Must be in sync with SmParseError list +extern const TranslateId SmParseErrorDesc[16]; + +OUString getParseErrorDesc(SmParseError err); +} + +class AbstractSmParser +{ +public: + virtual ~AbstractSmParser() {} + + /** Parse rBuffer to formula tree */ + virtual std::unique_ptr<SmTableNode> Parse(const OUString& rBuffer) = 0; + /** Parse rBuffer to formula subtree that constitutes an expression */ + virtual std::unique_ptr<SmNode> ParseExpression(const OUString& rBuffer) = 0; + + virtual const OUString& GetText() const = 0; + + virtual bool IsImportSymbolNames() const = 0; + virtual void SetImportSymbolNames(bool bVal) = 0; + virtual bool IsExportSymbolNames() const = 0; + virtual void SetExportSymbolNames(bool bVal) = 0; + + virtual const SmErrorDesc* NextError() = 0; + virtual const SmErrorDesc* PrevError() = 0; + virtual const SmErrorDesc* GetError() const = 0; + virtual const std::set<OUString>& GetUsedSymbols() const = 0; +}; + +/* 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..2c8ce3f5b --- /dev/null +++ b/starmath/inc/pch/precompiled_sm.hxx @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 2021-09-12 11:52:19 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 +*/ + +#include <sal/config.h> +#if PCH_LEVEL >= 1 +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdlib> +#include <cstring> +#include <functional> +#include <limits.h> +#include <limits> +#include <memory> +#include <new> +#include <optional> +#include <ostream> +#include <string> +#include <string_view> +#include <type_traits> +#include <unordered_map> +#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/mutex.hxx> +#include <rtl/alloc.h> +#include <rtl/character.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 <sal/log.hxx> +#include <sal/types.h> +#include <vcl/bitmap.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/cairo.hxx> +#include <vcl/devicecoordinate.hxx> +#include <vcl/dllapi.h> +#include <vcl/errcode.hxx> +#include <vcl/font.hxx> +#include <vcl/gradient.hxx> +#include <vcl/mapmod.hxx> +#include <vcl/metaactiontypes.hxx> +#include <vcl/outdev.hxx> +#include <vcl/region.hxx> +#include <vcl/rendercontext/AddFontSubstituteFlags.hxx> +#include <vcl/rendercontext/AntialiasingFlags.hxx> +#include <vcl/rendercontext/DrawGridFlags.hxx> +#include <vcl/rendercontext/DrawImageFlags.hxx> +#include <vcl/rendercontext/DrawModeFlags.hxx> +#include <vcl/rendercontext/DrawTextFlags.hxx> +#include <vcl/rendercontext/GetDefaultFontFlags.hxx> +#include <vcl/rendercontext/ImplMapRes.hxx> +#include <vcl/rendercontext/InvertFlags.hxx> +#include <vcl/rendercontext/RasterOp.hxx> +#include <vcl/rendercontext/SalLayoutFlags.hxx> +#include <vcl/rendercontext/State.hxx> +#include <vcl/rendercontext/SystemTextColorFlags.hxx> +#include <vcl/salnativewidgets.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/vclenum.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/numeric/ftools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/vector/b2enums.hxx> +#include <com/sun/star/awt/DeviceInfo.hpp> +#include <com/sun/star/drawing/LineCap.hpp> +#include <com/sun/star/i18n/WordType.hpp> +#include <com/sun/star/text/textfield/Type.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 <comphelper/fileformat.h> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weakref.hxx> +#include <editeng/editdata.hxx> +#include <editeng/editengdllapi.h> +#include <editeng/editobj.hxx> +#include <editeng/editstat.hxx> +#include <editeng/eedata.hxx> +#include <editeng/macros.hxx> +#include <i18nlangtag/lang.h> +#include <o3tl/cow_wrapper.hxx> +#include <o3tl/strong_int.hxx> +#include <o3tl/typed_flags_set.hxx> +#include <o3tl/unit_conversion.hxx> +#include <salhelper/simplereferenceobject.hxx> +#include <sfx2/dllapi.h> +#include <sfx2/docfile.hxx> +#include <sfx2/shell.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/style.hxx> +#include <svl/svldllapi.h> +#include <svl/typedwhich.hxx> +#include <svx/svxdllapi.h> +#include <tools/color.hxx> +#include <tools/degree.hxx> +#include <tools/diagnose_ex.h> +#include <tools/gen.hxx> +#include <tools/lineend.hxx> +#include <tools/link.hxx> +#include <tools/long.hxx> +#include <tools/mapunit.hxx> +#include <tools/poly.hxx> +#include <tools/ref.hxx> +#include <tools/solar.h> +#include <tools/toolsdllapi.h> +#include <unotools/fontdefs.hxx> +#include <unotools/options.hxx> +#include <unotools/streamwrap.hxx> +#include <unotools/unotoolsdllapi.h> +#include <xmloff/dllapi.h> +#endif // PCH_LEVEL >= 3 +#if PCH_LEVEL >= 4 +#include <ElementsDockingWindow.hxx> +#include <cfgitem.hxx> +#include <dialog.hxx> +#include <document.hxx> +#include <node.hxx> +#include <smmod.hxx> +#include <starmathdatabase.hxx> +#include <symbol.hxx> +#include <unomodel.hxx> +#include <utility.hxx> +#include <view.hxx> +#include <visitors.hxx> +#include <xparsmlbase.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..0b28581a0 --- /dev/null +++ b/starmath/inc/rect.hxx @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <tools/gen.hxx> +#include <vcl/outdev.hxx> + +class SmFormat; + + +inline tools::Long SmFromTo(tools::Long nFrom, tools::Long nTo, double fRelDist) +{ + return nFrom + static_cast<tools::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; + tools::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(tools::Long nWidth, tools::Long nHeight); + + + sal_uInt16 GetBorderWidth() const { return nBorderWidth; } + + void SetItalicSpaces(tools::Long nLeftSpace, tools::Long nRightSpace); + + void SetWidth(sal_uLong nWidth) { aSize.setWidth(nWidth); } + + void SetLeft(tools::Long nLeft); + void SetRight(tools::Long nRight); + void SetBottom(tools::Long nBottom); + void SetTop(tools::Long nTop); + + const Point & GetTopLeft() const { return aTopLeft; } + + tools::Long GetTop() const { return GetTopLeft().Y(); } + tools::Long GetLeft() const { return GetTopLeft().X(); } + tools::Long GetBottom() const { return GetTop() + GetHeight() - 1; } + tools::Long GetRight() const { return GetLeft() + GetWidth() - 1; } + tools::Long GetCenterY() const { return (GetTop() + GetBottom()) / 2; } + tools::Long GetWidth() const { return GetSize().Width(); } + tools::Long GetHeight() const { return GetSize().Height(); } + + tools::Long GetItalicLeftSpace() const { return nItalicLeftSpace; } + tools::Long GetItalicRightSpace() const { return nItalicRightSpace; } + + tools::Long GetHiAttrFence() const { return nHiAttrFence; } + tools::Long GetLoAttrFence() const { return nLoAttrFence; } + + tools::Long GetItalicLeft() const { return GetLeft() - GetItalicLeftSpace(); } + tools::Long GetItalicCenterX() const { return (GetItalicLeft() + GetItalicRight()) / 2; } + tools::Long GetItalicRight() const { return GetRight() + GetItalicRightSpace(); } + tools::Long GetItalicWidth() const { return GetWidth() + GetItalicLeftSpace() + GetItalicRightSpace(); } + + bool HasBaseline() const { return bHasBaseline; } + inline tools::Long GetBaseline() const; + tools::Long GetBaselineOffset() const { return GetBaseline() - GetTop(); } + + tools::Long GetAlignT() const { return nAlignT; } + tools::Long GetAlignM() const { return nAlignM; } + tools::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, + tools::Long nNewAlignM); + SmRect & ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode, + bool bKeepVerAlignParams); + + tools::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(tools::Long nLeftSpace, tools::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 tools::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()); +} + +/* 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..1a1e175c8 --- /dev/null +++ b/starmath/inc/smdll.hxx @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "smdllapi.hxx" + +namespace SmGlobals +{ +SM_DLLPUBLIC void ensure(); +} + +/* 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..635493619 --- /dev/null +++ b/starmath/inc/smdllapi.hxx @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <sal/types.h> + +#if defined(SM_DLLIMPLEMENTATION) +#define SM_DLLPUBLIC SAL_DLLPUBLIC_EXPORT +#else +#define SM_DLLPUBLIC SAL_DLLPUBLIC_IMPORT +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/inc/smediteng.hxx b/starmath/inc/smediteng.hxx new file mode 100644 index 000000000..6c95b5804 --- /dev/null +++ b/starmath/inc/smediteng.hxx @@ -0,0 +1,59 @@ +/* This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <editeng/editeng.hxx> +#include <unotools/lingucfg.hxx> + +class SmEditEngine final : public EditEngine +{ +public: + SmEditEngine(SfxItemPool* pItemPool); + SmEditEngine(const SmEditEngine&) = delete; + +public: + /** + * Runs checkZoom and if true runs updateZoom + */ + void executeZoom(EditView* pEditView = nullptr); + + /** + * Sets up default font parameters for the item pool. + */ + static void setSmItemPool(SfxItemPool* mpItemPool, const SvtLinguOptions& maLangOptions); + + // Deal with text scaling +private: + sal_Int32 m_nOldZoom; + sal_Int32 m_nNewZoom; + sal_Int32 m_nDefaultFontSize; + + /** + * Checks if the zoom of smeditwindow has changed. + * m_nNewZoom is updated. + * @return zoom has changed + */ + bool checkZoom(); + + /** + * Updates the zoom of smeditwindow. + * m_nOldZoom is set to m_nNewZoom. + */ + + void updateZoom(); + + // Gather information for more complex tasks +private: + ESelection m_aAllSelection; + + /** + * Finds the ESelection which contains all the text. + */ + void updateAllESelection(); +}; +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/inc/smmod.hrc b/starmath/inc/smmod.hrc new file mode 100644 index 000000000..885af2147 --- /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/. + */ + +#pragma once + +#include <unotools/resmgr.hxx> + +#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String)) + +const TranslateId RID_UI_SYMBOLSET_NAMES[] = +{ + NC_("RID_UI_SYMBOLSET_NAMES", "Greek"), + NC_("RID_UI_SYMBOLSET_NAMES", "Special") +}; + +const TranslateId 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") +}; + +/* 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..f75643d0f --- /dev/null +++ b/starmath/inc/smmod.hxx @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sfx2/module.hxx> +#include <sfx2/app.hxx> +#include <vcl/vclptr.hxx> +#include <unotools/options.hxx> + +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(TranslateId aId); + +class SmLocalizedSymbolData +{ +public: + SmLocalizedSymbolData() = delete; + + static OUString GetUiSymbolName( std::u16string_view rExportName ); + static OUString GetExportSymbolName( std::u16string_view rUiName ); + + static OUString GetUiSymbolSetName( std::u16string_view rExportName ); + static OUString GetExportSymbolSetName( std::u16string_view 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::optional<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)) ) + +/* 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..83c17c94d --- /dev/null +++ b/starmath/inc/starmath.hrc @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <svl/solar.hrc> + +class SfxBoolItem; +class SfxUInt16Item; + +#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_GRAPHIC_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 TypedWhichId<SfxBoolItem>(SID_SMA_START + 112) +#define SID_PRINTTEXT TypedWhichId<SfxBoolItem>(SID_SMA_START + 113) +#define SID_PRINTFRAME TypedWhichId<SfxBoolItem>(SID_SMA_START + 114) +#define SID_PRINTSIZE TypedWhichId<SfxUInt16Item>(SID_SMA_START + 115) +#define SID_PRINTZOOM TypedWhichId<SfxUInt16Item>(SID_SMA_START + 116) + +#define SID_COPYOBJECT (SID_SMA_START + 117) +#define SID_PASTEOBJECT (SID_SMA_START + 118) +#define SID_AUTOREDRAW TypedWhichId<SfxBoolItem>(SID_SMA_START + 119) + +#define SID_GETEDITTEXT (SID_SMA_START + 121) +#define SID_CMDBOXWINDOW (SID_SMA_START + 122) +#define SID_NO_RIGHT_SPACES TypedWhichId<SfxBoolItem>(SID_SMA_START + 124) +#define SID_SAVE_ONLY_USED_SYMBOLS TypedWhichId<SfxBoolItem>(SID_SMA_START + 125) +#define SID_ELEMENTSDOCKINGWINDOW (SID_SMA_START + 126) +#define SID_AUTO_CLOSE_BRACKETS TypedWhichId<SfxBoolItem>(SID_SMA_START + 127) +#define SID_SMEDITWINDOWZOOM TypedWhichId<SfxUInt16Item>(SID_SMA_START + 129) +#define SID_DEFAULT_SM_SYNTAX_VERSION TypedWhichId<SfxUInt16Item>(SID_SMA_START + 130) + +/* 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..015c7e7c4 --- /dev/null +++ b/starmath/inc/strings.hrc @@ -0,0 +1,403 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 . + */ + +/** Those are the starmath codes descriptions for ElementsDockingWindow.hxx. + * + * Those codes will be displayed as formulas on the ElementsDockingWindow. + * The user can then graphically insert them. + * When passing the mouse over them, those descriptions will be displayed. + */ + +#pragma once + +#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String)) + +// clang-format off +#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_FRACXY_HELP NC_("RID_FRACXY_HELP", "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_XSETQUOTIENTY_HELP NC_("RID_XSETQUOTIENTY_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_FUNCX_HELP NC_("RID_FUNCX_HELP", "General function" ) +#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_OPERX_HELP NC_("RID_OPERX_HELP", "General operator" ) +#define RID_OPER_FROMX_HELP NC_("RID_OPER_FROMX_HELP", "General operator Subscript Bottom" ) +#define RID_OPER_TOX_HELP NC_("RID_OPER_TOX_HELP", "General operator Superscript Top" ) +#define RID_OPER_FROMTOX_HELP NC_("RID_OPER_FROMTOX_HELP", "General operator Sup/Sub script" ) +#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 does not exist" ) +#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_AQUA_HELP NC_("RID_COLORX_AQUA_HELP", "Color Aqua" ) +#define RID_COLORX_FUCHSIA_HELP NC_("RID_COLORX_FUCHSIA_HELP", "Color Fuchsia" ) +#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_COLORX_RGBA_HELP NC_("RID_COLORX_RGBA_HELP", "Color RGBA" ) +#define RID_COLORX_HEX_HELP NC_("RID_COLORX_HEX_HELP", "Color hexadecimal" ) +#define RID_COLORX_CORAL_HELP NC_("RID_COLORX_CORAL_HELP", "Color Coral" ) +#define RID_COLORX_CRIMSON_HELP NC_("RID_COLORX_CRIMSON_HELP", "Color Crimson" ) +#define RID_COLORX_MIDNIGHT_HELP NC_("RID_COLORX_MIDNIGHT_HELP", "Color Midnight blue" ) +#define RID_COLORX_VIOLET_HELP NC_("RID_COLORX_VIOLET_HELP", "Color Violet" ) +#define RID_COLORX_ORANGE_HELP NC_("RID_COLORX_ORANGE_HELP", "Color Orange" ) +#define RID_COLORX_ORANGERED_HELP NC_("RID_COLORX_ORANGERED_HELP", "Color Orangered" ) +#define RID_COLORX_SEAGREEN_HELP NC_("RID_COLORX_SEAGREEN_HELP", "Color Seagreen" ) +#define RID_COLORX_INDIGO_HELP NC_("RID_COLORX_INDIGO_HELP", "Color Indigo" ) +#define RID_COLORX_HOTPINK_HELP NC_("RID_COLORX_HOTPINK_HELP", "Color Hot pink" ) +#define RID_COLORX_LAVENDER_HELP NC_("RID_COLORX_LAVENDER_HELP", "Color Lavender" ) +#define RID_COLORX_SNOW_HELP NC_("RID_COLORX_SNOW_HELP", "Color Snow" ) +#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_XOVERBRACEY_HELP NC_("RID_XOVERBRACEY_HELP", "Braces Top (Scalable)" ) +#define RID_XUNDERBRACEY_HELP NC_("RID_XUNDERBRACEY_HELP", "Braces Bottom (Scalable)" ) +#define RID_EVALUATEX_HELP NC_("RID_EVALUATEX_HELP", "Evaluate" ) +#define RID_EVALUATE_FROMX_HELP NC_("RID_EVALUATE_FROMX_HELP", "Evaluate Subscript Bottom" ) +#define RID_EVALUATE_TOX_HELP NC_("RID_EVALUATE_TOX_HELP", "Evaluate Superscript Top" ) +#define RID_EVALUATE_FROMTOX_HELP NC_("RID_EVALUATE_FROMTOX_HELP", "Evaluate Sup/Sub script" ) +#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 transform" ) +#define RID_FOURIER_HELP NC_("RID_FOURIER_HELP", "Fourier transform" ) +#define RID_BACKEPSILON_HELP NC_("RID_BACKEPSILON_HELP", "Backwards epsilon" ) +#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", "Does not precede" ) +#define RID_XNOTSUCCEEDSY_HELP NC_("RID_XNOTSUCCEEDSY_HELP", "Does not succeed" ) +#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_TAYLOR_SERIES_HELP NC_("RID_EXAMPLE_A_TAYLOR_SERIES_HELP", "Taylor series" ) +#define RID_EXAMPLE_GAUSS_DISTRIBUTION_HELP NC_("RID_EXAMPLE_GAUSS_DISTRIBUTION_HELP", "Gauss distribution" ) +#define RID_EXAMPLE_EULER_LAGRANGE_HELP NC_("RID_EXAMPLE_EULER_LAGRANGE_HELP", "Euler-Lagrange equation" ) +#define RID_EXAMPLE_FTC_HELP NC_("RID_EXAMPLE_FTC_HELP", "Fundamental theorem of calculus" ) +#define RID_EXAMPLE_CHAOS_HELP NC_("RID_EXAMPLE_CHAOS_HELP", "Chaos equation" ) +#define RID_EXAMPLE_EULER_IDENTITY_HELP NC_("RID_EXAMPLE_EULER_IDENTITY_HELP", "Euler's identity" ) +#define RID_EXAMPLE_2NEWTON NC_("RID_EXAMPLE_2NEWTON", "Newton's second law" ) +#define RID_EXAMPLE_GENERAL_RELATIVITY_HELP NC_("RID_EXAMPLE_GENERAL_RELATIVITY_HELP", "General relativity") +#define RID_EXAMPLE_SPECIAL_RELATIVITY_HELP NC_("RID_EXAMPLE_SPECIAL_RELATIVITY_HELP", "Special relativity") + +#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_AQUA NC_("STR_AQUA", "aqua" ) +#define STR_FUCHSIA NC_("STR_FUCHSIA", "fuchsia" ) +#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_CORAL NC_("STR_CORAL", "coral" ) +#define STR_CRIMSON NC_("STR_CRIMSON", "crimson" ) +#define STR_MIDNIGHT NC_("STR_MIDNIGHT", "midnight" ) +#define STR_VIOLET NC_("STR_VIOLET", "violet" ) +#define STR_ORANGE NC_("STR_ORANGE", "orange" ) +#define STR_ORANGERED NC_("STR_ORANGERED", "orangered" ) +#define STR_LAVENDER NC_("STR_LAVENDER", "lavender" ) +#define STR_SNOW NC_("STR_SNOW", "snow" ) +#define STR_SEAGREEN NC_("STR_SEAGREEN", "seagreen" ) +#define STR_INDIGO NC_("STR_INDIGO", "indigo" ) +#define STR_HOTPINK NC_("STR_HOTPINK", "hotpink" ) +#define STR_RGB NC_("STR_RGB", "rgb" ) +#define STR_RGBA NC_("STR_RGBA", "rgba" ) +#define STR_HEX NC_("STR_HEX", "hex" ) +#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_NONE NC_("RID_ERR_NONE", "no 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_NUMBEREXPECTED NC_("RID_ERR_NUMBEREXPECTED", "Expected number" ) +#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" ) +// clang-format on + +/* 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..e22625155 --- /dev/null +++ b/starmath/inc/strings.hxx @@ -0,0 +1,305 @@ +/* -*- 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/. + */ + +/** Those are the starmath codes for ElementsDockingWindow.hxx. + * + * Those codes will be displayed as formulas on the ElementsDockingWindow. + * The user can then graphically insert them. + */ + +#pragma once + +#include <rtl/ustring.hxx> + +inline constexpr OUStringLiteral RID_UNDOFORMATNAME = u"Format"; + +// clang-format off +#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_FRACXY "frac {<?>} {<?>} " +#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_XSETQUOTIENTY "<?> setquotient <?> " +#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_FUNCX "func <?>(<?>) " +#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_OPERX "oper oper <?> " +#define RID_OPER_FROMX "oper oper from{<?>} <?> " +#define RID_OPER_TOX "oper oper to{<?>} <?> " +#define RID_OPER_FROMTOX "oper oper from{<?>} to{<?>} <?> " +#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_AQUA "color aqua {<?>} " +#define RID_COLORX_FUCHSIA "color fuchsia {<?>} " +#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_COLORX_RGBA "color rgba 0 0 0 0 {<?>} " +#define RID_COLORX_HEX "color hex 000000 {<?>} " +#define RID_COLORX_CORAL "color coral {<?>} " +#define RID_COLORX_CRIMSON "color crimson {<?>} " +#define RID_COLORX_MIDNIGHT "color midnightblue {<?>} " +#define RID_COLORX_VIOLET "color violet {<?>} " +#define RID_COLORX_ORANGE "color orange {<?>} " +#define RID_COLORX_ORANGERED "color orangered {<?>} " +#define RID_COLORX_SEAGREEN "color seagreen {<?>} " +#define RID_COLORX_INDIGO "color indigo {<?>} " +#define RID_COLORX_HOTPINK "color hotpink {<?>} " +#define RID_COLORX_LAVENDER "color lavender {<?>} " +#define RID_COLORX_SNOW "color snow {<?>} " +#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_XOVERBRACEY "{<?>} overbrace {<?>} " +#define RID_XUNDERBRACEY "{<?>} underbrace {<?>} " +#define RID_EVALX "evaluate <?> " +#define RID_EVAL_FROMX "evaluate {<?>} from{<?>} " +#define RID_EVAL_TOX "evaluate {<?>} to{<?>} " +#define RID_EVAL_FROMTOX "evaluate {<?>} from{<?>} to{<?>} " +#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_BACKEPSILON "backepsilon " +#define RID_FOURIER "fourier " +#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 <?> " +// clang-format on + +/* 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..39ce0be6e --- /dev/null +++ b/starmath/inc/symbol.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 . + */ + +/** This stuff is used to work with %charname. + * + * Will remember the char name, char code and font. + */ + +#pragma once + +#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( std::u16string_view 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(); +}; + +/* 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..d3e141e5a --- /dev/null +++ b/starmath/inc/token.hxx @@ -0,0 +1,285 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 . + */ + +/** The tokens contain the information gathered by the parser. + * + * They contain: + * the data type (~ mathematical operation). + * The mathematical char. + * The corresponding code or information to recreate it. + * Location of the token in the starmath code. + */ + +#pragma once + +#include <tools/color.hxx> +#include <o3tl/string_view.hxx> +#include <o3tl/typed_flags_set.hxx> + +// std imports +#include <memory> + +// 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> +{ +}; +} + +// Tokens identifiers. Allow to know what kind of information the node contains. +enum SmTokenType +{ + // clang-format off + // Uncategorized + TEND, TSPECIAL, TNONE, TESCAPE, TUNKNOWN, + TBLANK, TSBLANK, TPLACE, TNOSPACE, TDOTSDOWN, + TNEWLINE, TDOTSAXIS, TDOTSLOW, TDOTSVERT, TBACKEPSILON, + TDOTSDIAG, TDOTSUP, TERROR, + // Basic + TPLUS, TMINUS, TMULTIPLY, TDIVIDEBY, // +-*/ + TGT, TLT, TGE, TLE, // > < >= <= + TASSIGN, TNEQ, TGG, TLL, // = != >>> <<< + TPARALLEL, TORTHO, TEQUIV, // Geometry + TOPER, TSUM, TPROD, TCOPROD, // Operators + TIM, TRE, THBAR, TLAMBDABAR, // Complex and constants + TPLUSMINUS, TMINUSPLUS, TSIM, TSIMEQ, // +- -+ ~ ~= + TLIM, TLIMSUP, TLIMINF, TTOWARD, // Limits + TOVER, TTIMES, TCDOT, TDIV, // Product type + TSLASH, TBACKSLASH, TWIDESLASH, TWIDEBACKSLASH, //Slash + TFRAC, TIT, // mathml related + // Structure + TMATRIX, TPOUND, TDPOUND, TSTACK, TBINOM, + // Logic + TAND, TOR, TNEG, // && || ! + TPRECEDES, TSUCCEEDS, TNOTPRECEDES, TNOTSUCCEEDS, // Order + TPRECEDESEQUAL, TSUCCEEDSEQUAL, TPRECEDESEQUIV, TSUCCEEDSEQUIV, // Order eq + TLEFTARROW, TRIGHTARROW, TUPARROW, TDOWNARROW, // Arrows + TDRARROW, TDLARROW, TDLRARROW, TDEF, // Double arrows, definition + TPROP, TNDIVIDES, TDIVIDES, TAPPROX, // Proportions, approximation + TLESLANT, TGESLANT, TTRANSL, TTRANSR, // <= >= corresponds + // Tensors + TOPLUS, TOMINUS, TOTIMES, TODIVIDE, TODOT, + TCIRC, + // Positions + TRSUB, TRSUP, TCSUB, TCSUP, TLSUB, + TLSUP, TFROM, TTO, TUOPER, TBOPER, + // Set theory + TSETN, TSETZ, TSETQ, TSETR, TSETC, + TIN, TNOTIN, TNI, TEMPTYSET, // Insideout + TSUBSET, TSUBSETEQ, TSUPSET, TSUPSETEQ, // Subsupset + TNSUBSET, TNSUPSET, TNSUBSETEQ, TNSUPSETEQ, // Not subsupset + TINTERSECT, TUNION, TSETMINUS, TSETQUOTIENT, // +-/ + TALEPH, TWP, TINFINITY, // Abstract sets + TFORALL, TEXISTS, TNOTEXISTS, // Existential + // Font + TFONT, TSIZE, TCOLOR, TPHANTOM, // Basic + TITALIC, TNITALIC, TBOLD, TNBOLD, // Bold ital + TALIGNL, TALIGNC, TALIGNR, // Align + TUNDERLINE, TOVERLINE, TOVERSTRIKE, TBAR, // Lines + TFIXED, TSANS, TSERIF, // Types + TACUTE, TGRAVE, THAT, TBREVE, // Accents + TWIDEVEC, TWIDEHARPOON, TWIDETILDE, TWIDEHAT, // Wide math + TVEC, THARPOON, TTILDE, TCIRCLE, // math + TCHECK, + TTEXT, TNUMBER, TCHARACTER, TIDENT, // Content type + // Brackets + TLEFT, TRIGHT, TUNDERBRACE, TOVERBRACE, // Scalable, upsidedown + TLGROUP, TRGROUP, TLPARENT, TRPARENT, // Structural + TLBRACKET, TRBRACKET, TLDBRACKET, TRDBRACKET, // Bracket x1 & x2 + TLCEIL, TRCEIL, TLFLOOR, TRFLOOR, // Reals -> Wholes + TLANGLE, TRANGLE, TLBRACE, TRBRACE, // <x> {x} + TLLINE, TRLINE, TLDLINE, TRDLINE, // Lines x1 x2 + TMLINE, TEVALUATE, TLRLINE, TLRDLINE, // Custom + // Differential calculus + TNABLA, TPARTIAL, TFOURIER, TLAPLACE, // Derivative, Transformation + TINTD, TINT, TIINT, TIIINT, // Integral + TLINT, TLLINT, TLLLINT, // Circuit integral + TDOT, TDDOT, TDDDOT, // Derivative dots + // Function + TFUNC, TLN, TLOG, TEXP, // Exp - Log + TSIN, TCOS, TTAN, TCOT, // Trigo + TSINH, TCOSH, TTANH, TCOTH, // Trigo hyperbolic + TASIN, TACOS, TATAN, TACOT, // Arctrigo + TASINH, TACOSH, TATANH, TACOTH, // Arctrigo hyperbolic + TSQRT, TNROOT, TFACT, TABS, // roots, n! |z| + // Color + TRGB, TRGBA, THEX, THTMLCOL, TDVIPSNAMESCOL, + TICONICCOL, TMATHMLCOL + // clang-format on +}; + +struct SmTokenTableEntry +{ + OUString aIdent; + SmTokenType eType; + sal_Unicode cMathChar; + TG nGroup; + sal_uInt16 nLevel; +}; + +struct SmColorTokenTableEntry +{ + OUString aIdent; + SmTokenType eType; + Color cColor; + + SmColorTokenTableEntry() + : eType(TERROR) + , cColor() + { + } + + SmColorTokenTableEntry(const SmColorTokenTableEntry* amColorTokenTableEntry) + : aIdent(amColorTokenTableEntry->aIdent) + , eType(amColorTokenTableEntry->eType) + , cColor(amColorTokenTableEntry->cColor) + { + } + + SmColorTokenTableEntry(const std::unique_ptr<SmColorTokenTableEntry> amColorTokenTableEntry) + : aIdent(amColorTokenTableEntry->aIdent) + , eType(amColorTokenTableEntry->eType) + , cColor(amColorTokenTableEntry->cColor) + { + } + + SmColorTokenTableEntry(const OUString& name, SmTokenType ctype, Color ncolor) + : aIdent(name) + , eType(ctype) + , cColor(ncolor) + { + } + + SmColorTokenTableEntry(const OUString& name, SmTokenType ctype, sal_uInt32 ncolor) + : aIdent(name) + , eType(ctype) + , cColor(ColorTransparency, ncolor) + { + } + + bool equals(std::u16string_view colorname) const + { + return o3tl::compareToIgnoreAsciiCase(colorname, aIdent) == 0; + } + + bool equals(sal_uInt32 colorcode) const { return colorcode == static_cast<sal_uInt32>(cColor); } + + bool equals(Color colorcode) const { return colorcode == cColor; } +}; + +struct SmToken +{ + OUString aText; // token text + SmTokenType eType; // token info + OUString cMathChar; + + // parse-help info + TG nGroup; + sal_uInt16 nLevel; + + SmToken() + : eType(TUNKNOWN) + , cMathChar('\0') + , nGroup(TG::NONE) + , nLevel(0) + { + } + + SmToken(SmTokenType eTokenType, sal_Unicode cMath, const OUString& rText, + TG nTokenGroup = TG::NONE, sal_uInt16 nTokenLevel = 0) + : aText(rText) + , eType(eTokenType) + , cMathChar(cMath) + , nGroup(nTokenGroup) + , nLevel(nTokenLevel) + { + } + + void operator=(const SmTokenTableEntry& aTokenTableEntry) + { + aText = aTokenTableEntry.aIdent; + eType = aTokenTableEntry.eType; + cMathChar = OUString(&aTokenTableEntry.cMathChar, 1); + nGroup = aTokenTableEntry.nGroup; + nLevel = aTokenTableEntry.nLevel; + } + + void operator=(const SmTokenTableEntry* aTokenTableEntry) + { + aText = aTokenTableEntry->aIdent; + eType = aTokenTableEntry->eType; + cMathChar = OUString(&aTokenTableEntry->cMathChar, 1); + nGroup = aTokenTableEntry->nGroup; + nLevel = aTokenTableEntry->nLevel; + } + + void operator=(const SmColorTokenTableEntry& aTokenTableEntry) + { + aText = u""; + eType = aTokenTableEntry.eType; + cMathChar = OUString::number(static_cast<sal_uInt32>(aTokenTableEntry.cColor), 16); + nGroup = TG::Color; + nLevel = 0; + } + + void operator=(const SmColorTokenTableEntry* aTokenTableEntry) + { + aText = u""; + eType = aTokenTableEntry->eType; + cMathChar = OUString::number(static_cast<sal_uInt32>(aTokenTableEntry->cColor), 16); + nGroup = TG::Color; + nLevel = 0; + } + + void operator=(const std::unique_ptr<SmColorTokenTableEntry>& aTokenTableEntry) + { + aText = u""; + eType = aTokenTableEntry->eType; + cMathChar = OUString::number(static_cast<sal_uInt32>(aTokenTableEntry->cColor), 16); + nGroup = TG::Color; + nLevel = 0; + } + + void setChar(sal_Unicode cChar) { cMathChar = OUString(&cChar, 1); } +}; + +/* 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..39d2f5c6f --- /dev/null +++ b/starmath/inc/types.hxx @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/types.h> +#include <rtl/ustring.hxx> + +inline constexpr OUStringLiteral FONTNAME_MATH = u"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_NONE = '\0'; +sal_Unicode const MS_NULLCHAR = '\0'; +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_FOURIER = 0x2131; + +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; + +/* 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..1bb66c4da --- /dev/null +++ b/starmath/inc/unomodel.hxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#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> + +inline constexpr OUStringLiteral PRTUIOPT_TITLE_ROW = u"TitleRow"; +inline constexpr OUStringLiteral PRTUIOPT_FORMULA_TEXT = u"FormulaText"; +inline constexpr OUStringLiteral PRTUIOPT_BORDER = u"Border"; +inline constexpr OUStringLiteral PRTUIOPT_PRINT_FORMAT = u"PrintFormat"; +inline constexpr OUStringLiteral PRTUIOPT_PRINT_SCALE = u"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() noexcept override; + + //XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire( ) noexcept override; + virtual void SAL_CALL release( ) noexcept 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; +}; + +/* 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..ee5040170 --- /dev/null +++ b/starmath/inc/utility.hxx @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <sal/log.hxx> +#include <vcl/font.hxx> +#include <vcl/weld.hxx> +#include <tools/fract.hxx> +#include <deque> + +inline tools::Long SmPtsTo100th_mm(tools::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(tools::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 tools::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 +{ + tools::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(tools::Long nWidth) { nBorderWidth = nWidth; } + tools::Long GetBorderWidth() const; + tools::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; +}; + +/* 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..26b988ffa --- /dev/null +++ b/starmath/inc/view.hxx @@ -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 . + */ + +#pragma once + +#include <sal/config.h> +#include <rtl/ref.hxx> +#include <sfx2/docinsert.hxx> +#include <sfx2/dockwin.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/ctrlitem.hxx> +#include <sfx2/shell.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/InterimItemWindow.hxx> +#include <vcl/timer.hxx> +#include "document.hxx" +#include "edit.hxx" + +class SmViewShell; +class SmPrintUIOptions; +class SmGraphicAccessible; +class SmGraphicWidget; +class SmElementsDockingWindow; + +class SmGraphicWindow final : public InterimItemWindow +{ +private: + Point aPixOffset; // offset to virtual window (pixel) + Size aTotPixSz; // total size of virtual window (pixel) + tools::Long nLinePixH; // size of a line/column (pixel) + tools::Long nColumnPixW; + sal_uInt16 nZoom; + + std::unique_ptr<weld::ScrolledWindow> mxScrolledWindow; + std::unique_ptr<SmGraphicWidget> mxGraphic; + std::unique_ptr<weld::CustomWeld> mxGraphicWin; + + DECL_LINK(ScrollHdl, weld::ScrolledWindow&, void); + +public: + explicit SmGraphicWindow(SmViewShell& rShell); + virtual void dispose() override; + virtual ~SmGraphicWindow() override; + + virtual bool IsStarMath() const override { return true; } + + void SetTotalSize(const Size& rNewSize); + Size GetTotalSize() const; + + void SetZoom(sal_uInt16 Factor); + sal_uInt16 GetZoom() const { return nZoom; } + + void ZoomToFitInWindow(); + + virtual void Resize() override; + void ShowContextMenu(const CommandEvent& rCEvt); + + void SetGraphicMapMode(const MapMode& rNewMapMode); + MapMode GetGraphicMapMode() const; + + SmGraphicWidget& GetGraphicWidget() + { + return *mxGraphic; + } + + const SmGraphicWidget& GetGraphicWidget() const + { + return *mxGraphic; + } +}; + +class SmGraphicWidget final : public weld::CustomWidgetController +{ +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 SmGraphicWidget(SmViewShell& rShell, SmGraphicWindow& rGraphicWindow); + virtual ~SmGraphicWidget() override; + + // CustomWidgetController + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + virtual bool MouseButtonDown(const MouseEvent &rMEvt) override; + virtual bool MouseMove(const MouseEvent &rMEvt) override; + virtual void GetFocus() override; + virtual void LoseFocus() override; + virtual bool KeyInput(const KeyEvent& rKEvt) override; + + void SetTotalSize(); + + SmViewShell& GetView() + { + return mrViewShell; + } + + const Point& GetFormulaDrawPos() const + { + return aFormulaDrawPos; + } + + // for Accessibility + virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override; + + SmGraphicAccessible* GetAccessible_Impl() + { + return mxAccessible.get(); + } + +private: + void SetIsCursorVisible(bool bVis) + { + bIsCursorVisible = bVis; + } + void SetCursor(const SmNode *pNode); + void SetCursor(const tools::Rectangle &rRect); + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + virtual bool Command(const CommandEvent& rCEvt) override; + + void RepaintViewShellDoc(); + DECL_LINK(CaretBlinkTimerHdl, Timer *, void); + void CaretBlinkInit(); + void CaretBlinkStart(); + void CaretBlinkStop(); + + SmGraphicWindow& mrGraphicWindow; + + Point aFormulaDrawPos; + // old style editing pieces + tools::Rectangle aCursorRect; + bool bIsCursorVisible; + bool bIsLineVisible; + AutoTimer aCaretBlinkTimer; + rtl::Reference<SmGraphicAccessible> mxAccessible; + SmViewShell& mrViewShell; +}; + +class SmGraphicController final : public SfxControllerItem +{ + SmGraphicWidget &rGraphic; +public: + SmGraphicController(SmGraphicWidget &, sal_uInt16, SfxBindings & ); + virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, + SfxItemState eState, + const SfxPoolItem* pState) override; +}; + +class SmEditController final : public SfxControllerItem +{ + SmEditWindow &rEdit; + +public: + SmEditController(SmEditWindow &, sal_uInt16, SfxBindings & ); + + virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState) override; +}; + +class SmCmdBoxWindow final : public SfxDockingWindow +{ + std::unique_ptr<SmEditWindow, o3tl::default_delete<SmEditWindow>> m_xEdit; + SmEditController aController; + bool bExiting; + + Timer aInitialFocusTimer; + + DECL_LINK(InitialFocusTimerHdl, Timer *, void); + + 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; + + // Window + virtual void GetFocus() override; + virtual void StateChanged( StateChangedType nStateChange ) override; + virtual void Command(const CommandEvent& rCEvt) override; + + Point WidgetToWindowPos(const weld::Widget& rWidget, const Point& rPos); + + void ShowContextMenu(const Point& rPos); + + void AdjustPosition(); + + SmEditWindow& GetEditWindow() + { + return *m_xEdit; + } + 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; } + +class SmViewShell final : public SfxViewShell +{ + std::unique_ptr<sfx2::DocumentInserter> mpDocInserter; + std::unique_ptr<SfxRequest> mpRequest; + VclPtr<SmGraphicWindow> mxGraphicWindow; + SmGraphicController maGraphicController; + OUString maStatusText; + bool mbPasteState; + /** 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; + + DECL_LINK( DialogClosedHdl, sfx2::FileDialogHelper*, void ); + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + static Size GetTextLineSize(OutputDevice const & rDevice, + const OUString& rLine); + static Size GetTextSize(OutputDevice const & rDevice, + std::u16string_view rText, + tools::Long MaxWidth); + static void DrawTextLine(OutputDevice& rDevice, + const Point& rPosition, + const OUString& rLine); + static void DrawText(OutputDevice& rDevice, + const Point& rPosition, + std::u16string_view 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(); + + SmGraphicWidget& GetGraphicWidget() + { + return mxGraphicWindow->GetGraphicWidget(); + } + const SmGraphicWidget& GetGraphicWidget() const + { + return mxGraphicWindow->GetGraphicWidget(); + } + + SmGraphicWindow& GetGraphicWindow() + { + return *mxGraphicWindow; + } + + SmElementsDockingWindow* GetDockingWindow(); + + 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 SmGraphicWidget 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; + } + static bool IsInlineEditEnabled(); + +private: + void ZoomByItemSet(const SfxItemSet *pSet); +}; + +/* 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..27c4e374e --- /dev/null +++ b/starmath/inc/visitors.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/. + */ + +/** Visitors are an easy way to automating operations with nodes. + * + * The available visitors are: + * SmVisitor base class + * SmDefaultingVisitor default visitor + * SmDrawingVisitor draws formula + * SmCaretPosGraphBuildingVisitor position of the node inside starmath code + * SmCloningVisitor duplicate nodes + * SmNodeToTextVisitor create code from nodes + * + */ + +#pragma once + +#include <sal/config.h> +#include <sal/log.hxx> + +#include "node.hxx" +#include "caret.hxx" + +/** 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( SmAttributeNode* 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( SmAttributeNode* 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( SmAttributeNode* 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( SmAttributeNode* 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( SmAttributeNode* 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( SmAttributeNode* 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. + * @param pNode + * @return + */ + void LineToText( SmNode* pNode ) { + Separate( ); + if( pNode ) pNode->Accept( this ); + Separate( ); + } + + /** + * Appends rText to the OUStringBuffer ( maCmdText ). + * @param rText + * @return + */ + void Append( std::u16string_view rText ) { + maCmdText.append( rText ); + } + + /** + * Append a blank for separation, if needed. + * It is needed if last char is not ' '. + * @return + */ + void Separate( ){ + if( !maCmdText.isEmpty() && maCmdText[ maCmdText.getLength() - 1 ] != ' ' ) + maCmdText.append(' '); + } + + /** Output text generated from the pNodes */ + OUStringBuffer maCmdText; +}; + +/* 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..ee17cf6f8 --- /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( SmAttributeNode* pNode ) override { + CPPUNIT_ASSERT_EQUAL_MESSAGE("SmAttributeNode should have type SmNodeType::Attribute", + SmNodeType::Attribute, 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..56c673c1b --- /dev/null +++ b/starmath/qa/cppunit/test_cursor.cxx @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <parse5.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->DoClose(); + xDocShRef.clear(); + BootstrapFixture::tearDown(); +} + +void Test::testCopyPaste() +{ + auto xTree = SmParser5().Parse("a * b + c"); + 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() +{ + auto xTree = SmParser5().Parse("a * b + c"); + 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() +{ + auto xTree = SmParser5().Parse("a * b + c"); + 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() +{ + auto xTree = SmParser5().Parse("a * b + c"); + 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..f16f195aa --- /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 <parse5.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() +{ + SmParser5 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); + tools::Long nWidthA = pNodeA->GetRect().GetWidth(); + tools::Long nWidthC = pNodeC->GetRect().GetWidth(); + tools::Long nWidthL = pNodeL->GetRect().GetWidth(); + tools::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..6f714321d --- /dev/null +++ b/starmath/qa/cppunit/test_nodetotextvisitors.cxx @@ -0,0 +1,666 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <parse5.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->DoClose(); + 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 = SmParser5().ParseExpression(input); + pNode1->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + SmNodeToTextVisitor(pNode1.get(), output1); + + // parse 2 + auto pNode2 = SmParser5().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 = SmParser5().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 = SmParser5().ParseExpression(sInput1); + pNode1->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + SmNodeToTextVisitor(pNode1.get(), sOutput1); + + // parse formula2 + OUString sInput2(formula2, strlen(formula2), RTL_TEXTENCODING_UTF8); + auto pNode2 = SmParser5().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 = SmParser5().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 = SmParser5().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 = SmParser5().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"); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("BinHor in SubSup", OUString("{ a ^ { b + c } + d }"), + xDocShRef->GetText()); +} + +void Test::testUnaryInMixedNumberAsNumerator() +{ + // set up a unary operator + auto pTree = SmParser5().Parse("- 1"); + 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"); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Unary in mixed number as Numerator", + OUString("{ 2 { - 1 over 2 } + 4 }"), 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; + auto pNode = SmParser5().ParseExpression(u"{ \U0001D44E }"); // non-BMP Unicode + pNode->Prepare(xDocShRef->GetFormat(), *xDocShRef, 0); + SmNodeToTextVisitor(pNode.get(), sOutput); + CPPUNIT_ASSERT_EQUAL(OUString(u"\U0001D44E"), 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..2171cde80 --- /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 <parse5.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 = SmParser5().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 = SmParser5().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..60eb85944 --- /dev/null +++ b/starmath/qa/cppunit/test_starmath.cxx @@ -0,0 +1,569 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_fonts.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; + 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_pViewShell = m_pSmCmdBoxWindow->GetView(); + CPPUNIT_ASSERT_MESSAGE("Should have a SmViewShell", m_pViewShell); +} + +void Test::tearDown() +{ + m_pSmCmdBoxWindow.disposeAndClear(); + m_pDispatcher.reset(); + m_xDocShRef->DoClose(); + m_xDocShRef.clear(); + + BootstrapFixture::tearDown(); +} + +#if HAVE_MORE_FONTS +void Test::testSmTmpDeviceRestoreFont() +{ + ScopedVclPtrInstance<Printer> pPrinter; + + 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; + + { + bool bUseMap100th_mm = true; + 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() +{ + SmEditWindow& rEditWindow = m_pSmCmdBoxWindow->GetEditWindow(); + { + OUString sMarkedText("<?> under <?> under <?>"); + rEditWindow.SetText(sMarkedText); + rEditWindow.Flush(); + OUString sFinalText = rEditWindow.GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be equal text", sMarkedText, sFinalText); + } + + { + ESelection aSelection; + + rEditWindow.SelNextMark(); + rEditWindow.Delete(); + rEditWindow.InsertText("a"); + + rEditWindow.SelNextMark(); + rEditWindow.SelNextMark(); + rEditWindow.Delete(); + rEditWindow.InsertText("c"); + + // should be safe i.e. do nothing + rEditWindow.SelNextMark(); + aSelection = rEditWindow.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); + + rEditWindow.SelPrevMark(); + rEditWindow.Delete(); + rEditWindow.InsertText("b"); + + // tdf#106116: should be safe i.e. do nothing + rEditWindow.SelPrevMark(); + aSelection = rEditWindow.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); + + rEditWindow.Flush(); + OUString sFinalText = rEditWindow.GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be a under b under c", OUString("a under b under c"), sFinalText); + } + + { + rEditWindow.SetText(OUString()); + rEditWindow.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); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be a SmParseError::ColorExpected", + SmParseError::ColorExpected, pErrorDesc->m_eType); + + pErrorDesc = m_xDocShRef->GetParser()->PrevError(); + + CPPUNIT_ASSERT_MESSAGE("Should be a SmParseError::UnexpectedChar", + pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be a SmParseError::UnexpectedChar", + SmParseError::UnexpectedChar, pErrorDesc->m_eType); + + pErrorDesc = m_xDocShRef->GetParser()->PrevError(); + + CPPUNIT_ASSERT_MESSAGE("Should be a SmParseError::RgroupExpected", + pErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be a SmParseError::RgroupExpected", + SmParseError::RgroupExpected, pErrorDesc->m_eType); + + const SmErrorDesc *pLastErrorDesc = m_xDocShRef->GetParser()->PrevError(); + + CPPUNIT_ASSERT_MESSAGE("Should be three syntax errors", + pLastErrorDesc); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be three syntax errors", + pErrorDesc, pLastErrorDesc); +} + +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() +{ + SmEditWindow& rEditWindow = m_pSmCmdBoxWindow->GetEditWindow(); + // Test the placeholder replacement. In this case, testing 'a + b', it + // should return '+a + b' when selecting '+<?>' in ElementsDock + rEditWindow.SetText("a + b"); + rEditWindow.SelectAll(); + rEditWindow.InsertText("+<?>"); + OUString sFinalText = rEditWindow.GetText(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be '+a + b'", OUString("+a + b"), sFinalText); +} + +void Test::viewZoom() +{ + sal_uInt16 nOrigZoom, 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); + sal_uInt16 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..41294eff8 --- /dev/null +++ b/starmath/qa/extras/data/color.mml @@ -0,0 +1,24 @@ +<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> + <mi mathcolor="#DC143C">c</mi> + <mi mathcolor="#FFB781">a</mi> + <mi mathcolor="#FF0">y</mi> + <mi mathcolor="#DC143D">x</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/mthmlentities.mml b/starmath/qa/extras/data/mthmlentities.mml new file mode 100644 index 000000000..328d689ce --- /dev/null +++ b/starmath/qa/extras/data/mthmlentities.mml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> + <semantics> + <mi>σ</mi> + <mi>∞</mi> + <mi>∞</mi> + <mi>σ</mi> + </semantics> +</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..f8ea8f8d2 --- /dev/null +++ b/starmath/qa/extras/data/tdf103430.mml @@ -0,0 +1,17 @@ +<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> + <mstyle mathcolor="#ffb781"> + <mo fontstyle="italic" fontweight="bold" mathvariant="normal" form="prefix" rspace="0">d</mo> + </mstyle> + <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/tdf137008.mml b/starmath/qa/extras/data/tdf137008.mml new file mode 100644 index 000000000..bc6ee25db --- /dev/null +++ b/starmath/qa/extras/data/tdf137008.mml @@ -0,0 +1 @@ +<math xmlns='http://www.w3.org/1998/Math/MathML'><mtable><mtr><mtd></mtd></mtr><mtr></mtr></mtable></math>
\ No newline at end of file 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..d41ba41e6 --- /dev/null +++ b/starmath/qa/extras/mmlexport-test.cxx @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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..323b0e0fb --- /dev/null +++ b/starmath/qa/extras/mmlimport-test.cxx @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#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(); + void testTdf137008(); + void testMathmlEntities(); + + 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(testTdf137008); + CPPUNIT_TEST(testMathmlEntities); + 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( + OString("failed to load " + OUStringToOString(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(u"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" + " color crimson c" + " color dvip apricot" + " a color yellow y" + " color rgb 220 20 61 x }"), + mxDocShell->GetText()); +} + +void Test::testSimple() +{ + loadURL(m_directories.getURLFromSrc(u"starmath/qa/extras/data/simple.mml")); + CPPUNIT_ASSERT_EQUAL_MESSAGE("loaded text", OUString("left ( { a + b } right ) ^ 2"), + mxDocShell->GetText()); +} + +void Test::testNsPrefixMath() +{ + loadURL(m_directories.getURLFromSrc(u"starmath/qa/extras/data/ns-prefix-math.mml")); + CPPUNIT_ASSERT_EQUAL_MESSAGE("loaded text", OUString("left ( { a + b } right ) ^ 2"), + mxDocShell->GetText()); +} + +void Test::testMaction() +{ + loadURL(m_directories.getURLFromSrc(u"starmath/qa/extras/data/maction.mml")); + CPPUNIT_ASSERT_EQUAL_MESSAGE("loaded text", OUString("matrix{ 1 ## 2 ## 3 }"), + mxDocShell->GetText()); +} + +void Test::testMspace() +{ + loadURL(m_directories.getURLFromSrc(u"starmath/qa/extras/data/mspace.mml")); + CPPUNIT_ASSERT_EQUAL(OUString("{ a b ~ c ~~``` d }"), mxDocShell->GetText()); +} + +void Test::testtdf99556() +{ + loadURL(m_directories.getURLFromSrc(u"starmath/qa/extras/data/tdf99556-1.mml")); + CPPUNIT_ASSERT_EQUAL_MESSAGE("loaded text", OUString("sqrt { }"), mxDocShell->GetText()); +} + +void Test::testTdf103430() +{ + loadURL(m_directories.getURLFromSrc(u"starmath/qa/extras/data/tdf103430.mml")); + CPPUNIT_ASSERT_EQUAL(OUString("frac { { nitalic d ^ 2 nitalic color blue y } } { { color dvip " + "apricot nitalic d font sans bold italic color red x } }"), + mxDocShell->GetText()); +} + +void Test::testTdf103500() +{ + loadURL(m_directories.getURLFromSrc(u"starmath/qa/extras/data/tdf103500.mml")); + CPPUNIT_ASSERT_EQUAL(OUString("{ { int csup b csub a { frac { 1 } { x } ` nitalic d x } } = { " + "intd csup b csub a { frac { 1 } { y } ` nitalic d y } } }"), + mxDocShell->GetText()); +} + +void Test::testTdf137008() +{ + // Without the fix in place, this test would have crashed + loadURL(m_directories.getURLFromSrc(u"starmath/qa/extras/data/tdf137008.mml")); + CPPUNIT_ASSERT_EQUAL(OUString("matrix{ { } # ## # }"), mxDocShell->GetText()); +} +void Test::testMathmlEntities() +{ + loadURL(m_directories.getURLFromSrc(u"starmath/qa/extras/data/mthmlentities.mml")); + CPPUNIT_ASSERT_EQUAL(OUString(u"{ \u03C3 \u221E \u221E \u03C3 }"), 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..70529fbad --- /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 weld::Builder +# + +# 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..11bcb1dd9 --- /dev/null +++ b/starmath/qa/unit/starmath-dialogs-test.cxx @@ -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/. + */ + +#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(u"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..48045629e --- /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_GRAPHIC_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..47468ce5f --- /dev/null +++ b/starmath/sdi/smslots.sdi @@ -0,0 +1,292 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_GRAPHIC_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 ; + ] + SID_IMPORT_MATHML_CLIPBOARD //idlpp ole : no , status : no + [ + ExecMethod = Execute ; + StateMethod = GetState ; + ] + //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/ElementsDockingWindow.cxx b/starmath/source/ElementsDockingWindow.cxx new file mode 100644 index 000000000..a3a4085a7 --- /dev/null +++ b/starmath/source/ElementsDockingWindow.cxx @@ -0,0 +1,750 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <ElementsDockingWindow.hxx> + +#include <starmath.hrc> +#include <strings.hrc> +#include <smmod.hxx> +#include <cfgitem.hxx> +#include <parse.hxx> +#include <view.hxx> +#include <visitors.hxx> +#include <document.hxx> +#include <strings.hxx> + +#include <sfx2/dispatch.hxx> +#include <sfx2/sfxmodelfactory.hxx> +#include <svl/stritem.hxx> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <vcl/uitest/eventdescription.hxx> +#include <vcl/uitest/logger.hxx> +#include <vcl/virdev.hxx> + +#include <unordered_map> +#include <utility> + +namespace +{ +// element, element help, element visual, element visual's translatable +typedef std::tuple<std::string_view, TranslateId, std::u16string_view, TranslateId> SmElementDescr; + +// SmParser 5 elements + +const SmElementDescr s_a5UnaryBinaryOperatorsList[] = +{ + {RID_PLUSX, RID_PLUSX_HELP, {}, {}}, + {RID_MINUSX, RID_MINUSX_HELP, {}, {}}, + {RID_PLUSMINUSX, RID_PLUSMINUSX_HELP, {}, {}}, + {RID_MINUSPLUSX, RID_MINUSPLUSX_HELP, {}, {}}, + {}, + {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_FRACXY, RID_FRACXY_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, {}, {}}, + {}, + {RID_NEGX, RID_NEGX_HELP, {}, {}}, + {RID_XANDY, RID_XANDY_HELP, {}, {}}, + {RID_XORY, RID_XORY_HELP, {}, {}} +}; + +const SmElementDescr s_a5RelationsList[] = +{ + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {RID_DLARROW, RID_DLARROW_HELP, {}, {}}, + {RID_DLRARROW, RID_DLRARROW_HELP, {}, {}}, + {RID_DRARROW, RID_DRARROW_HELP, {}, {}}, + {}, + {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 s_a5SetOperationsList[] = +{ + {RID_XINY, RID_XINY_HELP, {}, {}}, + {RID_XNOTINY, RID_XNOTINY_HELP, {}, {}}, + {RID_XOWNSY, RID_XOWNSY_HELP, {}, {}}, + {}, + {RID_XINTERSECTIONY, RID_XINTERSECTIONY_HELP, {}, {}}, + {RID_XUNIONY, RID_XUNIONY_HELP, {}, {}}, + {RID_XSETMINUSY, RID_XSETMINUSY_HELP, {}, {}}, + {RID_XSETQUOTIENTY, RID_XSETQUOTIENTY_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, {}, {}}, + {}, + {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 s_a5FunctionsList[] = +{ + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {RID_FUNCX, RID_FUNCX_HELP, {}, {}}, +}; + +const SmElementDescr s_a5OperatorsList[] = +{ + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {RID_OPERX, RID_OPERX_HELP, u"oper \xE22B <?>", {}}, + {RID_OPER_FROMX, RID_OPER_FROMX_HELP, u"oper \xE22B from <?> <?>", {}}, + {RID_OPER_TOX, RID_OPER_TOX_HELP, u"oper \xE22B to <?> <?>", {}}, + {RID_OPER_FROMTOX, RID_OPER_FROMTOX_HELP, u"oper \xE22B from <?> to <?> <?>", {}}, +}; + +const SmElementDescr s_a5AttributesList[] = +{ + {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, {}, {}}, + {}, + {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, {}, {}}, + {}, + {RID_PHANTOMX, RID_PHANTOMX_HELP, u"\"$1\"", STR_HIDE}, + {RID_BOLDX, RID_BOLDX_HELP, u"bold B", {}}, + {RID_ITALX, RID_ITALX_HELP, u"ital I", {}}, + {RID_SIZEXY, RID_SIZEXY_HELP, u"\"$1\"", STR_SIZE}, + {RID_FONTXY, RID_FONTXY_HELP, u"\"$1\"", STR_FONT}, + {}, + {RID_COLORX_BLACK, RID_COLORX_BLACK_HELP, u"color black { \"$1\" }", STR_BLACK}, + {RID_COLORX_BLUE, RID_COLORX_BLUE_HELP, u"color blue { \"$1\" }", STR_BLUE}, + {RID_COLORX_GREEN, RID_COLORX_GREEN_HELP, u"color green { \"$1\" }", STR_GREEN}, + {RID_COLORX_RED, RID_COLORX_RED_HELP, u"color red { \"$1\" }", STR_RED}, + {RID_COLORX_AQUA, RID_COLORX_AQUA_HELP, u"color aqua { \"$1\" }", STR_AQUA}, + {RID_COLORX_FUCHSIA, RID_COLORX_FUCHSIA_HELP, u"color fuchsia { \"$1\" }", STR_FUCHSIA}, + {RID_COLORX_YELLOW, RID_COLORX_YELLOW_HELP, u"color yellow { \"$1\" }", STR_YELLOW}, + {RID_COLORX_GRAY, RID_COLORX_GRAY_HELP, u"color gray { \"$1\" }", STR_GRAY}, + {RID_COLORX_LIME, RID_COLORX_LIME_HELP, u"color lime { \"$1\" }", STR_LIME}, + {RID_COLORX_MAROON, RID_COLORX_MAROON_HELP, u"color maroon { \"$1\" }", STR_MAROON}, + {RID_COLORX_NAVY, RID_COLORX_NAVY_HELP, u"color navy { \"$1\" }", STR_NAVY}, + {RID_COLORX_OLIVE, RID_COLORX_OLIVE_HELP, u"color olive { \"$1\" }", STR_OLIVE}, + {RID_COLORX_PURPLE, RID_COLORX_PURPLE_HELP, u"color purple { \"$1\" }", STR_PURPLE}, + {RID_COLORX_SILVER, RID_COLORX_SILVER_HELP, u"color silver { \"$1\" }", STR_SILVER}, + {RID_COLORX_TEAL, RID_COLORX_TEAL_HELP, u"color teal { \"$1\" }", STR_TEAL}, + {RID_COLORX_RGB, RID_COLORX_RGB_HELP, u"color rgb 0 0 0 { \"$1\" }", STR_RGB}, + //{RID_COLORX_RGBA, RID_COLORX_RGBA_HELP, u"color rgba 0 0 0 0 { \"$1\" }", STR_RGBA}, + {RID_COLORX_HEX, RID_COLORX_HEX_HELP, u"color hex 000000 { \"$1\" }", STR_HEX}, + {}, + {RID_COLORX_CORAL, RID_COLORX_CORAL_HELP, u"color coral { \"$1\" }", STR_CORAL}, + {RID_COLORX_MIDNIGHT, RID_COLORX_MIDNIGHT_HELP, u"color midnightblue { \"$1\" }", STR_MIDNIGHT}, + {RID_COLORX_CRIMSON, RID_COLORX_CRIMSON_HELP, u"color crimson { \"$1\" }", STR_CRIMSON}, + {RID_COLORX_VIOLET, RID_COLORX_VIOLET_HELP, u"color violet { \"$1\" }", STR_VIOLET}, + {RID_COLORX_ORANGE, RID_COLORX_ORANGE_HELP, u"color orange { \"$1\" }", STR_ORANGE}, + {RID_COLORX_ORANGERED, RID_COLORX_ORANGERED_HELP, u"color orangered { \"$1\" }", STR_ORANGERED}, + {RID_COLORX_SEAGREEN, RID_COLORX_SEAGREEN_HELP, u"color seagreen { \"$1\" }", STR_SEAGREEN}, + {RID_COLORX_INDIGO, RID_COLORX_INDIGO_HELP, u"color indigo { \"$1\" }", STR_INDIGO}, + {RID_COLORX_HOTPINK, RID_COLORX_HOTPINK_HELP, u"color hotpink { \"$1\" }", STR_HOTPINK}, + {RID_COLORX_LAVENDER, RID_COLORX_LAVENDER_HELP, u"color lavender { \"$1\" }", STR_LAVENDER}, + {RID_COLORX_SNOW, RID_COLORX_SNOW_HELP, u"color snow { \"$1\" }", STR_SNOW}, +}; + +const SmElementDescr s_a5BracketsList[] = +{ + {RID_LRGROUPX, RID_LRGROUPX_HELP, {}, {}}, + {}, + {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, {}, {}}, + {}, + {RID_SLRPARENTX, RID_SLRPARENTX_HELP, u"left ( binom{<?>}{<?>} right )", {}}, + {RID_SLRBRACKETX, RID_SLRBRACKETX_HELP, u"left [ binom{<?>}{<?>} right ]", {}}, + {RID_SLRDBRACKETX, RID_SLRDBRACKETX_HELP, u"left ldbracket binom{<?>}{<?>} right rdbracket", {}}, + {RID_SLRBRACEX, RID_SLRBRACEX_HELP, u"left lbrace binom{<?>}{<?>} right rbrace", {}}, + {RID_SLRANGLEX, RID_SLRANGLEX_HELP, u"left langle binom{<?>}{<?>} right rangle", {}}, + {RID_SLMRANGLEXY, RID_SLMRANGLEXY_HELP, u"left langle binom{<?>}{<?>} mline binom{<?>}{<?>} right rangle", {}}, + {RID_SLRCEILX, RID_SLRCEILX_HELP, u"left lceil binom{<?>}{<?>} right rceil", {}}, + {RID_SLRFLOORX, RID_SLRFLOORX_HELP, u"left lfloor binom{<?>}{<?>} right rfloor", {}}, + {RID_SLRLINEX, RID_SLRLINEX_HELP, u"left lline binom{<?>}{<?>} right rline", {}}, + {RID_SLRDLINEX, RID_SLRDLINEX_HELP, u"left ldline binom{<?>}{<?>} right rdline", {}}, + {}, + {RID_XOVERBRACEY, RID_XOVERBRACEY_HELP, u"{<?><?><?>} overbrace {<?>}", {}}, + {RID_XUNDERBRACEY, RID_XUNDERBRACEY_HELP, u"{<?><?><?>} underbrace {<?>} ", {}}, + {}, + {RID_EVALX, RID_EVALUATEX_HELP, {}, {}}, + {RID_EVAL_FROMX, RID_EVALUATE_FROMX_HELP, {}, {}}, + {RID_EVAL_TOX, RID_EVALUATE_TOX_HELP, {}, {}}, + {RID_EVAL_FROMTOX, RID_EVALUATE_FROMTOX_HELP, {}, {}}, +}; + +const SmElementDescr s_a5FormatsList[] = +{ + {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, {}, {}}, + {}, + {RID_NEWLINE, RID_NEWLINE_HELP, u"\u21B5", {}}, + {RID_SBLANK, RID_SBLANK_HELP, u"\"`\"", {}}, + {RID_BLANK, RID_BLANK_HELP, u"\"~\"", {}}, + {RID_NOSPACE, RID_NOSPACE_HELP, {}, {}}, + {RID_ALIGNLX, RID_ALIGNLX_HELP, u"\"$1\"", STR_ALIGN_LEFT}, + {RID_ALIGNCX, RID_ALIGNCX_HELP, u"\"$1\"", STR_ALIGN_CENTER}, + {RID_ALIGNRX, RID_ALIGNRX_HELP, u"\"$1\"", STR_ALIGN_RIGHT}, + {}, + {RID_BINOMXY, RID_BINOMXY_HELP, {}, {}}, + {RID_STACK, RID_STACK_HELP, {}, {}}, + {RID_MATRIX, RID_MATRIX_HELP, {}, {}}, +}; + +const SmElementDescr s_a5OthersList[] = +{ + {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, {}, {}}, + {RID_FOURIER, RID_FOURIER_HELP, {}, {}}, + {RID_BACKEPSILON, RID_BACKEPSILON_HELP, {}, {}}, + {}, + {RID_LEFTARROW, RID_LEFTARROW_HELP, {}, {}}, + {RID_RIGHTARROW, RID_RIGHTARROW_HELP, {}, {}}, + {RID_UPARROW, RID_UPARROW_HELP, {}, {}}, + {RID_DOWNARROW, RID_DOWNARROW_HELP, {}, {}}, + {}, + {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 s_a5ExamplesList[] = +{ + {"{func e}^{i %pi} + 1 = 0", RID_EXAMPLE_EULER_IDENTITY_HELP, {}, {}}, + {"C = %pi cdot d = 2 cdot %pi cdot r", RID_EXAMPLE_CIRCUMFERENCE_HELP, {}, {}}, + {"c = sqrt{ a^2 + b^2 }", RID_EXAMPLE_PYTHAGOREAN_THEO_HELP, {}, {}}, + {"vec F = m times vec a", RID_EXAMPLE_2NEWTON, {}, {}}, + {"E = m c^2", RID_EXAMPLE_MASS_ENERGY_EQUIV_HELP, {}, {}}, + {"G_{%mu %nu} + %LAMBDA g_{%mu %nu}= frac{8 %pi G}{c^4} T_{%mu %nu}", RID_EXAMPLE_GENERAL_RELATIVITY_HELP, {}, {}}, + {"%DELTA t' = { %DELTA t } over sqrt{ 1 - v^2 over c^2 }", RID_EXAMPLE_SPECIAL_RELATIVITY_HELP, {}, {}}, + {"d over dt left( {partial L}over{partial dot q} right) = {partial L}over{partial q}", RID_EXAMPLE_EULER_LAGRANGE_HELP, {}, {}}, + {"int from a to b f'(x) dx = f(b) - f(a)", RID_EXAMPLE_FTC_HELP, {}, {}}, + {"ldline %delta bold{r}(t) rdline approx e^{%lambda t} ldline %delta { bold{r} }_0 rdline", RID_EXAMPLE_CHAOS_HELP, {}, {}}, + {"f(x) = sum from { n=0 } to { infinity } { {f^{(n)}(x_0) } over { fact{n} } (x-x_0)^n }", RID_EXAMPLE_A_TAYLOR_SERIES_HELP, {}, {}}, + {"f(x) = {1} over { %sigma sqrt{2 %pi} } func e^-{ {(x-%mu)^2} over {2 %sigma^2} }", RID_EXAMPLE_GAUSS_DISTRIBUTION_HELP, {}, {}}, +}; + +const std::vector<TranslateId> s_a5Categories{ + RID_CATEGORY_UNARY_BINARY_OPERATORS, + RID_CATEGORY_RELATIONS, + RID_CATEGORY_SET_OPERATIONS, + RID_CATEGORY_FUNCTIONS, + RID_CATEGORY_OPERATORS, + RID_CATEGORY_ATTRIBUTES, + RID_CATEGORY_BRACKETS, + RID_CATEGORY_FORMATS, + RID_CATEGORY_OTHERS, + RID_CATEGORY_EXAMPLES, +}; + +template <size_t N> +constexpr std::pair<const SmElementDescr*, size_t> asPair(const SmElementDescr (&category)[N]) +{ + return { category, N }; +} + +struct TranslateId_hash +{ + size_t operator()(const TranslateId& val) const { return std::hash<std::string_view>()(val.mpId); } +}; + +const std::unordered_map<TranslateId, std::pair<const SmElementDescr*, size_t>, TranslateId_hash> s_a5CategoryDescriptions{ + { RID_CATEGORY_UNARY_BINARY_OPERATORS, asPair(s_a5UnaryBinaryOperatorsList) }, + { RID_CATEGORY_RELATIONS, asPair(s_a5RelationsList) }, + { RID_CATEGORY_SET_OPERATIONS, asPair(s_a5SetOperationsList) }, + { RID_CATEGORY_FUNCTIONS, asPair(s_a5FunctionsList) }, + { RID_CATEGORY_OPERATORS, asPair(s_a5OperatorsList) }, + { RID_CATEGORY_ATTRIBUTES, asPair(s_a5AttributesList) }, + { RID_CATEGORY_BRACKETS, asPair(s_a5BracketsList) }, + { RID_CATEGORY_FORMATS, asPair(s_a5FormatsList) }, + { RID_CATEGORY_OTHERS, asPair(s_a5OthersList) }, + { RID_CATEGORY_EXAMPLES, asPair(s_a5ExamplesList) }, +}; +} // namespace + +// static +const std::vector<TranslateId>& SmElementsControl::categories() +{ + return s_a5Categories; +} + +SmElementsControl::SmElementsControl(std::unique_ptr<weld::IconView> pIconView) + : mpDocShell(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT)) + , m_nSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion()) + , mbVerticalMode(true) + , mpIconView(std::move(pIconView)) +{ + maParser.reset(starmathdatabase::GetVersionSmParser(m_nSmSyntaxVersion)); + maParser->SetImportSymbolNames(true); + + mpIconView->connect_query_tooltip(LINK(this, SmElementsControl, QueryTooltipHandler)); + mpIconView->connect_item_activated(LINK(this, SmElementsControl, ElementActivatedHandler)); +} + +SmElementsControl::~SmElementsControl() +{ + mpDocShell->DoClose(); +} + +void SmElementsControl::setVerticalMode(bool bVerticalMode) +{ + if (mbVerticalMode == bVerticalMode) + return; + mbVerticalMode = bVerticalMode; + build(); +} + +Color SmElementsControl::GetTextColor() +{ + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + return rStyleSettings.GetFieldTextColor(); +} + +Color SmElementsControl::GetControlBackground() +{ + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + return rStyleSettings.GetFieldColor(); +} + +struct ElementData +{ + OUString maElementSource; + OUString maHelpText; + ElementData(const OUString& aElementSource, const OUString& aHelpText) + : maElementSource(aElementSource) + , maHelpText(aHelpText) + { + } +}; + +void SmElementsControl::addElement(const OUString& aElementVisual, const OUString& aElementSource, const OUString& aHelpText) +{ + std::unique_ptr<SmNode> pNode = maParser->ParseExpression(aElementVisual); + ScopedVclPtr<VirtualDevice> pDevice(mpIconView->create_virtual_device()); + pDevice->SetTextRenderModeForResolutionIndependentLayout(true); + pDevice->SetMapMode(MapMode(MapUnit::Map100thMM)); + pDevice->SetDrawMode(DrawModeFlags::Default); + pDevice->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default); + pDevice->SetDigitLanguage(LANGUAGE_ENGLISH); + + pDevice->SetBackground(GetControlBackground()); + pDevice->SetTextColor(GetTextColor()); + + pNode->Prepare(maFormat, *mpDocShell, 0); + pNode->SetSize(Fraction(10,8)); + pNode->Arrange(*pDevice, maFormat); + + Size aSize = pDevice->LogicToPixel(Size(pNode->GetWidth(), pNode->GetHeight())); + aSize.extendBy(10, 0); // Add 5 pixels from both sides to accommodate extending parts of italics + pDevice->SetOutputSizePixel(aSize); + SmDrawingVisitor(*pDevice, pDevice->PixelToLogic(Point(5, 0)), pNode.get()); + + maItemDatas.push_back(std::make_unique<ElementData>(aElementSource, aHelpText)); + const OUString aId(weld::toId(maItemDatas.back().get())); + mpIconView->insert(-1, nullptr, &aId, pDevice, nullptr); + if (mpIconView->get_item_width() < aSize.Width()) + mpIconView->set_item_width(aSize.Width()); +} + +OUString SmElementsControl::GetElementSource(const OUString& itemId) +{ + return weld::fromId<ElementData*>(itemId)->maElementSource; +} + +OUString SmElementsControl::GetElementHelpText(const OUString& itemId) +{ + return weld::fromId<ElementData*>(itemId)->maHelpText; +} + +void SmElementsControl::setElementSetId(TranslateId pSetId) +{ + if (msCurrentSetId == pSetId) + return; + msCurrentSetId = pSetId; + build(); +} + +void SmElementsControl::addElements(const TranslateId& rCategory) +{ + mpIconView->clear(); // tdf#152411 clear before freeze to let gtk a11y drop reference + mpIconView->freeze(); + mpIconView->set_item_width(0); + maItemDatas.clear(); + + const auto& [aElementsArray, aElementsArraySize] = s_a5CategoryDescriptions.at(rCategory); + + for (size_t i = 0; i < aElementsArraySize; i++) + { + const auto& [element, elementHelp, elementVisual, visualTranslatable] = aElementsArray[i]; + if (element.empty()) + { + mpIconView->append_separator({}); + } + else + { + OUString aElement(OUString::createFromAscii(element)); + OUString aVisual(elementVisual.empty() ? aElement : OUString(elementVisual)); + if (visualTranslatable) + aVisual = aVisual.replaceFirst("$1", SmResId(visualTranslatable)); + OUString aHelp(elementHelp ? SmResId(elementHelp) : OUString()); + addElement(aVisual, aElement, aHelp); + } + } + + mpIconView->set_size_request(0, 0); + mpIconView->thaw(); +} + +void SmElementsControl::build() +{ + switch(m_nSmSyntaxVersion) + { + case 5: + addElements(msCurrentSetId); + break; + case 6: + default: + throw std::range_error("parser version limit"); + } +} + +void SmElementsControl::setSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion) +{ + if( m_nSmSyntaxVersion != nSmSyntaxVersion ) + { + m_nSmSyntaxVersion = nSmSyntaxVersion; + maParser.reset(starmathdatabase::GetVersionSmParser(nSmSyntaxVersion)); + maParser->SetImportSymbolNames(true); + // Be careful, we need the parser in order to build !!! + build(); + } +} + +IMPL_LINK(SmElementsControl, QueryTooltipHandler, const weld::TreeIter&, iter, OUString) +{ + if (const OUString id = mpIconView->get_id(iter); !id.isEmpty()) + return GetElementHelpText(id); + return {}; +} + +IMPL_LINK_NOARG(SmElementsControl, ElementActivatedHandler, weld::IconView&, bool) +{ + if (const OUString id = mpIconView->get_selected_id(); !id.isEmpty()) + maSelectHdlLink.Call(GetElementSource(id)); + + mpIconView->unselect_all(); + return true; +} + +SmElementsDockingWindow::SmElementsDockingWindow(SfxBindings* pInputBindings, SfxChildWindow* pChildWindow, vcl::Window* pParent) + : SfxDockingWindow(pInputBindings, pChildWindow, pParent, "DockingElements", + "modules/smath/ui/dockingelements.ui") + , mxElementsControl(std::make_unique<SmElementsControl>(m_xBuilder->weld_icon_view("elements"))) + , 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 (const auto& category : SmElementsControl::categories()) + mxElementListBox->append_text(SmResId(category)); + + 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)); +} + +void SmElementsDockingWindow::GetFocus() +{ + SfxDockingWindow::GetFocus(); + if (mxElementListBox) + mxElementListBox->grab_focus(); +} + +SmElementsDockingWindow::~SmElementsDockingWindow () +{ + disposeOnce(); +} + +void SmElementsDockingWindow::dispose() +{ + mxElementsControl.reset(); + mxElementListBox.reset(); + SfxDockingWindow::dispose(); +} + +void SmElementsDockingWindow::ToggleFloatingMode() +{ + SfxDockingWindow::ToggleFloatingMode(); + + if (GetFloatingWindow()) + GetFloatingWindow()->SetMinOutputSizePixel( Size(100, 100) ); + + Invalidate(); +} + +void SmElementsDockingWindow::setSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion) +{ + mxElementsControl->setSmSyntaxVersion(nSmSyntaxVersion); +} + +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, OUString, sElementSource, void) +{ + SmViewShell* pViewSh = GetView(); + + if (pViewSh) + { + SfxStringItem aInsertCommand(SID_INSERTCOMMANDTEXT, sElementSource); + pViewSh->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_INSERTCOMMANDTEXT, SfxCallMode::RECORD, { &aInsertCommand }); + } +} + +IMPL_LINK( SmElementsDockingWindow, ElementSelectedHandle, weld::ComboBox&, rList, void) +{ + for (const auto& category : SmElementsControl::categories()) + { + OUString aCurrentCategoryString = SmResId(category); + if (aCurrentCategoryString == rList.get_active_text()) + { + mxElementsControl->setElementSetId(category); + setSmSyntaxVersion(GetView()->GetDoc()->GetSmSyntaxVersion()); + 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/SmElementsPanel.cxx b/starmath/source/SmElementsPanel.cxx new file mode 100644 index 000000000..165e950ea --- /dev/null +++ b/starmath/source/SmElementsPanel.cxx @@ -0,0 +1,98 @@ +/* -*- 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/. + * + * 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/dispatch.hxx> +#include <svl/stritem.hxx> + +#include "SmElementsPanel.hxx" +#include <starmath.hrc> +#include <smmod.hxx> +#include <strings.hrc> +#include <view.hxx> + +namespace sm::sidebar +{ +// static +std::unique_ptr<PanelLayout> SmElementsPanel::Create(weld::Widget& rParent, + const SfxBindings& rBindings) +{ + return std::make_unique<SmElementsPanel>(rParent, rBindings); +} + +SmElementsPanel::SmElementsPanel(weld::Widget& rParent, const SfxBindings& rBindings) + : PanelLayout(&rParent, "MathElementsPanel", "modules/smath/ui/sidebarelements_math.ui") + , mrBindings(rBindings) + , mxCategoryList(m_xBuilder->weld_tree_view("categorylist")) + , mxElementsControl(std::make_unique<SmElementsControl>(m_xBuilder->weld_icon_view("elements"))) +{ + for (const auto& rCategoryId : SmElementsControl::categories()) + mxCategoryList->append_text(SmResId(rCategoryId)); + + mxCategoryList->set_size_request(-1, mxCategoryList->get_height_rows(6)); + + mxCategoryList->connect_changed(LINK(this, SmElementsPanel, CategorySelectedHandle)); + mxCategoryList->select_text(SmResId(RID_CATEGORY_UNARY_BINARY_OPERATORS)); + + mxElementsControl->setElementSetId(RID_CATEGORY_UNARY_BINARY_OPERATORS); + mxElementsControl->SetSelectHdl(LINK(this, SmElementsPanel, ElementClickHandler)); +} + +SmElementsPanel::~SmElementsPanel() +{ + mxElementsControl.reset(); + mxCategoryList.reset(); +} + +IMPL_LINK(SmElementsPanel, CategorySelectedHandle, weld::TreeView&, rList, void) +{ + const OUString sSelected = rList.get_selected_text(); + for (const auto& rCategoryId : SmElementsControl::categories()) + { + OUString aCurrentCategoryString = SmResId(rCategoryId); + if (aCurrentCategoryString == sSelected) + { + mxElementsControl->setElementSetId(rCategoryId); + if (SmViewShell* pViewSh = GetView()) + mxElementsControl->setSmSyntaxVersion(pViewSh->GetDoc()->GetSmSyntaxVersion()); + return; + } + } +} + +IMPL_LINK(SmElementsPanel, ElementClickHandler, OUString, ElementSource, void) +{ + if (SmViewShell* pViewSh = GetView()) + { + SfxStringItem aInsertCommand(SID_INSERTCOMMANDTEXT, ElementSource); + pViewSh->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_INSERTCOMMANDTEXT, SfxCallMode::RECORD, { &aInsertCommand }); + } +} + +SmViewShell* SmElementsPanel::GetView() const +{ + SfxViewShell* pView = mrBindings.GetDispatcher()->GetFrame()->GetViewShell(); + return dynamic_cast<SmViewShell*>(pView); +} + +} // end of namespace sm::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/SmElementsPanel.hxx b/starmath/source/SmElementsPanel.hxx new file mode 100644 index 000000000..d80d53e72 --- /dev/null +++ b/starmath/source/SmElementsPanel.hxx @@ -0,0 +1,56 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <sfx2/bindings.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> +#include <vcl/customweld.hxx> +#include <vcl/EnumContext.hxx> + +#include <ElementsDockingWindow.hxx> + +#include <memory> + +namespace sm::sidebar +{ +class SmElementsPanel : public PanelLayout +{ +public: + static std::unique_ptr<PanelLayout> Create(weld::Widget& rParent, const SfxBindings& rBindings); + SmElementsPanel(weld::Widget& rParent, const SfxBindings& rBindings); + ~SmElementsPanel(); + +private: + DECL_LINK(CategorySelectedHandle, weld::TreeView&, void); + DECL_LINK(ElementClickHandler, OUString, void); + + SmViewShell* GetView() const; + + const SfxBindings& mrBindings; + + std::unique_ptr<weld::TreeView> mxCategoryList; + std::unique_ptr<SmElementsControl> mxElementsControl; +}; + +} // end of namespace sm::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/SmPanelFactory.cxx b/starmath/source/SmPanelFactory.cxx new file mode 100644 index 000000000..8e9f146a1 --- /dev/null +++ b/starmath/source/SmPanelFactory.cxx @@ -0,0 +1,136 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/ui/XUIElementFactory.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <sfx2/sidebar/SidebarPanelBase.hxx> +#include <vcl/weldutils.hxx> + +#include "SmElementsPanel.hxx" +#include "SmPropertiesPanel.hxx" + +namespace +{ +typedef comphelper::WeakComponentImplHelper<css::ui::XUIElementFactory, css::lang::XServiceInfo> + PanelFactoryInterfaceBase; + +class SmPanelFactory final : public PanelFactoryInterfaceBase +{ +public: + SmPanelFactory() = default; + + SmPanelFactory(const SmPanelFactory&) = delete; + const SmPanelFactory& operator=(const SmPanelFactory&) = delete; + + // XUIElementFactory + css::uno::Reference<css::ui::XUIElement> SAL_CALL + createUIElement(const OUString& ResourceURL, + const css::uno::Sequence<css::beans::PropertyValue>& Arguments) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override; + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + +css::uno::Reference<css::ui::XUIElement> SAL_CALL SmPanelFactory::createUIElement( + const OUString& ResourceURL, const css::uno::Sequence<css::beans::PropertyValue>& Arguments) +{ + try + { + const comphelper::NamedValueCollection aArguments(Arguments); + auto xFrame(aArguments.getOrDefault("Frame", css::uno::Reference<css::frame::XFrame>())); + auto xParentWindow( + aArguments.getOrDefault("ParentWindow", css::uno::Reference<css::awt::XWindow>())); + const sal_uInt64 nBindingsValue(aArguments.getOrDefault("SfxBindings", sal_uInt64(0))); + SfxBindings* pBindings = reinterpret_cast<SfxBindings*>(nBindingsValue); + + weld::Widget* pParent(nullptr); + if (auto pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xParentWindow.get())) + pParent = pTunnel->getWidget(); + + if (!pParent) + throw css::uno::RuntimeException("SmPanelFactory::createUIElement: no ParentWindow"); + if (!xFrame) + throw css::uno::RuntimeException("SmPanelFactory::createUIElement: no Frame"); + if (!pBindings) + throw css::uno::RuntimeException("SmPanelFactory::createUIElement: no SfxBindings"); + + std::unique_ptr<PanelLayout> pPanel; + css::ui::LayoutSize aLayoutSize{ -1, -1, -1 }; + if (ResourceURL.endsWith("/MathPropertiesPanel")) + { + pPanel = sm::sidebar::SmPropertiesPanel::Create(*pParent); + } + else if (ResourceURL.endsWith("/MathElementsPanel")) + { + pPanel = sm::sidebar::SmElementsPanel::Create(*pParent, *pBindings); + aLayoutSize = { 300, -1, -1 }; + } + + if (pPanel) + return sfx2::sidebar::SidebarPanelBase::Create(ResourceURL, xFrame, std::move(pPanel), + aLayoutSize); + } + catch (const css::uno::RuntimeException&) + { + throw; + } + catch (const css::uno::Exception&) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException("SmPanelFactory::createUIElement exception", + nullptr, anyEx); + } + + return {}; +} + +OUString SmPanelFactory::getImplementationName() +{ + return "org.libreoffice.comp.Math.sidebar.SmPanelFactory"; +} + +sal_Bool SmPanelFactory::supportsService(OUString const& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> SmPanelFactory::getSupportedServiceNames() +{ + return { "com.sun.star.ui.UIElementFactory" }; +} + +} // end of unnamed namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +org_libreoffice_comp_Math_sidebar_SmPanelFactory(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new SmPanelFactory); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/SmPropertiesPanel.cxx b/starmath/source/SmPropertiesPanel.cxx new file mode 100644 index 000000000..3ffd25c40 --- /dev/null +++ b/starmath/source/SmPropertiesPanel.cxx @@ -0,0 +1,88 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/theUICommandDescription.hpp> +#include <com/sun/star/uno/Sequence.hxx> + +#include <comphelper/dispatchcommand.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequenceashashmap.hxx> + +#include "SmPropertiesPanel.hxx" + +namespace sm::sidebar +{ +// static +std::unique_ptr<PanelLayout> SmPropertiesPanel::Create(weld::Widget& rParent) +{ + return std::make_unique<SmPropertiesPanel>(rParent); +} + +SmPropertiesPanel::SmPropertiesPanel(weld::Widget& rParent) + : PanelLayout(&rParent, "MathPropertiesPanel", "modules/smath/ui/sidebarproperties_math.ui") + , mpFormatFontsButton(m_xBuilder->weld_button("btnFormatFonts")) + , mpFormatFontSizeButton(m_xBuilder->weld_button("btnFormatFontSize")) + , mpFormatSpacingButton(m_xBuilder->weld_button("btnFormatSpacing")) + , mpFormatAlignmentButton(m_xBuilder->weld_button("btnFormatAlignment")) + , maButtonCommands{ { mpFormatFontsButton.get(), ".uno:ChangeFont" }, + { mpFormatFontSizeButton.get(), ".uno:ChangeFontSize" }, + { mpFormatSpacingButton.get(), ".uno:ChangeDistance" }, + { mpFormatAlignmentButton.get(), ".uno:ChangeAlignment" } } +{ + // Set localized labels to the buttons + auto xConfs + = css::frame::theUICommandDescription::get(comphelper::getProcessComponentContext()); + if (css::uno::Reference<css::container::XNameAccess> xConf{ + xConfs->getByName("com.sun.star.formula.FormulaProperties"), css::uno::UNO_QUERY }) + { + for (const auto & [ button, command ] : maButtonCommands) + { + comphelper::SequenceAsHashMap props(xConf->getByName(command)); + button->set_label(props.getUnpackedValueOrDefault("Name", button->get_label())); + } + } + + mpFormatFontsButton->connect_clicked(LINK(this, SmPropertiesPanel, ButtonClickHandler)); + mpFormatFontSizeButton->connect_clicked(LINK(this, SmPropertiesPanel, ButtonClickHandler)); + mpFormatSpacingButton->connect_clicked(LINK(this, SmPropertiesPanel, ButtonClickHandler)); + mpFormatAlignmentButton->connect_clicked(LINK(this, SmPropertiesPanel, ButtonClickHandler)); +} + +SmPropertiesPanel::~SmPropertiesPanel() +{ + maButtonCommands.clear(); + + mpFormatFontsButton.reset(); + mpFormatFontSizeButton.reset(); + mpFormatSpacingButton.reset(); + mpFormatAlignmentButton.reset(); +} + +IMPL_LINK(SmPropertiesPanel, ButtonClickHandler, weld::Button&, rButton, void) +{ + if (OUString command = maButtonCommands[&rButton]; !command.isEmpty()) + comphelper::dispatchCommand(command, {}); +} + +} // end of namespace sm::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/SmPropertiesPanel.hxx b/starmath/source/SmPropertiesPanel.hxx new file mode 100644 index 000000000..e81463f37 --- /dev/null +++ b/starmath/source/SmPropertiesPanel.hxx @@ -0,0 +1,51 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <sfx2/sidebar/PanelLayout.hxx> + +#include <map> +#include <memory> + +namespace sm::sidebar +{ +class SmPropertiesPanel : public PanelLayout +{ +public: + static std::unique_ptr<PanelLayout> Create(weld::Widget& rParent); + SmPropertiesPanel(weld::Widget& rParent); + ~SmPropertiesPanel(); + +private: + DECL_LINK(ButtonClickHandler, weld::Button&, void); + + std::unique_ptr<weld::Button> mpFormatFontsButton; + std::unique_ptr<weld::Button> mpFormatFontSizeButton; + std::unique_ptr<weld::Button> mpFormatSpacingButton; + std::unique_ptr<weld::Button> mpFormatAlignmentButton; + + std::map<weld::Button*, OUString> maButtonCommands; +}; + +} // end of namespace sm::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/accessibility.cxx b/starmath/source/accessibility.cxx new file mode 100644 index 000000000..591bbeafb --- /dev/null +++ b/starmath/source/accessibility.cxx @@ -0,0 +1,738 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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/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 <unotools/accessiblestatesethelper.hxx> +#include <comphelper/accessibleeventnotifier.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> +#include <vcl/unohelp2.hxx> +#include <vcl/settings.hxx> + +#include <tools/gen.hxx> + +#include <editeng/editobj.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; + +SmGraphicAccessible::SmGraphicAccessible(SmGraphicWidget *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( std::exchange(nClientId, 0), *this ); + } +} + +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->GetOutputSizePixel() ); + 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(); + + const Point aOutPos; + const Size aOutSize(pWin->GetOutputSizePixel()); + css::awt::Rectangle aRet; + + aRet.X = aOutPos.X(); + aRet.Y = aOutPos.Y(); + aRet.Width = aOutSize.Width(); + aRet.Height = aOutSize.Height(); + + return aRet; +} + +awt::Point SAL_CALL SmGraphicAccessible::getLocation() +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + + const css::awt::Rectangle aRect(getBounds()); + css::awt::Point aRet; + + aRet.X = aRect.X; + aRet.Y = aRect.Y; + + return aRet; +} + +awt::Point SAL_CALL SmGraphicAccessible::getLocationOnScreen() +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + + css::awt::Point aScreenLoc(0, 0); + + css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent()); + if (xParent) + { + css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext( + xParent->getAccessibleContext()); + css::uno::Reference<css::accessibility::XAccessibleComponent> xParentComponent( + xParentContext, css::uno::UNO_QUERY); + OSL_ENSURE(xParentComponent.is(), + "WeldEditAccessible::getLocationOnScreen: no parent component!"); + if (xParentComponent.is()) + { + css::awt::Point aParentScreenLoc(xParentComponent->getLocationOnScreen()); + css::awt::Point aOwnRelativeLoc(getLocation()); + aScreenLoc.X = aParentScreenLoc.X + aOwnRelativeLoc.X; + aScreenLoc.Y = aParentScreenLoc.Y + aOwnRelativeLoc.Y; + } + } + + return aScreenLoc; +} + +awt::Size SAL_CALL SmGraphicAccessible::getSize() +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + Size aSz(pWin->GetOutputSizePixel()); + return css::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(); + + weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea(); + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + + return static_cast<sal_Int32>(rDevice.GetTextColor()); +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getBackground() +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + + weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea(); + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + + Wallpaper aWall(rDevice.GetBackground()); + Color nCol; + if (aWall.IsBitmap() || aWall.IsGradient()) + nCol = Application::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(); + + return pWin->GetDrawingArea()->get_accessible_parent(); +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getAccessibleIndexInParent() +{ + SolarMutexGuard aGuard; + + // -1 for child not found/no parent (according to specification) + sal_Int32 nRet = -1; + + css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent()); + if (!xParent) + return nRet; + + try + { + css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext( + xParent->getAccessibleContext()); + + // iterate over parent's children and search for this object + if (xParentContext.is()) + { + sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); + for (sal_Int32 nChild = 0; (nChild < nChildCount) && (-1 == nRet); ++nChild) + { + css::uno::Reference<css::accessibility::XAccessible> xChild( + xParentContext->getAccessibleChild(nChild)); + if (xChild.get() == this) + nRet = nChild; + } + } + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("svx", "WeldEditAccessible::getAccessibleIndexInParent"); + } + + return nRet; +} + +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() +{ + return new utl::AccessibleRelationSetHelper(); // empty relation set +} + +Reference< XAccessibleStateSet > SAL_CALL SmGraphicAccessible::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = + new ::utl::AccessibleStateSetHelper; + + 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 ); + weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea(); + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + if (COL_TRANSPARENT != rDevice.GetBackground().GetColor()) + pStateSet->AddState( AccessibleStateType::OPAQUE ); + } + + return pStateSet; +} + +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( std::exchange(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 + SmDocShell* pDoc = pWin->GetView().GetDoc(); + 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()); + + weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea(); + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + + std::vector<sal_Int32> aXAry; + rDevice.SetFont( pNode->GetFont() ); + rDevice.GetTextArray( aNodeText, &aXAry, 0, aNodeText.getLength() ); + aTLPos.AdjustX(nNodeIndex > 0 ? aXAry[nNodeIndex - 1] : 0 ); + aSize.setWidth( nNodeIndex > 0 ? aXAry[nNodeIndex] - aXAry[nNodeIndex - 1] : aXAry[nNodeIndex] ); + + aTLPos = rDevice.LogicToPixel( aTLPos ); + aSize = rDevice.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; + + weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea(); + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + + // get position relative to formula draw position + Point aPos( aPoint.X, aPoint.Y ); + aPos = rDevice.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.Contains( 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" ); + + tools::Long nNodeX = pNode->GetLeft(); + + std::vector<sal_Int32> aXAry; + rDevice.SetFont( pNode->GetFont() ); + rDevice.GetTextArray( aTxt, &aXAry, 0, aTxt.getLength() ); + for (sal_Int32 i = 0; i < aTxt.getLength() && nRes == -1; ++i) + { + if (aXAry[i] + nNodeX > aPos.X()) + nRes = i; + } + 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) ); + + rtl::Reference<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" + }; +} + +/* 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..0ea2c2f36 --- /dev/null +++ b/starmath/source/accessibility.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 . + */ + +#pragma once + +#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 <view.hxx> + +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; + + SmGraphicWidget* pWin; + + SmGraphicAccessible( const SmGraphicAccessible & ) = delete; + SmGraphicAccessible & operator = ( const SmGraphicAccessible & ) = delete; + + SmDocShell * GetDoc_Impl(); + OUString GetAccessibleText_Impl(); + +public: + explicit SmGraphicAccessible( SmGraphicWidget *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; +}; + +/* 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..7692aaa3f --- /dev/null +++ b/starmath/source/cfgitem.cxx @@ -0,0 +1,1433 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svl/itemset.hxx> +#include <svl/intitem.hxx> +#include <svl/eitem.hxx> +#include <svl/languageoptions.hxx> +#include <unotools/configmgr.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 <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; + +constexpr OUStringLiteral SYMBOL_LIST = u"SymbolList"; +constexpr OUStringLiteral FONT_FORMAT_LIST = u"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_GetOtherPropertyNames() +{ + return Sequence<OUString>{ "LoadSave/IsSaveOnlyUsedSymbols", + "Misc/AutoCloseBrackets", + "Misc/DefaultSmSyntaxVersion", + "Misc/IgnoreSpacesRight", + "Misc/SmEditWindowZoomFactor", + "Print/FormulaText", + "Print/Frame", + "Print/Size", + "Print/Title", + "Print/ZoomFactor", + "View/AutoRedraw", + "View/FormulaCursor", + "View/ToolboxVisible" }; +} + +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; + sal_uInt16 nSmEditWindowZoomFactor; + sal_uInt16 nSmSyntaxVersion; + bool bPrintTitle; + bool bPrintFormulaText; + bool bPrintFrame; + bool bIsSaveOnlyUsedSymbols; + bool bIsAutoCloseBrackets; + bool bIgnoreSpacesRight; + bool bToolboxVisible; + bool bAutoRedraw; + bool bFormulaCursor; + + SmCfgOther(); +}; + +constexpr sal_uInt16 nDefaultSmSyntaxVersion(5); + +SmCfgOther::SmCfgOther() + : ePrintSize(PRINT_SIZE_NORMAL) + , nPrintZoomFactor(100) + , nSmEditWindowZoomFactor(100) + // Defaulted as 5 so I have time to code the parser 6 + , nSmSyntaxVersion(nDefaultSmSyntaxVersion) + , 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( std::u16string_view 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( std::u16string_view 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") + , bIsOtherModified(false) + , bIsFormatModified(false) +{ + EnableNotification({ {} }); // Listen to everything under the node +} + + +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, + std::u16string_view rBaseNode ) const +{ + Sequence< OUString > aNames = lcl_GetSymbolPropertyNames(); + sal_Int32 nProps = aNames.getLength(); + + OUString aDelim( "/" ); + for (auto& rName : asNonConstRange(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::UnlockCommit() +{ + if (--m_nCommitLock == 0) + Commit(); +} + + +void SmMathConfig::Clear() +{ + // Re-read data on next request + pOther.reset(); + pFormat.reset(); + pFontFormatList.reset(); +} + + +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 ) +{ + CommitLocker aLock(*this); + 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 ); +} + + +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, + std::u16string_view rSymbolName, std::u16string_view rBaseNode ) const +{ + Sequence< OUString > aNames = lcl_GetFontPropertyNames(); + sal_Int32 nProps = aNames.getLength(); + + OUString aDelim( "/" ); + for (auto& rName : asNonConstRange(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); + + const Sequence<OUString> aNames(lcl_GetOtherPropertyNames()); + const Sequence<Any> aValues(GetProperties(aNames)); + if (aNames.getLength() != aValues.getLength()) + return; + + const Any* pValues = aValues.getConstArray(); + const Any* pVal = pValues; + + // LoadSave/IsSaveOnlyUsedSymbols + if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp)) + pOther->bIsSaveOnlyUsedSymbols = bTmp; + ++pVal; + // Misc/AutoCloseBrackets + if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp)) + pOther->bIsAutoCloseBrackets = bTmp; + ++pVal; + // Misc/DefaultSmSyntaxVersion + if (sal_Int16 nTmp; pVal->hasValue() && (*pVal >>= nTmp)) + pOther->nSmSyntaxVersion = nTmp; + ++pVal; + // Misc/IgnoreSpacesRight + if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp)) + pOther->bIgnoreSpacesRight = bTmp; + ++pVal; + // Misc/SmEditWindowZoomFactor + if (sal_Int16 nTmp; pVal->hasValue() && (*pVal >>= nTmp)) + pOther->nSmEditWindowZoomFactor = nTmp; + ++pVal; + // Print/FormulaText + if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp)) + pOther->bPrintFormulaText = bTmp; + ++pVal; + // Print/Frame + if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp)) + pOther->bPrintFrame = bTmp; + ++pVal; + // Print/Size: + if (sal_Int16 nTmp; pVal->hasValue() && (*pVal >>= nTmp)) + pOther->ePrintSize = static_cast<SmPrintSize>(nTmp); + ++pVal; + // Print/Title + if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp)) + pOther->bPrintTitle = bTmp; + ++pVal; + // Print/ZoomFactor + if (sal_Int16 nTmp; pVal->hasValue() && (*pVal >>= nTmp)) + pOther->nPrintZoomFactor = nTmp; + ++pVal; + // View/AutoRedraw + if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp)) + pOther->bAutoRedraw = bTmp; + ++pVal; + // View/FormulaCursor + if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp)) + pOther->bFormulaCursor = bTmp; + ++pVal; + // View/ToolboxVisible + if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp)) + pOther->bToolboxVisible = bTmp; + ++pVal; + + OSL_ENSURE(pVal - pValues == aNames.getLength(), "property mismatch"); + SetOtherModified( false ); +} + + +void SmMathConfig::SaveOther() +{ + if (!pOther || !IsOtherModified()) + return; + + const Sequence<OUString> aNames(lcl_GetOtherPropertyNames()); + Sequence<Any> aValues(aNames.getLength()); + + Any* pValues = aValues.getArray(); + Any* pVal = pValues; + + // LoadSave/IsSaveOnlyUsedSymbols + *pVal++ <<= pOther->bIsSaveOnlyUsedSymbols; + // Misc/AutoCloseBrackets + *pVal++ <<= pOther->bIsAutoCloseBrackets; + // Misc/DefaultSmSyntaxVersion + *pVal++ <<= pOther->nSmSyntaxVersion; + // Misc/IgnoreSpacesRight + *pVal++ <<= pOther->bIgnoreSpacesRight; + // Misc/SmEditWindowZoomFactor + *pVal++ <<= pOther->nSmEditWindowZoomFactor; + // Print/FormulaText + *pVal++ <<= pOther->bPrintFormulaText; + // Print/Frame + *pVal++ <<= pOther->bPrintFrame; + // Print/Size: + *pVal++ <<= static_cast<sal_Int16>(pOther->ePrintSize); + // Print/Title + *pVal++ <<= pOther->bPrintTitle; + // Print/ZoomFactor + *pVal++ <<= pOther->nPrintZoomFactor; + // View/AutoRedraw + *pVal++ <<= pOther->bAutoRedraw; + // View/FormulaCursor + *pVal++ <<= pOther->bFormulaCursor; + // View/ToolboxVisible + *pVal++ <<= pOther->bToolboxVisible; + + OSL_ENSURE(pVal - pValues == aNames.getLength(), "property mismatch"); + PutProperties(aNames, aValues); + + 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; + + CommitLocker aLock(*this); + *pFormat = rFormat; + SetFormatModified( true ); + + if (bSaveFontFormatList) + { + // needed for SmFontTypeDialog's DefaultButtonClickHdl + if (pFontFormatList) + pFontFormatList->SetModified( true ); + } +} + + +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) + { + CommitLocker aLock(*this); + 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) + { + CommitLocker aLock(*this); + pOther->nPrintZoomFactor = nVal; + SetOtherModified( true ); + } +} + + +sal_uInt16 SmMathConfig::GetSmEditWindowZoomFactor() const +{ + sal_uInt16 smzoomfactor; + if (!pOther) + const_cast<SmMathConfig*>(this)->LoadOther(); + smzoomfactor = pOther->nSmEditWindowZoomFactor; + return smzoomfactor < 10 || smzoomfactor > 1000 ? 100 : smzoomfactor; +} + + +void SmMathConfig::SetSmEditWindowZoomFactor( sal_uInt16 nVal ) +{ + if (!pOther) + LoadOther(); + if (nVal != pOther->nSmEditWindowZoomFactor) + { + CommitLocker aLock(*this); + pOther->nSmEditWindowZoomFactor = nVal; + SetOtherModified( true ); + } +} + + +bool SmMathConfig::SetOtherIfNotEqual( bool &rbItem, bool bNewVal ) +{ + if (bNewVal != rbItem) + { + CommitLocker aLock(*this); + rbItem = bNewVal; + SetOtherModified( true ); + return true; + } + return false; +} + + +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; +} + +sal_uInt16 SmMathConfig::GetDefaultSmSyntaxVersion() const +{ + if (utl::ConfigManager::IsFuzzing()) + return nDefaultSmSyntaxVersion; + if (!pOther) + const_cast<SmMathConfig*>(this)->LoadOther(); + return pOther->nSmSyntaxVersion; +} + +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 ); +} + +void SmMathConfig::SetDefaultSmSyntaxVersion( sal_uInt16 nVal ) +{ + if (!pOther) + LoadOther(); + if (nVal != pOther->nSmSyntaxVersion) + { + CommitLocker aLock(*this); + pOther->nSmSyntaxVersion = nVal; + SetOtherModified( true ); + } +} + +bool SmMathConfig::IsIgnoreSpacesRight() const +{ + if (utl::ConfigManager::IsFuzzing()) + return false; + if (!pOther) + const_cast<SmMathConfig*>(this)->LoadOther(); + return pOther->bIgnoreSpacesRight; +} + + +void SmMathConfig::SetIgnoreSpacesRight( bool bVal ) +{ + if (!pOther) + LoadOther(); + if (SetOtherIfNotEqual( pOther->bIgnoreSpacesRight, bVal )) + { + // reformat (displayed) formulas accordingly + Broadcast(SfxHint(SfxHintId::MathFormatChanged)); + } + +} + + +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 >& rNames ) +{ + Clear(); + if (std::find(rNames.begin(), rNames.end(), "Misc/IgnoreSpacesRight") != rNames.end()) + Broadcast(SfxHint(SfxHintId::MathFormatChanged)); +} + + +void SmMathConfig::ItemSetToConfig(const SfxItemSet &rSet) +{ + CommitLocker aLock(*this); + + sal_uInt16 nU16; + bool bVal; + if (const SfxUInt16Item* pPrintSizeItem = rSet.GetItemIfSet(SID_PRINTSIZE)) + { nU16 = pPrintSizeItem->GetValue(); + SetPrintSize( static_cast<SmPrintSize>(nU16) ); + } + if (const SfxUInt16Item* pPrintZoomItem = rSet.GetItemIfSet(SID_PRINTZOOM)) + { nU16 = pPrintZoomItem->GetValue(); + SetPrintZoomFactor( nU16 ); + } + if (const SfxUInt16Item* pPrintZoomItem = rSet.GetItemIfSet(SID_SMEDITWINDOWZOOM)) + { nU16 = pPrintZoomItem->GetValue(); + SetSmEditWindowZoomFactor( nU16 ); + } + if (const SfxBoolItem* pPrintTitleItem = rSet.GetItemIfSet(SID_PRINTTITLE)) + { bVal = pPrintTitleItem->GetValue(); + SetPrintTitle( bVal ); + } + if (const SfxBoolItem* pPrintTextItem = rSet.GetItemIfSet(SID_PRINTTEXT)) + { bVal = pPrintTextItem->GetValue(); + SetPrintFormulaText( bVal ); + } + if (const SfxBoolItem* pPrintZoomItem = rSet.GetItemIfSet(SID_PRINTFRAME)) + { bVal = pPrintZoomItem->GetValue(); + SetPrintFrame( bVal ); + } + if (const SfxBoolItem* pRedrawItem = rSet.GetItemIfSet(SID_AUTOREDRAW)) + { bVal = pRedrawItem->GetValue(); + SetAutoRedraw( bVal ); + } + if (const SfxBoolItem* pSpacesItem = rSet.GetItemIfSet(SID_NO_RIGHT_SPACES)) + { bVal = pSpacesItem->GetValue(); + SetIgnoreSpacesRight( bVal ); + } + if (const SfxBoolItem* pSymbolsItem = rSet.GetItemIfSet(SID_SAVE_ONLY_USED_SYMBOLS)) + { bVal = pSymbolsItem->GetValue(); + SetSaveOnlyUsedSymbols( bVal ); + } + + if (const SfxBoolItem* pBracketsItem = rSet.GetItemIfSet(SID_AUTO_CLOSE_BRACKETS)) + { + bVal = pBracketsItem->GetValue(); + SetAutoCloseBrackets( bVal ); + } + + if (const SfxUInt16Item* pSyntaxItem = rSet.GetItemIfSet(SID_DEFAULT_SM_SYNTAX_VERSION)) + { + nU16 = pSyntaxItem->GetValue(); + SetDefaultSmSyntaxVersion( nU16 ); + } +} + + +void SmMathConfig::ConfigToItemSet(SfxItemSet &rSet) const +{ + rSet.Put(SfxUInt16Item(SID_PRINTSIZE, + sal::static_int_cast<sal_uInt16>(GetPrintSize()))); + rSet.Put(SfxUInt16Item(SID_PRINTZOOM, + GetPrintZoomFactor())); + rSet.Put(SfxUInt16Item(SID_SMEDITWINDOWZOOM, + GetSmEditWindowZoomFactor())); + + rSet.Put(SfxBoolItem(SID_PRINTTITLE, IsPrintTitle())); + rSet.Put(SfxBoolItem(SID_PRINTTEXT, IsPrintFormulaText())); + rSet.Put(SfxBoolItem(SID_PRINTFRAME, IsPrintFrame())); + rSet.Put(SfxBoolItem(SID_AUTOREDRAW, IsAutoRedraw())); + rSet.Put(SfxBoolItem(SID_NO_RIGHT_SPACES, IsIgnoreSpacesRight())); + rSet.Put(SfxBoolItem(SID_SAVE_ONLY_USED_SYMBOLS, IsSaveOnlyUsedSymbols())); + rSet.Put(SfxBoolItem(SID_AUTO_CLOSE_BRACKETS, IsAutoCloseBrackets())); + rSet.Put(SfxBoolItem(SID_DEFAULT_SM_SYNTAX_VERSION, GetDefaultSmSyntaxVersion())); +} + +/* 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..d5479ab42 --- /dev/null +++ b/starmath/source/cursor.cxx @@ -0,0 +1,1561 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <cursor.hxx> +#include <visitors.hxx> +#include <document.hxx> +#include <view.hxx> +#include <comphelper/string.hxx> +#include <editeng/editeng.hxx> +#include <osl/diagnose.h> + +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 + tools::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 + tools::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; + tools::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.release()); + + //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 = u""; + 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.setChar(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.setChar(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.setChar(MS_CDOT); + token.nGroup = TG::Product; + token.aText = "cdot"; + pNewNode = new SmMathSymbolNode(token); + }break; + case EqualElement: + { + SmToken token; + token.eType = TASSIGN; + token.setChar(MS_ASSIGN); + token.nGroup = TG::Relation; + token.aText = "="; + pNewNode = new SmMathSymbolNode(token); + }break; + case LessThanElement: + { + SmToken token; + token.eType = TLT; + token.setChar(MS_LT); + token.nGroup = TG::Relation; + token.aText = "<"; + pNewNode = new SmMathSymbolNode(token); + }break; + case GreaterThanElement: + { + SmToken token; + token.eType = TGT; + token.setChar(MS_GT); + token.nGroup = TG::Relation; + token.aText = ">"; + pNewNode = new SmMathSymbolNode(token); + }break; + case PercentElement: + { + SmToken token; + token.eType = TTEXT; + token.setChar(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(std::u16string_view _aString) +{ + BeginEdit(); + Delete(); + + OUString aString( comphelper::string::strip(_aString, ' ') ); + + //Create instance of special node + SmToken token; + token.eType = TSPECIAL; + token.cMathChar = u""; + 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 = mpDocShell->GetParser()->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); + mpDocShell->maText = formula; + mpDocShell->GetEditEngine().QuickInsertText( formula, ESelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL ) ); + mpDocShell->GetEditEngine().QuickFormatDoc(); +} + +void SmCursor::RequestRepaint(){ + if (SmViewShell *pViewSh = SmGetActiveView()) + { + if ( SfxObjectCreateMode::EMBEDDED == mpDocShell->GetCreateMode() ) + mpDocShell->Repaint(); + else + pViewSh->GetGraphicWidget().Invalidate(); + } +} + +bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType) 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; + } + + return true; +} + +/////////////////////////////////////// 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..19992b341 --- /dev/null +++ b/starmath/source/dialog.cxx @@ -0,0 +1,2055 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <comphelper/string.hxx> +#include <o3tl/temporary.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(std::u16string_view 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.empty()) + { + 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::Toggleable&, 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_xSmZoom(m_xBuilder->weld_metric_spin_button("smzoom", FieldUnit::PERCENT)) +{ + 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() +{ + if (SmViewShell *pViewSh = SmGetActiveView()) + pViewSh->GetEditWindow()->UpdateStatus(); +} + +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(SID_PRINTSIZE, nPrintSize)); + rSet->Put(SfxUInt16Item(SID_PRINTZOOM, sal::static_int_cast<sal_uInt16>(m_xZoom->get_value(FieldUnit::PERCENT)))); + rSet->Put(SfxBoolItem(SID_PRINTTITLE, m_xTitle->get_active())); + rSet->Put(SfxBoolItem(SID_PRINTTEXT, m_xText->get_active())); + rSet->Put(SfxBoolItem(SID_PRINTFRAME, m_xFrame->get_active())); + rSet->Put(SfxBoolItem(SID_NO_RIGHT_SPACES, m_xNoRightSpaces->get_active())); + rSet->Put(SfxBoolItem(SID_SAVE_ONLY_USED_SYMBOLS, m_xSaveOnlyUsedSymbols->get_active())); + rSet->Put(SfxBoolItem(SID_AUTO_CLOSE_BRACKETS, m_xAutoCloseBrackets->get_active())); + rSet->Put(SfxUInt16Item(SID_SMEDITWINDOWZOOM, sal::static_int_cast<sal_uInt16>(m_xSmZoom->get_value(FieldUnit::PERCENT)))); + + if (SmViewShell *pViewSh = SmGetActiveView()) + pViewSh->GetEditWindow()->UpdateStatus(); + + return true; +} + +void SmPrintOptionsTabPage::Reset(const SfxItemSet* rSet) +{ + SmPrintSize ePrintSize = static_cast<SmPrintSize>(rSet->Get(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(rSet->Get(SID_PRINTZOOM).GetValue(), FieldUnit::PERCENT); + + m_xSmZoom->set_sensitive(true); + m_xSmZoom->set_value(rSet->Get(SID_SMEDITWINDOWZOOM).GetValue(), FieldUnit::PERCENT); + + m_xTitle->set_active(rSet->Get(SID_PRINTTITLE).GetValue()); + m_xNoRightSpaces->set_active(rSet->Get(SID_NO_RIGHT_SPACES).GetValue()); + m_xSaveOnlyUsedSymbols->set_active(rSet->Get(SID_SAVE_ONLY_USED_SYMBOLS).GetValue()); + m_xAutoCloseBrackets->set_active(rSet->Get(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::Toggleable&, 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< tools::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 +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::Toggleable&, 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->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(vcl::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).Contains(rMEvt.GetPosPixel())) + { + tools::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<tools::Long>(1, nColumns); + nRows = std::max<tools::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", true))) + , 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(); + } ); + + const bool bEmptySymbolSet = aSymbolSet.empty(); + m_xSymbolSetDisplay->SetSymbolSet( aSymbolSet ); + if (!bEmptySymbolSet) + 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_UCS4 cChar = rText.iterateCodePoints(&o3tl::temporary(sal_Int32(0))); + 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 = weld::fromId<const Subset*>(m_xFontsSubsetLB->get_active_id()); + 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.subView( 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", true), 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, + std::u16string_view 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::strip(rSymbolSetName, ' ') ); + // 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, std::u16string_view 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(weld::toId(&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..59353879d --- /dev/null +++ b/starmath/source/document.cxx @@ -0,0 +1,1232 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/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 <vcl/mapmod.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 <parse.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 <utility> +#include <oox/mathml/export.hxx> +#include <ElementsDockingWindow.hxx> +#include <smediteng.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"); +} + +void SmDocShell::SetSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion) +{ + mnSmSyntaxVersion = nSmSyntaxVersion; + maParser.reset(starmathdatabase::GetVersionSmParser(mnSmSyntaxVersion)); + SmViewShell* pViewSh = SmGetActiveView(); + if (pViewSh) + { + SmElementsDockingWindow* dockingWindow = pViewSh->GetDockingWindow(); + if(dockingWindow) + { + dockingWindow->setSmSyntaxVersion(nSmSyntaxVersion); + } + } +} + +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_GRAPHIC_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->GetGraphicWidget().Invalidate(); + } + + if ( bIsEnabled ) + EnableSetModified( bIsEnabled ); + SetModified(); + + // launch accessible event if necessary + SmGraphicAccessible *pAcc = pViewSh ? pViewSh->GetGraphicWidget().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_GRAPHIC_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_GRAPHIC_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_GRAPHIC_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) + { + if (SmViewShell *pView = SmGetActiveView()) + pOutDev = &pView->GetGraphicWidget().GetDrawingArea()->get_ref_device(); + 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 + vcl::text::ComplexTextLayoutFlags nLayoutMode = pOutDev->GetLayoutMode(); + pOutDev->SetLayoutMode( vcl::text::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() +{ + SmEditEngine::setSmItemPool(mpEditEngineItemPool.get(), maLinguOptions); +} + +EditEngine& SmDocShell::GetEditEngine() +{ + if (!mpEditEngine) + { + //! + //! see also SmEditWindow::DataChanged ! + //! + mpEditEngineItemPool = EditEngine::CreatePool(); + SmEditEngine::setSmItemPool(mpEditEngineItemPool.get(), maLinguOptions); + mpEditEngine.reset( new SmEditEngine( mpEditEngineItemPool.get() ) ); + 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() && + rDev.GetOwnerWindow()->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 + vcl::text::ComplexTextLayoutFlags nLayoutMode = rDev.GetLayoutMode(); + rDev.SetLayoutMode( vcl::text::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( vcl::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( vcl::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<SfxItemSetFixed< + SID_PRINTTITLE, SID_PRINTZOOM, + SID_NO_RIGHT_SPACES, SID_SAVE_ONLY_USED_SYMBOLS, + SID_AUTO_CLOSE_BRACKETS, SID_SMEDITWINDOWZOOM>>(GetPool()); + 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); + if (SmViewShell* pViewSh = SmGetActiveView()) + pViewSh->GetGraphicWidget().Invalidate(); + + if (bIsEnabled) + EnableSetModified(bIsEnabled); +} + +SmDocShell::SmDocShell( SfxModelFlags i_nSfxCreationFlags ) + : SfxObjectShell(i_nSfxCreationFlags) + , m_pMlElementTree(nullptr) + , mpPrinter(nullptr) + , mpTmpPrinter(nullptr) + , mnModifyCount(0) + , mbFormulaArranged(false) + , mnSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion()) +{ + SvtLinguConfig().GetOptions(maLinguOptions); + + SetPool(&SfxGetpApp()->GetPool()); + + SmModule *pp = SM_MOD(); + maFormat = pp->GetConfig()->GetStandardFormat(); + + StartListening(maFormat); + StartListening(*pp->GetConfig()); + + SetBaseModel(new SmModel(this)); + SetSmSyntaxVersion(mnSmSyntaxVersion); +} + +SmDocShell::~SmDocShell() +{ + SmModule *pp = SM_MOD(); + + EndListening(maFormat); + EndListening(*pp->GetConfig()); + + mpCursor.reset(); + mpEditEngine.reset(); + mpEditEngineItemPool.clear(); + mpPrinter.disposeAndClear(); + + mathml::SmMlIteratorFree(m_pMlElementTree); +} + +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); + aEquation.useHTMLMLEntities(true); + 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); + aEquation.SetUseHTMLMLEntities(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->GetFontFaceCollectionCount() == 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_GRAPHIC_SM: + //! very old (pre UNO) and ugly hack to invalidate the SmGraphicWidget. + //! 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_GRAPHIC_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..1c92b03fb --- /dev/null +++ b/starmath/source/edit.cxx @@ -0,0 +1,863 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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/ptrstyle.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 <osl/diagnose.h> +#include <o3tl/string_view.hxx> + +#include <edit.hxx> +#include <smmod.hxx> +#include <view.hxx> +#include <document.hxx> +#include <cfgitem.hxx> +#include <smediteng.hxx> + +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; + } +} + +SmEditTextWindow::SmEditTextWindow(SmEditWindow& rEditWindow) + : mrEditWindow(rEditWindow) + , aModifyIdle("SmEditWindow ModifyIdle") + , aCursorMoveIdle("SmEditWindow CursorMoveIdle") +{ + SetAcceptsTab(true); + + aModifyIdle.SetInvokeHandler(LINK(this, SmEditTextWindow, ModifyTimerHdl)); + aModifyIdle.SetPriority(TaskPriority::LOWEST); + + if (!SmViewShell::IsInlineEditEnabled()) + { + aCursorMoveIdle.SetInvokeHandler(LINK(this, SmEditTextWindow, CursorMoveTimerHdl)); + aCursorMoveIdle.SetPriority(TaskPriority::LOWEST); + } +} + +SmEditTextWindow::~SmEditTextWindow() +{ + aModifyIdle.Stop(); + StartCursorMove(); +} + +EditEngine* SmEditTextWindow::GetEditEngine() const +{ + SmDocShell *pDoc = mrEditWindow.GetDoc(); + assert(pDoc); + return &pDoc->GetEditEngine(); +} + +void SmEditTextWindow::EditViewScrollStateChange() +{ + mrEditWindow.SetScrollBarRanges(); +} + +void SmEditTextWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + Color aBgColor = rStyleSettings.GetWindowColor(); + + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + rDevice.SetBackground(aBgColor); + + SetHelpId(HID_SMA_COMMAND_WIN_EDIT); + + EnableRTL(false); + + EditEngine* pEditEngine = GetEditEngine(); + + m_xEditView.reset(new EditView(pEditEngine, nullptr)); + m_xEditView->setEditViewCallbacks(this); + + pEditEngine->InsertView(m_xEditView.get()); + + m_xEditView->SetOutputArea(mrEditWindow.AdjustScrollBars()); + + m_xEditView->SetBackgroundColor(aBgColor); + + pDrawingArea->set_cursor(PointerStyle::Text); + + pEditEngine->SetStatusEventHdl(LINK(this, SmEditTextWindow, EditStatusHdl)); + + InitAccessible(); + + //Apply zoom to smeditwindow text + if(GetEditView()) + static_cast<SmEditEngine*>(GetEditEngine())->executeZoom(GetEditView()); +} + +SmEditWindow::SmEditWindow(SmCmdBoxWindow &rMyCmdBoxWin, weld::Builder& rBuilder) + : rCmdBox(rMyCmdBoxWin) + , mxScrolledWindow(rBuilder.weld_scrolled_window("scrolledwindow", true)) +{ + mxScrolledWindow->connect_vadjustment_changed(LINK(this, SmEditWindow, ScrollHdl)); + + CreateEditView(rBuilder); +} + +SmEditWindow::~SmEditWindow() COVERITY_NOEXCEPT_FALSE +{ + DeleteEditView(); + mxScrolledWindow.reset(); +} + +weld::Window* SmEditWindow::GetFrameWeld() const +{ + return rCmdBox.GetFrameWeld(); +} + +void SmEditTextWindow::StartCursorMove() +{ + if (!SmViewShell::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() const +{ + return mxTextControl ? mxTextControl->GetEditView() : nullptr; +} + +EditEngine * SmEditWindow::GetEditEngine() +{ + if (SmDocShell *pDoc = GetDoc()) + return &pDoc->GetEditEngine(); + return nullptr; +} + +void SmEditTextWindow::StyleUpdated() +{ + WeldEditView::StyleUpdated(); + EditEngine *pEditEngine = GetEditEngine(); + SmDocShell *pDoc = mrEditWindow.GetDoc(); + + if (pEditEngine && pDoc) + { + //! + //! see also SmDocShell::GetEditEngine() ! + //! + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + pDoc->UpdateEditEngineDefaultFonts(); + 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 ); + + Resize(); + } + + // Apply zoom to smeditwindow text + static_cast<SmEditEngine*>(GetEditEngine())->executeZoom(GetEditView()); +} + +IMPL_LINK_NOARG(SmEditTextWindow, ModifyTimerHdl, Timer *, void) +{ + UpdateStatus(false); + aModifyIdle.Stop(); +} + +IMPL_LINK_NOARG(SmEditTextWindow, 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 (SmViewShell::IsInlineEditEnabled()) + return; + + ESelection aNewSelection(GetSelection()); + + if (aNewSelection != aOldSelection) + { + if (SmViewShell *pViewSh = mrEditWindow.GetView()) + { + // get row and column to look for + sal_Int32 nRow; + sal_uInt16 nCol; + SmGetLeftSelectionPart(aNewSelection, nRow, nCol); + pViewSh->GetGraphicWidget().SetCursorPos(static_cast<sal_uInt16>(nRow), nCol); + aOldSelection = aNewSelection; + } + } + aCursorMoveIdle.Stop(); +} + +bool SmEditTextWindow::MouseButtonUp(const MouseEvent &rEvt) +{ + bool bRet = WeldEditView::MouseButtonUp(rEvt); + if (!SmViewShell::IsInlineEditEnabled()) + CursorMoveTimerHdl(&aCursorMoveIdle); + mrEditWindow.InvalidateSlots(); + return bRet; +} + +bool SmEditTextWindow::Command(const CommandEvent& rCEvt) +{ + // no zooming in Command window + const CommandWheelData* pWData = rCEvt.GetWheelData(); + if (pWData && CommandWheelMode::ZOOM == pWData->GetMode()) + return true; + + //pass alt press/release to parent impl + if (rCEvt.GetCommand() == CommandEventId::ModKeyChange) + return false; + + if (rCEvt.GetCommand() == CommandEventId::ContextMenu) + { + ReleaseMouse(); + SmCmdBoxWindow& rCmdBox = mrEditWindow.GetCmdBox(); + rCmdBox.ShowContextMenu(rCmdBox.WidgetToWindowPos(*GetDrawingArea(), rCEvt.GetMousePosPixel())); + GrabFocus(); + return true; + } + + bool bConsumed = WeldEditView::Command(rCEvt); + if (bConsumed) + UserPossiblyChangedText(); + return bConsumed; +} + +bool SmEditTextWindow::KeyInput(const KeyEvent& rKEvt) +{ + if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE) + { + bool bCallBase = true; + SfxViewShell* pViewShell = mrEditWindow.GetView(); + if ( dynamic_cast<const SmViewShell *>(pViewShell) ) + { + // Terminate possible InPlace mode + bCallBase = !pViewShell->Escape(); + } + return !bCallBase; + } + + StartCursorMove(); + + bool autoClose = false; + EditView* pEditView = GetEditView(); + 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 (o3tl::trim(selected) == u"<?>") + 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; + } + + bool bConsumed = WeldEditView::KeyInput(rKEvt); + if (!bConsumed) + { + SmViewShell *pView = mrEditWindow.GetView(); + if (pView) + bConsumed = pView->KeyInput(rKEvt); + if (pView && !bConsumed) + { + // F1 (help) leads to the destruction of this + Flush(); + if ( aModifyIdle.IsActive() ) + aModifyIdle.Stop(); + } + else + { + // SFX has maybe called a slot of the view and thus (because of a hack in SFX) + // set the focus to the view + SmViewShell* pVShell = mrEditWindow.GetView(); + if ( pVShell && pVShell->GetGraphicWidget().HasFocus() ) + { + GrabFocus(); + } + } + } + else + { + UserPossiblyChangedText(); + } + + // 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); + } + + mrEditWindow.InvalidateSlots(); + return bConsumed; +} + +void SmEditTextWindow::UserPossiblyChangedText() +{ + // have doc-shell modified only for formula input/change and not + // cursor travelling and such things... + SmDocShell *pDocShell = mrEditWindow.GetDoc(); + EditEngine *pEditEngine = GetEditEngine(); + if (pDocShell && pEditEngine && pEditEngine->IsModified()) + pDocShell->SetModified(true); + aModifyIdle.Start(); +} + +void SmEditWindow::CreateEditView(weld::Builder& rBuilder) +{ + assert(!mxTextControl); + + EditEngine *pEditEngine = GetEditEngine(); + //! pEditEngine may be 0. + //! For example when the program is used by the document-converter + if (!pEditEngine) + return; + + mxTextControl.reset(new SmEditTextWindow(*this)); + mxTextControlWin.reset(new weld::CustomWeld(rBuilder, "editview", *mxTextControl)); + + SetScrollBarRanges(); +} + +IMPL_LINK_NOARG(SmEditTextWindow, EditStatusHdl, EditStatus&, void) +{ + Resize(); +} + +IMPL_LINK(SmEditWindow, ScrollHdl, weld::ScrolledWindow&, rScrolledWindow, void) +{ + if (EditView* pEditView = GetEditView()) + { + pEditView->SetVisArea(tools::Rectangle( + Point(0, + rScrolledWindow.vadjustment_get_value()), + pEditView->GetVisArea().GetSize())); + pEditView->Invalidate(); + } +} + +tools::Rectangle SmEditWindow::AdjustScrollBars() +{ + tools::Rectangle aRect(Point(), rCmdBox.GetOutputSizePixel()); + + if (mxScrolledWindow) + { + const auto nScrollSize = mxScrolledWindow->get_scroll_thickness(); + const auto nMargin = nScrollSize + 2; + aRect.AdjustRight(-nMargin); + aRect.AdjustBottom(-nMargin); + } + + return aRect; +} + +void SmEditWindow::SetScrollBarRanges() +{ + EditEngine *pEditEngine = GetEditEngine(); + if (!pEditEngine) + return; + if (!mxScrolledWindow) + return; + EditView* pEditView = GetEditView(); + if (!pEditView) + return; + + int nVUpper = pEditEngine->GetTextHeight(); + int nVCurrentDocPos = pEditView->GetVisArea().Top(); + const Size aOut(pEditView->GetOutputArea().GetSize()); + int nVStepIncrement = aOut.Height() * 2 / 10; + int nVPageIncrement = aOut.Height() * 8 / 10; + int nVPageSize = aOut.Height(); + + /* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has + effectively... + + lower = gtk_adjustment_get_lower + upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size + + and requires that upper > lower or the deceleration animation never ends + */ + nVPageSize = std::min(nVPageSize, nVUpper); + + mxScrolledWindow->vadjustment_configure(nVCurrentDocPos, 0, nVUpper, + nVStepIncrement, nVPageIncrement, nVPageSize); +} + +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) +{ + if (!mxTextControl) + return; + mxTextControl->SetText(rText); +} + +void SmEditWindow::Flush() +{ + if (!mxTextControl) + return; + mxTextControl->Flush(); +} + +void SmEditWindow::GrabFocus() +{ + if (!mxTextControl) + return; + mxTextControl->GrabFocus(); +} + +void SmEditTextWindow::SetText(const OUString& rText) +{ + EditEngine *pEditEngine = GetEditEngine(); + OSL_ENSURE( pEditEngine, "EditEngine missing" ); + if (!pEditEngine || pEditEngine->IsModified()) + return; + + EditView* pEditView = GetEditView(); + 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(); + + // Apply zoom to smeditwindow text + static_cast<SmEditEngine*>(pEditView->GetEditEngine())->executeZoom(pEditView); + pEditView->SetSelection(eSelection); +} + +void SmEditTextWindow::GetFocus() +{ + WeldEditView::GetFocus(); + + EditEngine *pEditEngine = GetEditEngine(); + if (pEditEngine) + pEditEngine->SetStatusEventHdl(LINK(this, SmEditTextWindow, EditStatusHdl)); + + //Let SmViewShell know we got focus + if (mrEditWindow.GetView() && SmViewShell::IsInlineEditEnabled()) + mrEditWindow.GetView()->SetInsertIntoEditWindow(true); +} + +void SmEditTextWindow::LoseFocus() +{ + EditEngine *pEditEngine = GetEditEngine(); + if (pEditEngine) + pEditEngine->SetStatusEventHdl( Link<EditStatus&,void>() ); + + WeldEditView::LoseFocus(); +} + +bool SmEditWindow::IsAllSelected() const +{ + EditEngine *pEditEngine = const_cast<SmEditWindow *>(this)->GetEditEngine(); + if (!pEditEngine) + return false; + EditView* pEditView = GetEditView(); + if (!pEditView) + return false; + bool bRes = false; + 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() +{ + if (EditView* pEditView = GetEditView()) + { + // 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) +{ + if (EditView* pEditView = GetEditView()) + { + 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(); + } +} + +void SmEditWindow::SelNextMark() +{ + if (!mxTextControl) + return; + mxTextControl->SelNextMark(); +} + +// Makes selection to next <?> symbol +void SmEditTextWindow::SelNextMark() +{ + EditEngine *pEditEngine = GetEditEngine(); + if (!pEditEngine) + return; + EditView* pEditView = GetEditView(); + if (!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(); + if (!pEditEngine) + return; + EditView* pEditView = GetEditView(); + if (!pEditView) + return; + + ESelection eSelection = pEditView->GetSelection(); + sal_Int32 nPara = eSelection.nStartPara; + sal_Int32 nMax = eSelection.nStartPos; + OUString aText(pEditEngine->GetText(nPara)); + static const OUStringLiteral aMark(u"<?>"); + 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)); +} + +// returns true iff 'rText' contains a mark +static bool HasMark(std::u16string_view rText) +{ + return rText.find(u"<?>") != std::u16string_view::npos; +} + +ESelection SmEditWindow::GetSelection() const +{ + if (mxTextControl) + return mxTextControl->GetSelection(); + return ESelection(); +} + +ESelection SmEditTextWindow::GetSelection() const +{ + // pointer may be 0 when reloading a document and the old view + // was already destroyed + if (EditView* pEditView = GetEditView()) + return pEditView->GetSelection(); + return ESelection(); +} + +void SmEditWindow::SetSelection(const ESelection &rSel) +{ + if (EditView* pEditView = GetEditView()) + 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 +{ + EditView* pEditView = GetEditView(); + return pEditView && pEditView->HasSelection(); +} + +void SmEditTextWindow::UpdateStatus(bool bSetDocModified) +{ + SmModule *pMod = SM_MOD(); + if (pMod && pMod->GetConfig()->IsAutoRedraw()) + Flush(); + + if (SmDocShell *pModifyDoc = bSetDocModified ? mrEditWindow.GetDoc() : nullptr) + pModifyDoc->SetModified(); + + static_cast<SmEditEngine*>(GetEditEngine())->executeZoom(GetEditView()); +} + +void SmEditWindow::UpdateStatus() +{ + mxTextControl->UpdateStatus(/*bSetDocModified*/false); +} + +void SmEditWindow::Cut() +{ + if (mxTextControl) + { + mxTextControl->Cut(); + mxTextControl->UpdateStatus(true); + } +} + +void SmEditWindow::Copy() +{ + if (mxTextControl) + mxTextControl->Copy(); +} + +void SmEditWindow::Paste() +{ + if (mxTextControl) + { + mxTextControl->Paste(); + mxTextControl->UpdateStatus(true); + } +} + +void SmEditWindow::Delete() +{ + if (mxTextControl) + { + mxTextControl->Delete(); + mxTextControl->UpdateStatus(true); + } +} + +void SmEditWindow::InsertText(const OUString& rText) +{ + if (!mxTextControl) + return; + mxTextControl->InsertText(rText); +} + +void SmEditTextWindow::InsertText(const OUString& rText) +{ + EditView* pEditView = GetEditView(); + 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; + + pEditView->InsertText(string); + + // 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 SmEditTextWindow::Flush() +{ + EditEngine *pEditEngine = GetEditEngine(); + if (pEditEngine && pEditEngine->IsModified()) + { + pEditEngine->ClearModifyFlag(); + if (SmViewShell *pViewSh = mrEditWindow.GetView()) + { + 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 (EditView* pEditView = GetEditView()) + { + if (EditEngine* pEditEngine = pEditView->GetEditEngine()) + { + pEditEngine->SetStatusEventHdl( Link<EditStatus&,void>() ); + pEditEngine->RemoveView(pEditView); + } + mxTextControlWin.reset(); + mxTextControl.reset(); + } +} + +/* 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..7fd3a476a --- /dev/null +++ b/starmath/source/eqnolefilehdr.hxx @@ -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 . + */ + +#pragma once + +#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 ); + +/* 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..65ec09558 --- /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] = 0; + 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/mathml/attribute.cxx b/starmath/source/mathml/attribute.cxx new file mode 100644 index 000000000..a1be708ae --- /dev/null +++ b/starmath/source/mathml/attribute.cxx @@ -0,0 +1,475 @@ +/* -*- 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 <mathml/attribute.hxx> + +void SmMlAttribute::clearPreviousAttributeValue() +{ + switch (m_aSmMlAttributeValueType) + { + case SmMlAttributeValueType::NMlEmpty: + break; + case SmMlAttributeValueType::MlHref: + if (m_aAttributeValue.m_aHref.m_aLnk) + delete m_aAttributeValue.m_aHref.m_aLnk; + break; + case SmMlAttributeValueType::MlLspace: + if (m_aAttributeValue.m_aLspace.m_aLengthValue.m_aOriginalText) + delete m_aAttributeValue.m_aLspace.m_aLengthValue.m_aOriginalText; + break; + case SmMlAttributeValueType::MlMathsize: + if (m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aOriginalText) + delete m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aOriginalText; + break; + case SmMlAttributeValueType::MlMaxsize: + if (m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aOriginalText) + delete m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aOriginalText; + break; + case SmMlAttributeValueType::MlMinsize: + if (m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aOriginalText) + delete m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aOriginalText; + break; + case SmMlAttributeValueType::MlRspace: + if (m_aAttributeValue.m_aRspace.m_aLengthValue.m_aOriginalText) + delete m_aAttributeValue.m_aRspace.m_aLengthValue.m_aOriginalText; + break; + default: + break; + } +} + +void SmMlAttribute::setDefaultAttributeValue() +{ + switch (m_aSmMlAttributeValueType) + { + case SmMlAttributeValueType::NMlEmpty: + break; + case SmMlAttributeValueType::MlAccent: + m_aAttributeValue.m_aAccent.m_aAccent = SmMlAttributeValueAccent::MlFalse; + break; + case SmMlAttributeValueType::MlDir: + m_aAttributeValue.m_aDir.m_aDir = SmMlAttributeValueDir::MlLtr; + break; + case SmMlAttributeValueType::MlDisplaystyle: + m_aAttributeValue.m_aDisplaystyle.m_aDisplaystyle + = SmMlAttributeValueDisplaystyle::MlFalse; + break; + case SmMlAttributeValueType::MlFence: + m_aAttributeValue.m_aFence.m_aFence = SmMlAttributeValueFence::MlFalse; + break; + case SmMlAttributeValueType::MlForm: + m_aAttributeValue.m_aForm.m_aForm = SmMlAttributeValueForm::MlInfix; + break; + case SmMlAttributeValueType::MlHref: + m_aAttributeValue.m_aHref.m_aHref = SmMlAttributeValueHref::NMlEmpty; + m_aAttributeValue.m_aHref.m_aLnk = new OUString(u""); + break; + case SmMlAttributeValueType::MlLspace: + m_aAttributeValue.m_aLspace.m_aLengthValue.m_aLengthUnit = SmLengthUnit::MlEm; + m_aAttributeValue.m_aLspace.m_aLengthValue.m_aLengthValue = 5.0 / 18; + m_aAttributeValue.m_aLspace.m_aLengthValue.m_aOriginalText = new OUString(u"5/18em"); + break; + case SmMlAttributeValueType::MlMathbackground: + m_aAttributeValue.m_aMathbackground.m_aMathbackground + = SmMlAttributeValueMathbackground::MlTransparent; + break; + case SmMlAttributeValueType::MlMathcolor: + m_aAttributeValue.m_aMathcolor.m_aMathcolor = SmMlAttributeValueMathcolor::MlDefault; + break; + case SmMlAttributeValueType::MlMathsize: + m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aLengthUnit = SmLengthUnit::MlP; + m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aLengthValue = 100; + m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aOriginalText = new OUString(u"100%"); + break; + case SmMlAttributeValueType::MlMathvariant: + m_aAttributeValue.m_aMathvariant.m_aMathvariant = SmMlAttributeValueMathvariant::normal; + break; + case SmMlAttributeValueType::MlMaxsize: + m_aAttributeValue.m_aMaxsize.m_aMaxsize = SmMlAttributeValueMaxsize::MlInfinity; + m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aLengthUnit = SmLengthUnit::MlP; + m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aLengthValue = 10000; + m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aOriginalText = new OUString(u"10000%"); + break; + case SmMlAttributeValueType::MlMinsize: + m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aLengthUnit = SmLengthUnit::MlP; + m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aLengthValue = 1; + m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aOriginalText = new OUString(u"1%"); + break; + case SmMlAttributeValueType::MlMovablelimits: + m_aAttributeValue.m_aMovablelimits.m_aMovablelimits + = SmMlAttributeValueMovablelimits::MlFalse; + break; + case SmMlAttributeValueType::MlRspace: + m_aAttributeValue.m_aRspace.m_aLengthValue.m_aLengthUnit = SmLengthUnit::MlEm; + m_aAttributeValue.m_aRspace.m_aLengthValue.m_aLengthValue = 5.0 / 18; + m_aAttributeValue.m_aRspace.m_aLengthValue.m_aOriginalText = new OUString(u"5/18em"); + break; + case SmMlAttributeValueType::MlSeparator: + m_aAttributeValue.m_aSeparator.m_aSeparator = SmMlAttributeValueSeparator::MlFalse; + break; + case SmMlAttributeValueType::MlStretchy: + m_aAttributeValue.m_aStretchy.m_aStretchy = SmMlAttributeValueStretchy::MlFalse; + break; + case SmMlAttributeValueType::MlSymmetric: + m_aAttributeValue.m_aSymmetric.m_aSymmetric = SmMlAttributeValueSymmetric::MlFalse; + break; + } +} + +void SmMlAttribute::setAttributeValue(const SmMlAttribute* aAttribute) +{ + switch (aAttribute->getMlAttributeValueType()) + { + case SmMlAttributeValueType::NMlEmpty: + clearPreviousAttributeValue(); + m_aSmMlAttributeValueType = SmMlAttributeValueType::NMlEmpty; + break; + case SmMlAttributeValueType::MlAccent: + setMlAccent(aAttribute->getMlAccent()); + break; + case SmMlAttributeValueType::MlDir: + setMlDir(aAttribute->getMlDir()); + break; + case SmMlAttributeValueType::MlDisplaystyle: + setMlDisplaystyle(aAttribute->getMlDisplaystyle()); + break; + case SmMlAttributeValueType::MlFence: + setMlFence(aAttribute->getMlFence()); + break; + case SmMlAttributeValueType::MlForm: + setMlForm(aAttribute->getMlForm()); + break; + case SmMlAttributeValueType::MlHref: + setMlHref(aAttribute->getMlHref()); + break; + case SmMlAttributeValueType::MlLspace: + setMlLspace(aAttribute->getMlLspace()); + break; + case SmMlAttributeValueType::MlMathbackground: + setMlMathbackground(aAttribute->getMlMathbackground()); + break; + case SmMlAttributeValueType::MlMathcolor: + setMlMathcolor(aAttribute->getMlMathcolor()); + break; + case SmMlAttributeValueType::MlMathsize: + setMlMathsize(aAttribute->getMlMathsize()); + break; + case SmMlAttributeValueType::MlMathvariant: + setMlMathvariant(aAttribute->getMlMathvariant()); + break; + case SmMlAttributeValueType::MlMaxsize: + setMlMaxsize(aAttribute->getMlMaxsize()); + break; + case SmMlAttributeValueType::MlMinsize: + setMlMinsize(aAttribute->getMlMinsize()); + break; + case SmMlAttributeValueType::MlMovablelimits: + setMlMovablelimits(aAttribute->getMlMovablelimits()); + break; + case SmMlAttributeValueType::MlRspace: + setMlRspace(aAttribute->getMlRspace()); + break; + case SmMlAttributeValueType::MlSeparator: + setMlSeparator(aAttribute->getMlSeparator()); + break; + case SmMlAttributeValueType::MlStretchy: + setMlStretchy(aAttribute->getMlStretchy()); + break; + case SmMlAttributeValueType::MlSymmetric: + setMlSymmetric(aAttribute->getMlSymmetric()); + break; + } +} + +/* get values */ +/*************************************************************************************************/ + +const struct SmMlAccent* SmMlAttribute::getMlAccent() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlAccent) + return &m_aAttributeValue.m_aAccent; + return nullptr; +} + +const struct SmMlDir* SmMlAttribute::getMlDir() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlDir) + return &m_aAttributeValue.m_aDir; + return nullptr; +} + +const struct SmMlDisplaystyle* SmMlAttribute::getMlDisplaystyle() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlDisplaystyle) + return &m_aAttributeValue.m_aDisplaystyle; + return nullptr; +} + +const struct SmMlFence* SmMlAttribute::getMlFence() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlFence) + return &m_aAttributeValue.m_aFence; + return nullptr; +} + +const struct SmMlForm* SmMlAttribute::getMlForm() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlForm) + return &m_aAttributeValue.m_aForm; + return nullptr; +} + +const struct SmMlHref* SmMlAttribute::getMlHref() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlHref) + return &m_aAttributeValue.m_aHref; + return nullptr; +} + +const struct SmMlLspace* SmMlAttribute::getMlLspace() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlLspace) + return &m_aAttributeValue.m_aLspace; + return nullptr; +} + +const struct SmMlMathbackground* SmMlAttribute::getMlMathbackground() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMathbackground) + return &m_aAttributeValue.m_aMathbackground; + return nullptr; +} + +const struct SmMlMathcolor* SmMlAttribute::getMlMathcolor() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMathcolor) + return &m_aAttributeValue.m_aMathcolor; + return nullptr; +} + +const struct SmMlMathsize* SmMlAttribute::getMlMathsize() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlAccent) + return &m_aAttributeValue.m_aMathsize; + return nullptr; +} + +const struct SmMlMathvariant* SmMlAttribute::getMlMathvariant() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMathvariant) + return &m_aAttributeValue.m_aMathvariant; + return nullptr; +} + +const struct SmMlMaxsize* SmMlAttribute::getMlMaxsize() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMaxsize) + return &m_aAttributeValue.m_aMaxsize; + return nullptr; +} + +const struct SmMlMinsize* SmMlAttribute::getMlMinsize() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMinsize) + return &m_aAttributeValue.m_aMinsize; + return nullptr; +} + +const struct SmMlMovablelimits* SmMlAttribute::getMlMovablelimits() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMovablelimits) + return &m_aAttributeValue.m_aMovablelimits; + return nullptr; +} + +const struct SmMlRspace* SmMlAttribute::getMlRspace() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlRspace) + return &m_aAttributeValue.m_aRspace; + return nullptr; +} + +const struct SmMlSeparator* SmMlAttribute::getMlSeparator() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlSeparator) + return &m_aAttributeValue.m_aSeparator; + return nullptr; +} + +const struct SmMlStretchy* SmMlAttribute::getMlStretchy() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlStretchy) + return &m_aAttributeValue.m_aStretchy; + return nullptr; +} + +const struct SmMlSymmetric* SmMlAttribute::getMlSymmetric() const +{ + if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlSymmetric) + return &m_aAttributeValue.m_aSymmetric; + return nullptr; +} + +/* set values */ +/*************************************************************************************************/ + +void SmMlAttribute::setMlAccent(const SmMlAccent* aAccent) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aAccent.m_aAccent = aAccent->m_aAccent; +} + +void SmMlAttribute::setMlDir(const SmMlDir* aDir) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aDir.m_aDir = aDir->m_aDir; +} + +void SmMlAttribute::setMlDisplaystyle(const SmMlDisplaystyle* aDisplaystyle) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aDisplaystyle.m_aDisplaystyle = aDisplaystyle->m_aDisplaystyle; +} + +void SmMlAttribute::setMlFence(const SmMlFence* aFence) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aFence.m_aFence = aFence->m_aFence; +} + +void SmMlAttribute::setMlForm(const SmMlForm* aForm) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aForm.m_aForm = aForm->m_aForm; +} + +void SmMlAttribute::setMlHref(const SmMlHref* aHref) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aHref.m_aHref = aHref->m_aHref; + m_aAttributeValue.m_aHref.m_aLnk = new OUString(*aHref->m_aLnk); +} + +void SmMlAttribute::setMlLspace(const SmMlLspace* aLspace) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aLspace.m_aLengthValue.m_aLengthUnit + = aLspace->m_aLengthValue.m_aLengthUnit; + m_aAttributeValue.m_aLspace.m_aLengthValue.m_aLengthValue + = aLspace->m_aLengthValue.m_aLengthValue; + m_aAttributeValue.m_aLspace.m_aLengthValue.m_aOriginalText + = new OUString(*aLspace->m_aLengthValue.m_aOriginalText); +} + +void SmMlAttribute::setMlMathbackground(const SmMlMathbackground* aMathbackground) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aMathbackground.m_aMathbackground = aMathbackground->m_aMathbackground; +} + +void SmMlAttribute::setMlMathcolor(const SmMlMathcolor* aMathcolor) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aMathcolor.m_aMathcolor = aMathcolor->m_aMathcolor; +} + +void SmMlAttribute::setMlMathsize(const SmMlMathsize* aMathsize) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aLengthUnit + = aMathsize->m_aLengthValue.m_aLengthUnit; + m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aLengthValue + = aMathsize->m_aLengthValue.m_aLengthValue; + m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aOriginalText + = new OUString(*aMathsize->m_aLengthValue.m_aOriginalText); +} + +void SmMlAttribute::setMlMathvariant(const SmMlMathvariant* aMathvariant) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aMathvariant.m_aMathvariant = aMathvariant->m_aMathvariant; +} + +void SmMlAttribute::setMlMaxsize(const SmMlMaxsize* aMaxsize) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aMaxsize.m_aMaxsize = aMaxsize->m_aMaxsize; + m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aLengthUnit + = aMaxsize->m_aLengthValue.m_aLengthUnit; + m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aLengthValue + = aMaxsize->m_aLengthValue.m_aLengthValue; + m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aOriginalText + = new OUString(*aMaxsize->m_aLengthValue.m_aOriginalText); +} + +void SmMlAttribute::setMlMinsize(const SmMlMinsize* aMinsize) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aLengthUnit + = aMinsize->m_aLengthValue.m_aLengthUnit; + m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aLengthValue + = aMinsize->m_aLengthValue.m_aLengthValue; + m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aOriginalText + = new OUString(*aMinsize->m_aLengthValue.m_aOriginalText); +} + +void SmMlAttribute::setMlMovablelimits(const SmMlMovablelimits* aMovablelimits) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aMovablelimits.m_aMovablelimits = aMovablelimits->m_aMovablelimits; +} + +void SmMlAttribute::setMlRspace(const SmMlRspace* aRspace) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aRspace.m_aLengthValue.m_aLengthUnit + = aRspace->m_aLengthValue.m_aLengthUnit; + m_aAttributeValue.m_aRspace.m_aLengthValue.m_aLengthValue + = aRspace->m_aLengthValue.m_aLengthValue; + m_aAttributeValue.m_aRspace.m_aLengthValue.m_aOriginalText + = new OUString(*aRspace->m_aLengthValue.m_aOriginalText); +} + +void SmMlAttribute::setMlSeparator(const SmMlSeparator* aSeparator) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aSeparator.m_aSeparator = aSeparator->m_aSeparator; +} + +void SmMlAttribute::setMlStretchy(const SmMlStretchy* aStretchy) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aStretchy.m_aStretchy = aStretchy->m_aStretchy; +} + +void SmMlAttribute::setMlSymmetric(const SmMlSymmetric* aSymmetric) +{ + m_bSet = true; + clearPreviousAttributeValue(); + m_aAttributeValue.m_aSymmetric.m_aSymmetric = aSymmetric->m_aSymmetric; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/mathml/def.cxx b/starmath/source/mathml/def.cxx new file mode 100644 index 000000000..484dcd665 --- /dev/null +++ b/starmath/source/mathml/def.cxx @@ -0,0 +1,124 @@ +/* -*- 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 <mathml/attribute.hxx> + +SmMlAttributePos starmathdatabase::MlAttributeListEmpty[] = { + // clang-format off + { SmMlAttributeValueType::NMlEmpty, 0 } + // clang-format on +}; + +SmMlAttributePos starmathdatabase::MlAttributeListMath[] = { + // clang-format off + { SmMlAttributeValueType::NMlEmpty, 0 } + // clang-format on +}; + +SmMlAttributePos starmathdatabase::MlAttributeListMi[] = { + // clang-format off + { SmMlAttributeValueType::MlHref, 0 }, + { SmMlAttributeValueType::MlDir, 1 }, + { SmMlAttributeValueType::MlMathbackground, 2 }, + { SmMlAttributeValueType::MlMathcolor, 3 }, + { SmMlAttributeValueType::MlDisplaystyle, 4 }, + { SmMlAttributeValueType::MlMathsize, 5 }, + { SmMlAttributeValueType::MlMathvariant, 6 } + // clang-format on +}; + +SmMlAttributePos starmathdatabase::MlAttributeListMerror[] = { + // clang-format off + { SmMlAttributeValueType::MlHref, 0 }, + { SmMlAttributeValueType::MlMathbackground, 1 }, + { SmMlAttributeValueType::MlMathcolor, 2 }, + { SmMlAttributeValueType::MlDisplaystyle, 3 } + // clang-format on +}; + +SmMlAttributePos starmathdatabase::MlAttributeListMn[] = { + // clang-format off + { SmMlAttributeValueType::MlHref, 0 }, + { SmMlAttributeValueType::MlDir, 1 }, + { SmMlAttributeValueType::MlMathbackground, 2 }, + { SmMlAttributeValueType::MlMathcolor, 3 }, + { SmMlAttributeValueType::MlDisplaystyle, 4 }, + { SmMlAttributeValueType::MlMathsize, 5 }, + { SmMlAttributeValueType::MlMathvariant, 6 } + // clang-format on +}; + +SmMlAttributePos starmathdatabase::MlAttributeListMo[] = { + // clang-format off + { SmMlAttributeValueType::MlHref, 0 }, + { SmMlAttributeValueType::MlDir, 1 }, + { SmMlAttributeValueType::MlMathbackground, 2 }, + { SmMlAttributeValueType::MlMathcolor, 3 }, + { SmMlAttributeValueType::MlDisplaystyle, 4 }, + { SmMlAttributeValueType::MlMathsize, 5 }, + { SmMlAttributeValueType::MlMathvariant, 6 }, + { SmMlAttributeValueType::MlFence, 7 }, + { SmMlAttributeValueType::MlForm, 8 }, + { SmMlAttributeValueType::MlMaxsize, 9 }, + { SmMlAttributeValueType::MlMinsize, 10 }, + { SmMlAttributeValueType::MlMovablelimits, 11 }, + { SmMlAttributeValueType::MlLspace, 12 }, + { SmMlAttributeValueType::MlRspace, 13 }, + { SmMlAttributeValueType::MlAccent, 14 }, + { SmMlAttributeValueType::MlStretchy, 15 }, + { SmMlAttributeValueType::MlSeparator, 16 }, + { SmMlAttributeValueType::MlSymmetric, 17 } + // clang-format on +}; + +SmMlAttributePos starmathdatabase::MlAttributeListMrow[] = { + // clang-format off + { SmMlAttributeValueType::MlHref, 0 }, + { SmMlAttributeValueType::MlDir, 1 }, + { SmMlAttributeValueType::MlMathbackground, 2 }, + { SmMlAttributeValueType::MlMathcolor, 3 } + // clang-format on +}; + +SmMlAttributePos starmathdatabase::MlAttributeListMtext[] = { + // clang-format off + { SmMlAttributeValueType::MlHref, 0 }, + { SmMlAttributeValueType::MlDir, 1 }, + { SmMlAttributeValueType::MlMathbackground, 2 }, + { SmMlAttributeValueType::MlMathcolor, 3 }, + { SmMlAttributeValueType::MlDisplaystyle, 4 }, + { SmMlAttributeValueType::MlMathsize, 5 }, + { SmMlAttributeValueType::MlMathvariant, 6 } + // clang-format on +}; + +SmMlAttributePos starmathdatabase::MlAttributeListMstyle[] = { + // clang-format off + { SmMlAttributeValueType::MlHref, 0 }, + { SmMlAttributeValueType::MlDir, 1 }, + { SmMlAttributeValueType::MlMathbackground, 2 }, + { SmMlAttributeValueType::MlMathcolor, 3 }, + { SmMlAttributeValueType::MlDisplaystyle, 4 }, + { SmMlAttributeValueType::MlMathsize, 5 }, + { SmMlAttributeValueType::MlMathvariant, 6 }, + { SmMlAttributeValueType::MlFence, 7 }, + { SmMlAttributeValueType::MlForm, 8 }, + { SmMlAttributeValueType::MlMaxsize, 9 }, + { SmMlAttributeValueType::MlMinsize, 10 }, + { SmMlAttributeValueType::MlMovablelimits, 11 }, + { SmMlAttributeValueType::MlLspace, 12 }, + { SmMlAttributeValueType::MlRspace, 13 }, + { SmMlAttributeValueType::MlAccent, 14 }, + { SmMlAttributeValueType::MlStretchy, 15 }, + { SmMlAttributeValueType::MlSeparator, 16 }, + { SmMlAttributeValueType::MlSymmetric, 17 } + // clang-format on +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/mathml/element.cxx b/starmath/source/mathml/element.cxx new file mode 100644 index 000000000..4f8f2a64f --- /dev/null +++ b/starmath/source/mathml/element.cxx @@ -0,0 +1,136 @@ +/* -*- 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 <mathml/element.hxx> + +void SmMlElement::SmImplAttributeType() +{ + switch (m_aElementType) + { + case SmMlElementType::NMlEmpty: + m_aAttributePosList = std::vector<SmMlAttributePos>(0); + break; + case SmMlElementType::NMlStructural: + m_aAttributePosList = std::vector<SmMlAttributePos>(0); + break; + case SmMlElementType::NMlSmNode: + m_aAttributePosList = std::vector<SmMlAttributePos>(0); + break; + case SmMlElementType::MlMath: + m_aAttributePosList = std::vector<SmMlAttributePos>(0); + //m_aAttributePosList = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMath), std::end(starmathdatabase::MlAttributeListMath)); + break; + case SmMlElementType::MlMi: + m_aAttributePosList + = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMi), + std::end(starmathdatabase::MlAttributeListMi)); + break; + case SmMlElementType::MlMerror: + m_aAttributePosList + = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMerror), + std::end(starmathdatabase::MlAttributeListMerror)); + break; + case SmMlElementType::MlMn: + m_aAttributePosList + = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMn), + std::end(starmathdatabase::MlAttributeListMn)); + break; + case SmMlElementType::MlMo: + m_aAttributePosList + = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMo), + std::end(starmathdatabase::MlAttributeListMo)); + break; + case SmMlElementType::MlMrow: + m_aAttributePosList + = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMrow), + std::end(starmathdatabase::MlAttributeListMrow)); + break; + case SmMlElementType::MlMtext: + m_aAttributePosList + = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMtext), + std::end(starmathdatabase::MlAttributeListMtext)); + break; + case SmMlElementType::MlMstyle: + m_aAttributePosList + = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMstyle), + std::end(starmathdatabase::MlAttributeListMstyle)); + break; + default: + break; + } + // Create attribute vector with given pattern + m_aAttributeList = starmathdatabase::makeMlAttributeList(m_aAttributePosList); +} + +SmMlAttribute SmMlElement::getAttribute(SmMlAttributeValueType aAttributeType) const +{ + // Look for the attribute position and return if exists + for (size_t i = 0; i < m_aAttributePosList.size(); ++i) + { + if (m_aAttributePosList[i].m_aAttributeValueType == aAttributeType) + return m_aAttributeList[m_aAttributePosList[i].m_nPos]; + } + return SmMlAttribute(); +} + +bool SmMlElement::isAttributeSet(SmMlAttributeValueType aAttributeType) const +{ + // Look for the attribute position and return if exists + for (size_t i = 0; i < m_aAttributePosList.size(); ++i) + { + if (m_aAttributePosList[i].m_aAttributeValueType == aAttributeType) + return m_aAttributeList[m_aAttributePosList[i].m_nPos].isSet(); + } + return false; +} + +void SmMlElement::setAttribute(const SmMlAttribute* aAttribute) +{ + // Look for the attribute position and assign if exists + for (size_t i = 0; i < m_aAttributePosList.size(); ++i) + { + if (m_aAttributePosList[i].m_aAttributeValueType == aAttribute->getMlAttributeValueType()) + { + m_aAttributeList[m_aAttributePosList[i].m_nPos].setMlAttributeValue(aAttribute); + break; + } + } +} + +void SmMlElement::setSubElement(size_t nPos, SmMlElement* aElement) +{ + // This is the new parent element + aElement->setParentElement(this); + aElement->setSubElementId(nPos); + // Check if the vector is long enough + // Careful nOldSize can be 0 and -1 will underflow + // We must put something on the empty locations + size_t nOldSize = m_aSubElements.size(); + if (nPos + 1 > nOldSize) + { + m_aSubElements.resize(nPos + 1); + for (; nOldSize < nPos; ++nOldSize) + m_aSubElements[nOldSize] = nullptr; + } + // Assign value + m_aSubElements[nPos] = aElement; +} + +std::vector<SmMlAttribute> +starmathdatabase::makeMlAttributeList(std::vector<SmMlAttributePos> aAttributePosList) +{ + std::vector<SmMlAttribute> aAttributeList(aAttributePosList.size()); + for (size_t i = 0; i < aAttributePosList.size(); ++i) + { + aAttributeList[i].setMlAttributeValueType(aAttributePosList[i].m_aAttributeValueType); + } + return aAttributeList; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/mathml/export.cxx b/starmath/source/mathml/export.cxx new file mode 100644 index 000000000..b55be5092 --- /dev/null +++ b/starmath/source/mathml/export.cxx @@ -0,0 +1,1110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 . + */ + +// Our mathml +#include <mathml/export.hxx> +#include <mathml/iterator.hxx> + +// LO tools to use +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/uno/Any.h> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> + +// Extra LO tools +#include <comphelper/genericpropertyset.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/itemset.hxx> +#include <svl/stritem.hxx> +#include <unotools/streamwrap.hxx> +#include <xmloff/namespacemap.hxx> + +// Our starmath tools +#include <document.hxx> +#include <smmod.hxx> +#include <strings.hrc> +#include <unomodel.hxx> +#include <xparsmlbase.hxx> +#include <starmathdatabase.hxx> + +// Old parser +#include <mathmlexport.hxx> + +using namespace ::com::sun::star; +using namespace xmloff::token; + +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; + +// SmMLExportWrapper +/*************************************************************************************************/ + +bool SmMLExportWrapper::Export(SfxMedium& rMedium) +{ + bool bRet = true; + uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); + + // Check all fine + SAL_WARN_IF(m_xModel == nullptr, "starmath", "Missing model"); + SAL_WARN_IF(xContext == nullptr, "starmath", "Missing context"); + if (m_xModel == nullptr || xContext == nullptr) + return false; + + //Get model + uno::Reference<lang::XComponent> xModelComp = m_xModel; + SAL_WARN_IF(xModelComp == nullptr, "starmath", "Missing model component"); + SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(m_xModel); + SAL_WARN_IF(pModel == nullptr, "starmath", "Failed to get threw uno tunnel"); + if (xModelComp == nullptr || pModel == nullptr) + return false; + + // Get doc shell + SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell()); + if (pDocShell == nullptr) + { + SAL_WARN("starmath", "Failed to fetch sm document"); + return false; + } + + // Check if it is a standalone window or embed object + bool bEmbedded = SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode(); + + // Medium item set + SfxItemSet* pMediumItemSet = rMedium.GetItemSet(); + if (pDocShell == nullptr) + { + SAL_WARN("starmath", "Failed to get medium item set"); + return false; + } + + // Progress bar ~ + uno::Reference<task::XStatusIndicator> xStatusIndicator; + + if (!bEmbedded) + { + // Extra check to ensure everything is fine + if (pDocShell->GetMedium() != &rMedium) + { + SAL_WARN("starmath", "Input medium and sm document medium do not match"); + //return false; + } + + // Fetch progress bar + const SfxUnoAnyItem* pItem = pMediumItemSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL); + if (pItem) + { + // set progress range and start status indicator + pItem->GetValue() >>= xStatusIndicator; + xStatusIndicator->start(SmResId(STR_STATSTR_WRITING), 3); + xStatusIndicator->setValue(0); + } + } + + // create XPropertySet with three properties for status indicator + static const 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 } + }; + uno::Reference<beans::XPropertySet> xInfoSet( + comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap))); + + // Always print pretty + xInfoSet->setPropertyValue("UsePrettyPrinting", Any(true)); + + // Set base URI + xInfoSet->setPropertyValue(u"BaseURI", Any(rMedium.GetBaseURL(true))); + + if (!m_bFlat) //Storage (Package) of Stream + { + // Fetch the output storage + uno::Reference<embed::XStorage> xStg = rMedium.GetOutputStorage(); + if (xStg == nullptr) + { + SAL_WARN("starmath", "Failed to fetch output storage"); + return false; + } + + // TODO/LATER: handle the case of embedded links gracefully + if (bEmbedded) //&& !pStg->IsRoot() ) + { + const SfxStringItem* pDocHierarchItem + = pMediumItemSet->GetItem(SID_DOC_HIERARCHICALNAME); + if (pDocHierarchItem != nullptr) + { + OUString aName = pDocHierarchItem->GetValue(); + if (!aName.isEmpty()) + xInfoSet->setPropertyValue("StreamRelPath", Any(aName)); + } + } + else + { + // Write file metadata ( data, LO version ... ) + // Note: export through an XML exporter component (storage version) + if (xStatusIndicator.is()) + xStatusIndicator->setValue(1); + + bRet = WriteThroughComponentS(xStg, xModelComp, u"meta.xml", xContext, xInfoSet, + u"com.sun.star.comp.Math.MLOasisMetaExporter", 6); + } + + // Write starmath formula + // Note: export through an XML exporter component (storage version) + if (bRet) + { + if (xStatusIndicator.is()) + xStatusIndicator->setValue(2); + + if (pDocShell->GetSmSyntaxVersion() == 5) + bRet = WriteThroughComponentS(xStg, xModelComp, u"content.xml", xContext, xInfoSet, + u"com.sun.star.comp.Math.XMLContentExporter", 5); + else + bRet = WriteThroughComponentS(xStg, xModelComp, u"content.xml", xContext, xInfoSet, + u"com.sun.star.comp.Math.MLContentExporter", 6); + } + + // Write starmath settings + // Note: export through an XML exporter component (storage version) + if (bRet) + { + if (xStatusIndicator.is()) + xStatusIndicator->setValue(3); + + bRet = WriteThroughComponentS(xStg, xModelComp, u"settings.xml", xContext, xInfoSet, + u"com.sun.star.comp.Math.MLOasisSettingsExporter", 6); + } + } + else + { + // Fetch the output stream + SvStream* pStream = rMedium.GetOutStream(); + if (pStream == nullptr) + { + SAL_WARN("starmath", "Missing output stream"); + return false; + } + uno::Reference<io::XOutputStream> xOut(new utl::OOutputStreamWrapper(*pStream)); + + if (xStatusIndicator.is()) + xStatusIndicator->setValue(1); + + // Write everything in the same place + // Note: export through an XML exporter component (output stream version) + if (pDocShell->GetSmSyntaxVersion() == 5) + bRet = WriteThroughComponentOS(xOut, xModelComp, xContext, xInfoSet, + u"com.sun.star.comp.Math.XMLContentExporter", 5); + else + bRet = WriteThroughComponentOS(xOut, xModelComp, xContext, xInfoSet, + u"com.sun.star.comp.Math.MLContentExporter", 6); + } + + if (xStatusIndicator.is()) + xStatusIndicator->end(); + return bRet; +} + +OUString SmMLExportWrapper::Export(SmMlElement* pElementTree) +{ + uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); + + // Check all fine + m_pElementTree = nullptr; + SAL_WARN_IF(m_xModel == nullptr, "starmath", "Missing model"); + SAL_WARN_IF(xContext == nullptr, "starmath", "Missing context"); + if (m_xModel == nullptr || xContext == nullptr) + return u""; + + //Get model + uno::Reference<lang::XComponent> xModelComp = m_xModel; + SAL_WARN_IF(xModelComp == nullptr, "starmath", "Missing model component"); + SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(m_xModel); + SAL_WARN_IF(pModel == nullptr, "starmath", "Failed to get threw uno tunnel"); + if (xModelComp == nullptr || pModel == nullptr) + return u""; + + // Get doc shell + SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell()); + if (pDocShell == nullptr) + { + SAL_WARN("starmath", "Failed to fetch sm document"); + return u""; + } + + // create XPropertySet with three properties for status indicator + static const 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 } + }; + uno::Reference<beans::XPropertySet> xInfoSet( + comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap))); + + // Always print pretty + xInfoSet->setPropertyValue("UsePrettyPrinting", Any(true)); + + // Fetch mathml tree + m_pElementTree = pElementTree; + + // Write stuff + // Note: export through an XML exporter component (memory stream version) + return WriteThroughComponentMS(xModelComp, xContext, xInfoSet); +} + +// export through an XML exporter component (output stream version) +bool SmMLExportWrapper::WriteThroughComponentOS(const Reference<io::XOutputStream>& xOutputStream, + const Reference<XComponent>& xComponent, + Reference<uno::XComponentContext> const& rxContext, + Reference<beans::XPropertySet> const& rPropSet, + const char16_t* pComponentName, + int_fast16_t nSyntaxVersion) +{ + // We need a output stream but it is already checked by caller + // We need a component but it is already checked by caller + // We need a context but it is already checked by caller + // We need a property set but it is already checked by caller + // We need a component name but it is already checked by caller + + // get sax writer + Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(rxContext); + + // connect XML writer to output stream + xSaxWriter->setOutputStream(xOutputStream); + if (m_bUseHTMLMLEntities) + xSaxWriter->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntitiesExport); + + // prepare arguments (prepend doc handler to given arguments) + Sequence<Any> aArgs{ Any(xSaxWriter), Any(rPropSet) }; + + // get filter component + auto xExporterData = rxContext->getServiceManager()->createInstanceWithArgumentsAndContext( + OUString(pComponentName), aArgs, rxContext); + Reference<document::XExporter> xExporter(xExporterData, UNO_QUERY); + + // Check everything is fine + if (!xExporter.is()) + { + SAL_WARN("starmath", "can't instantiate export filter component"); + return false; + } + + // connect model and filter + xExporter->setSourceDocument(xComponent); + Reference<XFilter> xFilter(xExporter, UNO_QUERY); + uno::Sequence<PropertyValue> aProps(0); + + // filter + if (nSyntaxVersion == 5) + { + SmXMLExport* pFilter = comphelper::getFromUnoTunnel<SmXMLExport>(xFilter); + if (pFilter == nullptr) + { + SAL_WARN("starmath", "Failed to fetch SmMLExport"); + return false; + } + xFilter->filter(aProps); + return pFilter->GetSuccess(); + } + + // filter + SmMLExport* pFilter = comphelper::getFromUnoTunnel<SmMLExport>(xFilter); + + // Setup filter + if (pFilter == nullptr) + { + SAL_WARN("starmath", "Failed to fetch SmMLExport"); + return false; + } + pFilter->setUseExportTag(m_bUseExportTag); + pFilter->setElementTree(m_pElementTree); + + // Execute operation + xFilter->filter(aProps); + return pFilter->getSuccess(); +} + +// export through an XML exporter component (storage version) +bool SmMLExportWrapper::WriteThroughComponentS(const Reference<embed::XStorage>& xStorage, + const Reference<XComponent>& xComponent, + const char16_t* pStreamName, + Reference<uno::XComponentContext> const& rxContext, + Reference<beans::XPropertySet> const& rPropSet, + const char16_t* pComponentName, + int_fast16_t nSyntaxVersion) +{ + // We need a storage name but it is already checked by caller + // We need a component name but it is already checked by caller + // We need a stream name but it is already checked by caller + // We need a context but it is already checked by caller + // We need a property set but it is already checked by caller + // We need a component but it is already checked by caller + + // open stream + Reference<io::XStream> xStream; + try + { + xStream = xStorage->openStreamElement( + OUString(pStreamName), embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE); + } + catch (const uno::Exception&) + { + SAL_WARN("starmath", "Can't create output stream in package"); + return false; + } + + // Set stream as text / xml + uno::Reference<beans::XPropertySet> xSet(xStream, uno::UNO_QUERY); + xSet->setPropertyValue("MediaType", Any(OUString(u"text/xml"))); + + // all streams must be encrypted in encrypted document + xSet->setPropertyValue("UseCommonStoragePasswordEncryption", Any(true)); + + // set Base URL + rPropSet->setPropertyValue("StreamName", Any(OUString(pStreamName))); + + // write the stuff + // Note: export through an XML exporter component (output stream version) + return WriteThroughComponentOS(xStream->getOutputStream(), xComponent, rxContext, rPropSet, + pComponentName, nSyntaxVersion); +} + +// export through an XML exporter component (memory stream version) +OUString +SmMLExportWrapper::WriteThroughComponentMS(const Reference<XComponent>& xComponent, + Reference<uno::XComponentContext> const& rxContext, + Reference<beans::XPropertySet> const& rPropSet) +{ + // We need a component but it is already checked by caller + // We need a context but it is already checked by caller + // We need a property set it is already checked by caller + + // open stream + SvMemoryStream aMemoryStream(8192, 1024); + uno::Reference<io::XOutputStream> xStream(new utl::OOutputStreamWrapper(aMemoryStream)); + + // Set the stream as text + uno::Reference<beans::XPropertySet> xSet(xStream, uno::UNO_QUERY); + xSet->setPropertyValue("MediaType", Any(OUString("text/xml"))); + + // write the stuff + // Note: export through an XML exporter component (output stream version) + bool bOk = WriteThroughComponentOS(xStream, xComponent, rxContext, rPropSet, + u"com.sun.star.comp.Mathml.MLContentExporter", 6); + + // We don't want to read uninitialized data + if (!bOk) + return u""; + + // Recover data and generate string + OString aString(static_cast<const char*>(aMemoryStream.GetData()), + aMemoryStream.GetSize() / sizeof(char)); + return OStringToOUString(aString, RTL_TEXTENCODING_UTF8); +} + +// SmMLExport technical +/*************************************************************************************************/ + +sal_Int64 SAL_CALL SmMLExport::getSomething(const uno::Sequence<sal_Int8>& rId) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf<SvXMLExport>{}); +} + +const uno::Sequence<sal_Int8>& SmMLExport::getUnoTunnelId() noexcept +{ + static const comphelper::UnoIdInit theSmMLExportUnoTunnelId; + return theSmMLExportUnoTunnelId.getSeq(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +Math_MLExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new SmMLExport(context, "com.sun.star.comp.Math.XMLExporter", + SvXMLExportFlags::OASIS | SvXMLExportFlags::ALL)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +Math_MLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new SmMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter", + SvXMLExportFlags::OASIS | SvXMLExportFlags::META)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +Math_MLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new SmMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter", + SvXMLExportFlags::OASIS | SvXMLExportFlags::SETTINGS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +Math_MLContentExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new SmMLExport(context, "com.sun.star.comp.Math.XMLContentExporter", + SvXMLExportFlags::OASIS | SvXMLExportFlags::CONTENT)); +} + +SmDocShell* SmMLExport::getSmDocShell() +{ + SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(GetModel()); + if (pModel != nullptr) + return static_cast<SmDocShell*>(pModel->GetObjectShell()); + return nullptr; +} + +ErrCode SmMLExport::exportDoc(enum XMLTokenEnum eClass) +{ + if (!(getExportFlags() & SvXMLExportFlags::CONTENT)) + { + // Everything that isn't the formula itself get's default export + SvXMLExport::exportDoc(eClass); + return ERRCODE_NONE; + } + + // Checks if it has to export a particular tree + if (m_pElementTree == nullptr) + { + // Set element tree + SmDocShell* pDocShell = getSmDocShell(); + if (pDocShell != nullptr) + m_pElementTree = pDocShell->GetMlElementTree(); + else + { + m_bSuccess = false; + return SVSTREAM_INVALID_PARAMETER; + } + } + + // Start document and encrypt if necessary + GetDocHandler()->startDocument(); + addChaffWhenEncryptedStorage(); + + // make use of a default namespace + // Math doesn't need namespaces from xmloff, since it now uses default namespaces + // Because that is common with current MathML usage in the web -> ResetNamespaceMap(); + GetNamespaceMap_().Add(OUString(u""), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH); + + // Add xmlns line + if (m_bUseExportTag) + { + GetAttrList().AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH), + GetNamespaceMap().GetNameByKey(XML_NAMESPACE_MATH)); + } + + // Export and close document + ExportContent_(); + GetDocHandler()->endDocument(); + + return ERRCODE_NONE; +} + +void SmMLExport::GetViewSettings(Sequence<PropertyValue>& aProps) +{ + // Get the document shell + SmDocShell* pDocShell = getSmDocShell(); + if (pDocShell == nullptr) + { + SAL_WARN("starmath", "Missing document shell so no view settings"); + return; + } + + // Allocate enough memory + aProps.realloc(4); + PropertyValue* pValue = aProps.getArray(); + + // The view settings are the formula display settings + tools::Rectangle aRect(pDocShell->GetVisArea()); + + pValue[0].Name = "ViewAreaTop"; + pValue[0].Value <<= aRect.Top(); + + pValue[1].Name = "ViewAreaLeft"; + pValue[1].Value <<= aRect.Left(); + + pValue[2].Name = "ViewAreaWidth"; + pValue[2].Value <<= aRect.GetWidth(); + + pValue[3].Name = "ViewAreaHeight"; + pValue[3].Value <<= aRect.GetHeight(); +} + +void SmMLExport::GetConfigurationSettings(Sequence<PropertyValue>& rProps) +{ + // Get model property set (settings) + Reference<XPropertySet> xProps(GetModel(), UNO_QUERY); + if (!xProps.is()) + { + SAL_WARN("starmath", "Missing model properties so no configuration settings"); + return; + } + + // Get model property set info (settings values) + Reference<XPropertySetInfo> xPropertySetInfo = xProps->getPropertySetInfo(); + if (!xPropertySetInfo.is()) + { + SAL_WARN("starmath", "Missing model properties info so no configuration settings"); + return; + } + + // Allocate to store the properties + Sequence<Property> aProps = xPropertySetInfo->getProperties(); + const sal_Int32 nCount = aProps.getLength(); + rProps.realloc(nCount); + auto pProps = rProps.getArray(); + + // Copy properties + // This needs further revision + // Based in code mathmlexport.cxx::GetConfigurationSettings + for (sal_Int32 i = 0; i < nCount; ++i) + { + if (aProps[i].Name != "Formula" && aProps[i].Name != "BasicLibraries" + && aProps[i].Name != "DialogLibraries" && aProps[i].Name != "RuntimeUID") + { + pProps[i].Name = aProps[i].Name; + pProps[i].Value = xProps->getPropertyValue(aProps[i].Name); + } + } +} + +SmMLExport::SmMLExport(const css::uno::Reference<css::uno::XComponentContext>& rContext, + OUString const& implementationName, SvXMLExportFlags nExportFlags) + : SvXMLExport(rContext, implementationName, util::MeasureUnit::INCH, XML_MATH, nExportFlags) + , m_pElementTree(nullptr) + , m_bSuccess(true) + , m_bUseExportTag(true) +{ +} + +// SmMLExport +/*************************************************************************************************/ + +void SmMLExport::declareMlError() +{ + SAL_WARN("starmath", "Invalid use of mathml."); + m_bSuccess = false; +} + +void SmMLExport::exportMlAttributeLength(xmloff::token::XMLTokenEnum pAttribute, + const SmLengthValue& aLengthValue) +{ + if (!aLengthValue.m_aOriginalText->isEmpty()) + { + addAttribute(pAttribute, *aLengthValue.m_aOriginalText); + } + else + { + OUStringBuffer aSizeBuffer(64); + aSizeBuffer.append(aLengthValue.m_aLengthValue); + switch (aLengthValue.m_aLengthUnit) + { + case SmLengthUnit::MlEm: + aSizeBuffer.append(u"em"); + break; + case SmLengthUnit::MlEx: + aSizeBuffer.append(u"ex"); + break; + case SmLengthUnit::MlPx: + aSizeBuffer.append(u"px"); + break; + case SmLengthUnit::MlIn: + aSizeBuffer.append(u"in"); + break; + case SmLengthUnit::MlCm: + aSizeBuffer.append(u"cm"); + break; + case SmLengthUnit::MlMm: + aSizeBuffer.append(u"mm"); + break; + case SmLengthUnit::MlPt: + aSizeBuffer.append(u"pt"); + break; + case SmLengthUnit::MlPc: + aSizeBuffer.append(u"pc"); + break; + case SmLengthUnit::MlP: + aSizeBuffer.append(u"%"); + break; + case SmLengthUnit::MlM: + break; + default: + declareMlError(); + break; + } + addAttribute(pAttribute, aSizeBuffer.makeStringAndClear()); + } +} + +void SmMLExport::exportMlAttributes(const SmMlElement* pMlElement) +{ + size_t nAttributeCount = pMlElement->getAttributeCount(); + for (size_t i = 0; i < nAttributeCount; ++i) + { + SmMlAttribute aAttribute = pMlElement->getAttribute(i); + if (!aAttribute.isSet()) + continue; + + switch (aAttribute.getMlAttributeValueType()) + { + case SmMlAttributeValueType::MlAccent: + { + auto aAttributeValue = aAttribute.getMlAccent(); + switch (aAttributeValue->m_aAccent) + { + case SmMlAttributeValueAccent::MlFalse: + addAttribute(XML_ACCENT, XML_FALSE); + break; + case SmMlAttributeValueAccent::MlTrue: + addAttribute(XML_ACCENT, XML_TRUE); + break; + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlDir: + { + auto aAttributeValue = aAttribute.getMlDir(); + switch (aAttributeValue->m_aDir) + { + case SmMlAttributeValueDir::MlLtr: + addAttribute(XML_DIR, XML_LTR); + break; + case SmMlAttributeValueDir::MlRtl: + addAttribute(XML_DIR, XML_RTL); + break; + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlDisplaystyle: + { + auto aAttributeValue = aAttribute.getMlDisplaystyle(); + switch (aAttributeValue->m_aDisplaystyle) + { + case SmMlAttributeValueDisplaystyle::MlTrue: + addAttribute(XML_DISPLAYSTYLE, XML_FALSE); + break; + case SmMlAttributeValueDisplaystyle::MlFalse: + addAttribute(XML_DISPLAYSTYLE, XML_TRUE); + break; + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlFence: + { + auto aAttributeValue = aAttribute.getMlFence(); + switch (aAttributeValue->m_aFence) + { + case SmMlAttributeValueFence::MlTrue: + addAttribute(XML_FENCE, XML_FALSE); + break; + case SmMlAttributeValueFence::MlFalse: + addAttribute(XML_FENCE, XML_TRUE); + break; + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlHref: + { + auto aAttributeValue = aAttribute.getMlHref(); + switch (aAttributeValue->m_aHref) + { + case SmMlAttributeValueHref::NMlEmpty: + break; + case SmMlAttributeValueHref::NMlValid: + addAttribute(XML_HREF, *aAttributeValue->m_aLnk); + break; + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlLspace: + { + auto aSizeData = aAttribute.getMlLspace(); + auto aLengthData = aSizeData->m_aLengthValue; + exportMlAttributeLength(XML_LSPACE, aLengthData); + break; + } + case SmMlAttributeValueType::MlMathbackground: + { + auto aAttributeValue = aAttribute.getMlMathbackground(); + switch (aAttributeValue->m_aMathbackground) + { + case SmMlAttributeValueMathbackground::MlTransparent: + addAttribute(XML_MATHBACKGROUND, "transparent"); + break; + case SmMlAttributeValueMathbackground::MlRgb: + { + const OUString& rTextColor = starmathdatabase::Identify_Color_MATHML( + sal_uInt32(aAttributeValue->m_aCol)) + .aIdent; + addAttribute(XML_MATHBACKGROUND, rTextColor); + break; + } + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlMathcolor: + { + auto aAttributeValue = aAttribute.getMlMathcolor(); + switch (aAttributeValue->m_aMathcolor) + { + case SmMlAttributeValueMathcolor::MlDefault: + break; + case SmMlAttributeValueMathcolor::MlRgb: + { + const OUString& rTextColor = starmathdatabase::Identify_Color_MATHML( + sal_uInt32(aAttributeValue->m_aCol)) + .aIdent; + addAttribute(XML_MATHCOLOR, rTextColor); + break; + } + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlMathsize: + { + auto aSizeData = aAttribute.getMlMathsize(); + auto aLengthData = aSizeData->m_aLengthValue; + exportMlAttributeLength(XML_MATHSIZE, aLengthData); + break; + } + case SmMlAttributeValueType::MlMathvariant: + { + auto aAttributeValue = aAttribute.getMlMathvariant(); + switch (aAttributeValue->m_aMathvariant) + { + case SmMlAttributeValueMathvariant::normal: + addAttribute(XML_MATHVARIANT, "normal"); + break; + case SmMlAttributeValueMathvariant::bold: + addAttribute(XML_MATHVARIANT, "bold"); + break; + case SmMlAttributeValueMathvariant::italic: + addAttribute(XML_MATHVARIANT, "italic"); + break; + case SmMlAttributeValueMathvariant::double_struck: + addAttribute(XML_MATHVARIANT, "double-struck"); + break; + case SmMlAttributeValueMathvariant::script: + addAttribute(XML_MATHVARIANT, "script"); + break; + case SmMlAttributeValueMathvariant::fraktur: + addAttribute(XML_MATHVARIANT, "fraktur"); + break; + case SmMlAttributeValueMathvariant::sans_serif: + addAttribute(XML_MATHVARIANT, "sans-serif"); + break; + case SmMlAttributeValueMathvariant::monospace: + addAttribute(XML_MATHVARIANT, "monospace"); + break; + case SmMlAttributeValueMathvariant::bold_italic: + addAttribute(XML_MATHVARIANT, "bold-italic"); + break; + case SmMlAttributeValueMathvariant::bold_fraktur: + addAttribute(XML_MATHVARIANT, "bold-fracktur"); + break; + case SmMlAttributeValueMathvariant::bold_script: + addAttribute(XML_MATHVARIANT, "bold-script"); + break; + case SmMlAttributeValueMathvariant::bold_sans_serif: + addAttribute(XML_MATHVARIANT, "bold-sans-serif"); + break; + case SmMlAttributeValueMathvariant::sans_serif_italic: + addAttribute(XML_MATHVARIANT, "sans-serif-italic"); + break; + case SmMlAttributeValueMathvariant::sans_serif_bold_italic: + addAttribute(XML_MATHVARIANT, "sans-serif-bold-italic"); + break; + case SmMlAttributeValueMathvariant::initial: + addAttribute(XML_MATHVARIANT, "initial"); + break; + case SmMlAttributeValueMathvariant::tailed: + addAttribute(XML_MATHVARIANT, "tailed"); + break; + case SmMlAttributeValueMathvariant::looped: + addAttribute(XML_MATHVARIANT, "looped"); + break; + case SmMlAttributeValueMathvariant::stretched: + addAttribute(XML_MATHVARIANT, "stretched"); + break; + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlMaxsize: + { + auto aSizeData = aAttribute.getMlMaxsize(); + auto aLengthData = aSizeData->m_aLengthValue; + switch (aSizeData->m_aMaxsize) + { + case SmMlAttributeValueMaxsize::MlInfinity: + { + addAttribute(XML_MAXSIZE, XML_INFINITY); + break; + } + case SmMlAttributeValueMaxsize::MlFinite: + { + exportMlAttributeLength(XML_MAXSIZE, aLengthData); + break; + } + } + break; + } + case SmMlAttributeValueType::MlMinsize: + { + auto aSizeData = aAttribute.getMlMinsize(); + auto aLengthData = aSizeData->m_aLengthValue; + exportMlAttributeLength(XML_MINSIZE, aLengthData); + break; + } + case SmMlAttributeValueType::MlMovablelimits: + { + auto aAttributeValue = aAttribute.getMlMovablelimits(); + switch (aAttributeValue->m_aMovablelimits) + { + case SmMlAttributeValueMovablelimits::MlFalse: + addAttribute(XML_MOVABLELIMITS, XML_FALSE); + break; + case SmMlAttributeValueMovablelimits::MlTrue: + addAttribute(XML_MOVABLELIMITS, XML_TRUE); + break; + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlRspace: + { + auto aSizeData = aAttribute.getMlRspace(); + auto aLengthData = aSizeData->m_aLengthValue; + exportMlAttributeLength(XML_RSPACE, aLengthData); + break; + } + case SmMlAttributeValueType::MlSeparator: + { + auto aAttributeValue = aAttribute.getMlSeparator(); + switch (aAttributeValue->m_aSeparator) + { + case SmMlAttributeValueSeparator::MlFalse: + addAttribute(XML_SEPARATOR, XML_FALSE); + break; + case SmMlAttributeValueSeparator::MlTrue: + addAttribute(XML_SEPARATOR, XML_TRUE); + break; + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlStretchy: + { + auto aAttributeValue = aAttribute.getMlStretchy(); + switch (aAttributeValue->m_aStretchy) + { + case SmMlAttributeValueStretchy::MlFalse: + addAttribute(XML_STRETCHY, XML_FALSE); + break; + case SmMlAttributeValueStretchy::MlTrue: + addAttribute(XML_STRETCHY, XML_TRUE); + break; + default: + declareMlError(); + break; + } + break; + } + case SmMlAttributeValueType::MlSymmetric: + { + auto aAttributeValue = aAttribute.getMlSymmetric(); + switch (aAttributeValue->m_aSymmetric) + { + case SmMlAttributeValueSymmetric::MlFalse: + addAttribute(XML_SYMMETRIC, XML_FALSE); + break; + case SmMlAttributeValueSymmetric::MlTrue: + addAttribute(XML_SYMMETRIC, XML_TRUE); + break; + default: + declareMlError(); + break; + } + break; + } + default: + declareMlError(); + break; + } + } +} + +SvXMLElementExport* SmMLExport::exportMlElement(const SmMlElement* pMlElement) +{ + SvXMLElementExport* pElementExport; + switch (pMlElement->getMlElementType()) + { + case SmMlElementType::MlMath: + pElementExport = createElementExport(XML_MATH); + break; + case SmMlElementType::MlMi: + pElementExport = createElementExport(XML_MI); + break; + case SmMlElementType::MlMerror: + pElementExport = createElementExport(XML_MERROR); + break; + case SmMlElementType::MlMn: + pElementExport = createElementExport(XML_MN); + break; + case SmMlElementType::MlMo: + pElementExport = createElementExport(XML_MO); + break; + case SmMlElementType::MlMrow: + pElementExport = createElementExport(XML_MROW); + break; + case SmMlElementType::MlMtext: + pElementExport = createElementExport(XML_MTEXT); + break; + case SmMlElementType::MlMstyle: + pElementExport = createElementExport(XML_MSTYLE); + break; + default: + pElementExport = nullptr; + } + const OUString& aElementText = pMlElement->getText(); + exportMlAttributes(pMlElement); + if (aElementText.isEmpty()) + GetDocHandler()->characters(aElementText); + return pElementExport; +} + +namespace +{ +struct exportMlElementTreeExecData +{ +private: + SmMLExport* m_pSmMLExport; + std::vector<SvXMLElementExport*> m_aSvXMLElementExportList; + size_t m_nDepth; + +public: + inline exportMlElementTreeExecData(SmMLExport* pSmMLExport) + : m_pSmMLExport(pSmMLExport) + , m_aSvXMLElementExportList(1024) + , m_nDepth(0) + { + } + + inline void deleteDepthData() + { + delete m_aSvXMLElementExportList[m_nDepth]; + --m_nDepth; + } + + inline void setDepthData(SvXMLElementExport* aSvXMLElementExportList) + { + if (m_nDepth == m_aSvXMLElementExportList.size()) + m_aSvXMLElementExportList.resize(m_aSvXMLElementExportList.size() + 1024); + m_aSvXMLElementExportList[m_nDepth] = aSvXMLElementExportList; + } + + inline void incrementDepth() { ++m_nDepth; } + + inline SmMLExport* getSmMLExport() { return m_pSmMLExport; }; +}; + +} // end unnamed namespace + +static inline void exportMlElementTreeExec(SmMlElement* aSmMlElement, void* aData) +{ + // Prepare data + exportMlElementTreeExecData* pData = static_cast<exportMlElementTreeExecData*>(aData); + pData->setDepthData(pData->getSmMLExport()->exportMlElement(aSmMlElement)); + + // Prepare for following + // If it has sub elements, then it will be the next + if (aSmMlElement->getSubElementsCount() != 0) + pData->incrementDepth(); + else // Otherwise remounts up to where it should be + { + while (aSmMlElement->getParentElement() != nullptr) + { + // get parent + SmMlElement* pParent = aSmMlElement->getParentElement(); + pData->deleteDepthData(); + // was this the last branch ? + if (aSmMlElement->getSubElementId() + 1 != pParent->getSubElementsCount()) // yes -> up + break; // no -> stop going up + // Prepare for next round + aSmMlElement = pParent; + } + } +} + +void SmMLExport::exportMlElementTree() +{ + exportMlElementTreeExecData* aData = new exportMlElementTreeExecData(this); + mathml::SmMlIteratorTopToBottom(m_pElementTree, exportMlElementTreeExec, aData); + delete aData; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/source/mathml/import.cxx b/starmath/source/mathml/import.cxx new file mode 100644 index 000000000..52d47028e --- /dev/null +++ b/starmath/source/mathml/import.cxx @@ -0,0 +1,1430 @@ +/* -*- 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/. + */ + +// Our mathml +#include <mathml/import.hxx> + +// LO tools to use +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/packages/zip/ZipIOException.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/xml/sax/FastParser.hpp> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/Parser.hpp> +#include <com/sun/star/xml/sax/SAXParseException.hpp> + +// Extra LO tools +#include <comphelper/fileformat.h> +#include <comphelper/genericpropertyset.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <comphelper/servicehelper.hxx> +#include <rtl/character.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/sfxsids.hrc> +#include <sot/storage.hxx> +#include <svtools/sfxecode.hxx> +#include <svl/itemset.hxx> +#include <svl/stritem.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <unotools/streamwrap.hxx> +#include <xmloff/DocumentSettingsContext.hxx> +#include <xmloff/xmlmetai.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <o3tl/string_view.hxx> + +// Our starmath tools +#include <cfgitem.hxx> +#include <document.hxx> +#include <xparsmlbase.hxx> +#include <smmod.hxx> +#include <starmathdatabase.hxx> +#include <unomodel.hxx> + +// Old parser +#include <mathmlimport.hxx> + +using namespace ::com::sun::star; +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::xml::sax; +using namespace ::xmloff::token; + +// SmMLImportContext +/*************************************************************************************************/ + +SmMlElement* SmMLImportWrapper::getElementTree() +{ + return m_pMlImport == nullptr ? nullptr : m_pMlImport->getElementTree(); +} + +ErrCode SmMLImportWrapper::Import(SfxMedium& rMedium) +{ + // Fetch context + uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); + if (!xContext.is()) + { + SAL_WARN("starmath", "Failed to fetch model while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Check model + if (!m_xModel.is()) + { + SAL_WARN("starmath", "Failed to fetch model while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Make a model component from our SmModel + uno::Reference<lang::XComponent> xModelComp = m_xModel; + if (!xModelComp.is()) + { + SAL_WARN("starmath", "Failed to make model while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Try to get an XStatusIndicator from the Medium + uno::Reference<task::XStatusIndicator> xStatusIndicator; + + // Get model via uno + SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(m_xModel); + if (pModel == nullptr) + { + SAL_WARN("starmath", "Failed to fetch sm model while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Get doc shell + m_pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell()); + if (m_pDocShell == nullptr) + { + SAL_WARN("starmath", "Failed to fetch smdoc shell while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Check if it is an embed object + bool bEmbedded = m_pDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED; + + if (!bEmbedded) + { + // Extra check to ensure everything is fine + if (m_pDocShell->GetMedium() != &rMedium) + { + SAL_WARN("starmath", "Given medium and doc shell medium differ while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Fetch the item set + SfxItemSet* pSet = rMedium.GetItemSet(); + if (pSet) + { + const SfxUnoAnyItem* pItem = pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL); + if (pItem != nullptr) + pItem->GetValue() >>= xStatusIndicator; + } + } + + // Create property list + static const comphelper::PropertyMapEntry aInfoMap[] + = { { u"PrivateData", 0, cppu::UnoType<XInterface>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { u"BaseURI", 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, + 0 }, + { u"StreamRelPath", 0, ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { u"StreamName", 0, ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 } }; + uno::Reference<beans::XPropertySet> xInfoSet( + comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap))); + + // Set base URI + // needed for relative URLs; but it's OK to import e.g. MathML from the clipboard without one + SAL_INFO_IF(rMedium.GetBaseURL().isEmpty(), "starmath", "SmMLImportWrapper: no base URL"); + xInfoSet->setPropertyValue("BaseURI", Any(rMedium.GetBaseURL())); + + // Fetch progress range + sal_Int32 nProgressRange(rMedium.IsStorage() ? 3 : 1); + if (xStatusIndicator.is()) + { + xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange); + xStatusIndicator->setValue(0); + } + + // Get storage + if (rMedium.IsStorage()) + { + // TODO/LATER: handle the case of embedded links gracefully + if (bEmbedded) // && !rMedium.GetStorage()->IsRoot() ) + { + OUString aName(u"dummyObjName"); + if (rMedium.GetItemSet()) + { + const SfxStringItem* pDocHierarchItem + = rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME); + if (pDocHierarchItem != nullptr) + aName = pDocHierarchItem->GetValue(); + } + + if (!aName.isEmpty()) + xInfoSet->setPropertyValue("StreamRelPath", Any(aName)); + } + + // Check if use OASIS ( new document format ) + bool bOASIS = SotStorage::GetVersion(rMedium.GetStorage()) > SOFFICE_FILEFORMAT_60; + if (xStatusIndicator.is()) + xStatusIndicator->setValue(1); + + // Error code in case of needed + ErrCode nWarn = ERRCODE_NONE; + + // Read metadata + // read a component from storage + if (!bEmbedded) + { + if (bOASIS) + nWarn = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"meta.xml", + xContext, xInfoSet, + u"com.sun.star.comp.Math.MLOasisMetaImporter", 6); + else + nWarn + = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"meta.xml", xContext, + xInfoSet, u"com.sun.star.comp.Math.XMLMetaImporter", 5); + } + + // Check if successful + if (nWarn != ERRCODE_NONE) + { + if (xStatusIndicator.is()) + xStatusIndicator->end(); + SAL_WARN("starmath", "Failed to read file"); + return nWarn; + } + + // Increase success indicator + if (xStatusIndicator.is()) + xStatusIndicator->setValue(2); + + // Read settings + // read a component from storage + if (bOASIS) + nWarn = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"settings.xml", + xContext, xInfoSet, + u"com.sun.star.comp.Math.MLOasisSettingsImporter", 6); + else + nWarn + = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"settings.xml", xContext, + xInfoSet, u"com.sun.star.comp.Math.XMLSettingsImporter", 5); + + // Check if successful + if (nWarn == ERRCODE_IO_BROKENPACKAGE) + { + if (xStatusIndicator.is()) + xStatusIndicator->end(); + SAL_WARN("starmath", "Failed to read file"); + return nWarn; + } + + // Increase success indicator + if (xStatusIndicator.is()) + xStatusIndicator->setValue(3); + + // Read document + // read a component from storage + if (m_pDocShell->GetSmSyntaxVersion() == 5) + nWarn + = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"content.xml", xContext, + xInfoSet, u"com.sun.star.comp.Math.XMLImporter", 5); + else + nWarn + = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"content.xml", xContext, + xInfoSet, u"com.sun.star.comp.Math.MLImporter", 6); + // Check if successful + if (nWarn != ERRCODE_NONE) + { + if (xStatusIndicator.is()) + xStatusIndicator->end(); + SAL_WARN("starmath", "Failed to read file"); + return nWarn; + } + + // Finish + if (xStatusIndicator.is()) + xStatusIndicator->end(); + return ERRCODE_NONE; + } + else + { + // Create input stream + Reference<io::XInputStream> xInputStream + = new utl::OInputStreamWrapper(rMedium.GetInStream()); + + // Increase success indicator + if (xStatusIndicator.is()) + xStatusIndicator->setValue(1); + + // Read data + // read a component from input stream + ErrCode nError = ERRCODE_NONE; + if (m_pDocShell->GetSmSyntaxVersion() == 5) + nError = ReadThroughComponentIS(xInputStream, xModelComp, xContext, xInfoSet, + u"com.sun.star.comp.Math.XMLImporter", false, 5); + else + nError = ReadThroughComponentIS(xInputStream, xModelComp, xContext, xInfoSet, + u"com.sun.star.comp.Math.MLImporter", false, 6); + + // Finish + if (xStatusIndicator.is()) + xStatusIndicator->end(); + + // Declare any error + if (nError != ERRCODE_NONE) + SAL_WARN("starmath", "Failed to read file"); + + return nError; + } +} + +ErrCode SmMLImportWrapper::Import(std::u16string_view aSource) +{ + // Fetch context + uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); + if (!xContext.is()) + { + SAL_WARN("starmath", "Failed to fetch model while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Check model + if (!m_xModel.is()) + { + SAL_WARN("starmath", "Failed to fetch model while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Make a model component from our SmModel + uno::Reference<lang::XComponent> xModelComp = m_xModel; + if (!xModelComp.is()) + { + SAL_WARN("starmath", "Failed to make model while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Get model via uno + SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(m_xModel); + if (pModel == nullptr) + { + SAL_WARN("starmath", "Failed to fetch sm model while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Get doc shell + m_pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell()); + if (m_pDocShell == nullptr) + { + SAL_WARN("starmath", "Failed to fetch smdoc shell while file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Create property list + static const comphelper::PropertyMapEntry aInfoMap[] + = { { u"PrivateData", 0, cppu::UnoType<XInterface>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { u"BaseURI", 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, + 0 }, + { u"StreamRelPath", 0, ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { u"StreamName", 0, ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 } }; + uno::Reference<beans::XPropertySet> xInfoSet( + comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap))); + + // Read data + // read a component from text + ErrCode nError = ReadThroughComponentMS(aSource, xModelComp, xContext, xInfoSet); + + // Declare any error + if (nError != ERRCODE_NONE) + { + SAL_WARN("starmath", "Failed to read file"); + return nError; + } + + return ERRCODE_NONE; +} + +// read a component from input stream +ErrCode SmMLImportWrapper::ReadThroughComponentIS( + const Reference<io::XInputStream>& xInputStream, const Reference<XComponent>& xModelComponent, + Reference<uno::XComponentContext> const& rxContext, + Reference<beans::XPropertySet> const& rPropSet, const char16_t* pFilterName, bool bEncrypted, + int_fast16_t nSyntaxVersion) +{ + // Needs an input stream but checked by caller + // Needs a context but checked by caller + // Needs property set but checked by caller + // Needs a filter name but checked by caller + + // Prepare ParserInputSource + xml::sax::InputSource aParserInput; + aParserInput.aInputStream = xInputStream; + + // Prepare property list + Sequence<Any> aArgs{ Any(rPropSet) }; + + // Get filter + Reference<XInterface> xFilter + = rxContext->getServiceManager()->createInstanceWithArgumentsAndContext( + OUString(pFilterName), aArgs, rxContext); + if (!xFilter.is()) + { + SAL_WARN("starmath", "Can't instantiate filter component " << OUString(pFilterName)); + return ERRCODE_SFX_DOLOADFAILED; + } + + // Connect model and filter + Reference<XImporter> xImporter(xFilter, UNO_QUERY); + xImporter->setTargetDocument(xModelComponent); + + // Finally, parser the stream + try + { + Reference<css::xml::sax::XFastParser> xFastParser(xFilter, UNO_QUERY); + Reference<css::xml::sax::XFastDocumentHandler> xFastDocHandler(xFilter, UNO_QUERY); + if (xFastParser) + { + xFastParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities); + xFastParser->parseStream(aParserInput); + } + else if (xFastDocHandler) + { + Reference<css::xml::sax::XFastParser> xParser + = css::xml::sax::FastParser::create(rxContext); + xParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities); + xParser->setFastDocumentHandler(xFastDocHandler); + xParser->parseStream(aParserInput); + } + else + { + Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, UNO_QUERY); + assert(xDocHandler); + Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(rxContext); + xParser->setDocumentHandler(xDocHandler); + xParser->parseStream(aParserInput); + } + + if (nSyntaxVersion == 5) + { + SmXMLImport* pXMlImport = comphelper::getFromUnoTunnel<SmXMLImport>(xFilter); + if (pXMlImport != nullptr && pXMlImport->GetSuccess()) + return ERRCODE_NONE; + else + { + SAL_WARN("starmath", "Filter failed on file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + } + + m_pMlImport = comphelper::getFromUnoTunnel<SmMLImport>(xFilter); + if (m_pMlImport != nullptr && m_pMlImport->getSuccess()) + return ERRCODE_NONE; + else + { + SAL_WARN("starmath", "Filter failed on file input"); + return ERRCODE_SFX_DOLOADFAILED; + } + } + catch (const xml::sax::SAXParseException& r) + { + // Sax parser sends wrapped exceptions, try to find the original one + xml::sax::SAXException aTmp; + xml::sax::SAXException aSaxEx = *static_cast<const xml::sax::SAXException*>(&r); + while (aSaxEx.WrappedException >>= aTmp) + aSaxEx = aTmp; + + packages::zip::ZipIOException aBrokenPackage; + if (aSaxEx.WrappedException >>= aBrokenPackage) + { + SAL_WARN("starmath", "Failed to read file SAXParseException"); + return ERRCODE_IO_BROKENPACKAGE; + } + + if (bEncrypted) + { + SAL_WARN("starmath", "Wrong file password SAXParseException"); + return ERRCODE_SFX_WRONGPASSWORD; + } + } + catch (const xml::sax::SAXException& r) + { + packages::zip::ZipIOException aBrokenPackage; + if (r.WrappedException >>= aBrokenPackage) + { + SAL_WARN("starmath", "Failed to read file SAXException"); + return ERRCODE_IO_BROKENPACKAGE; + } + + if (bEncrypted) + { + SAL_WARN("starmath", "Wrong file password SAXException"); + return ERRCODE_SFX_WRONGPASSWORD; + } + } + catch (const packages::zip::ZipIOException&) + { + SAL_WARN("starmath", "Failed to unzip file ZipIOException"); + return ERRCODE_IO_BROKENPACKAGE; + } + catch (const io::IOException&) + { + SAL_WARN("starmath", "Failed to read file ZipIOException"); + return ERRCODE_IO_UNKNOWN; + } + catch (const std::range_error&) + { + SAL_WARN("starmath", "Failed to read file"); + return ERRCODE_ABORT; + } + + return ERRCODE_ABORT; +} + +// read a component from storage +ErrCode SmMLImportWrapper::ReadThroughComponentS(const uno::Reference<embed::XStorage>& xStorage, + const Reference<XComponent>& xModelComponent, + const char16_t* pStreamName, + Reference<uno::XComponentContext> const& rxContext, + Reference<beans::XPropertySet> const& rPropSet, + const char16_t* pFilterName, + int_fast16_t nSyntaxVersion) +{ + // Needs a storage but checked by caller + // Needs a model but checked by caller + // Needs a stream name but checked by caller + // Needs a context but checked by caller + // Needs a property set but checked by caller + // Needs a filter name but checked by caller + + // Get the input stream + try + { + // Create the stream for the event read + uno::Reference<io::XStream> xEventsStream + = xStorage->openStreamElement(OUString(pStreamName), 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; + aAny >>= bEncrypted; + + // Set base URL and open stream + rPropSet->setPropertyValue("StreamName", Any(OUString(pStreamName))); + Reference<io::XInputStream> xStream = xEventsStream->getInputStream(); + + // Execute read + return ReadThroughComponentIS(xStream, xModelComponent, rxContext, rPropSet, pFilterName, + bEncrypted, nSyntaxVersion); + } + catch (packages::WrongPasswordException&) + { + SAL_WARN("starmath", "Wrong file password"); + return ERRCODE_SFX_WRONGPASSWORD; + } + catch (packages::zip::ZipIOException&) + { + SAL_WARN("starmath", "Failed to unzip file"); + return ERRCODE_IO_BROKENPACKAGE; + } + catch (uno::Exception&) + { + } + + return ERRCODE_SFX_DOLOADFAILED; +} + +// read a component from text +ErrCode SmMLImportWrapper::ReadThroughComponentMS( + std::u16string_view aText, const css::uno::Reference<css::lang::XComponent>& xModelComponent, + css::uno::Reference<css::uno::XComponentContext> const& rxContext, + css::uno::Reference<css::beans::XPropertySet> const& rPropSet) +{ + // Needs a storage but checked by caller + // Needs a model but checked by caller + // Needs a stream name but checked by caller + // Needs a context but checked by caller + // Needs a property set but checked by caller + // Needs a filter name but checked by caller + + // Get the input stream + try + { + // Generate input memory stream + SvMemoryStream aMemoryStream; + aMemoryStream.WriteOString(OUStringToOString(aText, RTL_TEXTENCODING_UTF8)); + uno::Reference<io::XInputStream> xStream(new utl::OInputStreamWrapper(aMemoryStream)); + + // Execute read + return ReadThroughComponentIS(xStream, xModelComponent, rxContext, rPropSet, + u"com.sun.star.comp.Math.MLImporter", false, 6); + } + catch (packages::WrongPasswordException&) + { + SAL_WARN("starmath", "Wrong file password"); + return ERRCODE_SFX_WRONGPASSWORD; + } + catch (packages::zip::ZipIOException&) + { + SAL_WARN("starmath", "Failed to unzip file"); + return ERRCODE_IO_BROKENPACKAGE; + } + catch (uno::Exception&) + { + } + + return ERRCODE_SFX_DOLOADFAILED; +} + +// SmMLImport technical +/*************************************************************************************************/ + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +Math_MLImporter_get_implementation(uno::XComponentContext* pCtx, + uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire( + new SmMLImport(pCtx, "com.sun.star.comp.Math.XMLImporter", SvXMLImportFlags::ALL)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +Math_MLOasisMetaImporter_get_implementation(uno::XComponentContext* pCtx, + uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SmMLImport(pCtx, "com.sun.star.comp.Math.XMLOasisMetaImporter", + SvXMLImportFlags::META)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +Math_MLOasisSettingsImporter_get_implementation(uno::XComponentContext* pCtx, + uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SmMLImport(pCtx, "com.sun.star.comp.Math.XMLOasisSettingsImporter", + SvXMLImportFlags::SETTINGS)); +} + +// SmMLImportContext +/*************************************************************************************************/ + +namespace +{ +class SmMLImportContext : public SvXMLImportContext +{ +private: + SmMlElement** m_pParent; + SmMlElement* m_pElement; + SmMlElement* m_pStyle; + +public: + SmMLImportContext(SmMLImport& rImport, SmMlElement** pParent) + : SvXMLImportContext(rImport) + , m_pParent(pParent) + , m_pElement(nullptr) + , m_pStyle(nullptr) + { + } + +private: + void declareMlError(); + +public: + /** Handles characters (text) + */ + virtual void SAL_CALL characters(const OUString& aChars) override; + + /** Starts the mathml element + */ + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, const Reference<XFastAttributeList>& aAttributeList) override; + + /** Ends the mathml element + */ + virtual void SAL_CALL endFastElement(sal_Int32 Element) override; + + /** Creates child element + */ + virtual uno::Reference<XFastContextHandler> + SAL_CALL createFastChildContext(sal_Int32 nElement, + const uno::Reference<XFastAttributeList>& Attribs) override; + + /** Inherits the style from it's parents + */ + void inheritStyle(); + + /** Inherits the style from it's parents on end + */ + void inheritStyleEnd(); + + /** Handle mathml attributes + */ + void handleAttributes(const Reference<XFastAttributeList>& aAttributeList); + + /** Handle mathml length attributes + */ + SmLengthValue handleLengthAttribute(const OUString& aAttribute); +}; + +uno::Reference<XFastContextHandler> SAL_CALL +SmMLImportContext::createFastChildContext(sal_Int32, const uno::Reference<XFastAttributeList>&) +{ + uno::Reference<xml::sax::XFastContextHandler> xContext; + xContext = new SmMLImportContext(static_cast<SmMLImport&>(GetImport()), &m_pElement); + return xContext; +} + +void SmMLImportContext::declareMlError() +{ + SmMLImport& aSmMLImport = static_cast<SmMLImport&>(GetImport()); + aSmMLImport.declareMlError(); +} + +void SmMLImportContext::inheritStyle() +{ + while ((m_pStyle = m_pStyle->getParentElement()) != nullptr) + { + if (m_pStyle->getParentElement()->getMlElementType() == SmMlElementType::MlMstyle + || m_pStyle->getParentElement()->getMlElementType() == SmMlElementType::MlMath) + break; + } + + // Parent inheritation + // Mathcolor, mathsize, dir and displaystyle are inherited from parent + SmMlElement* pParent = *m_pParent; + m_pElement->setAttribute(pParent->getAttribute(SmMlAttributeValueType::MlMathcolor)); + m_pElement->setAttribute(pParent->getAttribute(SmMlAttributeValueType::MlMathsize)); + m_pElement->setAttribute(pParent->getAttribute(SmMlAttributeValueType::MlDir)); + m_pElement->setAttribute(pParent->getAttribute(SmMlAttributeValueType::MlDisplaystyle)); + + // Inherit operator dictionary overwrites + if (m_pStyle != nullptr + && (m_pElement->getMlElementType() == SmMlElementType::MlMo + || m_pElement->getMlElementType() == SmMlElementType::MlMstyle + || m_pElement->getMlElementType() == SmMlElementType::MlMath)) + { + // TODO fetch operator dictionary first and then overwrite + if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlAccent)) + m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlAccent)); + if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlFence)) + m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlFence)); + if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlLspace)) + m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlLspace)); + if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlMaxsize)) + m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlMaxsize)); + if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlMinsize)) + m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlMinsize)); + if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlMovablelimits)) + m_pElement->setAttribute( + m_pStyle->getAttribute(SmMlAttributeValueType::MlMovablelimits)); + if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlRspace)) + m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlRspace)); + if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlSeparator)) + m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlSeparator)); + if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlStretchy)) + m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlStretchy)); + if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlSymmetric)) + m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlSymmetric)); + + if (m_pElement->getMlElementType() == SmMlElementType::MlMo) + { + // Set form based in position + SmMlAttribute aAttribute(SmMlAttributeValueType::MlForm); + SmMlForm aForm; + if (m_pElement->getSubElementId() == 0) + aForm = { SmMlAttributeValueForm::MlPrefix }; + else + aForm = { SmMlAttributeValueForm::MlInfix }; + aAttribute.setMlForm(&aForm); + m_pElement->setAttribute(aAttribute); + } + } + + // Inherit mathvariant + if (m_pStyle && m_pStyle->isAttributeSet(SmMlAttributeValueType::MlMathvariant)) + m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlMathvariant)); +} + +void SmMLImportContext::inheritStyleEnd() +{ + // Mo: check it is the end: postfix + if (m_pElement->getMlElementType() == SmMlElementType::MlMo) + { + if ((*m_pParent)->getSubElementsCount() == m_pElement->getSubElementId()) + { + // Set form based in position + SmMlAttribute aAttribute(SmMlAttributeValueType::MlForm); + SmMlForm aForm = { SmMlAttributeValueForm::MlPosfix }; + aAttribute.setMlForm(&aForm); + m_pElement->setAttribute(aAttribute); + } + } + + // Mi: 1 char -> italic + if (m_pElement->getMlElementType() != SmMlElementType::MlMi) + return; + + // Inherit mathvariant + if (!m_pStyle->isAttributeSet(SmMlAttributeValueType::MlMathvariant)) + { + sal_Int32 nIndexUtf16 = 0; + // Check if there is only one code point + m_pElement->getText().iterateCodePoints(&nIndexUtf16, 1); + // Mathml says that 1 code point -> italic + if (nIndexUtf16 == m_pElement->getText().getLength()) + { + SmMlAttribute aAttribute(SmMlAttributeValueType::MlMathvariant); + SmMlMathvariant aMathvariant = { SmMlAttributeValueMathvariant::italic }; + aAttribute.setMlMathvariant(&aMathvariant); + aAttribute.setSet(false); + m_pElement->setAttribute(aAttribute); + } + } +} + +SmLengthValue SmMLImportContext::handleLengthAttribute(const OUString& aAttribute) +{ + // Locate unit indication + int32_t nUnitPos; + for (nUnitPos = 0; + nUnitPos < aAttribute.getLength() + && (rtl::isAsciiHexDigit(aAttribute[nUnitPos]) || aAttribute[nUnitPos] == '.'); + ++nUnitPos) + ; + + // Find unit + SmLengthUnit nUnit = SmLengthUnit::MlM; + if (nUnitPos != aAttribute.getLength()) + { + OUString aUnit = aAttribute.copy(nUnitPos); + if (aUnit.compareToIgnoreAsciiCaseAscii("ex")) + nUnit = SmLengthUnit::MlEx; + if (aUnit.compareToIgnoreAsciiCaseAscii("px")) + nUnit = SmLengthUnit::MlPx; + if (aUnit.compareToIgnoreAsciiCaseAscii("in")) + nUnit = SmLengthUnit::MlIn; + if (aUnit.compareToIgnoreAsciiCaseAscii("cm")) + nUnit = SmLengthUnit::MlCm; + if (aUnit.compareToIgnoreAsciiCaseAscii("mm")) + nUnit = SmLengthUnit::MlMm; + if (aUnit.compareToIgnoreAsciiCaseAscii("pt")) + nUnit = SmLengthUnit::MlPt; + if (aUnit.compareToIgnoreAsciiCaseAscii("pc")) + nUnit = SmLengthUnit::MlPc; + if (aUnit.compareToIgnoreAsciiCaseAscii("%")) + nUnit = SmLengthUnit::MlP; + else + declareMlError(); + } + + // Get value + std::u16string_view aValue = aAttribute.subView(0, nUnitPos); + double nValue = o3tl::toDouble(aValue); + if (nValue == 0) + { + nUnit = SmLengthUnit::MlM; + nValue = 1.0; + declareMlError(); + } + + // Return + SmLengthValue aLengthValue = { nUnit, nValue, new OUString(aAttribute) }; + return aLengthValue; +} + +void SmMLImportContext::handleAttributes(const Reference<XFastAttributeList>& aAttributeList) +{ + for (auto& aIter : sax_fastparser::castToFastAttributeList(aAttributeList)) + { + SmMlAttribute aAttribute(SmMlAttributeValueType::NMlEmpty); + switch (aIter.getToken() & TOKEN_MASK) + { + case XML_ACCENT: + { + if (IsXMLToken(aIter, XML_TRUE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlAccent); + SmMlAccent aAccent = { SmMlAttributeValueAccent::MlTrue }; + aAttribute.setMlAccent(&aAccent); + } + else if (IsXMLToken(aIter, XML_FALSE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlAccent); + SmMlAccent aAccent = { SmMlAttributeValueAccent::MlFalse }; + aAttribute.setMlAccent(&aAccent); + } + else + { + declareMlError(); + } + break; + } + case XML_DIR: + { + if (IsXMLToken(aIter, XML_RTL)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlDir); + SmMlDir aDir = { SmMlAttributeValueDir::MlRtl }; + aAttribute.setMlDir(&aDir); + } + else if (IsXMLToken(aIter, XML_LTR)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlDir); + SmMlDir aDir = { SmMlAttributeValueDir::MlLtr }; + aAttribute.setMlDir(&aDir); + } + else + { + declareMlError(); + } + break; + } + case XML_DISPLAYSTYLE: + if (IsXMLToken(aIter, XML_TRUE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlDisplaystyle); + SmMlDisplaystyle aDisplaystyle = { SmMlAttributeValueDisplaystyle::MlTrue }; + aAttribute.setMlDisplaystyle(&aDisplaystyle); + } + else if (IsXMLToken(aIter, XML_FALSE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlDisplaystyle); + SmMlDisplaystyle aDisplaystyle = { SmMlAttributeValueDisplaystyle::MlFalse }; + aAttribute.setMlDisplaystyle(&aDisplaystyle); + } + else + { + declareMlError(); + } + break; + case XML_FENCE: + if (IsXMLToken(aIter, XML_TRUE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlFence); + SmMlFence aFence = { SmMlAttributeValueFence::MlTrue }; + aAttribute.setMlFence(&aFence); + } + else if (IsXMLToken(aIter, XML_FALSE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlFence); + SmMlFence aFence = { SmMlAttributeValueFence::MlFalse }; + aAttribute.setMlFence(&aFence); + } + else + { + declareMlError(); + } + break; + case XML_HREF: + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlHref); + OUString* aRef = new OUString(aIter.toString()); + SmMlHref aHref = { SmMlAttributeValueHref::NMlValid, aRef }; + aAttribute.setMlHref(&aHref); + break; + } + case XML_LSPACE: + { + SmMlLspace aLspace; + aLspace.m_aLengthValue = handleLengthAttribute(aIter.toString()); + aAttribute.setMlLspace(&aLspace); + break; + } + case XML_MATHBACKGROUND: + { + if (IsXMLToken(aIter, XML_TRANSPARENT)) + { + SmMlMathbackground aMathbackground + = { SmMlAttributeValueMathbackground::MlTransparent, COL_TRANSPARENT }; + aAttribute.setMlMathbackground(&aMathbackground); + } + else + { + Color aColor + = starmathdatabase::Identify_ColorName_HTML(aIter.toString()).cColor; + SmMlMathbackground aMathbackground + = { SmMlAttributeValueMathbackground::MlRgb, aColor }; + aAttribute.setMlMathbackground(&aMathbackground); + } + break; + } + case XML_MATHCOLOR: + { + if (IsXMLToken(aIter, XML_DEFAULT)) + { + SmMlMathcolor aMathcolor + = { SmMlAttributeValueMathcolor::MlDefault, COL_BLACK }; + aAttribute.setMlMathcolor(&aMathcolor); + } + else + { + Color aColor + = starmathdatabase::Identify_ColorName_HTML(aIter.toString()).cColor; + SmMlMathcolor aMathcolor = { SmMlAttributeValueMathcolor::MlRgb, aColor }; + aAttribute.setMlMathcolor(&aMathcolor); + } + break; + } + case XML_MATHSIZE: + { + SmMlMathsize aMathsize; + aMathsize.m_aLengthValue = handleLengthAttribute(aIter.toString()); + aAttribute.setMlMathsize(&aMathsize); + break; + } + case XML_MATHVARIANT: + { + OUString aVariant = aIter.toString(); + SmMlAttributeValueMathvariant nVariant = SmMlAttributeValueMathvariant::normal; + if (aVariant.compareTo(u"normal")) + nVariant = SmMlAttributeValueMathvariant::normal; + else if (aVariant.compareTo(u"bold")) + nVariant = SmMlAttributeValueMathvariant::bold; + else if (aVariant.compareTo(u"italic")) + nVariant = SmMlAttributeValueMathvariant::italic; + else if (aVariant.compareTo(u"double-struck")) + nVariant = SmMlAttributeValueMathvariant::double_struck; + else if (aVariant.compareTo(u"script")) + nVariant = SmMlAttributeValueMathvariant::script; + else if (aVariant.compareTo(u"fraktur")) + nVariant = SmMlAttributeValueMathvariant::fraktur; + else if (aVariant.compareTo(u"sans-serif")) + nVariant = SmMlAttributeValueMathvariant::sans_serif; + else if (aVariant.compareTo(u"monospace")) + nVariant = SmMlAttributeValueMathvariant::monospace; + else if (aVariant.compareTo(u"bold-italic")) + nVariant = SmMlAttributeValueMathvariant::bold_italic; + else if (aVariant.compareTo(u"bold-fracktur")) + nVariant = SmMlAttributeValueMathvariant::bold_fraktur; + else if (aVariant.compareTo(u"bold-script")) + nVariant = SmMlAttributeValueMathvariant::bold_script; + else if (aVariant.compareTo(u"bold-sans-serif")) + nVariant = SmMlAttributeValueMathvariant::bold_sans_serif; + else if (aVariant.compareTo(u"sans-serif-italic")) + nVariant = SmMlAttributeValueMathvariant::sans_serif_italic; + else if (aVariant.compareTo(u"sans-serif-bold-italic")) + nVariant = SmMlAttributeValueMathvariant::sans_serif_bold_italic; + else if (aVariant.compareTo(u"initial")) + nVariant = SmMlAttributeValueMathvariant::initial; + else if (aVariant.compareTo(u"tailed")) + nVariant = SmMlAttributeValueMathvariant::tailed; + else if (aVariant.compareTo(u"looped")) + nVariant = SmMlAttributeValueMathvariant::looped; + else if (aVariant.compareTo(u"stretched")) + nVariant = SmMlAttributeValueMathvariant::stretched; + else + declareMlError(); + SmMlMathvariant aMathvariant = { nVariant }; + aAttribute.setMlMathvariant(&aMathvariant); + break; + } + case XML_MAXSIZE: + { + SmMlMaxsize aMaxsize; + if (IsXMLToken(aIter, XML_INFINITY)) + { + aMaxsize.m_aMaxsize = SmMlAttributeValueMaxsize::MlInfinity; + aMaxsize.m_aLengthValue = { SmLengthUnit::MlP, 10000, new OUString(u"10000%") }; + } + else + { + aMaxsize.m_aMaxsize = SmMlAttributeValueMaxsize::MlFinite; + aMaxsize.m_aLengthValue = handleLengthAttribute(aIter.toString()); + } + aAttribute.setMlMaxsize(&aMaxsize); + break; + } + case XML_MINSIZE: + { + SmMlMinsize aMinsize; + aMinsize.m_aLengthValue = handleLengthAttribute(aIter.toString()); + aAttribute.setMlMinsize(&aMinsize); + break; + } + case XML_MOVABLELIMITS: + if (IsXMLToken(aIter, XML_TRUE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlMovablelimits); + SmMlMovablelimits aMovablelimits = { SmMlAttributeValueMovablelimits::MlTrue }; + aAttribute.setMlMovablelimits(&aMovablelimits); + } + else if (IsXMLToken(aIter, XML_FALSE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlMovablelimits); + SmMlMovablelimits aMovablelimits = { SmMlAttributeValueMovablelimits::MlFalse }; + aAttribute.setMlMovablelimits(&aMovablelimits); + } + else + { + declareMlError(); + } + break; + case XML_RSPACE: + { + SmMlRspace aRspace; + aRspace.m_aLengthValue = handleLengthAttribute(aIter.toString()); + aAttribute.setMlRspace(&aRspace); + break; + } + case XML_SEPARATOR: + if (IsXMLToken(aIter, XML_TRUE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlSeparator); + SmMlSeparator aSeparator = { SmMlAttributeValueSeparator::MlTrue }; + aAttribute.setMlSeparator(&aSeparator); + } + else if (IsXMLToken(aIter, XML_FALSE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlSeparator); + SmMlSeparator aSeparator = { SmMlAttributeValueSeparator::MlFalse }; + aAttribute.setMlSeparator(&aSeparator); + } + else + { + declareMlError(); + } + break; + case XML_STRETCHY: + if (IsXMLToken(aIter, XML_TRUE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlStretchy); + SmMlStretchy aStretchy = { SmMlAttributeValueStretchy::MlTrue }; + aAttribute.setMlStretchy(&aStretchy); + } + else if (IsXMLToken(aIter, XML_FALSE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlStretchy); + SmMlStretchy aStretchy = { SmMlAttributeValueStretchy::MlFalse }; + aAttribute.setMlStretchy(&aStretchy); + } + else + { + declareMlError(); + } + break; + case XML_SYMMETRIC: + if (IsXMLToken(aIter, XML_TRUE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlSymmetric); + SmMlSymmetric aSymmetric = { SmMlAttributeValueSymmetric::MlTrue }; + aAttribute.setMlSymmetric(&aSymmetric); + } + else if (IsXMLToken(aIter, XML_FALSE)) + { + aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlSymmetric); + SmMlSymmetric aSymmetric = { SmMlAttributeValueSymmetric::MlFalse }; + aAttribute.setMlSymmetric(&aSymmetric); + } + else + { + declareMlError(); + } + break; + default: + declareMlError(); + break; + } + if (aAttribute.isNullAttribute()) + declareMlError(); + else + m_pElement->setAttribute(aAttribute); + } +} + +void SmMLImportContext::characters(const OUString& aChars) { m_pElement->setText(aChars); } + +void SmMLImportContext::startFastElement(sal_Int32 nElement, + const Reference<XFastAttributeList>& aAttributeList) +{ + switch (nElement) + { + case XML_ELEMENT(MATH, XML_MATH): + m_pElement = new SmMlElement(SmMlElementType::MlMath); + break; + case XML_ELEMENT(MATH, XML_MI): + m_pElement = new SmMlElement(SmMlElementType::MlMi); + break; + case XML_ELEMENT(MATH, XML_MERROR): + m_pElement = new SmMlElement(SmMlElementType::MlMerror); + break; + case XML_ELEMENT(MATH, XML_MN): + m_pElement = new SmMlElement(SmMlElementType::MlMn); + break; + case XML_ELEMENT(MATH, XML_MO): + m_pElement = new SmMlElement(SmMlElementType::MlMo); + break; + case XML_ELEMENT(MATH, XML_MROW): + m_pElement = new SmMlElement(SmMlElementType::MlMrow); + break; + case XML_ELEMENT(MATH, XML_MTEXT): + m_pElement = new SmMlElement(SmMlElementType::MlMtext); + break; + case XML_ELEMENT(MATH, XML_MSTYLE): + m_pElement = new SmMlElement(SmMlElementType::MlMstyle); + break; + default: + m_pElement = new SmMlElement(SmMlElementType::NMlEmpty); + declareMlError(); + break; + } + SmMlElement* pParent = *m_pParent; + pParent->setSubElement(pParent->getSubElementsCount(), m_pElement); + inheritStyle(); + handleAttributes(aAttributeList); +} + +void SmMLImportContext::endFastElement(sal_Int32) { inheritStyleEnd(); } +} + +// SmMLImport +/*************************************************************************************************/ + +const uno::Sequence<sal_Int8>& SmMLImport::getUnoTunnelId() noexcept +{ + static const comphelper::UnoIdInit theSmMLImportUnoTunnelId; + return theSmMLImportUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL SmMLImport::getSomething(const uno::Sequence<sal_Int8>& rId) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf<SvXMLImport>{}); +} + +SvXMLImportContext* +SmMLImport::CreateFastContext(sal_Int32 nElement, + const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/) +{ + SvXMLImportContext* pContext = nullptr; + + switch (nElement) + { + case XML_ELEMENT(OFFICE, XML_DOCUMENT): + { + if (m_pElementTree == nullptr) + m_pElementTree = new SmMlElement(SmMlElementType::NMlEmpty); + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(GetModel(), + uno::UNO_QUERY_THROW); + pContext = new SmMLImportContext(*this, &m_pElementTree); + break; + } + case XML_ELEMENT(OFFICE, XML_DOCUMENT_META): + { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(GetModel(), + uno::UNO_QUERY_THROW); + pContext = new SvXMLMetaDocumentContext(*this, xDPS->getDocumentProperties()); + break; + } + case XML_ELEMENT(OFFICE, XML_DOCUMENT_SETTINGS): + { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(GetModel(), + uno::UNO_QUERY_THROW); + pContext = new XMLDocumentSettingsContext(*this); + break; + } + default: + declareMlError(); + break; + } + return pContext; +} + +void SmMLImport::endDocument() +{ + uno::Reference<frame::XModel> xModel = GetModel(); + if (!xModel.is()) + { + SAL_WARN("starmath", "Failed to set view settings because missing model"); + SvXMLImport::endDocument(); + return; + } + + SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel); + if (!pModel) + { + SAL_WARN("starmath", "Failed to set view settings because missing sm model"); + SvXMLImport::endDocument(); + return; + } + + SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell()); + if (!pDocShell) + { + SAL_WARN("starmath", "Failed to set view settings because missing sm doc shell"); + SvXMLImport::endDocument(); + return; + } + + // Check if there is element tree + if (m_pElementTree == nullptr) + { + m_bSuccess = true; + SvXMLImport::endDocument(); + return; + } + + // Get element tree and setup + + if (m_pElementTree->getSubElementsCount() == 0) + { + delete m_pElementTree; + m_pElementTree = nullptr; + } + else + { + SmMlElement* pTmpElememt = m_pElementTree->getSubElement(0); + delete m_pElementTree; + m_pElementTree = pTmpElememt; + } + pDocShell->SetMlElementTree(m_pElementTree); + + m_bSuccess = true; + SvXMLImport::endDocument(); +} + +void SmMLImport::SetViewSettings(const Sequence<PropertyValue>& aViewProps) +{ + uno::Reference<frame::XModel> xModel = GetModel(); + if (!xModel.is()) + { + SAL_WARN("starmath", "Failed to set view settings because missing model"); + return; + } + + SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel); + if (!pModel) + { + SAL_WARN("starmath", "Failed to set view settings because missing sm model"); + return; + } + + SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell()); + if (!pDocShell) + { + SAL_WARN("starmath", "Failed to set view settings because missing sm doc shell"); + return; + } + + tools::Rectangle aRect(pDocShell->GetVisArea()); + + tools::Long nTmp = 0; + + for (const PropertyValue& rValue : aViewProps) + { + if (rValue.Name == "ViewAreaTop") + { + rValue.Value >>= nTmp; + aRect.SaturatingSetPosY(nTmp); + } + else if (rValue.Name == "ViewAreaLeft") + { + rValue.Value >>= nTmp; + aRect.SaturatingSetPosX(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 SmMLImport::SetConfigurationSettings(const Sequence<PropertyValue>& aConfProps) +{ + uno::Reference<frame::XModel> xModel = GetModel(); + if (!xModel.is()) + { + SAL_WARN("starmath", "Failed to set view settings because missing model"); + return; + } + + uno::Reference<XPropertySet> xProps(xModel, UNO_QUERY); + if (!xProps.is()) + { + SAL_WARN("starmath", "Failed to set view settings because missing model properties"); + return; + } + + Reference<XPropertySetInfo> xInfo(xProps->getPropertySetInfo()); + if (!xInfo.is()) + { + SAL_WARN("starmath", + "Failed to set view settings because missing model properties information"); + return; + } + + static const OUStringLiteral sFormula(u"Formula"); + static const OUStringLiteral sBasicLibraries(u"BasicLibraries"); + static const OUStringLiteral sDialogLibraries(u"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&) + { + SAL_WARN("starmath", "Unexpected issue while loading document properties"); + } + } + } +} + +SmMLImport::SmMLImport(const css::uno::Reference<css::uno::XComponentContext>& rContext, + OUString const& implementationName, SvXMLImportFlags nImportFlags) + : SvXMLImport(rContext, implementationName, nImportFlags) + , m_pElementTree(nullptr) + , m_bSuccess(false) + , m_nSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion()) +{ +} + +/** Handles an error on the mathml structure + */ +void SmMLImport::declareMlError() +{ + m_bSuccess = false; + SAL_WARN("starmath", "MathML error"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/mathml/iterator.cxx b/starmath/source/mathml/iterator.cxx new file mode 100644 index 000000000..24de35c18 --- /dev/null +++ b/starmath/source/mathml/iterator.cxx @@ -0,0 +1,77 @@ +/* -*- 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 <mathml/iterator.hxx> + +/** The purpose of this iterator is to be able to iterate threw an infinite element tree + * infinite -> as much as your memory can hold + * No call-backs that will end up in out of stack + */ + +namespace mathml +{ +static inline void deleteElement(SmMlElement* aSmMlElement, void*) { delete aSmMlElement; } + +static inline void cloneElement(SmMlElement* aSmMlElement, void* aData) +{ + // Prepare data + SmMlElement* aNewSmMlElement = new SmMlElement(*aSmMlElement); + SmMlElement* aCopyTree = *static_cast<SmMlElement**>(aData); + + // Append data + aCopyTree->setSubElement(aCopyTree->getSubElementsCount(), aNewSmMlElement); + + // Prepare for following + // If it has sub elements, then it will be the next + if (aSmMlElement->getSubElementsCount() != 0) + aCopyTree = aNewSmMlElement; + else // Otherwise remounts up to where it should be + { + while (aSmMlElement->getParentElement() != nullptr) + { + // get parent + SmMlElement* pParent = aSmMlElement->getParentElement(); + aCopyTree = aCopyTree->getParentElement(); + // was this the last branch ? + if (aSmMlElement->getSubElementId() + 1 != pParent->getSubElementsCount()) + break; // no -> stop going up + // Prepare for next round + aSmMlElement = pParent; + } + } + + // Closing extras + *static_cast<SmMlElement**>(aData) = aCopyTree; +} + +void SmMlIteratorFree(SmMlElement* pMlElementTree) +{ + if (pMlElementTree == nullptr) + return; + for (size_t i = 0; i < pMlElementTree->getSubElementsCount(); ++i) + { + SmMlIteratorFree(pMlElementTree->getSubElement(i)); + } + deleteElement(pMlElementTree, nullptr); +} + +SmMlElement* SmMlIteratorCopy(SmMlElement* pMlElementTree) +{ + if (pMlElementTree == nullptr) + return nullptr; + SmMlElement* aDummyElement = new SmMlElement(); + SmMlIteratorTopToBottom(pMlElementTree, cloneElement, &aDummyElement); + SmMlElement* aResultElement = aDummyElement->getSubElement(0); + delete aDummyElement; + return aResultElement; +} + +} // end namespace mathml + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/mathml/mathmlMo.cxx b/starmath/source/mathml/mathmlMo.cxx new file mode 100644 index 000000000..fb76b070e --- /dev/null +++ b/starmath/source/mathml/mathmlMo.cxx @@ -0,0 +1,1127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <mathmlMo.hxx> + +static moOperatorData moOperatorDataDictionaryData[starmathdatabase::MATHML_MO_COUNT] + = { { u"\u2018", moOpDF::prefix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp }, + { u"\u2019", moOpDF::postfix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp }, + { u"\u201C", moOpDF::prefix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp }, + { u"\u201D", moOpDF::postfix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp }, + { u"(", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u")", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"[", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"]", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"{", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"|", moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"||", moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"|||", moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"}", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2016", moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence }, + { u"\u2308", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2309", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u230A", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u230B", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2329", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u232A", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2772", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2773", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u27E6", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u27E7", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u27E8", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u27E9", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u27EA", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u27EB", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u27EC", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u27ED", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u27EE", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u27EF", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2980", moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence }, + { u"\u2983", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2984", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2985", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2986", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2987", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2988", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2989", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u298A", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u298B", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u298C", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u298D", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u298E", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u298F", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2990", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2991", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2992", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2993", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2994", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2995", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2996", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2997", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2998", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u29FC", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u29FD", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric }, + { u";", moOpDF::infix, 30, 0, 3, moOpDP::separator | moOpDP::linebreakstyleAfter }, + { u",", moOpDF::infix, 40, 0, 3, moOpDP::separator | moOpDP::linebreakstyleAfter }, + { u"\u2063", moOpDF::infix, 40, 0, 0, moOpDP::separator | moOpDP::linebreakstyleAfter }, + { u"\u2234", moOpDF::infix, 70, 5, 5, moOpDP::nonedp }, + { u"\u2235", moOpDF::infix, 70, 5, 5, moOpDP::nonedp }, + { u"->", moOpDF::infix, 90, 5, 5, moOpDP::nonedp }, + { u"..", moOpDF::postfix, 100, 0, 0, moOpDP::nonedp }, + { u"...", moOpDF::postfix, 100, 0, 0, moOpDP::nonedp }, + { u":", moOpDF::infix, 100, 1, 2, moOpDP::nonedp }, + { u"\u03F6", moOpDF::infix, 110, 5, 5, moOpDP::nonedp }, + { u"\u2026", moOpDF::infix, 150, 0, 0, moOpDP::nonedp }, + { u"\u22EE", moOpDF::infix, 150, 5, 5, moOpDP::nonedp }, + { u"\u22EF", moOpDF::infix, 150, 0, 0, moOpDP::nonedp }, + { u"\u22F1", moOpDF::infix, 150, 5, 5, moOpDP::nonedp }, + { u"\u220B", moOpDF::infix, 160, 5, 5, moOpDP::nonedp }, + { u"\u22A2", moOpDF::infix, 170, 5, 5, moOpDP::nonedp }, + { u"\u22A3", moOpDF::infix, 170, 5, 5, moOpDP::nonedp }, + { u"\u22A4", moOpDF::infix, 170, 5, 5, moOpDP::nonedp }, + { u"\u22A8", moOpDF::infix, 170, 5, 5, moOpDP::nonedp }, + { u"\u22A9", moOpDF::infix, 170, 5, 5, moOpDP::nonedp }, + { u"\u22AC", moOpDF::infix, 170, 5, 5, moOpDP::nonedp }, + { u"\u22AD", moOpDF::infix, 170, 5, 5, moOpDP::nonedp }, + { u"\u22AE", moOpDF::infix, 170, 5, 5, moOpDP::nonedp }, + { u"\u22AF", moOpDF::infix, 170, 5, 5, moOpDP::nonedp }, + { u"\u2228", moOpDF::infix, 190, 4, 4, moOpDP::nonedp }, + { u"&&", moOpDF::infix, 200, 4, 4, moOpDP::nonedp }, + { u"\u2227", moOpDF::infix, 200, 4, 4, moOpDP::nonedp }, + { u"\u2200", moOpDF::prefix, 230, 2, 1, moOpDP::nonedp }, + { u"\u2203", moOpDF::prefix, 230, 2, 1, moOpDP::nonedp }, + { u"\u2204", moOpDF::prefix, 230, 2, 1, moOpDP::nonedp }, + { u"\u2201", moOpDF::infix, 240, 1, 2, moOpDP::nonedp }, + { u"\u2208", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2209", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u220C", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2282", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2282\u20D2", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2283", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2283\u20D2", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2284", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2285", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2286", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2287", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2288", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u2289", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u228A", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"\u228B", moOpDF::infix, 240, 5, 5, moOpDP::nonedp }, + { u"<=", moOpDF::infix, 241, 5, 5, moOpDP::nonedp }, + { u"\u2264", moOpDF::infix, 241, 5, 5, moOpDP::nonedp }, + { u"\u2265", moOpDF::infix, 242, 5, 5, moOpDP::nonedp }, + { u">", moOpDF::infix, 243, 5, 5, moOpDP::nonedp }, + { u">=", moOpDF::infix, 243, 5, 5, moOpDP::nonedp }, + { u"\u226F", moOpDF::infix, 244, 5, 5, moOpDP::nonedp }, + { u"<", moOpDF::infix, 245, 5, 5, moOpDP::nonedp }, + { u"\u226E", moOpDF::infix, 246, 5, 5, moOpDP::nonedp }, + { u"\u2248", moOpDF::infix, 247, 5, 5, moOpDP::nonedp }, + { u"\u223C", moOpDF::infix, 250, 5, 5, moOpDP::nonedp }, + { u"\u2249", moOpDF::infix, 250, 5, 5, moOpDP::nonedp }, + { u"\u2262", moOpDF::infix, 252, 5, 5, moOpDP::nonedp }, + { u"\u2260", moOpDF::infix, 255, 5, 5, moOpDP::nonedp }, + { u"!=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"*=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"+=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"-=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"/=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u":=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"=", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"==", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u221D", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2224", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2225", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2226", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2241", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2243", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2244", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2245", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2246", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2247", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u224D", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2254", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2257", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2259", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u225A", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u225B", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u225C", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u225F", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2261", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2268", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2269", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u226A", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u226A\u0338", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u226B", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u226B\u0338", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u226D", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2270", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2271", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u227A", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u227B", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u227C", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u227D", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2280", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2281", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22A5", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22B4", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22B5", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22C9", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u22CA", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u22CB", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u22CC", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u22D4", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22D6", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22D7", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22D8", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22D9", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22EA", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22EB", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22EC", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u22ED", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u25A0", moOpDF::infix, 260, 3, 3, moOpDP::nonedp }, + { u"\u25A1", moOpDF::infix, 260, 3, 3, moOpDP::nonedp }, + { u"\u25AA", moOpDF::infix, 260, 3, 3, moOpDP::nonedp }, + { u"\u25AB", moOpDF::infix, 260, 3, 3, moOpDP::nonedp }, + { u"\u25AD", moOpDF::infix, 260, 3, 3, moOpDP::nonedp }, + { u"\u25AE", moOpDF::infix, 260, 3, 3, moOpDP::nonedp }, + { u"\u25AF", moOpDF::infix, 260, 3, 3, moOpDP::nonedp }, + { u"\u25B0", moOpDF::infix, 260, 3, 3, moOpDP::nonedp }, + { u"\u25B1", moOpDF::infix, 260, 3, 3, moOpDP::nonedp }, + { u"\u25B3", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25B4", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25B5", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25B6", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25B7", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25B8", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25B9", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25BC", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25BD", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25BE", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25BF", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25C0", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25C1", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25C2", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25C3", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25C4", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25C5", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25C6", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25C7", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25C8", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25C9", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25CC", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25CD", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25CE", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25CF", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25D6", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25D7", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u25E6", moOpDF::infix, 260, 4, 4, moOpDP::nonedp }, + { u"\u29C0", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u29C1", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u29E3", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u29E4", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u29E5", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u29E6", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u29F3", moOpDF::infix, 260, 3, 3, moOpDP::nonedp }, + { u"\u2A87", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2A88", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2AAF", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2AAF\u0338", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2AB0", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2AB0\u0338", moOpDF::infix, 260, 5, 5, moOpDP::nonedp }, + { u"\u2044", moOpDF::infix, 265, 4, 4, moOpDP::stretchy }, + { u"\u2206", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u220A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u220D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u220E", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u2215", moOpDF::infix, 265, 4, 4, moOpDP::stretchy }, + { u"\u2217", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2218", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2219", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u221F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2223", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2236", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2237", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2238", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2239", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u223A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u223B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u223D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u223D\u0331", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u223E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u223F", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u2242", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2242\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u224A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u224B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u224C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u224E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u224E\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u224F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u224F\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2250", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2251", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2252", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2253", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2255", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2256", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2258", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u225D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u225E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2263", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2266", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2266\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2267", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u226C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2272", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2273", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2274", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2275", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2276", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2277", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2278", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2279", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u227E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u227F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u227F\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u228C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u228D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u228E", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u228F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u228F\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2290", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2290\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2291", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2292", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2293", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2294", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u229A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u229B", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u229C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u229D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22A6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22A7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22AA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22AB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22B0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22B1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22B2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22B3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22B6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22B7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22B9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22BA", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22BB", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22BC", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22BD", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22BE", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u22BF", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u22C4", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22C6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22C7", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22C8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22CD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22CE", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22CF", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22D0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22D1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22D2", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22D3", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u22D5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22DA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22DB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22DC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22DD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22DE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22DF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22E0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22E1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22E2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22E3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22E4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22E5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22E6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22E7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22E8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22E9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22F0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22F2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22F3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22F4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22F5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22F6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22F7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22F8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22F9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22FA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22FB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22FC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22FD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22FE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u22FF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u25B2", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2758", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2981", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u2982", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29A0", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29A1", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29A2", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29A3", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29A4", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29A5", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29A6", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29A7", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29A8", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29A9", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29AA", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29AB", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29AC", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29AD", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29AE", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29AF", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29B0", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29B1", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29B2", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29B3", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29B4", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29B5", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29B6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29B7", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29B8", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29B9", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29BA", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29BB", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29BC", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29BD", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29BE", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29BF", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29C2", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29C3", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29C4", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29C5", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29C6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29C7", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29C8", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29C9", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29CA", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29CB", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29CC", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29CD", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29CE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29CF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29CF\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29D0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29D0\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29D1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29D2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29D3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29D4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29D5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29D6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29D7", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29D8", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29D9", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29DB", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29DC", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29DD", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29DE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29E0", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29E1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u29E2", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29E7", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29E8", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29E9", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29EA", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29EB", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29EC", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29ED", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29EE", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29F0", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29F1", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29F2", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29F5", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29F6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29F7", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29F8", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29F9", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29FA", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29FB", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u29FE", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u29FF", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A1D", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u2A1E", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u2A1F", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u2A20", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u2A21", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"\u2A22", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A23", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A24", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A25", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A26", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A27", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A28", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A29", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A2A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A2B", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A2C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A2D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A2E", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A30", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A31", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A32", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A33", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A34", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A35", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A36", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A37", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A38", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A39", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A3A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A3B", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A3C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A3D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A3E", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A40", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A41", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A42", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A43", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A44", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A45", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A46", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A47", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A48", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A49", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A4A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A4B", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A4C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A4D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A4E", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A4F", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A50", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A51", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A52", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A53", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A54", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A55", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A56", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A57", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A58", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A59", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A5A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A5B", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A5C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A5D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A5E", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A5F", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A60", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A61", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A62", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A63", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A64", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A65", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A66", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A67", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A68", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A69", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A6A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A6B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A6C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A6D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A6E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A6F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A70", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A71", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A72", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2A73", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A74", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A75", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A76", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A77", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A78", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A79", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A7A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A7B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A7C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A7D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A7D\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A7E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A7E\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A7F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A80", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A81", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A82", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A83", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A84", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A85", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A86", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A89", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A8A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A8B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A8C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A8D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A8E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A8F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A90", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A91", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A92", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A93", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A94", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A95", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A96", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A97", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A98", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A99", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A9A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A9B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A9C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A9D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A9E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2A9F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA1\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA2\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AA9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AAA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AAB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AAC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AAD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AAE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AB1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AB2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AB3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AB4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AB5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AB6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AB7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AB8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AB9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ABA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ABB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ABC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ABD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ABE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ABF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AC0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AC1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AC2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AC3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AC4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AC5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AC6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AC7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AC8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AC9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ACA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ACB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ACC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ACD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ACE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ACF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AD0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AD1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AD2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AD3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AD4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AD5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AD6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AD7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AD8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AD9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ADA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ADB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ADD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ADD\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ADE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2ADF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AE0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AE1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AE2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AE3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AE4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AE5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AE6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AE7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AE8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AE9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AEA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AEB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AEC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AED", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AEE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AEF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AF0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AF1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AF2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AF3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AF4", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2AF5", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2AF6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2AF7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AF8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AF9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AFA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp }, + { u"\u2AFB", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2AFD", moOpDF::infix, 265, 4, 4, moOpDP::nonedp }, + { u"\u2AFE", moOpDF::infix, 265, 3, 3, moOpDP::nonedp }, + { u"|", moOpDF::infix, 270, 2, 2, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"||", moOpDF::infix, 270, 2, 2, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"|||", moOpDF::infix, 270, 2, 2, moOpDP::stretchyfence | moOpDP::symmetric }, + { u"\u2190", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u2191", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2192", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u2193", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2194", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u2195", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2196", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2197", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2198", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2199", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u219A", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u219B", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u219C", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u219D", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u219E", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u219F", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21A0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21A1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21A2", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21A3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21A4", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21A5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21A6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21A7", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21A8", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21A9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21AA", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21AB", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21AC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21AD", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21AE", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21AF", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21B0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21B1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21B2", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21B3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21B4", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21B5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21B6", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21B7", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21B8", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u21B9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21BA", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u21BB", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u21BC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21BD", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21BE", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21BF", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21C0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21C1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21C2", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21C3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21C4", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21C5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21C6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21C7", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21C8", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21C9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21CA", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21CB", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21CC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21CD", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21CE", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21CF", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21D0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21D1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21D2", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21D3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21D4", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21D5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21D6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21D7", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21D8", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21D9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21DA", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21DB", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21DC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21DD", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21DE", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u21DF", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u21E0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21E1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21E2", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21E3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21E4", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21E5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21E6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21E7", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21E8", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21E9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21EA", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21EB", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21EC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21ED", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21EE", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21EF", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21F0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21F1", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u21F2", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u21F3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21F4", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21F5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u21F6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21F7", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21F8", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21F9", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21FA", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21FB", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21FC", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u21FD", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21FE", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u21FF", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u22B8", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u27F0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u27F1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u27F5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u27F6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u27F7", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u27F8", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u27F9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u27FA", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u27FB", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u27FC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u27FD", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u27FE", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u27FF", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u2900", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2901", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2902", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2903", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2904", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2905", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2906", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2907", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2908", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2909", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u290A", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u290B", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u290C", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u290D", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u290E", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u290F", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u2910", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u2911", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2912", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2913", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2914", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2915", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2916", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2917", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2918", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2919", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u291A", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u291B", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u291C", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u291D", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u291E", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u291F", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2920", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2921", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2922", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2923", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2924", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2925", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2926", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2927", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2928", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2929", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u292A", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u292B", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u292C", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u292D", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u292E", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u292F", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2930", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2931", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2932", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2933", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2934", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2935", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2936", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2937", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2938", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2939", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u293A", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u293B", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u293C", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u293D", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u293E", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u293F", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2940", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2941", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2942", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2943", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2944", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2945", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2946", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2947", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2948", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2949", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u294A", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u294B", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u294C", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u294D", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u294E", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u294F", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2950", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u2951", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2952", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u2953", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u2954", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2955", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2956", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2957", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2958", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2959", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u295A", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u295B", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u295C", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u295D", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u295E", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u295F", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent }, + { u"\u2960", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2961", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2962", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2963", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2964", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2965", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2966", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2967", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2968", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2969", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u296A", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u296B", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u296C", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u296D", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u296E", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u296F", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2970", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2971", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2972", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2973", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2974", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2975", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2976", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2977", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2978", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u2979", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u297A", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u297B", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u297C", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u297D", moOpDF::infix, 270, 5, 5, moOpDP::accent }, + { u"\u297E", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u297F", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2999", moOpDF::infix, 270, 3, 3, moOpDP::nonedp }, + { u"\u299A", moOpDF::infix, 270, 3, 3, moOpDP::nonedp }, + { u"\u299B", moOpDF::infix, 270, 3, 3, moOpDP::nonedp }, + { u"\u299C", moOpDF::infix, 270, 3, 3, moOpDP::nonedp }, + { u"\u299D", moOpDF::infix, 270, 3, 3, moOpDP::nonedp }, + { u"\u299E", moOpDF::infix, 270, 3, 3, moOpDP::nonedp }, + { u"\u299F", moOpDF::infix, 270, 3, 3, moOpDP::nonedp }, + { u"\u29DF", moOpDF::infix, 270, 3, 3, moOpDP::nonedp }, + { u"\u29EF", moOpDF::infix, 270, 3, 3, moOpDP::nonedp }, + { u"\u29F4", moOpDF::infix, 270, 5, 5, moOpDP::nonedp }, + { u"\u2B45", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"\u2B46", moOpDF::infix, 270, 5, 5, moOpDP::stretchy }, + { u"+", moOpDF::infix, 275, 4, 4, moOpDP::nonedp }, + { u"+", moOpDF::prefix, 275, 0, 1, moOpDP::nonedp }, + { u"-", moOpDF::infix, 275, 4, 4, moOpDP::nonedp }, + { u"-", moOpDF::prefix, 275, 0, 1, moOpDP::nonedp }, + { u"\u00B1", moOpDF::infix, 275, 4, 4, moOpDP::nonedp }, + { u"\u00B1", moOpDF::prefix, 275, 0, 1, moOpDP::nonedp }, + { u"\u2212", moOpDF::infix, 275, 4, 4, moOpDP::nonedp }, + { u"\u2212", moOpDF::prefix, 275, 0, 1, moOpDP::nonedp }, + { u"\u2213", moOpDF::infix, 275, 4, 4, moOpDP::nonedp }, + { u"\u2213", moOpDF::prefix, 275, 0, 1, moOpDP::nonedp }, + { u"\u2214", moOpDF::infix, 275, 4, 4, moOpDP::nonedp }, + { u"\u229E", moOpDF::infix, 275, 4, 4, moOpDP::nonedp }, + { u"\u229F", moOpDF::infix, 275, 4, 4, moOpDP::nonedp }, + { u"\u2211", moOpDF::prefix, 290, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A0A", moOpDF::prefix, 290, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A0B", moOpDF::prefix, 290, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u222C", moOpDF::prefix, 300, 0, 1, moOpDP::largeop | moOpDP::symmetric }, + { u"\u222D", moOpDF::prefix, 300, 0, 1, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2295", moOpDF::infix, 300, 4, 4, moOpDP::nonedp }, + { u"\u2296", moOpDF::infix, 300, 4, 4, moOpDP::nonedp }, + { u"\u2298", moOpDF::infix, 300, 4, 4, moOpDP::nonedp }, + { u"\u2A01", moOpDF::prefix, 300, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u222B", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric }, + { u"\u222E", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric }, + { u"\u222F", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2230", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2231", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2232", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2233", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A0C", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A0D", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A0E", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A0F", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A10", moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A11", moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A12", moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A13", moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A14", moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A15", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A16", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A17", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A18", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A19", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A1A", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A1B", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u2A1C", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric }, + { u"\u22C3", moOpDF::prefix, 320, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A03", moOpDF::prefix, 320, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A04", moOpDF::prefix, 320, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u22C0", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u22C1", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u22C2", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A00", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A02", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A05", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A06", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A07", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A08", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2A09", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2AFC", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2AFF", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2240", moOpDF::infix, 340, 4, 4, moOpDP::nonedp }, + { u"\u220F", moOpDF::prefix, 350, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2210", moOpDF::prefix, 350, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric }, + { u"\u2229", moOpDF::infix, 350, 4, 4, moOpDP::nonedp }, + { u"\u222A", moOpDF::infix, 350, 4, 4, moOpDP::nonedp }, + { u"*", moOpDF::infix, 390, 3, 3, moOpDP::nonedp }, + { u".", moOpDF::infix, 390, 3, 3, moOpDP::nonedp }, + { u"\u00D7", moOpDF::infix, 390, 4, 4, moOpDP::nonedp }, + { u"\u2022", moOpDF::infix, 390, 4, 4, moOpDP::nonedp }, + { u"\u2043", moOpDF::infix, 390, 4, 4, moOpDP::nonedp }, + { u"\u2062", moOpDF::infix, 390, 0, 0, moOpDP::nonedp }, + { u"\u22A0", moOpDF::infix, 390, 4, 4, moOpDP::nonedp }, + { u"\u22A1", moOpDF::infix, 390, 4, 4, moOpDP::nonedp }, + { u"\u22C5", moOpDF::infix, 390, 4, 4, moOpDP::nonedp }, + { u"\u2A2F", moOpDF::infix, 390, 4, 4, moOpDP::nonedp }, + { u"\u2A3F", moOpDF::infix, 390, 4, 4, moOpDP::nonedp }, + { u"\u00B7", moOpDF::infix, 400, 4, 4, moOpDP::nonedp }, + { u"\u2297", moOpDF::infix, 410, 4, 4, moOpDP::nonedp }, + { u"%", moOpDF::infix, 640, 3, 3, moOpDP::nonedp }, + { u"\\", moOpDF::infix, 650, 0, 0, moOpDP::nonedp }, + { u"\u2216", moOpDF::infix, 650, 4, 4, moOpDP::nonedp }, + { u"/", moOpDF::infix, 660, 1, 1, moOpDP::nonedp }, + { u"\u00F7", moOpDF::infix, 660, 4, 4, moOpDP::nonedp }, + { u"\u2220", moOpDF::prefix, 670, 0, 0, moOpDP::nonedp }, + { u"\u2221", moOpDF::prefix, 670, 0, 0, moOpDP::nonedp }, + { u"\u2222", moOpDF::prefix, 670, 0, 0, moOpDP::nonedp }, + { u"\u00AC", moOpDF::prefix, 680, 2, 1, moOpDP::nonedp }, + { u"\u2299", moOpDF::infix, 710, 4, 4, moOpDP::nonedp }, + { u"\u2202", moOpDF::prefix, 740, 2, 1, moOpDP::nonedp }, + { u"\u2207", moOpDF::prefix, 740, 2, 1, moOpDP::nonedp }, + { u"**", moOpDF::infix, 780, 1, 1, moOpDP::nonedp }, + { u"<>", moOpDF::infix, 780, 1, 1, moOpDP::nonedp }, + { u"^", moOpDF::infix, 780, 1, 1, moOpDP::nonedp }, + { u"\u2032", moOpDF::postfix, 800, 0, 0, moOpDP::nonedp }, + { u"\u266D", moOpDF::postfix, 800, 0, 2, moOpDP::nonedp }, + { u"\u266E", moOpDF::postfix, 800, 0, 2, moOpDP::nonedp }, + { u"\u266F", moOpDF::postfix, 800, 0, 2, moOpDP::nonedp }, + { u"!", moOpDF::postfix, 810, 1, 0, moOpDP::nonedp }, + { u"!!", moOpDF::postfix, 810, 1, 0, moOpDP::nonedp }, + { u"//", moOpDF::infix, 820, 1, 1, moOpDP::nonedp }, + { u"@", moOpDF::infix, 825, 1, 1, moOpDP::nonedp }, + { u"?", moOpDF::infix, 835, 1, 1, moOpDP::nonedp }, + { u"\u2145", moOpDF::prefix, 845, 2, 1, moOpDP::nonedp }, + { u"\u2146", moOpDF::prefix, 845, 2, 0, moOpDP::nonedp }, + { u"\u221A", moOpDF::prefix, 845, 1, 1, moOpDP::stretchy }, + { u"\u221B", moOpDF::prefix, 845, 1, 1, moOpDP::nonedp }, + { u"\u221C", moOpDF::prefix, 845, 1, 1, moOpDP::nonedp }, + { u"\u2061", moOpDF::infix, 850, 0, 0, moOpDP::nonedp }, + { u"\"", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"&", moOpDF::postfix, 880, 0, 0, moOpDP::nonedp }, + { u"\'", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"++", moOpDF::postfix, 880, 0, 0, moOpDP::nonedp }, + { u"--", moOpDF::postfix, 880, 0, 0, moOpDP::nonedp }, + { u"^", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"_", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"`", moOpDF::postfix, 880, 0, 00, moOpDP::accent }, + { u"~", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u00A8", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u00AA", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u00AF", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u00B0", moOpDF::postfix, 880, 0, 0, moOpDP::nonedp }, + { u"\u00B2", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u00B3", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u00B4", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u00B8", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u00B9", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u00BA", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u02C6", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u02C7", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u02C9", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u02CA", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u02CB", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u02CD", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u02D8", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u02D9", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u02DA", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u02DC", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u02DD", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u02F7", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u0302", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u0311", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u201A", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u201B", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u201E", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u201F", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u2033", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u2034", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u2035", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u2036", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u2037", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u203E", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u2057", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u2064", moOpDF::infix, 880, 0, 0, moOpDP::nonedp }, + { u"\u20DB", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u20DC", moOpDF::postfix, 880, 0, 0, moOpDP::accent }, + { u"\u23B4", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u23B5", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u23DC", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u23DD", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u23DE", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u23DF", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u23E0", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"\u23E1", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent }, + { u"_", moOpDF::infix, 900, 1, 1, moOpDP::nonedp } }; + +std::vector<moOperatorData> starmathdatabase::moOperatorDataDictionary( + moOperatorDataDictionaryData, moOperatorDataDictionaryData + starmathdatabase::MATHML_MO_COUNT); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/source/mathml/mathmlattr.cxx b/starmath/source/mathml/mathmlattr.cxx new file mode 100644 index 000000000..5e54f0d92 --- /dev/null +++ b/starmath/source/mathml/mathmlattr.cxx @@ -0,0 +1,165 @@ +/* -*- 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 <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> +#include <rtl/math.h> + +#include <cstddef> +#include <string_view> +#include <unordered_map> + +static std::size_t ParseMathMLUnsignedNumber(std::u16string_view rStr, Fraction& rUN) +{ + auto nLen = rStr.length(); + std::size_t nDecimalPoint = std::u16string_view::npos; + std::size_t nIdx; + sal_Int64 nom = 0; + sal_Int64 den = 1; + bool validNomDen = true; + for (nIdx = 0; nIdx < nLen; nIdx++) + { + auto cD = rStr[nIdx]; + if (cD == u'.') + { + if (nDecimalPoint != std::u16string_view::npos) + return std::u16string_view::npos; + nDecimalPoint = nIdx; + continue; + } + if (cD < u'0' || u'9' < cD) + break; + if (validNomDen + && (o3tl::checked_multiply(nom, sal_Int64(10), nom) + || o3tl::checked_add(nom, sal_Int64(cD - u'0'), nom) + || nom >= std::numeric_limits<sal_Int32>::max() + || (nDecimalPoint != std::u16string_view::npos + && o3tl::checked_multiply(den, sal_Int64(10), den)))) + { + validNomDen = false; + } + } + if (nIdx == 0 || (nIdx == 1 && nDecimalPoint == 0)) + return std::u16string_view::npos; + + // If the input "xx.yyy" can be represented with nom = xx*10^n + yyy and den = 10^n in sal_Int64 + // (where n is the length of "yyy"), then use that to create an accurate Fraction (and TODO: we + // could even ignore trailing "0" characters in "yyy", for a smaller n and thus a greater chance + // of validNomDen); if not, use the less accurate approach of creating a Fraction from double: + if (validNomDen) + { + rUN = Fraction(nom, den); + } + else + { + rUN = Fraction( + rtl_math_uStringToDouble(rStr.data(), rStr.data() + nIdx, '.', 0, nullptr, nullptr)); + } + + return nIdx; +} + +static std::size_t ParseMathMLNumber(std::u16string_view rStr, Fraction& rN) +{ + if (rStr.empty()) + return std::u16string_view::npos; + bool bNegative = (rStr[0] == '-'); + std::size_t nOffset = bNegative ? 1 : 0; + auto nIdx = ParseMathMLUnsignedNumber(rStr.substr(nOffset), rN); + if (nIdx == std::u16string_view::npos || !rN.IsValid()) + return std::u16string_view::npos; + if (bNegative) + rN *= -1; + return nOffset + nIdx; +} + +bool ParseMathMLAttributeLengthValue(std::u16string_view rStr, MathMLAttributeLengthValue& rV) +{ + auto nIdx = ParseMathMLNumber(rStr, rV.aNumber); + if (nIdx == std::u16string_view::npos) + return false; + std::u16string_view sRest = rStr.substr(nIdx); + if (sRest.empty()) + { + rV.eUnit = MathMLLengthUnit::None; + } + if (o3tl::starts_with(sRest, u"em")) + { + rV.eUnit = MathMLLengthUnit::Em; + } + if (o3tl::starts_with(sRest, u"ex")) + { + rV.eUnit = MathMLLengthUnit::Ex; + } + if (o3tl::starts_with(sRest, u"px")) + { + rV.eUnit = MathMLLengthUnit::Px; + } + if (o3tl::starts_with(sRest, u"in")) + { + rV.eUnit = MathMLLengthUnit::In; + } + if (o3tl::starts_with(sRest, u"cm")) + { + rV.eUnit = MathMLLengthUnit::Cm; + } + if (o3tl::starts_with(sRest, u"mm")) + { + rV.eUnit = MathMLLengthUnit::Mm; + } + if (o3tl::starts_with(sRest, u"pt")) + { + rV.eUnit = MathMLLengthUnit::Pt; + } + if (o3tl::starts_with(sRest, u"pc")) + { + rV.eUnit = MathMLLengthUnit::Pc; + } + if (sRest[0] == u'%') + { + rV.eUnit = MathMLLengthUnit::Percent; + } + return true; +} + +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/mathml/mathmlexport.cxx b/starmath/source/mathml/mathmlexport.cxx new file mode 100644 index 000000000..187dd1953 --- /dev/null +++ b/starmath/source/mathml/mathmlexport.cxx @@ -0,0 +1,1457 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <officecfg/Office/Common.hxx> +#include <rtl/math.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxsids.hrc> +#include <osl/diagnose.h> +#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/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/namespacemap.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 <stack> + +#include <mathmlexport.hxx> +#include <xparsmlbase.hxx> +#include <strings.hrc> +#include <smmod.hxx> +#include <unomodel.hxx> +#include <document.hxx> +#include <utility.hxx> +#include <cfgitem.hxx> +#include <starmathdatabase.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::getFromUnoTunnel<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 = 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); + } + } + + static constexpr OUStringLiteral sUsePrettyPrinting(u"UsePrettyPrinting"); + static constexpr OUStringLiteral sBaseURI(u"BaseURI"); + static constexpr OUStringLiteral sStreamRelPath(u"StreamRelPath"); + static constexpr OUStringLiteral sStreamName(u"StreamName"); + + // create XPropertySet with three properties for status indicator + static const comphelper::PropertyMapEntry aInfoMap[] = { + { sUsePrettyPrinting, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::MAYBEVOID, + 0 }, + { sBaseURI, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, + { sStreamRelPath, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, + 0 }, + { sStreamName, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 } + }; + uno::Reference<beans::XPropertySet> xInfoSet( + comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap))); + + bool bUsePrettyPrinting + = bFlat || officecfg::Office::Common::Save::Document::PrettyPrinting::get(); + xInfoSet->setPropertyValue(sUsePrettyPrinting, Any(bUsePrettyPrinting)); + + // Set base URI + xInfoSet->setPropertyValue(sBaseURI, Any(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 + = rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME); + if (pDocHierarchItem) + aName = pDocHierarchItem->GetValue(); + } + + if (!aName.isEmpty()) + { + xInfoSet->setPropertyValue(sStreamRelPath, Any(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); + if (m_bUseHTMLMLEntities) + xSaxWriter->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntitiesExport); + + // prepare arguments (prepend doc handler to given arguments) + Sequence<Any> aArgs{ Any(xSaxWriter), Any(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::getFromUnoTunnel<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); + static const OUStringLiteral sMediaType = u"MediaType"; + static const OUStringLiteral sTextXml = u"text/xml"; + xSet->setPropertyValue(sMediaType, Any(OUString(sTextXml))); + + // all streams must be encrypted in encrypted document + static const OUStringLiteral sUseCommonStoragePasswordEncryption + = u"UseCommonStoragePasswordEncryption"; + xSet->setPropertyValue(sUseCommonStoragePasswordEncryption, Any(true)); + + // set Base URL + if (rPropSet.is()) + { + rPropSet->setPropertyValue("StreamName", Any(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(rContext, implementationName, util::MeasureUnit::INCH, XML_MATH, nExportFlags) + , pTree(nullptr) + , bSuccess(false) +{ +} + +sal_Int64 SAL_CALL SmXMLExport::getSomething(const uno::Sequence<sal_Int8>& rId) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf<SvXMLExport>{}); +} + +const uno::Sequence<sal_Int8>& SmXMLExport::getUnoTunnelId() noexcept +{ + static const comphelper::UnoIdInit theSmXMLExportUnoTunnelId; + return theSmXMLExportUnoTunnelId.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::getFromUnoTunnel<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::getFromUnoTunnel<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; + + SmModule* pMod = SM_MOD(); + sal_uInt16 nSmSyntaxVersion = pMod->GetConfig()->GetDefaultSmSyntaxVersion(); + + // Convert symbol names + if (pDocShell) + { + nSmSyntaxVersion = pDocShell->GetSmSyntaxVersion(); + AbstractSmParser* rParser = pDocShell->GetParser(); + bool bVal = rParser->IsExportSymbolNames(); + rParser->SetExportSymbolNames(true); + auto pTmpTree = rParser->Parse(aText); + aText = rParser->GetText(); + pTmpTree.reset(); + rParser->SetExportSymbolNames(bVal); + } + + OUStringBuffer sStrBuf(12); + sStrBuf.append(u"StarMath "); + if (nSmSyntaxVersion == 5) + sStrBuf.append(u"5.0"); + else + sStrBuf.append(static_cast<sal_Int32>(nSmSyntaxVersion)); + + AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING, sStrBuf.makeStringAndClear()); + 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::getFromUnoTunnel<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; + + const 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.getArray(), + [bUsedSymbolsOnly, &xProps](const 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' + static constexpr OUStringLiteral sUserDefinedSymbolsInUse + = u"UserDefinedSymbolsInUse"; + if (bUsedSymbolsOnly && prop.Name == "Symbols") + aActualName = sUserDefinedSymbolsInUse; + 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); + GetDocHandler()->characters(OUStringChar(MS_BACKSLASH)); + } + + 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 = pTemp->GetText()[0]; + sal_Unicode cTmp = ConvertMathToMathML(nArse); + if (cTmp != 0) + nArse = cTmp; + OSL_ENSURE(nArse != 0xffff, "Non existent symbol"); + GetDocHandler()->characters(OUString(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.makeStringAndClear()); + } + + 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); + AddAttribute(XML_NAMESPACE_MATH, XML_FORM, XML_PREFIX); + 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); + AddAttribute(XML_NAMESPACE_MATH, XML_FORM, XML_POSTFIX); + 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); + static constexpr OUStringLiteral nArse = u"\u00AF"; + GetDocHandler()->characters(nArse); + } + break; + case TUNDERLINE: + { + //proper entity support required + SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO, true, true); + static constexpr OUStringLiteral nArse = u"\u0332"; + 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; + } + + sal_uInt32 nc; + switch (pNode->GetToken().eType) + { + case TPHANTOM: + // No attribute needed. An <mphantom> element will be used below. + break; + case TMATHMLCOL: + { + nc = pNode->GetToken().cMathChar.toUInt32(16); + const OUString& sssStr = starmathdatabase::Identify_Color_MATHML(nc).aIdent; + AddAttribute(XML_NAMESPACE_MATH, XML_MATHCOLOR, sssStr); + } + break; + case TRGB: + case TRGBA: + case THEX: + case THTMLCOL: + case TDVIPSNAMESCOL: + case TICONICCOL: + { + nc = pNode->GetToken().cMathChar.toUInt32(16); + OUString ssStr("#" + Color(ColorTransparency, nc).AsRGBHEXString()); + AddAttribute(XML_NAMESPACE_MATH, XML_MATHCOLOR, ssStr); + } + 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::Attribute: + 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/mathml/mathmlimport.cxx b/starmath/source/mathml/mathmlimport.cxx new file mode 100644 index 000000000..44d8cc1e3 --- /dev/null +++ b/starmath/source/mathml/mathmlimport.cxx @@ -0,0 +1,2696 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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/Parser.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/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/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/xmlmetai.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <tools/diagnose_ex.h> +#include <o3tl/string_view.hxx> + +#include <mathmlattr.hxx> +#include <xparsmlbase.hxx> +#include <mathmlimport.hxx> +#include <document.hxx> +#include <smdll.hxx> +#include <unomodel.hxx> +#include <utility.hxx> +#include <visitors.hxx> +#include <starmathdatabase.hxx> +#include <smmod.hxx> +#include <cfgitem.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::getFromUnoTunnel<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 = pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL); + if (pItem) + pItem->GetValue() >>= xStatusIndicator; + } + + if (SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode()) + bEmbedded = true; + } + + static const 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 } }; + 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", Any(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 + = rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME); + if (pDocHierarchItem) + aName = pDocHierarchItem->GetValue(); + } + + if (!aName.isEmpty()) + { + xInfoSet->setPropertyValue("StreamRelPath", Any(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"), + m_bUseHTMLMLEntities); + + 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"), + m_bUseHTMLMLEntities); + + 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", m_bUseHTMLMLEntities); + } + 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, + m_bUseHTMLMLEntities); + } + + 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, + bool bUseHTMLMLEntities) +{ + 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 ParserInputSource + xml::sax::InputSource aParserInput; + aParserInput.aInputStream = xInputStream; + + Sequence<Any> aArgs{ Any(rPropSet) }; + + // get filter + Reference<XInterface> xFilter + = rxContext->getServiceManager()->createInstanceWithArgumentsAndContext( + OUString::createFromAscii(pFilterName), aArgs, rxContext); + SAL_WARN_IF(!xFilter, "starmath", "Can't instantiate filter component " << pFilterName); + if (!xFilter.is()) + return nError; + + // connect model and filter + Reference<XImporter> xImporter(xFilter, UNO_QUERY); + xImporter->setTargetDocument(xModelComponent); + + // finally, parser the stream + try + { + Reference<css::xml::sax::XFastParser> xFastParser(xFilter, UNO_QUERY); + Reference<css::xml::sax::XFastDocumentHandler> xFastDocHandler(xFilter, UNO_QUERY); + if (xFastParser) + { + if (bUseHTMLMLEntities) + xFastParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities); + xFastParser->parseStream(aParserInput); + } + else if (xFastDocHandler) + { + Reference<css::xml::sax::XFastParser> xParser + = css::xml::sax::FastParser::create(rxContext); + if (bUseHTMLMLEntities) + xParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities); + xParser->setFastDocumentHandler(xFastDocHandler); + xParser->parseStream(aParserInput); + } + else + { + Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, UNO_QUERY); + assert(xDocHandler); + Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(rxContext); + xParser->setDocumentHandler(xDocHandler); + xParser->parseStream(aParserInput); + } + + auto pFilter = comphelper::getFromUnoTunnel<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, bool bUseHTMLMLEntities) +{ + 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", Any(sStreamName)); + } + + Reference<io::XInputStream> xStream = xEventsStream->getInputStream(); + return ReadThroughComponent(xStream, xModelComponent, rxContext, rPropSet, pFilterName, + bEncrypted, bUseHTMLMLEntities); + } + 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) + , mnSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion()) +{ +} + +const uno::Sequence<sal_Int8>& SmXMLImport::getUnoTunnelId() noexcept +{ + static const comphelper::UnoIdInit theSmXMLImportUnoTunnelId; + return theSmXMLImportUnoTunnelId.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) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf<SvXMLImport>{}); +} + +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::getFromUnoTunnel<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 + { + // Get text from imported formula + SmNodeToTextVisitor tmpvisitor(pTreeTmp, aText); + } + + // Convert symbol names + AbstractSmParser* 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); + pDocShell->SetSmSyntaxVersion(mnSmSyntaxVersion); + } + 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 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()*/); +} + +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)) + { + // sometimes they have namespace, sometimes not? + switch (aIter.getToken() & TOKEN_MASK) + { + case XML_FONTWEIGHT: + nIsBold = sal_Int8(IsXMLToken(aIter, XML_BOLD)); + break; + case XML_FONTSTYLE: + nIsItalic = sal_Int8(IsXMLToken(aIter, XML_ITALIC)); + break; + case XML_FONTSIZE: + case XML_MATHSIZE: + { + OUString sValue = aIter.toString(); + ::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 = aIter.toString(); + break; + case XML_COLOR: + sColor = aIter.toString(); + break; + case XML_MATHCOLOR: + sColor = aIter.toString(); + break; + case XML_MATHVARIANT: + bMvFound = true; + break; + default: + XMLOFF_WARN_UNKNOWN("starmath", aIter); + 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 = u""; + 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 (!sColor.isEmpty()) + { + SmColorTokenTableEntry aSmColorTokenTableEntry; + aSmColorTokenTableEntry = starmathdatabase::Identify_ColorName_HTML(sColor); + if (aSmColorTokenTableEntry.eType == TRGB) + aSmColorTokenTableEntry = starmathdatabase::Identify_Color_Parser( + sal_uInt32(aSmColorTokenTableEntry.cColor)); + if (aSmColorTokenTableEntry.eType != TERROR) + { + aToken = aSmColorTokenTableEntry; + std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken)); + pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack)); + rNodeStack.push_front(std::move(pFontNode)); + } + // If not known, not implemented yet. Giving up. + } + if (sFontFamily.isEmpty()) + return; + + 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)); +} + +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: + XMLOFF_WARN_UNKNOWN("starmath", aIter); + 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 = u""; + 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 = u""; + 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; + bool bIsStretchy; + +public: + SmXMLFencedContext_Impl(SmXMLImport& rImport) + : SmXMLRowContext_Impl(rImport) + , cBegin('(') + , cEnd(')') + , bIsStretchy(false) + { + } + + 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)) + { + switch (aIter.getToken()) + { + //temp, starmath cannot handle multichar brackets (I think) + case XML_OPEN: + cBegin = aIter.toString()[0]; + break; + case XML_CLOSE: + cEnd = aIter.toString()[0]; + break; + case XML_STRETCHY: + bIsStretchy = IsXMLToken(aIter, XML_TRUE); + break; + default: + XMLOFF_WARN_UNKNOWN("starmath", aIter); + /*Go to superclass*/ + break; + } + } +} + +void SmXMLFencedContext_Impl::endFastElement(sal_Int32 /*nElement*/) +{ + SmToken aToken; + aToken.cMathChar = u""; + aToken.aText = ","; + aToken.nLevel = 5; + + std::unique_ptr<SmStructureNode> pSNode(new SmBraceNode(aToken)); + if (bIsStretchy) + aToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(cBegin); + else + aToken = starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(cBegin); + if (aToken.eType == TERROR) + aToken = SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5); + std::unique_ptr<SmNode> pLeft(new SmMathSymbolNode(aToken)); + if (bIsStretchy) + aToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(cEnd); + else + aToken = starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(cEnd); + if (aToken.eType == TERROR) + aToken = SmToken(TRPARENT, MS_RPARENT, ")", TG::LBrace, 5); + std::unique_ptr<SmNode> pRight(new SmMathSymbolNode(aToken)); + + SmNodeArray aRelationArray; + SmNodeStack& rNodeStack = GetSmImport().GetNodeStack(); + aToken.cMathChar = u""; + 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)); + // mfenced is always scalable. Stretchy keyword is not official, but in case of been in there + // can be used as a hint. + 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 = u""; + 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 +{ + sal_uInt8 mnStarMathVersion; + +public: + SmXMLAnnotationContext_Impl(SmXMLImport& rImport) + : SmXMLImportContext(rImport) + , mnStarMathVersion(0) + { + } + + 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)) + { + // sometimes they have namespace, sometimes not? + switch (aIter.getToken() & TOKEN_MASK) + { + case XML_ENCODING: + mnStarMathVersion + = aIter.toView() == "StarMath 5.0" ? 5 : aIter.toView() == "StarMath 6" ? 6 : 0; + break; + default: + XMLOFF_WARN_UNKNOWN("starmath", aIter); + break; + } + } +} + +void SmXMLAnnotationContext_Impl::characters(const OUString& rChars) +{ + if (mnStarMathVersion) + { + GetSmImport().SetText(GetSmImport().GetText() + rChars); + GetSmImport().SetSmSyntaxVersion(mnStarMathVersion); + } +} + +namespace +{ +class SmXMLTextContext_Impl : public SmXMLImportContext +{ +protected: + SmToken aToken; + +public: + SmXMLTextContext_Impl(SmXMLImport& rImport) + : SmXMLImportContext(rImport) + { + aToken.cMathChar = u""; + 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 = u""; + 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 = u""; + 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; + bool bIsFenced; + bool isPrefix; + bool isInfix; + bool isPostfix; + SmToken aToken; + +public: + SmXMLOperatorContext_Impl(SmXMLImport& rImport) + : SmXMLImportContext(rImport) + , maTokenAttrHelper(*this) + , bIsStretchy(false) + , bIsFenced(false) + , isPrefix(false) + , isInfix(false) + , isPostfix(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.setChar(rChars[0]); + SmToken bToken; + if (bIsFenced) + { + if (isPrefix) + bToken + = starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(aToken.cMathChar[0]); + else if (isInfix) + bToken = SmToken(TMLINE, MS_VERTLINE, "mline", TG::NONE, 0); + else if (isPostfix) + bToken + = starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(aToken.cMathChar[0]); + else + bToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl( + aToken.cMathChar[0]); + } + else + bToken = starmathdatabase::Identify_SmXMLOperatorContext_Impl(aToken.cMathChar[0], + bIsStretchy); + if (bToken.eType != TERROR) + aToken = bToken; +} + +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[0])) + 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)) + { + switch (aIter.getToken()) + { + case XML_STRETCHY: + bIsStretchy = IsXMLToken(aIter, XML_TRUE); + break; + case XML_FENCE: + bIsFenced = IsXMLToken(aIter, XML_TRUE); + break; + case XML_FORM: + isPrefix = IsXMLToken(aIter, XML_PREFIX); // < + isInfix = IsXMLToken(aIter, XML_INFIX); // | + isPostfix = IsXMLToken(aIter, XML_POSTFIX); // > + break; + default: + XMLOFF_WARN_UNKNOWN("starmath", aIter); + 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<tools::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<tools::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(o3tl::trim(sValue), aLV) + || !lcl_CountBlanks(aLV, &nWide, &nNarrow)) + SAL_WARN("starmath", "ignore mspace's width: " << sValue); + break; + default: + XMLOFF_WARN_UNKNOWN("starmath", aIter); + break; + } + } + SmToken aToken; + aToken.eType = TBLANK; + aToken.cMathChar = u""; + 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 = u""; + 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 = u""; + 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 = u""; + aToken.eType = TUNDERLINE; + + std::unique_ptr<SmNode> pFirst; + std::unique_ptr<SmStructureNode> pNode(new SmAttributeNode(aToken)); + if ((pTest->GetToken().cMathChar[0] & 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 = u""; + aToken.eType = TACUTE; + + std::unique_ptr<SmAttributeNode> pNode(new SmAttributeNode(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 = u""; + 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*/ +}; + +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 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 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) +{ +} + +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); + } +} + +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 = u""; + aToken.eType = TFRAC; + 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.setChar(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.setChar(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 = u""; + 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 = u""; + + 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 = u""; + + 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.setChar(MS_LBRACE); + aToken.nLevel = 5; + aToken.eType = TLGROUP; + aToken.nGroup = TG::NONE; + aToken.aText = "{"; + aRelationArray[0] = new SmLineNode(aToken); + + aToken.setChar(MS_RBRACE); + aToken.nLevel = 0; + aToken.eType = TRGROUP; + aToken.nGroup = TG::NONE; + 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 it's + //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 = u""; + 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 = u""; + 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)) + { + switch (aIter.getToken()) + { + case XML_SELECTION: + { + sal_Int32 n = aIter.toInt32(); + if (n > 0) + mnSelection = static_cast<size_t>(n); + } + break; + default: + XMLOFF_WARN_UNKNOWN("starmath", aIter); + 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() noexcept { cleanup(); } + +void SmXMLImport::SetViewSettings(const Sequence<PropertyValue>& aViewProps) +{ + uno::Reference<frame::XModel> xModel = GetModel(); + if (!xModel.is()) + return; + + SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel); + + if (!pModel) + return; + + SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell()); + if (!pDocShell) + return; + + tools::Rectangle aRect(pDocShell->GetVisArea()); + + tools::Long nTmp = 0; + + for (const PropertyValue& rValue : aViewProps) + { + if (rValue.Name == "ViewAreaTop") + { + rValue.Value >>= nTmp; + aRect.SaturatingSetPosY(nTmp); + } + else if (rValue.Name == "ViewAreaLeft") + { + rValue.Value >>= nTmp; + aRect.SaturatingSetPosX(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; + + static const OUStringLiteral sFormula(u"Formula"); + static const OUStringLiteral sBasicLibraries(u"BasicLibraries"); + static const OUStringLiteral sDialogLibraries(u"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, + false); + } + catch (...) + { + } + + xDocSh->SetLoading(SfxLoadedFlags::ALL); + + xDocSh->DoClose(); + + return nRet != ERRCODE_NONE; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/source/mathml/starmathdatabase.cxx b/starmath/source/mathml/starmathdatabase.cxx new file mode 100644 index 000000000..66bdf3f34 --- /dev/null +++ b/starmath/source/mathml/starmathdatabase.cxx @@ -0,0 +1,794 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <starmathdatabase.hxx> +#include <types.hxx> + +SmToken starmathdatabase::Identify_SmXMLOperatorContext_Impl(sal_Unicode cChar, bool bIsStretchy) +{ + switch (cChar) + { + case MS_COPROD: + return SmToken(TCOPROD, MS_COPROD, "coprod", TG::Oper, 5); + case MS_IIINT: + return SmToken(TIIINT, MS_IIINT, "iiint", TG::Oper, 5); + case MS_IINT: + return SmToken(TIINT, MS_IINT, "iint", TG::Oper, 5); + case MS_INT: + if (bIsStretchy) + return SmToken(TINTD, MS_INT, "intd", TG::Oper, 5); + else + return SmToken(TINT, MS_INT, "int", TG::Oper, 5); + case MS_LINT: + return SmToken(TLINT, MS_LINT, "lint", TG::Oper, 5); + case MS_LLINT: + return SmToken(TLLINT, MS_LLINT, "llint", TG::Oper, 5); + case MS_LLLINT: + return SmToken(TLLLINT, MS_LLLINT, "lllint", TG::Oper, 5); + case MS_PROD: + return SmToken(TPROD, MS_PROD, "prod", TG::Oper, 5); + case MS_SUM: + return SmToken(TSUM, MS_SUM, "sum", TG::Oper, 5); + case MS_FACT: + return SmToken(TFACT, MS_FACT, "!", TG::UnOper, 5); + case MS_NEG: + return SmToken(TNEG, MS_NEG, "neg", TG::UnOper, 5); + case MS_OMINUS: + return SmToken(TOMINUS, MS_OMINUS, "ominus", TG::Sum, 0); + case MS_OPLUS: + return SmToken(TOPLUS, MS_OPLUS, "oplus", TG::Sum, 0); + case MS_UNION: + return SmToken(TUNION, MS_UNION, "union", TG::Sum, 0); + case MS_OR: + return SmToken(TOR, MS_OR, "|", TG::Sum, 5); + case MS_PLUSMINUS: + return SmToken(TPLUSMINUS, MS_PLUSMINUS, "+-", TG::Sum | TG::UnOper, 5); + case MS_MINUSPLUS: + return SmToken(TMINUSPLUS, MS_MINUSPLUS, "-+", TG::Sum | TG::UnOper, 5); + case 0xe083: + case MS_PLUS: + return SmToken(TPLUS, MS_PLUS, "+", TG::Sum | TG::UnOper, 5); + case MS_MINUS: + return SmToken(TMINUS, MS_MINUS, "-", TG::Sum | TG::UnOper, 5); + case 0x2022: + case MS_CDOT: + return SmToken(TCDOT, MS_CDOT, "cdot", TG::Product, 0); + case MS_DIV: + return SmToken(TDIV, MS_DIV, "div", TG::Product, 0); + case MS_TIMES: + return SmToken(TTIMES, MS_TIMES, "times", TG::Product, 0); + case MS_INTERSECT: + return SmToken(TINTERSECT, MS_INTERSECT, "intersection", TG::Product, 0); + case MS_ODIVIDE: + return SmToken(TODIVIDE, MS_ODIVIDE, "odivide", TG::Product, 0); + case MS_ODOT: + return SmToken(TODOT, MS_ODOT, "odot", TG::Product, 0); + case MS_OTIMES: + return SmToken(TOTIMES, MS_OTIMES, "otimes", TG::Product, 0); + case MS_AND: + return SmToken(TAND, MS_AND, "&", TG::Product, 0); + case MS_MULTIPLY: + return SmToken(TMULTIPLY, MS_MULTIPLY, "*", TG::Product, 0); + case MS_SLASH: + if (bIsStretchy) + return SmToken(TWIDESLASH, MS_SLASH, "wideslash", TG::Product, 0); + else + return SmToken(TSLASH, MS_SLASH, "slash", TG::Product, 0); + case MS_BACKSLASH: + if (bIsStretchy) + return SmToken(TWIDEBACKSLASH, MS_BACKSLASH, "widebslash", TG::Product, 0); + else + return SmToken(TBACKSLASH, MS_BACKSLASH, "bslash", TG::Product, 0); + case MS_DEF: + return SmToken(TDEF, MS_DEF, "def", TG::Relation, 0); + case MS_LINE: + return SmToken(TDIVIDES, MS_LINE, "divides", TG::Relation, 0); + case MS_EQUIV: + return SmToken(TEQUIV, MS_EQUIV, "equiv", TG::Relation, 0); + case MS_GE: + return SmToken(TGE, MS_GE, ">=", TG::Relation, 0); + case MS_GESLANT: + return SmToken(TGESLANT, MS_GESLANT, "geslant", TG::Relation, 0); + case MS_GG: + return SmToken(TGG, MS_GG, ">>", TG::Relation, 0); + case MS_GT: + return SmToken(TGT, MS_GT, ">", TG::Relation, 0); + case MS_IN: + return SmToken(TIN, MS_IN, "in", TG::Relation, 0); + case MS_LE: + return SmToken(TLE, MS_LE, "<=", TG::Relation, 0); + case MS_LESLANT: + return SmToken(TLESLANT, MS_LESLANT, "leslant", TG::Relation, 0); + case MS_LL: + return SmToken(TLL, MS_LL, "<<", TG::Relation, 0); + case MS_LT: + return SmToken(TLT, MS_LT, "<", TG::Relation, 0); + case MS_NDIVIDES: + return SmToken(TNDIVIDES, MS_NDIVIDES, "ndivides", TG::Relation, 0); + case MS_NEQ: + return SmToken(TNEQ, MS_NEQ, "<>", TG::Relation, 0); + case MS_NOTIN: + return SmToken(TNOTIN, MS_NOTIN, "notin", TG::Relation, 0); + case MS_NOTPRECEDES: + return SmToken(TNOTPRECEDES, MS_NOTPRECEDES, "nprec", TG::Relation, 0); + case MS_NSUBSET: + return SmToken(TNSUBSET, MS_NSUBSET, "nsubset", TG::Relation, 0); + case MS_NSUBSETEQ: + return SmToken(TNSUBSETEQ, MS_NSUBSETEQ, "nsubseteq", TG::Relation, 0); + case MS_NOTSUCCEEDS: + return SmToken(TNOTSUCCEEDS, MS_NOTSUCCEEDS, "nsucc", TG::Relation, 0); + case MS_NSUPSET: + return SmToken(TNSUPSET, MS_NSUPSET, "nsupset", TG::Relation, 0); + case MS_NSUPSETEQ: + return SmToken(TNSUPSETEQ, MS_NSUPSETEQ, "nsupseteq", TG::Relation, 0); + case MS_ORTHO: + return SmToken(TORTHO, MS_ORTHO, "ortho", TG::Relation, 0); + case MS_NI: + return SmToken(TNI, MS_NI, "owns", TG::Relation, 0); + case MS_DLINE: + return SmToken(TPARALLEL, MS_DLINE, "parallel", TG::Relation, 0); + case MS_PRECEDES: + return SmToken(TPRECEDES, MS_PRECEDES, "prec", TG::Relation, 0); + case MS_PRECEDESEQUAL: + return SmToken(TPRECEDESEQUAL, MS_PRECEDESEQUAL, "preccurlyeq", TG::Relation, 0); + case MS_PRECEDESEQUIV: + return SmToken(TPRECEDESEQUIV, MS_PRECEDESEQUIV, "precsim", TG::Relation, 0); + case MS_PROP: + return SmToken(TPROP, MS_PROP, "prop", TG::Relation, 0); + case MS_SIM: + return SmToken(TSIM, MS_SIM, "sim", TG::Relation, 0); + case 0x2245: + case MS_SIMEQ: + return SmToken(TSIMEQ, MS_SIMEQ, "simeq", TG::Relation, 0); + case MS_SUBSET: + return SmToken(TSUBSET, MS_SUBSET, "subset", TG::Relation, 0); + case MS_SUBSETEQ: + return SmToken(TSUBSETEQ, MS_SUBSETEQ, "subseteq", TG::Relation, 0); + case MS_SUCCEEDS: + return SmToken(TSUCCEEDS, MS_SUCCEEDS, "succ", TG::Relation, 0); + case MS_SUCCEEDSEQUAL: + return SmToken(TSUCCEEDSEQUAL, MS_SUCCEEDSEQUAL, "succcurlyeq", TG::Relation, 0); + case MS_SUCCEEDSEQUIV: + return SmToken(TSUCCEEDSEQUIV, MS_SUCCEEDSEQUIV, "succsim", TG::Relation, 0); + case MS_SUPSET: + return SmToken(TSUPSET, MS_SUPSET, "supset", TG::Relation, 0); + case MS_SUPSETEQ: + return SmToken(TSUPSETEQ, MS_SUPSETEQ, "supseteq", TG::Relation, 0); + case MS_RIGHTARROW: + return SmToken(TTOWARD, MS_RIGHTARROW, "toward", TG::Relation, 0); + case MS_TRANSL: + return SmToken(TTRANSL, MS_TRANSL, "transl", TG::Relation, 0); + case MS_TRANSR: + return SmToken(TTRANSR, MS_TRANSR, "transr", TG::Relation, 0); + case MS_ASSIGN: + return SmToken(TASSIGN, MS_ASSIGN, "=", TG::Relation, 0); + case MS_LANGLE: + return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5); + case MS_LMATHANGLE: + return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5); + case MS_LBRACE: + return SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5); + case MS_LCEIL: + return SmToken(TLCEIL, MS_LCEIL, "lceil", TG::LBrace, 5); + case MS_LFLOOR: + return SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TG::LBrace, 5); + case MS_LDBRACKET: + return SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TG::LBrace, 5); + case MS_LBRACKET: + return SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5); + case MS_LPARENT: + return SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5); + case MS_RANGLE: + return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5); + case MS_RMATHANGLE: + return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5); + case MS_RBRACE: + return SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5); + case MS_RCEIL: + return SmToken(TRCEIL, MS_RCEIL, "rceil", TG::RBrace, 5); + case MS_RFLOOR: + return SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TG::RBrace, 5); + case MS_RDBRACKET: + return SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TG::RBrace, 5); + case MS_RBRACKET: + return SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5); + case MS_RPARENT: + return SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5); + case MS_NONE: + return SmToken(TNONE, MS_NONE, "none", TG::RBrace | TG::LBrace, 5); + default: + return SmToken(TERROR, MS_NONE, "", TG::NONE, SAL_MAX_UINT16); + } +} + +SmToken starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(sal_Unicode cChar) +{ + switch (cChar) + { + case MS_VERTLINE: + return SmToken(TLLINE, MS_VERTLINE, "lline", TG::LBrace, 5); + case MS_DVERTLINE: + return SmToken(TLDLINE, MS_DVERTLINE, "ldline", TG::LBrace, 5); + case MS_LANGLE: + return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5); + case MS_LMATHANGLE: + return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5); + case MS_LBRACE: + return SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5); + case MS_LCEIL: + return SmToken(TLCEIL, MS_LCEIL, "lceil", TG::LBrace, 5); + case MS_LFLOOR: + return SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TG::LBrace, 5); + case MS_LDBRACKET: + return SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TG::LBrace, 5); + case MS_LBRACKET: + return SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5); + case MS_LPARENT: + return SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5); + case MS_RANGLE: + return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5); + case MS_RMATHANGLE: + return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5); + case MS_RBRACE: + return SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5); + case MS_RCEIL: + return SmToken(TRCEIL, MS_RCEIL, "rceil", TG::RBrace, 5); + case MS_RFLOOR: + return SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TG::RBrace, 5); + case MS_RDBRACKET: + return SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TG::RBrace, 5); + case MS_RBRACKET: + return SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5); + case MS_RPARENT: + return SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5); + case MS_NONE: + return SmToken(TNONE, MS_NONE, "none", TG::LBrace | TG::RBrace, 5); + default: + return SmToken(TERROR, MS_NONE, "", TG::NONE, SAL_MAX_UINT16); + } +} + +SmToken starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(sal_Unicode cChar) +{ + switch (cChar) + { + case MS_VERTLINE: + return SmToken(TRLINE, MS_VERTLINE, "rline", TG::RBrace, 5); + case MS_DVERTLINE: + return SmToken(TRDLINE, MS_DVERTLINE, "rdline", TG::RBrace, 5); + case MS_LANGLE: + return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5); + case MS_LMATHANGLE: + return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5); + case MS_LBRACE: + return SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5); + case MS_LCEIL: + return SmToken(TLCEIL, MS_LCEIL, "lceil", TG::LBrace, 5); + case MS_LFLOOR: + return SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TG::LBrace, 5); + case MS_LDBRACKET: + return SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TG::LBrace, 5); + case MS_LBRACKET: + return SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5); + case MS_LPARENT: + return SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5); + case MS_RANGLE: + return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5); + case MS_RMATHANGLE: + return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5); + case MS_RBRACE: + return SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5); + case MS_RCEIL: + return SmToken(TRCEIL, MS_RCEIL, "rceil", TG::RBrace, 5); + case MS_RFLOOR: + return SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TG::RBrace, 5); + case MS_RDBRACKET: + return SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TG::RBrace, 5); + case MS_RBRACKET: + return SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5); + case MS_RPARENT: + return SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5); + case MS_NONE: + return SmToken(TNONE, MS_NONE, "none", TG::LBrace | TG::RBrace, 5); + default: + return SmToken(TERROR, MS_NONE, "", TG::NONE, SAL_MAX_UINT16); + } +} + +SmToken starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(sal_Unicode cChar) +{ + switch (cChar) + { + case MS_VERTLINE: + return SmToken(TLRLINE, MS_VERTLINE, "lrline", TG::LBrace | TG::RBrace, 5); + case MS_DVERTLINE: + return SmToken(TLRDLINE, MS_DVERTLINE, "lrdline", TG::LBrace | TG::RBrace, 5); + case MS_LANGLE: + return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5); + case MS_LMATHANGLE: + return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5); + case MS_LBRACE: + return SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5); + case MS_LCEIL: + return SmToken(TLCEIL, MS_LCEIL, "lceil", TG::LBrace, 5); + case MS_LFLOOR: + return SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TG::LBrace, 5); + case MS_LDBRACKET: + return SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TG::LBrace, 5); + case MS_LBRACKET: + return SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5); + case MS_LPARENT: + return SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5); + case MS_RANGLE: + return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5); + case MS_RMATHANGLE: + return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5); + case MS_RBRACE: + return SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5); + case MS_RCEIL: + return SmToken(TRCEIL, MS_RCEIL, "rceil", TG::RBrace, 5); + case MS_RFLOOR: + return SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TG::RBrace, 5); + case MS_RDBRACKET: + return SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TG::RBrace, 5); + case MS_RBRACKET: + return SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5); + case MS_RPARENT: + return SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5); + case MS_NONE: + return SmToken(TNONE, MS_NONE, "none", TG::LBrace | TG::RBrace, 5); + default: + return SmToken(TERROR, MS_NONE, "", TG::NONE, SAL_MAX_UINT16); + } +} + +const SmColorTokenTableEntry starmathdatabase::aColorTokenTableParse[] + = { { "aliceblue", THTMLCOL, COL_SM_ALICEBLUE }, + { "antiquewhite", THTMLCOL, COL_SM_ANTIQUEWHITE }, + { "aqua", TMATHMLCOL, COL_SM_AQUA }, + { "aquamarine", THTMLCOL, COL_SM_AQUAMARINE }, + { "azure", THTMLCOL, COL_SM_AZURE }, + { "beige", THTMLCOL, COL_SM_BEIGE }, + { "bisque", THTMLCOL, COL_SM_BISQUE }, + { "black", TMATHMLCOL, COL_SM_BLACK }, + { "blanchedalmond", THTMLCOL, COL_SM_BLANCHEDALMOND }, + { "blue", TMATHMLCOL, COL_SM_BLUE }, + { "blueviolet", THTMLCOL, COL_SM_BLUEVIOLET }, + { "brown", THTMLCOL, COL_SM_BROWN }, + { "burlywood", THTMLCOL, COL_SM_BURLYWOOD }, + { "cadetblue", THTMLCOL, COL_SM_CADETBLUE }, + { "chartreuse", THTMLCOL, COL_SM_CHARTREUSE }, + { "chocolate", THTMLCOL, COL_SM_CHOCOLATE }, + { "coral", THTMLCOL, COL_SM_CORAL }, + { "cornflowerblue", THTMLCOL, COL_SM_CORNFLOWERBLUE }, + { "cornsilk", THTMLCOL, COL_SM_CORNSILK }, + { "crimson", THTMLCOL, COL_SM_CRIMSON }, + { "cyan", TMATHMLCOL, COL_SM_CYAN }, + { "darkblue", THTMLCOL, COL_SM_DARKBLUE }, + { "darkcyan", THTMLCOL, COL_SM_DARKCYAN }, + { "darkgoldenrod", THTMLCOL, COL_SM_DARKGOLDENROD }, + { "darkgray", THTMLCOL, COL_SM_DARKGRAY }, + { "darkgreen", THTMLCOL, COL_SM_DARKGREEN }, + { "darkgrey", THTMLCOL, COL_SM_DARKGREY }, + { "darkkhaki", THTMLCOL, COL_SM_DARKKHAKI }, + { "darkmagenta", THTMLCOL, COL_SM_DARKMAGENTA }, + { "darkolivegreen", THTMLCOL, COL_SM_DARKOLIVEGREEN }, + { "darkorange", THTMLCOL, COL_SM_DARKORANGE }, + { "darkorchid", THTMLCOL, COL_SM_DARKORCHID }, + { "darkred", THTMLCOL, COL_SM_DARKRED }, + { "darksalmon", THTMLCOL, COL_SM_DARKSALMON }, + { "darkseagreen", THTMLCOL, COL_SM_DARKSEAGREEN }, + { "darkslateblue", THTMLCOL, COL_SM_DARKSLATEBLUE }, + { "darkslategray", THTMLCOL, COL_SM_DARKSLATEGRAY }, + { "darkslategrey", THTMLCOL, COL_SM_DARKSLATEGREY }, + { "darkturquoise", THTMLCOL, COL_SM_DARKTURQUOISE }, + { "darkviolet", THTMLCOL, COL_SM_DARKVIOLET }, + { "debian", TICONICCOL, COL_SM_DEBIAN_MAGENTA }, + { "deeppink", THTMLCOL, COL_SM_DEEPPINK }, + { "deepskyblue", THTMLCOL, COL_SM_DEEPSKYBLUE }, + { "dimgray", THTMLCOL, COL_SM_DIMGRAY }, + { "dimgrey", THTMLCOL, COL_SM_DIMGREY }, + { "dodgerblue", THTMLCOL, COL_SM_DODGERBLUE }, + { "dvip", TDVIPSNAMESCOL, COL_SM_BLACK }, + { "firebrick", THTMLCOL, COL_SM_FIREBRICK }, + { "floralwhite", THTMLCOL, COL_SM_FLORALWHITE }, + { "forestgreen", THTMLCOL, COL_SM_FORESTGREEN }, + { "fuchsia", TMATHMLCOL, COL_SM_FUCHSIA }, + { "gainsboro", THTMLCOL, COL_SM_GAINSBORO }, + { "ghostwhite", THTMLCOL, COL_SM_GHOSTWHITE }, + { "gold", THTMLCOL, COL_SM_GOLD }, + { "goldenrod", THTMLCOL, COL_SM_GOLDENROD }, + { "gray", TMATHMLCOL, COL_SM_GRAY }, + { "green", TMATHMLCOL, COL_SM_GREEN }, + { "greenyellow", THTMLCOL, COL_SM_GREENYELLOW }, + { "grey", THTMLCOL, COL_SM_GREY }, + { "hex", THEX, COL_SM_BLACK }, + { "honeydew", THTMLCOL, COL_SM_HONEYDEW }, + { "hotpink", THTMLCOL, COL_SM_HOTPINK }, + { "indianred", THTMLCOL, COL_SM_INDIANRED }, + { "indigo", THTMLCOL, COL_SM_INDIGO }, + { "ivory", THTMLCOL, COL_SM_IVORY }, + { "khaki", THTMLCOL, COL_SM_KHAKI }, + { "lavender", THTMLCOL, COL_SM_LAVENDER }, + { "lavenderblush", THTMLCOL, COL_SM_LAVENDERBLUSH }, + { "lawngreen", THTMLCOL, COL_SM_LAWNGREEN }, + { "lemonchiffon", THTMLCOL, COL_SM_LEMONCHIFFON }, + { "lightblue", THTMLCOL, COL_SM_LIGHTBLUE }, + { "lightcoral", THTMLCOL, COL_SM_LIGHTCORAL }, + { "lightcyan", THTMLCOL, COL_SM_LIGHTCYAN }, + { "lightgoldenrodyellow", THTMLCOL, COL_SM_LIGHTGOLDENRODYELLOW }, + { "lightgray", THTMLCOL, COL_SM_LIGHTGRAY }, + { "lightgreen", THTMLCOL, COL_SM_LIGHTGREEN }, + { "lightgrey", THTMLCOL, COL_SM_LIGHTGREY }, + { "lightpink", THTMLCOL, COL_SM_LIGHTPINK }, + { "lightsalmon", THTMLCOL, COL_SM_LIGHTSALMON }, + { "lightseagreen", THTMLCOL, COL_SM_LIGHTSEAGREEN }, + { "lightskyblue", THTMLCOL, COL_SM_LIGHTSKYBLUE }, + { "lightslategray", THTMLCOL, COL_SM_LIGHTSLATEGRAY }, + { "lightslategrey", THTMLCOL, COL_SM_LIGHTSLATEGREY }, + { "lightsteelblue", THTMLCOL, COL_SM_LIGHTSTEELBLUE }, + { "lightyellow", THTMLCOL, COL_SM_LIGHTYELLOW }, + { "lime", TMATHMLCOL, COL_SM_LIME }, + { "limegreen", THTMLCOL, COL_SM_LIMEGREEN }, + { "linen", THTMLCOL, COL_SM_LINEN }, + { "lo", TICONICCOL, COL_SM_LO_GREEN }, + { "magenta", TMATHMLCOL, COL_SM_MAGENTA }, + { "maroon", TMATHMLCOL, COL_SM_MAROON }, + { "mediumaquamarine", THTMLCOL, COL_SM_MEDIUMAQUAMARINE }, + { "mediumblue", THTMLCOL, COL_SM_MEDIUMBLUE }, + { "mediumorchid", THTMLCOL, COL_SM_MEDIUMORCHID }, + { "mediumpurple", THTMLCOL, COL_SM_MEDIUMPURPLE }, + { "mediumseagreen", THTMLCOL, COL_SM_MEDIUMSEAGREEN }, + { "mediumslateblue", THTMLCOL, COL_SM_MEDIUMSLATEBLUE }, + { "mediumspringgreen", THTMLCOL, COL_SM_MEDIUMSPRINGGREEN }, + { "mediumturquoise", THTMLCOL, COL_SM_MEDIUMTURQUOISE }, + { "mediumvioletred", THTMLCOL, COL_SM_MEDIUMVIOLETRED }, + { "midnightblue", THTMLCOL, COL_SM_MIDNIGHTBLUE }, + { "mintcream", THTMLCOL, COL_SM_MINTCREAM }, + { "mistyrose", THTMLCOL, COL_SM_MISTYROSE }, + { "moccasin", THTMLCOL, COL_SM_MOCCASIN }, + { "navajowhite", THTMLCOL, COL_SM_NAVAJOWHITE }, + { "navy", TMATHMLCOL, COL_SM_NAVY }, + { "oldlace", THTMLCOL, COL_SM_OLDLACE }, + { "olive", TMATHMLCOL, COL_SM_OLIVE }, + { "olivedrab", THTMLCOL, COL_SM_OLIVEDRAB }, + { "orange", THTMLCOL, COL_SM_ORANGE }, + { "orangered", THTMLCOL, COL_SM_ORANGERED }, + { "orchid", THTMLCOL, COL_SM_ORCHID }, + { "palegoldenrod", THTMLCOL, COL_SM_PALEGOLDENROD }, + { "palegreen", THTMLCOL, COL_SM_PALEGREEN }, + { "paleturquoise", THTMLCOL, COL_SM_PALETURQUOISE }, + { "palevioletred", THTMLCOL, COL_SM_PALEVIOLETRED }, + { "papayawhip", THTMLCOL, COL_SM_PAPAYAWHIP }, + { "peachpuff", THTMLCOL, COL_SM_PEACHPUFF }, + { "peru", THTMLCOL, COL_SM_PERU }, + { "pink", THTMLCOL, COL_SM_PINK }, + { "plum", THTMLCOL, COL_SM_PLUM }, + { "powderblue", THTMLCOL, COL_SM_POWDERBLUE }, + { "purple", TMATHMLCOL, COL_SM_PURPLE }, + { "rebeccapurple", THTMLCOL, COL_SM_REBECCAPURPLE }, + { "red", TMATHMLCOL, COL_SM_RED }, + { "rgb", TRGB, COL_AUTO }, + { "rgba", TRGBA, COL_AUTO }, + { "rosybrown", THTMLCOL, COL_SM_ROSYBROWN }, + { "royalblue", THTMLCOL, COL_SM_ROYALBLUE }, + { "saddlebrown", THTMLCOL, COL_SM_SADDLEBROWN }, + { "salmon", THTMLCOL, COL_SM_SALMON }, + { "sandybrown", THTMLCOL, COL_SM_SANDYBROWN }, + { "seagreen", THTMLCOL, COL_SM_SEAGREEN }, + { "seashell", THTMLCOL, COL_SM_SEASHELL }, + { "sienna", THTMLCOL, COL_SM_SIENNA }, + { "silver", TMATHMLCOL, COL_SM_SILVER }, + { "skyblue", THTMLCOL, COL_SM_SKYBLUE }, + { "slateblue", THTMLCOL, COL_SM_SLATEBLUE }, + { "slategray", THTMLCOL, COL_SM_SLATEGRAY }, + { "slategrey", THTMLCOL, COL_SM_SLATEGREY }, + { "snow", THTMLCOL, COL_SM_SNOW }, + { "springgreen", THTMLCOL, COL_SM_SPRINGGREEN }, + { "steelblue", THTMLCOL, COL_SM_STEELBLUE }, + { "tan", THTMLCOL, COL_SM_TAN }, + { "teal", TMATHMLCOL, COL_SM_TEAL }, + { "thistle", THTMLCOL, COL_SM_THISTLE }, + { "tomato", THTMLCOL, COL_SM_TOMATO }, + { "turquoise", THTMLCOL, COL_SM_TURQUOISE }, + { "ubuntu", TICONICCOL, COL_SM_UBUNTU_ORANGE }, + { "violet", THTMLCOL, COL_SM_VIOLET }, + { "wheat", THTMLCOL, COL_SM_WHEAT }, + { "white", TMATHMLCOL, COL_SM_WHITE }, + { "whitesmoke", THTMLCOL, COL_SM_WHITESMOKE }, + { "yellow", TMATHMLCOL, COL_SM_YELLOW }, + { "yellowgreen", THTMLCOL, COL_SM_YELLOWGREEN } }; + +const SmColorTokenTableEntry starmathdatabase::aColorTokenTableHTML[] + = { { "aliceblue", THTMLCOL, COL_SM_ALICEBLUE }, + { "antiquewhite", THTMLCOL, COL_SM_ANTIQUEWHITE }, + { "aqua", TMATHMLCOL, COL_SM_AQUA }, + { "aquamarine", THTMLCOL, COL_SM_AQUAMARINE }, + { "azure", THTMLCOL, COL_SM_AZURE }, + { "beige", THTMLCOL, COL_SM_BEIGE }, + { "bisque", THTMLCOL, COL_SM_BISQUE }, + { "black", TMATHMLCOL, COL_SM_BLACK }, + { "blanchedalmond", THTMLCOL, COL_SM_BLANCHEDALMOND }, + { "blue", TMATHMLCOL, COL_SM_BLUE }, + { "blueviolet", THTMLCOL, COL_SM_BLUEVIOLET }, + { "brown", THTMLCOL, COL_SM_BROWN }, + { "burlywood", THTMLCOL, COL_SM_BURLYWOOD }, + { "cadetblue", THTMLCOL, COL_SM_CADETBLUE }, + { "chartreuse", THTMLCOL, COL_SM_CHARTREUSE }, + { "chocolate", THTMLCOL, COL_SM_CHOCOLATE }, + { "coral", THTMLCOL, COL_SM_CORAL }, + { "cornflowerblue", THTMLCOL, COL_SM_CORNFLOWERBLUE }, + { "cornsilk", THTMLCOL, COL_SM_CORNSILK }, + { "crimson", THTMLCOL, COL_SM_CRIMSON }, + { "cyan", TMATHMLCOL, COL_SM_CYAN }, + { "darkblue", THTMLCOL, COL_SM_DARKBLUE }, + { "darkcyan", THTMLCOL, COL_SM_DARKCYAN }, + { "darkgoldenrod", THTMLCOL, COL_SM_DARKGOLDENROD }, + { "darkgray", THTMLCOL, COL_SM_DARKGRAY }, + { "darkgreen", THTMLCOL, COL_SM_DARKGREEN }, + { "darkgrey", THTMLCOL, COL_SM_DARKGREY }, + { "darkkhaki", THTMLCOL, COL_SM_DARKKHAKI }, + { "darkmagenta", THTMLCOL, COL_SM_DARKMAGENTA }, + { "darkolivegreen", THTMLCOL, COL_SM_DARKOLIVEGREEN }, + { "darkorange", THTMLCOL, COL_SM_DARKORANGE }, + { "darkorchid", THTMLCOL, COL_SM_DARKORCHID }, + { "darkred", THTMLCOL, COL_SM_DARKRED }, + { "darksalmon", THTMLCOL, COL_SM_DARKSALMON }, + { "darkseagreen", THTMLCOL, COL_SM_DARKSEAGREEN }, + { "darkslateblue", THTMLCOL, COL_SM_DARKSLATEBLUE }, + { "darkslategray", THTMLCOL, COL_SM_DARKSLATEGRAY }, + { "darkslategrey", THTMLCOL, COL_SM_DARKSLATEGREY }, + { "darkturquoise", THTMLCOL, COL_SM_DARKTURQUOISE }, + { "darkviolet", THTMLCOL, COL_SM_DARKVIOLET }, + { "deeppink", THTMLCOL, COL_SM_DEEPPINK }, + { "deepskyblue", THTMLCOL, COL_SM_DEEPSKYBLUE }, + { "dimgray", THTMLCOL, COL_SM_DIMGRAY }, + { "dimgrey", THTMLCOL, COL_SM_DIMGREY }, + { "dodgerblue", THTMLCOL, COL_SM_DODGERBLUE }, + { "firebrick", THTMLCOL, COL_SM_FIREBRICK }, + { "floralwhite", THTMLCOL, COL_SM_FLORALWHITE }, + { "forestgreen", THTMLCOL, COL_SM_FORESTGREEN }, + { "fuchsia", TMATHMLCOL, COL_SM_FUCHSIA }, + { "gainsboro", THTMLCOL, COL_SM_GAINSBORO }, + { "ghostwhite", THTMLCOL, COL_SM_GHOSTWHITE }, + { "gold", THTMLCOL, COL_SM_GOLD }, + { "goldenrod", THTMLCOL, COL_SM_GOLDENROD }, + { "gray", TMATHMLCOL, COL_SM_GRAY }, + { "green", TMATHMLCOL, COL_SM_GREEN }, + { "greenyellow", THTMLCOL, COL_SM_GREENYELLOW }, + { "grey", THTMLCOL, COL_SM_GREY }, + { "honeydew", THTMLCOL, COL_SM_HONEYDEW }, + { "hotpink", THTMLCOL, COL_SM_HOTPINK }, + { "indianred", THTMLCOL, COL_SM_INDIANRED }, + { "indigo", THTMLCOL, COL_SM_INDIGO }, + { "ivory", THTMLCOL, COL_SM_IVORY }, + { "khaki", THTMLCOL, COL_SM_KHAKI }, + { "lavender", THTMLCOL, COL_SM_LAVENDER }, + { "lavenderblush", THTMLCOL, COL_SM_LAVENDERBLUSH }, + { "lawngreen", THTMLCOL, COL_SM_LAWNGREEN }, + { "lemonchiffon", THTMLCOL, COL_SM_LEMONCHIFFON }, + { "lightblue", THTMLCOL, COL_SM_LIGHTBLUE }, + { "lightcoral", THTMLCOL, COL_SM_LIGHTCORAL }, + { "lightcyan", THTMLCOL, COL_SM_LIGHTCYAN }, + { "lightgoldenrodyellow", THTMLCOL, COL_SM_LIGHTGOLDENRODYELLOW }, + { "lightgray", THTMLCOL, COL_SM_LIGHTGRAY }, + { "lightgreen", THTMLCOL, COL_SM_LIGHTGREEN }, + { "lightgrey", THTMLCOL, COL_SM_LIGHTGREY }, + { "lightpink", THTMLCOL, COL_SM_LIGHTPINK }, + { "lightsalmon", THTMLCOL, COL_SM_LIGHTSALMON }, + { "lightseagreen", THTMLCOL, COL_SM_LIGHTSEAGREEN }, + { "lightskyblue", THTMLCOL, COL_SM_LIGHTSKYBLUE }, + { "lightslategray", THTMLCOL, COL_SM_LIGHTSLATEGRAY }, + { "lightslategrey", THTMLCOL, COL_SM_LIGHTSLATEGREY }, + { "lightsteelblue", THTMLCOL, COL_SM_LIGHTSTEELBLUE }, + { "lightyellow", THTMLCOL, COL_SM_LIGHTYELLOW }, + { "lime", TMATHMLCOL, COL_SM_LIME }, + { "limegreen", THTMLCOL, COL_SM_LIMEGREEN }, + { "linen", THTMLCOL, COL_SM_LINEN }, + { "magenta", TMATHMLCOL, COL_SM_MAGENTA }, + { "maroon", TMATHMLCOL, COL_SM_MAROON }, + { "mediumaquamarine", THTMLCOL, COL_SM_MEDIUMAQUAMARINE }, + { "mediumblue", THTMLCOL, COL_SM_MEDIUMBLUE }, + { "mediumorchid", THTMLCOL, COL_SM_MEDIUMORCHID }, + { "mediumpurple", THTMLCOL, COL_SM_MEDIUMPURPLE }, + { "mediumseagreen", THTMLCOL, COL_SM_MEDIUMSEAGREEN }, + { "mediumslateblue", THTMLCOL, COL_SM_MEDIUMSLATEBLUE }, + { "mediumspringgreen", THTMLCOL, COL_SM_MEDIUMSPRINGGREEN }, + { "mediumturquoise", THTMLCOL, COL_SM_MEDIUMTURQUOISE }, + { "mediumvioletred", THTMLCOL, COL_SM_MEDIUMVIOLETRED }, + { "midnightblue", THTMLCOL, COL_SM_MIDNIGHTBLUE }, + { "mintcream", THTMLCOL, COL_SM_MINTCREAM }, + { "mistyrose", THTMLCOL, COL_SM_MISTYROSE }, + { "moccasin", THTMLCOL, COL_SM_MOCCASIN }, + { "navajowhite", THTMLCOL, COL_SM_NAVAJOWHITE }, + { "navy", TMATHMLCOL, COL_SM_NAVY }, + { "oldlace", THTMLCOL, COL_SM_OLDLACE }, + { "olive", TMATHMLCOL, COL_SM_OLIVE }, + { "olivedrab", THTMLCOL, COL_SM_OLIVEDRAB }, + { "orange", THTMLCOL, COL_SM_ORANGE }, + { "orangered", THTMLCOL, COL_SM_ORANGERED }, + { "orchid", THTMLCOL, COL_SM_ORCHID }, + { "palegoldenrod", THTMLCOL, COL_SM_PALEGOLDENROD }, + { "palegreen", THTMLCOL, COL_SM_PALEGREEN }, + { "paleturquoise", THTMLCOL, COL_SM_PALETURQUOISE }, + { "palevioletred", THTMLCOL, COL_SM_PALEVIOLETRED }, + { "papayawhip", THTMLCOL, COL_SM_PAPAYAWHIP }, + { "peachpuff", THTMLCOL, COL_SM_PEACHPUFF }, + { "peru", THTMLCOL, COL_SM_PERU }, + { "pink", THTMLCOL, COL_SM_PINK }, + { "plum", THTMLCOL, COL_SM_PLUM }, + { "powderblue", THTMLCOL, COL_SM_POWDERBLUE }, + { "purple", TMATHMLCOL, COL_SM_PURPLE }, + { "rebeccapurple", THTMLCOL, COL_SM_REBECCAPURPLE }, + { "red", TMATHMLCOL, COL_SM_RED }, + { "rosybrown", THTMLCOL, COL_SM_ROSYBROWN }, + { "royalblue", THTMLCOL, COL_SM_ROYALBLUE }, + { "saddlebrown", THTMLCOL, COL_SM_SADDLEBROWN }, + { "salmon", THTMLCOL, COL_SM_SALMON }, + { "sandybrown", THTMLCOL, COL_SM_SANDYBROWN }, + { "seagreen", THTMLCOL, COL_SM_SEAGREEN }, + { "seashell", THTMLCOL, COL_SM_SEASHELL }, + { "sienna", THTMLCOL, COL_SM_SIENNA }, + { "silver", TMATHMLCOL, COL_SM_SILVER }, + { "skyblue", THTMLCOL, COL_SM_SKYBLUE }, + { "slateblue", THTMLCOL, COL_SM_SLATEBLUE }, + { "slategray", THTMLCOL, COL_SM_SLATEGRAY }, + { "slategrey", THTMLCOL, COL_SM_SLATEGREY }, + { "snow", THTMLCOL, COL_SM_SNOW }, + { "springgreen", THTMLCOL, COL_SM_SPRINGGREEN }, + { "steelblue", THTMLCOL, COL_SM_STEELBLUE }, + { "tan", THTMLCOL, COL_SM_TAN }, + { "teal", TMATHMLCOL, COL_SM_TEAL }, + { "thistle", THTMLCOL, COL_SM_THISTLE }, + { "tomato", THTMLCOL, COL_SM_TOMATO }, + { "turquoise", THTMLCOL, COL_SM_TURQUOISE }, + { "violet", THTMLCOL, COL_SM_VIOLET }, + { "wheat", THTMLCOL, COL_SM_WHEAT }, + { "white", TMATHMLCOL, COL_SM_WHITE }, + { "whitesmoke", THTMLCOL, COL_SM_WHITESMOKE }, + { "yellow", TMATHMLCOL, COL_SM_YELLOW }, + { "yellowgreen", THTMLCOL, COL_SM_YELLOWGREEN } }; + +const SmColorTokenTableEntry starmathdatabase::aColorTokenTableDVIPS[] + = { { "apricot", TDVIPSNAMESCOL, COL_SM_DIV_APRICOT }, + { "aquamarine", TDVIPSNAMESCOL, COL_SM_DIV_AQUAMARINE }, + { "bittersweet", TDVIPSNAMESCOL, COL_SM_DIV_BITTERSWEET }, + { "black", TDVIPSNAMESCOL, COL_SM_BLACK }, + { "blue", TDVIPSNAMESCOL, COL_SM_BLACK } }; + +const SmColorTokenTableEntry starmathdatabase::aColorTokenTableMATHML[] = { + // clang-format off + { "aqua", TMATHMLCOL, COL_SM_AQUA }, + { "black", TMATHMLCOL, COL_SM_BLACK }, + { "blue", TMATHMLCOL, COL_SM_BLUE }, + { "fuchsia", TMATHMLCOL, COL_SM_FUCHSIA }, + { "gray", TMATHMLCOL, COL_SM_GRAY }, + { "green", TMATHMLCOL, COL_SM_GREEN }, + { "lime", TMATHMLCOL, COL_SM_LIME }, + { "maroon", TMATHMLCOL, COL_SM_MAROON }, + { "navy", TMATHMLCOL, COL_SM_NAVY }, + { "olive", TMATHMLCOL, COL_SM_OLIVE }, + { "purple", TMATHMLCOL, COL_SM_PURPLE }, + { "red", TMATHMLCOL, COL_SM_RED }, + { "silver", TMATHMLCOL, COL_SM_SILVER }, + { "teal", TMATHMLCOL, COL_SM_TEAL }, + { "white", TMATHMLCOL, COL_SM_WHITE }, + { "yellow", TMATHMLCOL, COL_SM_YELLOW } + // clang-format on +}; + +const SmColorTokenTableEntry starmathdatabase::aColorTokenTableERROR[] + = { { "", TERROR, COL_SM_BLACK } }; + +SmColorTokenTableEntry starmathdatabase::Identify_Color_Parser(sal_uInt32 cColor) +{ + for (auto i = std::begin(aColorTokenTableParse); i < std::end(aColorTokenTableParse); ++i) + if (i->equals(cColor)) + return i; + for (auto i = std::begin(aColorTokenTableDVIPS); i < std::end(aColorTokenTableDVIPS); ++i) + if (i->equals(cColor)) + return i; + if ((cColor & 0x00FFFFFF) == cColor) + return SmColorTokenTableEntry("", TRGB, cColor); + else + return SmColorTokenTableEntry("", TRGBA, cColor); +} + +SmColorTokenTableEntry starmathdatabase::Identify_Color_HTML(sal_uInt32 cColor) +{ + for (auto i = std::begin(aColorTokenTableHTML); i < std::end(aColorTokenTableHTML); ++i) + if (i->equals(cColor)) + return i; + if ((cColor & 0x00FFFFFF) == cColor) + return SmColorTokenTableEntry("", TRGB, cColor); + else + return SmColorTokenTableEntry("", TRGBA, cColor); +} + +SmColorTokenTableEntry starmathdatabase::Identify_Color_MATHML(sal_uInt32 cColor) +{ + for (auto i = std::begin(aColorTokenTableMATHML); i < std::end(aColorTokenTableMATHML); ++i) + if (i->equals(cColor)) + return i; + if ((cColor & 0x00FFFFFF) == cColor) + return SmColorTokenTableEntry("", TRGB, cColor); + else + return SmColorTokenTableEntry("", TRGBA, cColor); +} + +SmColorTokenTableEntry starmathdatabase::Identify_Color_DVIPSNAMES(sal_uInt32 cColor) +{ + for (auto i = std::begin(aColorTokenTableDVIPS); i < std::end(aColorTokenTableDVIPS); ++i) + if (i->equals(cColor)) + return i; + if ((cColor & 0x00FFFFFF) == cColor) + return SmColorTokenTableEntry("", TRGB, cColor); + else + return SmColorTokenTableEntry("", TRGBA, cColor); +} + +const SmColorTokenTableEntry* +starmathdatabase::Identify_ColorName_Parser(std::u16string_view colorname) +{ + if (colorname.empty()) + return &aColorTokenTableERROR[0]; + for (auto i = std::begin(aColorTokenTableParse); i < std::end(aColorTokenTableParse); ++i) + { + sal_Int32 matches = o3tl::compareToIgnoreAsciiCase(colorname, i->aIdent); + if (matches == 0) + return i; + if (matches < 0) + break; + } + return &aColorTokenTableERROR[0]; +} +SmColorTokenTableEntry starmathdatabase::Identify_ColorName_HTML(std::u16string_view colorname) +{ + if (colorname.empty()) + return SmColorTokenTableEntry("", TERROR, COL_SM_BLACK); + if (colorname[0] == '#') + { + Color col = Color::STRtoRGB(colorname); + return SmColorTokenTableEntry("", TRGB, col); + } + for (auto i = std::begin(aColorTokenTableHTML); i < std::end(aColorTokenTableHTML); ++i) + { + sal_Int32 matches = o3tl::compareToIgnoreAsciiCase(colorname, i->aIdent); + if (matches == 0) + return i; + if (matches < 0) + break; + } + return SmColorTokenTableEntry("", TERROR, COL_SM_BLACK); +} +const SmColorTokenTableEntry* +starmathdatabase::Identify_ColorName_DVIPSNAMES(std::u16string_view colorname) +{ + if (colorname.empty()) + return &aColorTokenTableERROR[0]; + for (auto i = std::begin(aColorTokenTableDVIPS); i < std::end(aColorTokenTableDVIPS); ++i) + { + sal_Int32 matches = o3tl::compareToIgnoreAsciiCase(colorname, i->aIdent); + if (matches == 0) + return i; + if (matches < 0) + break; + } + return &aColorTokenTableERROR[0]; +} diff --git a/starmath/source/mathml/xparsmlbase.cxx b/starmath/source/mathml/xparsmlbase.cxx new file mode 100644 index 000000000..7e0e93ab7 --- /dev/null +++ b/starmath/source/mathml/xparsmlbase.cxx @@ -0,0 +1,2166 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <xparsmlbase.hxx> + +static ::css::beans::Pair<::rtl::OUString, ::rtl::OUString> + icustomMathmlHtmlEntitiesData[starmathdatabase::STARMATH_MATHMLHTML_ENTITY_NUMBER] = { + // clang-format off + { u"AElig", u"\u00C6" }, + { u"AMP", u"\u0026" }, + { u"Aacute", u"\u00C1" }, + { u"Abreve", u"\u0102" }, + { u"Acirc", u"\u00C2" }, + { u"Acy", u"\u0410" }, + { u"Afr", u"\U0001D504" }, + { u"Agrave", u"\u00C0" }, + { u"Alpha", u"\u0391" }, + { u"Amacr", u"\u0100" }, + { u"And", u"\u2A53" }, + { u"Aogon", u"\u0104" }, + { u"Aopf", u"\U0001D538" }, + { u"ApplyFunction", u"\u2061" }, + { u"Aring", u"\u00C5" }, + { u"Ascr", u"\U0001D49C" }, + { u"Assign", u"\u2254" }, + { u"Atilde", u"\u00C3" }, + { u"Auml", u"\u00C4" }, + { u"Backslash", u"\u2216" }, + { u"Barv", u"\u2AE7" }, + { u"Barwed", u"\u2306" }, + { u"Bcy", u"\u0411" }, + { u"Because", u"\u2235" }, + { u"Bernoullis", u"\u212C" }, + { u"Beta", u"\u0392" }, + { u"Bfr", u"\U0001D505" }, + { u"Bopf", u"\U0001D539" }, + { u"Breve", u"\u02D8" }, + { u"Bscr", u"\u212C" }, + { u"Bumpeq", u"\u224E" }, + { u"CHcy", u"\u0427" }, + { u"COPY", u"\u00A9" }, + { u"Cacute", u"\u0106" }, + { u"Cap", u"\u22D2" }, + { u"CapitalDifferentialD", u"\u2145" }, + { u"Cayleys", u"\u212D" }, + { u"Ccaron", u"\u010C" }, + { u"Ccedil", u"\u00C7" }, + { u"Ccirc", u"\u0108" }, + { u"Cconint", u"\u2230" }, + { u"Cdot", u"\u010A" }, + { u"Cedilla", u"\u00B8" }, + { u"CenterDot", u"\u00B7" }, + { u"Cfr", u"\u212D" }, + { u"Chi", u"\u03A7" }, + { u"CircleDot", u"\u2299" }, + { u"CircleMinus", u"\u2296" }, + { u"CirclePlus", u"\u2295" }, + { u"CircleTimes", u"\u2297" }, + { u"ClockwiseContourIntegral", u"\u2232" }, + { u"CloseCurlyDoubleQuote", u"\u201D" }, + { u"CloseCurlyQuote", u"\u2019" }, + { u"Colon", u"\u2237" }, + { u"Colone", u"\u2A74" }, + { u"Congruent", u"\u2261" }, + { u"Conint", u"\u222F" }, + { u"ContourIntegral", u"\u222E" }, + { u"Copf", u"\u2102" }, + { u"Coproduct", u"\u2210" }, + { u"CounterClockwiseContourIntegral", u"\u2233" }, + { u"Cross", u"\u2A2F" }, + { u"Cscr", u"\U0001D49E" }, + { u"Cup", u"\u22D3" }, + { u"CupCap", u"\u224D" }, + { u"DD", u"\u2145" }, + { u"DDotrahd", u"\u2911" }, + { u"DJcy", u"\u0402" }, + { u"DScy", u"\u0405" }, + { u"DZcy", u"\u040F" }, + { u"Dagger", u"\u2021" }, + { u"Darr", u"\u21A1" }, + { u"Dashv", u"\u2AE4" }, + { u"Dcaron", u"\u010E" }, + { u"Dcy", u"\u0414" }, + { u"Del", u"\u2207" }, + { u"Delta", u"\u0394" }, + { u"Dfr", u"\U0001D507" }, + { u"DiacriticalAcute", u"\u00B4" }, + { u"DiacriticalDot", u"\u02D9" }, + { u"DiacriticalDoubleAcute", u"\u02DD" }, + { u"DiacriticalGrave", u"\u0060" }, + { u"DiacriticalTilde", u"\u02DC" }, + { u"Diamond", u"\u22C4" }, + { u"DifferentialD", u"\u2146" }, + { u"Dopf", u"\U0001D53B" }, + { u"Dot", u"\u00A8" }, + { u"DotDot", u"\u20DC" }, + { u"DotEqual", u"\u2250" }, + { u"DoubleContourIntegral", u"\u222F" }, + { u"DoubleDot", u"\u00A8" }, + { u"DoubleDownArrow", u"\u21D3" }, + { u"DoubleLeftArrow", u"\u21D0" }, + { u"DoubleLeftRightArrow", u"\u21D4" }, + { u"DoubleLeftTee", u"\u2AE4" }, + { u"DoubleLongLeftArrow", u"\u27F8" }, + { u"DoubleLongLeftRightArrow", u"\u27FA" }, + { u"DoubleLongRightArrow", u"\u27F9" }, + { u"DoubleRightArrow", u"\u21D2" }, + { u"DoubleRightTee", u"\u22A8" }, + { u"DoubleUpArrow", u"\u21D1" }, + { u"DoubleUpDownArrow", u"\u21D5" }, + { u"DoubleVerticalBar", u"\u2225" }, + { u"DownArrow", u"\u2193" }, + { u"DownArrowBar", u"\u2913" }, + { u"DownArrowUpArrow", u"\u21F5" }, + { u"DownBreve", u"\u0311" }, + { u"DownLeftRightVector", u"\u2950" }, + { u"DownLeftTeeVector", u"\u295E" }, + { u"DownLeftVector", u"\u21BD" }, + { u"DownLeftVectorBar", u"\u2956" }, + { u"DownRightTeeVector", u"\u295F" }, + { u"DownRightVector", u"\u21C1" }, + { u"DownRightVectorBar", u"\u2957" }, + { u"DownTee", u"\u22A4" }, + { u"DownTeeArrow", u"\u21A7" }, + { u"Downarrow", u"\u21D3" }, + { u"Dscr", u"\U0001D49F" }, + { u"Dstrok", u"\u0110" }, + { u"ENG", u"\u014A" }, + { u"ETH", u"\u00D0" }, + { u"Eacute", u"\u00C9" }, + { u"Ecaron", u"\u011A" }, + { u"Ecirc", u"\u00CA" }, + { u"Ecy", u"\u042D" }, + { u"Edot", u"\u0116" }, + { u"Efr", u"\U0001D508" }, + { u"Egrave", u"\u00C8" }, + { u"Element", u"\u2208" }, + { u"Emacr", u"\u0112" }, + { u"EmptySmallSquare", u"\u25FB" }, + { u"EmptyVerySmallSquare", u"\u25AB" }, + { u"Eogon", u"\u0118" }, + { u"Eopf", u"\U0001D53C" }, + { u"Epsilon", u"\u0395" }, + { u"Equal", u"\u2A75" }, + { u"EqualTilde", u"\u2242" }, + { u"Equilibrium", u"\u21CC" }, + { u"Escr", u"\u2130" }, + { u"Esim", u"\u2A73" }, + { u"Eta", u"\u0397" }, + { u"Euml", u"\u00CB" }, + { u"Exists", u"\u2203" }, + { u"ExponentialE", u"\u2147" }, + { u"Fcy", u"\u0424" }, + { u"Ffr", u"\U0001D509" }, + { u"FilledSmallSquare", u"\u25FC" }, + { u"FilledVerySmallSquare", u"\u25AA" }, + { u"Fopf", u"\U0001D53D" }, + { u"ForAll", u"\u2200" }, + { u"Fouriertrf", u"\u2131" }, + { u"Fscr", u"\u2131" }, + { u"GJcy", u"\u0403" }, + { u"GT", u"\u003E" }, + { u"Gamma", u"\u0393" }, + { u"Gammad", u"\u03DC" }, + { u"Gbreve", u"\u011E" }, + { u"Gcedil", u"\u0122" }, + { u"Gcirc", u"\u011C" }, + { u"Gcy", u"\u0413" }, + { u"Gdot", u"\u0120" }, + { u"Gfr", u"\U0001D50A" }, + { u"Gg", u"\u22D9" }, + { u"Gopf", u"\U0001D53E" }, + { u"GreaterEqual", u"\u2265" }, + { u"GreaterEqualLess", u"\u22DB" }, + { u"GreaterFullEqual", u"\u2267" }, + { u"GreaterGreater", u"\u2AA2" }, + { u"GreaterLess", u"\u2277" }, + { u"GreaterSlantEqual", u"\u2A7E" }, + { u"GreaterTilde", u"\u2273" }, + { u"Gscr", u"\U0001D4A2" }, + { u"Gt", u"\u226B" }, + { u"HARDcy", u"\u042A" }, + { u"Hacek", u"\u02C7" }, + { u"Hat", u"\u005E" }, + { u"Hcirc", u"\u0124" }, + { u"Hfr", u"\u210C" }, + { u"HilbertSpace", u"\u210B" }, + { u"Hopf", u"\u210D" }, + { u"HorizontalLine", u"\u2500" }, + { u"Hscr", u"\u210B" }, + { u"Hstrok", u"\u0126" }, + { u"HumpDownHump", u"\u224E" }, + { u"HumpEqual", u"\u224F" }, + { u"IEcy", u"\u0415" }, + { u"IJlig", u"\u0132" }, + { u"IOcy", u"\u0401" }, + { u"Iacute", u"\u00CD" }, + { u"Icirc", u"\u00CE" }, + { u"Icy", u"\u0418" }, + { u"Idot", u"\u0130" }, + { u"Ifr", u"\u2111" }, + { u"Igrave", u"\u00CC" }, + { u"Im", u"\u2111" }, + { u"Imacr", u"\u012A" }, + { u"ImaginaryI", u"\u2148" }, + { u"Implies", u"\u21D2" }, + { u"Int", u"\u222C" }, + { u"Integral", u"\u222B" }, + { u"Intersection", u"\u22C2" }, + { u"InvisibleComma", u"\u2063" }, + { u"InvisibleTimes", u"\u2062" }, + { u"Iogon", u"\u012E" }, + { u"Iopf", u"\U0001D540" }, + { u"Iota", u"\u0399" }, + { u"Iscr", u"\u2110" }, + { u"Itilde", u"\u0128" }, + { u"Iukcy", u"\u0406" }, + { u"Iuml", u"\u00CF" }, + { u"Jcirc", u"\u0134" }, + { u"Jcy", u"\u0419" }, + { u"Jfr", u"\U0001D50D" }, + { u"Jopf", u"\U0001D541" }, + { u"Jscr", u"\U0001D4A5" }, + { u"Jsercy", u"\u0408" }, + { u"Jukcy", u"\u0404" }, + { u"KHcy", u"\u0425" }, + { u"KJcy", u"\u040C" }, + { u"Kappa", u"\u039A" }, + { u"Kcedil", u"\u0136" }, + { u"Kcy", u"\u041A" }, + { u"Kfr", u"\U0001D50E" }, + { u"Kopf", u"\U0001D542" }, + { u"Kscr", u"\U0001D4A6" }, + { u"LJcy", u"\u0409" }, + { u"LT", u"\u003C" }, + { u"Lacute", u"\u0139" }, + { u"Lambda", u"\u039B" }, + { u"Lang", u"\u27EA" }, + { u"Laplacetrf", u"\u2112" }, + { u"Larr", u"\u219E" }, + { u"Lcaron", u"\u013D" }, + { u"Lcedil", u"\u013B" }, + { u"Lcy", u"\u041B" }, + { u"LeftAngleBracket", u"\u27E8" }, + { u"LeftArrow", u"\u2190" }, + { u"LeftArrowBar", u"\u21E4" }, + { u"LeftArrowRightArrow", u"\u21C6" }, + { u"LeftCeiling", u"\u2308" }, + { u"LeftDoubleBracket", u"\u27E6" }, + { u"LeftDownTeeVector", u"\u2961" }, + { u"LeftDownVector", u"\u21C3" }, + { u"LeftDownVectorBar", u"\u2959" }, + { u"LeftFloor", u"\u230A" }, + { u"LeftRightArrow", u"\u2194" }, + { u"LeftRightVector", u"\u294E" }, + { u"LeftTee", u"\u22A3" }, + { u"LeftTeeArrow", u"\u21A4" }, + { u"LeftTeeVector", u"\u295A" }, + { u"LeftTriangle", u"\u22B2" }, + { u"LeftTriangleBar", u"\u29CF" }, + { u"LeftTriangleEqual", u"\u22B4" }, + { u"LeftUpDownVector", u"\u2951" }, + { u"LeftUpTeeVector", u"\u2960" }, + { u"LeftUpVector", u"\u21BF" }, + { u"LeftUpVectorBar", u"\u2958" }, + { u"LeftVector", u"\u21BC" }, + { u"LeftVectorBar", u"\u2952" }, + { u"Leftarrow", u"\u21D0" }, + { u"Leftrightarrow", u"\u21D4" }, + { u"LessEqualGreater", u"\u22DA" }, + { u"LessFullEqual", u"\u2266" }, + { u"LessGreater", u"\u2276" }, + { u"LessLess", u"\u2AA1" }, + { u"LessSlantEqual", u"\u2A7D" }, + { u"LessTilde", u"\u2272" }, + { u"Lfr", u"\U0001D50F" }, + { u"Ll", u"\u22D8" }, + { u"Lleftarrow", u"\u21DA" }, + { u"Lmidot", u"\u013F" }, + { u"LongLeftArrow", u"\u27F5" }, + { u"LongLeftRightArrow", u"\u27F7" }, + { u"LongRightArrow", u"\u27F6" }, + { u"Longleftarrow", u"\u27F8" }, + { u"Longleftrightarrow", u"\u27FA" }, + { u"Longrightarrow", u"\u27F9" }, + { u"Lopf", u"\U0001D543" }, + { u"LowerLeftArrow", u"\u2199" }, + { u"LowerRightArrow", u"\u2198" }, + { u"Lscr", u"\u2112" }, + { u"Lsh", u"\u21B0" }, + { u"Lstrok", u"\u0141" }, + { u"Lt", u"\u226A" }, + { u"Map", u"\u2905" }, + { u"Mcy", u"\u041C" }, + { u"MediumSpace", u"\u205F" }, + { u"Mellintrf", u"\u2133" }, + { u"Mfr", u"\U0001D510" }, + { u"MinusPlus", u"\u2213" }, + { u"Mopf", u"\U0001D544" }, + { u"Mscr", u"\u2133" }, + { u"Mu", u"\u039C" }, + { u"NJcy", u"\u040A" }, + { u"Nacute", u"\u0143" }, + { u"Ncaron", u"\u0147" }, + { u"Ncedil", u"\u0145" }, + { u"Ncy", u"\u041D" }, + { u"NegativeMediumSpace", u"\u200B" }, + { u"NegativeThickSpace", u"\u200B" }, + { u"NegativeThinSpace", u"\u200B" }, + { u"NegativeVeryThinSpace", u"\u200B" }, + { u"NestedGreaterGreater", u"\u226B" }, + { u"NestedLessLess", u"\u226A" }, + { u"NewLine", u"\u000A" }, + { u"Nfr", u"\U0001D511" }, + { u"NoBreak", u"\u2060" }, + { u"NonBreakingSpace", u"\u00A0" }, + { u"Nopf", u"\u2115" }, + { u"Not", u"\u2AEC" }, + { u"NotCongruent", u"\u2262" }, + { u"NotCupCap", u"\u226D" }, + { u"NotDoubleVerticalBar", u"\u2226" }, + { u"NotElement", u"\u2209" }, + { u"NotEqual", u"\u2260" }, + { u"NotEqualTilde", u"\u2242\u0338" }, + { u"NotExists", u"\u2204" }, + { u"NotGreater", u"\u226F" }, + { u"NotGreaterEqual", u"\u2271" }, + { u"NotGreaterFullEqual", u"\u2267\u0338" }, + { u"NotGreaterGreater", u"\u226B\u0338" }, + { u"NotGreaterLess", u"\u2279" }, + { u"NotGreaterSlantEqual", u"\u2A7E\u0338" }, + { u"NotGreaterTilde", u"\u2275" }, + { u"NotHumpDownHump", u"\u224E\u0338" }, + { u"NotHumpEqual", u"\u224F\u0338" }, + { u"NotLeftTriangle", u"\u22EA" }, + { u"NotLeftTriangleBar", u"\u29CF\u0338" }, + { u"NotLeftTriangleEqual", u"\u22EC" }, + { u"NotLess", u"\u226E" }, + { u"NotLessEqual", u"\u2270" }, + { u"NotLessGreater", u"\u2278" }, + { u"NotLessLess", u"\u226A\u0338" }, + { u"NotLessSlantEqual", u"\u2A7D\u0338" }, + { u"NotLessTilde", u"\u2274" }, + { u"NotNestedGreaterGreater", u"\u2AA2\u0338" }, + { u"NotNestedLessLess", u"\u2AA1\u0338" }, + { u"NotPrecedes", u"\u2280" }, + { u"NotPrecedesEqual", u"\u2AAF\u0338" }, + { u"NotPrecedesSlantEqual", u"\u22E0" }, + { u"NotReverseElement", u"\u220C" }, + { u"NotRightTriangle", u"\u22EB" }, + { u"NotRightTriangleBar", u"\u29D0\u0338" }, + { u"NotRightTriangleEqual", u"\u22ED" }, + { u"NotSquareSubset", u"\u228F\u0338" }, + { u"NotSquareSubsetEqual", u"\u22E2" }, + { u"NotSquareSuperset", u"\u2290\u0338" }, + { u"NotSquareSupersetEqual", u"\u22E3" }, + { u"NotSubset", u"\u2282\u20D2" }, + { u"NotSubsetEqual", u"\u2288" }, + { u"NotSucceeds", u"\u2281" }, + { u"NotSucceedsEqual", u"\u2AB0\u0338" }, + { u"NotSucceedsSlantEqual", u"\u22E1" }, + { u"NotSucceedsTilde", u"\u227F\u0338" }, + { u"NotSuperset", u"\u2283\u20D2" }, + { u"NotSupersetEqual", u"\u2289" }, + { u"NotTilde", u"\u2241" }, + { u"NotTildeEqual", u"\u2244" }, + { u"NotTildeFullEqual", u"\u2247" }, + { u"NotTildeTilde", u"\u2249" }, + { u"NotVerticalBar", u"\u2224" }, + { u"Nscr", u"\U0001D4A9" }, + { u"Ntilde", u"\u00D1" }, + { u"Nu", u"\u039D" }, + { u"OElig", u"\u0152" }, + { u"Oacute", u"\u00D3" }, + { u"Ocirc", u"\u00D4" }, + { u"Ocy", u"\u041E" }, + { u"Odblac", u"\u0150" }, + { u"Ofr", u"\U0001D512" }, + { u"Ograve", u"\u00D2" }, + { u"Omacr", u"\u014C" }, + { u"Omega", u"\u03A9" }, + { u"Omicron", u"\u039F" }, + { u"Oopf", u"\U0001D546" }, + { u"OpenCurlyDoubleQuote", u"\u201C" }, + { u"OpenCurlyQuote", u"\u2018" }, + { u"Or", u"\u2A54" }, + { u"Oscr", u"\U0001D4AA" }, + { u"Oslash", u"\u00D8" }, + { u"Otilde", u"\u00D5" }, + { u"Otimes", u"\u2A37" }, + { u"Ouml", u"\u00D6" }, + { u"OverBar", u"\u203E" }, + { u"OverBrace", u"\u23DE" }, + { u"OverBracket", u"\u23B4" }, + { u"OverParenthesis", u"\u23DC" }, + { u"PartialD", u"\u2202" }, + { u"Pcy", u"\u041F" }, + { u"Pfr", u"\U0001D513" }, + { u"Phi", u"\u03A6" }, + { u"Pi", u"\u03A0" }, + { u"PlusMinus", u"\u00B1" }, + { u"Poincareplane", u"\u210C" }, + { u"Popf", u"\u2119" }, + { u"Pr", u"\u2ABB" }, + { u"Precedes", u"\u227A" }, + { u"PrecedesEqual", u"\u2AAF" }, + { u"PrecedesSlantEqual", u"\u227C" }, + { u"PrecedesTilde", u"\u227E" }, + { u"Prime", u"\u2033" }, + { u"Product", u"\u220F" }, + { u"Proportion", u"\u2237" }, + { u"Proportional", u"\u221D" }, + { u"Pscr", u"\U0001D4AB" }, + { u"Psi", u"\u03A8" }, + { u"QUOT", u"\u0022" }, + { u"Qfr", u"\U0001D514" }, + { u"Qopf", u"\u211A" }, + { u"Qscr", u"\U0001D4AC" }, + { u"RBarr", u"\u2910" }, + { u"REG", u"\u00AE" }, + { u"Racute", u"\u0154" }, + { u"Rang", u"\u27EB" }, + { u"Rarr", u"\u21A0" }, + { u"Rarrtl", u"\u2916" }, + { u"Rcaron", u"\u0158" }, + { u"Rcedil", u"\u0156" }, + { u"Rcy", u"\u0420" }, + { u"Re", u"\u211C" }, + { u"ReverseElement", u"\u220B" }, + { u"ReverseEquilibrium", u"\u21CB" }, + { u"ReverseUpEquilibrium", u"\u296F" }, + { u"Rfr", u"\u211C" }, + { u"Rho", u"\u03A1" }, + { u"RightAngleBracket", u"\u27E9" }, + { u"RightArrow", u"\u2192" }, + { u"RightArrowBar", u"\u21E5" }, + { u"RightArrowLeftArrow", u"\u21C4" }, + { u"RightCeiling", u"\u2309" }, + { u"RightDoubleBracket", u"\u27E7" }, + { u"RightDownTeeVector", u"\u295D" }, + { u"RightDownVector", u"\u21C2" }, + { u"RightDownVectorBar", u"\u2955" }, + { u"RightFloor", u"\u230B" }, + { u"RightTee", u"\u22A2" }, + { u"RightTeeArrow", u"\u21A6" }, + { u"RightTeeVector", u"\u295B" }, + { u"RightTriangle", u"\u22B3" }, + { u"RightTriangleBar", u"\u29D0" }, + { u"RightTriangleEqual", u"\u22B5" }, + { u"RightUpDownVector", u"\u294F" }, + { u"RightUpTeeVector", u"\u295C" }, + { u"RightUpVector", u"\u21BE" }, + { u"RightUpVectorBar", u"\u2954" }, + { u"RightVector", u"\u21C0" }, + { u"RightVectorBar", u"\u2953" }, + { u"Rightarrow", u"\u21D2" }, + { u"Ropf", u"\u211D" }, + { u"RoundImplies", u"\u2970" }, + { u"Rrightarrow", u"\u21DB" }, + { u"Rscr", u"\u211B" }, + { u"Rsh", u"\u21B1" }, + { u"RuleDelayed", u"\u29F4" }, + { u"SHCHcy", u"\u0429" }, + { u"SHcy", u"\u0428" }, + { u"SOFTcy", u"\u042C" }, + { u"Sacute", u"\u015A" }, + { u"Sc", u"\u2ABC" }, + { u"Scaron", u"\u0160" }, + { u"Scedil", u"\u015E" }, + { u"Scirc", u"\u015C" }, + { u"Scy", u"\u0421" }, + { u"Sfr", u"\U0001D516" }, + { u"ShortDownArrow", u"\u2193" }, + { u"ShortLeftArrow", u"\u2190" }, + { u"ShortRightArrow", u"\u2192" }, + { u"ShortUpArrow", u"\u2191" }, + { u"Sigma", u"\u03A3" }, + { u"SmallCircle", u"\u2218" }, + { u"Sopf", u"\U0001D54A" }, + { u"Sqrt", u"\u221A" }, + { u"Square", u"\u25A1" }, + { u"SquareIntersection", u"\u2293" }, + { u"SquareSubset", u"\u228F" }, + { u"SquareSubsetEqual", u"\u2291" }, + { u"SquareSuperset", u"\u2290" }, + { u"SquareSupersetEqual", u"\u2292" }, + { u"SquareUnion", u"\u2294" }, + { u"Sscr", u"\U0001D4AE" }, + { u"Star", u"\u22C6" }, + { u"Sub", u"\u22D0" }, + { u"Subset", u"\u22D0" }, + { u"SubsetEqual", u"\u2286" }, + { u"Succeeds", u"\u227B" }, + { u"SucceedsEqual", u"\u2AB0" }, + { u"SucceedsSlantEqual", u"\u227D" }, + { u"SucceedsTilde", u"\u227F" }, + { u"SuchThat", u"\u220B" }, + { u"Sum", u"\u2211" }, + { u"Sup", u"\u22D1" }, + { u"Superset", u"\u2283" }, + { u"SupersetEqual", u"\u2287" }, + { u"Supset", u"\u22D1" }, + { u"THORN", u"\u00DE" }, + { u"TRADE", u"\u2122" }, + { u"TSHcy", u"\u040B" }, + { u"TScy", u"\u0426" }, + { u"Tab", u"\u0009" }, + { u"Tau", u"\u03A4" }, + { u"Tcaron", u"\u0164" }, + { u"Tcedil", u"\u0162" }, + { u"Tcy", u"\u0422" }, + { u"Tfr", u"\U0001D517" }, + { u"Therefore", u"\u2234" }, + { u"Theta", u"\u0398" }, + { u"ThickSpace", u"\u205F\u200A" }, + { u"ThinSpace", u"\u2009" }, + { u"Tilde", u"\u223C" }, + { u"TildeEqual", u"\u2243" }, + { u"TildeFullEqual", u"\u2245" }, + { u"TildeTilde", u"\u2248" }, + { u"Topf", u"\U0001D54B" }, + { u"TripleDot", u"\u20DB" }, + { u"Tscr", u"\U0001D4AF" }, + { u"Tstrok", u"\u0166" }, + { u"Uacute", u"\u00DA" }, + { u"Uarr", u"\u219F" }, + { u"Uarrocir", u"\u2949" }, + { u"Ubrcy", u"\u040E" }, + { u"Ubreve", u"\u016C" }, + { u"Ucirc", u"\u00DB" }, + { u"Ucy", u"\u0423" }, + { u"Udblac", u"\u0170" }, + { u"Ufr", u"\U0001D518" }, + { u"Ugrave", u"\u00D9" }, + { u"Umacr", u"\u016A" }, + { u"UnderBar", u"\u005F" }, + { u"UnderBrace", u"\u23DF" }, + { u"UnderBracket", u"\u23B5" }, + { u"UnderParenthesis", u"\u23DD" }, + { u"Union", u"\u22C3" }, + { u"UnionPlus", u"\u228E" }, + { u"Uogon", u"\u0172" }, + { u"Uopf", u"\U0001D54C" }, + { u"UpArrow", u"\u2191" }, + { u"UpArrowBar", u"\u2912" }, + { u"UpArrowDownArrow", u"\u21C5" }, + { u"UpDownArrow", u"\u2195" }, + { u"UpEquilibrium", u"\u296E" }, + { u"UpTee", u"\u22A5" }, + { u"UpTeeArrow", u"\u21A5" }, + { u"Uparrow", u"\u21D1" }, + { u"Updownarrow", u"\u21D5" }, + { u"UpperLeftArrow", u"\u2196" }, + { u"UpperRightArrow", u"\u2197" }, + { u"Upsi", u"\u03D2" }, + { u"Upsilon", u"\u03A5" }, + { u"Uring", u"\u016E" }, + { u"Uscr", u"\U0001D4B0" }, + { u"Utilde", u"\u0168" }, + { u"Uuml", u"\u00DC" }, + { u"VDash", u"\u22AB" }, + { u"Vbar", u"\u2AEB" }, + { u"Vcy", u"\u0412" }, + { u"Vdash", u"\u22A9" }, + { u"Vdashl", u"\u2AE6" }, + { u"Vee", u"\u22C1" }, + { u"Verbar", u"\u2016" }, + { u"Vert", u"\u2016" }, + { u"VerticalBar", u"\u2223" }, + { u"VerticalLine", u"\u007C" }, + { u"VerticalSeparator", u"\u2758" }, + { u"VerticalTilde", u"\u2240" }, + { u"VeryThinSpace", u"\u200A" }, + { u"Vfr", u"\U0001D519" }, + { u"Vopf", u"\U0001D54D" }, + { u"Vscr", u"\U0001D4B1" }, + { u"Vvdash", u"\u22AA" }, + { u"Wcirc", u"\u0174" }, + { u"Wedge", u"\u22C0" }, + { u"Wfr", u"\U0001D51A" }, + { u"Wopf", u"\U0001D54E" }, + { u"Wscr", u"\U0001D4B2" }, + { u"Xfr", u"\U0001D51B" }, + { u"Xi", u"\u039E" }, + { u"Xopf", u"\U0001D54F" }, + { u"Xscr", u"\U0001D4B3" }, + { u"YAcy", u"\u042F" }, + { u"YIcy", u"\u0407" }, + { u"YUcy", u"\u042E" }, + { u"Yacute", u"\u00DD" }, + { u"Ycirc", u"\u0176" }, + { u"Ycy", u"\u042B" }, + { u"Yfr", u"\U0001D51C" }, + { u"Yopf", u"\U0001D550" }, + { u"Yscr", u"\U0001D4B4" }, + { u"Yuml", u"\u0178" }, + { u"ZHcy", u"\u0416" }, + { u"Zacute", u"\u0179" }, + { u"Zcaron", u"\u017D" }, + { u"Zcy", u"\u0417" }, + { u"Zdot", u"\u017B" }, + { u"ZeroWidthSpace", u"\u200B" }, + { u"Zeta", u"\u0396" }, + { u"Zfr", u"\u2128" }, + { u"Zopf", u"\u2124" }, + { u"Zscr", u"\U0001D4B5" }, + { u"aacute", u"\u00E1" }, + { u"abreve", u"\u0103" }, + { u"ac", u"\u223E" }, + { u"acE", u"\u223E\u0333" }, + { u"acd", u"\u223F" }, + { u"acirc", u"\u00E2" }, + { u"acute", u"\u00B4" }, + { u"acy", u"\u0430" }, + { u"aelig", u"\u00E6" }, + { u"af", u"\u2061" }, + { u"afr", u"\U0001D51E" }, + { u"agrave", u"\u00E0" }, + { u"alefsym", u"\u2135" }, + { u"aleph", u"\u2135" }, + { u"alpha", u"\u03B1" }, + { u"amacr", u"\u0101" }, + { u"amalg", u"\u2A3F" }, + { u"amp", u"\u0026" }, + { u"and", u"\u2227" }, + { u"andand", u"\u2A55" }, + { u"andd", u"\u2A5C" }, + { u"andslope", u"\u2A58" }, + { u"andv", u"\u2A5A" }, + { u"ang", u"\u2220" }, + { u"ange", u"\u29A4" }, + { u"angle", u"\u2220" }, + { u"angmsd", u"\u2221" }, + { u"angmsdaa", u"\u29A8" }, + { u"angmsdab", u"\u29A9" }, + { u"angmsdac", u"\u29AA" }, + { u"angmsdad", u"\u29AB" }, + { u"angmsdae", u"\u29AC" }, + { u"angmsdaf", u"\u29AD" }, + { u"angmsdag", u"\u29AE" }, + { u"angmsdah", u"\u29AF" }, + { u"angrt", u"\u221F" }, + { u"angrtvb", u"\u22BE" }, + { u"angrtvbd", u"\u299D" }, + { u"angsph", u"\u2222" }, + { u"angst", u"\u00C5" }, + { u"angzarr", u"\u237C" }, + { u"aogon", u"\u0105" }, + { u"aopf", u"\U0001D552" }, + { u"ap", u"\u2248" }, + { u"apE", u"\u2A70" }, + { u"apacir", u"\u2A6F" }, + { u"ape", u"\u224A" }, + { u"apid", u"\u224B" }, + { u"apos", u"\u0027" }, + { u"approx", u"\u2248" }, + { u"approxeq", u"\u224A" }, + { u"aring", u"\u00E5" }, + { u"ascr", u"\U0001D4B6" }, + { u"ast", u"\u002A" }, + { u"asymp", u"\u2248" }, + { u"asympeq", u"\u224D" }, + { u"atilde", u"\u00E3" }, + { u"auml", u"\u00E4" }, + { u"awconint", u"\u2233" }, + { u"awint", u"\u2A11" }, + { u"bNot", u"\u2AED" }, + { u"backcong", u"\u224C" }, + { u"backepsilon", u"\u03F6" }, + { u"backprime", u"\u2035" }, + { u"backsim", u"\u223D" }, + { u"backsimeq", u"\u22CD" }, + { u"barvee", u"\u22BD" }, + { u"barwed", u"\u2305" }, + { u"barwedge", u"\u2305" }, + { u"bbrk", u"\u23B5" }, + { u"bbrktbrk", u"\u23B6" }, + { u"bcong", u"\u224C" }, + { u"bcy", u"\u0431" }, + { u"bdquo", u"\u201E" }, + { u"becaus", u"\u2235" }, + { u"because", u"\u2235" }, + { u"bemptyv", u"\u29B0" }, + { u"bepsi", u"\u03F6" }, + { u"bernou", u"\u212C" }, + { u"beta", u"\u03B2" }, + { u"beth", u"\u2136" }, + { u"between", u"\u226C" }, + { u"bfr", u"\U0001D51F" }, + { u"bigcap", u"\u22C2" }, + { u"bigcirc", u"\u25EF" }, + { u"bigcup", u"\u22C3" }, + { u"bigodot", u"\u2A00" }, + { u"bigoplus", u"\u2A01" }, + { u"bigotimes", u"\u2A02" }, + { u"bigsqcup", u"\u2A06" }, + { u"bigstar", u"\u2605" }, + { u"bigtriangledown", u"\u25BD" }, + { u"bigtriangleup", u"\u25B3" }, + { u"biguplus", u"\u2A04" }, + { u"bigvee", u"\u22C1" }, + { u"bigwedge", u"\u22C0" }, + { u"bkarow", u"\u290D" }, + { u"blacklozenge", u"\u29EB" }, + { u"blacksquare", u"\u25AA" }, + { u"blacktriangle", u"\u25B4" }, + { u"blacktriangledown", u"\u25BE" }, + { u"blacktriangleleft", u"\u25C2" }, + { u"blacktriangleright", u"\u25B8" }, + { u"blank", u"\u2423" }, + { u"blk12", u"\u2592" }, + { u"blk14", u"\u2591" }, + { u"blk34", u"\u2593" }, + { u"block", u"\u2588" }, + { u"bne", u"\u003D\u20E5" }, + { u"bnequiv", u"\u2261\u20E5" }, + { u"bnot", u"\u2310" }, + { u"bopf", u"\U0001D553" }, + { u"bot", u"\u22A5" }, + { u"bottom", u"\u22A5" }, + { u"bowtie", u"\u22C8" }, + { u"boxDL", u"\u2557" }, + { u"boxDR", u"\u2554" }, + { u"boxDl", u"\u2556" }, + { u"boxDr", u"\u2553" }, + { u"boxH", u"\u2550" }, + { u"boxHD", u"\u2566" }, + { u"boxHU", u"\u2569" }, + { u"boxHd", u"\u2564" }, + { u"boxHu", u"\u2567" }, + { u"boxUL", u"\u255D" }, + { u"boxUR", u"\u255A" }, + { u"boxUl", u"\u255C" }, + { u"boxUr", u"\u2559" }, + { u"boxV", u"\u2551" }, + { u"boxVH", u"\u256C" }, + { u"boxVL", u"\u2563" }, + { u"boxVR", u"\u2560" }, + { u"boxVh", u"\u256B" }, + { u"boxVl", u"\u2562" }, + { u"boxVr", u"\u255F" }, + { u"boxbox", u"\u29C9" }, + { u"boxdL", u"\u2555" }, + { u"boxdR", u"\u2552" }, + { u"boxdl", u"\u2510" }, + { u"boxdr", u"\u250C" }, + { u"boxh", u"\u2500" }, + { u"boxhD", u"\u2565" }, + { u"boxhU", u"\u2568" }, + { u"boxhd", u"\u252C" }, + { u"boxhu", u"\u2534" }, + { u"boxminus", u"\u229F" }, + { u"boxplus", u"\u229E" }, + { u"boxtimes", u"\u22A0" }, + { u"boxuL", u"\u255B" }, + { u"boxuR", u"\u2558" }, + { u"boxul", u"\u2518" }, + { u"boxur", u"\u2514" }, + { u"boxv", u"\u2502" }, + { u"boxvH", u"\u256A" }, + { u"boxvL", u"\u2561" }, + { u"boxvR", u"\u255E" }, + { u"boxvh", u"\u253C" }, + { u"boxvl", u"\u2524" }, + { u"boxvr", u"\u251C" }, + { u"bprime", u"\u2035" }, + { u"breve", u"\u02D8" }, + { u"brvbar", u"\u00A6" }, + { u"bscr", u"\U0001D4B7" }, + { u"bsemi", u"\u204F" }, + { u"bsim", u"\u223D" }, + { u"bsime", u"\u22CD" }, + { u"bsol", u"\u005C" }, + { u"bsolb", u"\u29C5" }, + { u"bsolhsub", u"\u27C8" }, + { u"bull", u"\u2022" }, + { u"bullet", u"\u2022" }, + { u"bump", u"\u224E" }, + { u"bumpE", u"\u2AAE" }, + { u"bumpe", u"\u224F" }, + { u"bumpeq", u"\u224F" }, + { u"cacute", u"\u0107" }, + { u"cap", u"\u2229" }, + { u"capand", u"\u2A44" }, + { u"capbrcup", u"\u2A49" }, + { u"capcap", u"\u2A4B" }, + { u"capcup", u"\u2A47" }, + { u"capdot", u"\u2A40" }, + { u"caps", u"\u2229\uFE00" }, + { u"caret", u"\u2041" }, + { u"caron", u"\u02C7" }, + { u"ccaps", u"\u2A4D" }, + { u"ccaron", u"\u010D" }, + { u"ccedil", u"\u00E7" }, + { u"ccirc", u"\u0109" }, + { u"ccups", u"\u2A4C" }, + { u"ccupssm", u"\u2A50" }, + { u"cdot", u"\u010B" }, + { u"cedil", u"\u00B8" }, + { u"cemptyv", u"\u29B2" }, + { u"cent", u"\u00A2" }, + { u"centerdot", u"\u00B7" }, + { u"cfr", u"\U0001D520" }, + { u"chcy", u"\u0447" }, + { u"check", u"\u2713" }, + { u"checkmark", u"\u2713" }, + { u"chi", u"\u03C7" }, + { u"cir", u"\u25CB" }, + { u"cirE", u"\u29C3" }, + { u"circ", u"\u02C6" }, + { u"circeq", u"\u2257" }, + { u"circlearrowleft", u"\u21BA" }, + { u"circlearrowright", u"\u21BB" }, + { u"circledR", u"\u00AE" }, + { u"circledS", u"\u24C8" }, + { u"circledast", u"\u229B" }, + { u"circledcirc", u"\u229A" }, + { u"circleddash", u"\u229D" }, + { u"cire", u"\u2257" }, + { u"cirfnint", u"\u2A10" }, + { u"cirmid", u"\u2AEF" }, + { u"cirscir", u"\u29C2" }, + { u"clubs", u"\u2663" }, + { u"clubsuit", u"\u2663" }, + { u"colon", u"\u003A" }, + { u"colone", u"\u2254" }, + { u"coloneq", u"\u2254" }, + { u"comma", u"\u002C" }, + { u"commat", u"\u0040" }, + { u"comp", u"\u2201" }, + { u"compfn", u"\u2218" }, + { u"complement", u"\u2201" }, + { u"complexes", u"\u2102" }, + { u"cong", u"\u2245" }, + { u"congdot", u"\u2A6D" }, + { u"conint", u"\u222E" }, + { u"copf", u"\U0001D554" }, + { u"coprod", u"\u2210" }, + { u"copy", u"\u00A9" }, + { u"copysr", u"\u2117" }, + { u"crarr", u"\u21B5" }, + { u"cross", u"\u2717" }, + { u"cscr", u"\U0001D4B8" }, + { u"csub", u"\u2ACF" }, + { u"csube", u"\u2AD1" }, + { u"csup", u"\u2AD0" }, + { u"csupe", u"\u2AD2" }, + { u"ctdot", u"\u22EF" }, + { u"cudarrl", u"\u2938" }, + { u"cudarrr", u"\u2935" }, + { u"cuepr", u"\u22DE" }, + { u"cuesc", u"\u22DF" }, + { u"cularr", u"\u21B6" }, + { u"cularrp", u"\u293D" }, + { u"cup", u"\u222A" }, + { u"cupbrcap", u"\u2A48" }, + { u"cupcap", u"\u2A46" }, + { u"cupcup", u"\u2A4A" }, + { u"cupdot", u"\u228D" }, + { u"cupor", u"\u2A45" }, + { u"cups", u"\u222A\uFE00" }, + { u"curarr", u"\u21B7" }, + { u"curarrm", u"\u293C" }, + { u"curlyeqprec", u"\u22DE" }, + { u"curlyeqsucc", u"\u22DF" }, + { u"curlyvee", u"\u22CE" }, + { u"curlywedge", u"\u22CF" }, + { u"curren", u"\u00A4" }, + { u"curvearrowleft", u"\u21B6" }, + { u"curvearrowright", u"\u21B7" }, + { u"cuvee", u"\u22CE" }, + { u"cuwed", u"\u22CF" }, + { u"cwconint", u"\u2232" }, + { u"cwint", u"\u2231" }, + { u"cylcty", u"\u232D" }, + { u"dArr", u"\u21D3" }, + { u"dHar", u"\u2965" }, + { u"dagger", u"\u2020" }, + { u"daleth", u"\u2138" }, + { u"darr", u"\u2193" }, + { u"dash", u"\u2010" }, + { u"dashv", u"\u22A3" }, + { u"dbkarow", u"\u290F" }, + { u"dblac", u"\u02DD" }, + { u"dcaron", u"\u010F" }, + { u"dcy", u"\u0434" }, + { u"dd", u"\u2146" }, + { u"ddagger", u"\u2021" }, + { u"ddarr", u"\u21CA" }, + { u"ddotseq", u"\u2A77" }, + { u"deg", u"\u00B0" }, + { u"delta", u"\u03B4" }, + { u"demptyv", u"\u29B1" }, + { u"dfisht", u"\u297F" }, + { u"dfr", u"\U0001D521" }, + { u"dharl", u"\u21C3" }, + { u"dharr", u"\u21C2" }, + { u"diam", u"\u22C4" }, + { u"diamond", u"\u22C4" }, + { u"diamondsuit", u"\u2666" }, + { u"diams", u"\u2666" }, + { u"die", u"\u00A8" }, + { u"digamma", u"\u03DD" }, + { u"disin", u"\u22F2" }, + { u"div", u"\u00F7" }, + { u"divide", u"\u00F7" }, + { u"divideontimes", u"\u22C7" }, + { u"divonx", u"\u22C7" }, + { u"djcy", u"\u0452" }, + { u"dlcorn", u"\u231E" }, + { u"dlcrop", u"\u230D" }, + { u"dollar", u"\u0024" }, + { u"dopf", u"\U0001D555" }, + { u"dot", u"\u02D9" }, + { u"doteq", u"\u2250" }, + { u"doteqdot", u"\u2251" }, + { u"dotminus", u"\u2238" }, + { u"dotplus", u"\u2214" }, + { u"dotsquare", u"\u22A1" }, + { u"doublebarwedge", u"\u2306" }, + { u"downarrow", u"\u2193" }, + { u"downdownarrows", u"\u21CA" }, + { u"downharpoonleft", u"\u21C3" }, + { u"downharpoonright", u"\u21C2" }, + { u"drbkarow", u"\u2910" }, + { u"drcorn", u"\u231F" }, + { u"drcrop", u"\u230C" }, + { u"dscr", u"\U0001D4B9" }, + { u"dscy", u"\u0455" }, + { u"dsol", u"\u29F6" }, + { u"dstrok", u"\u0111" }, + { u"dtdot", u"\u22F1" }, + { u"dtri", u"\u25BF" }, + { u"dtrif", u"\u25BE" }, + { u"duarr", u"\u21F5" }, + { u"duhar", u"\u296F" }, + { u"dwangle", u"\u29A6" }, + { u"dzcy", u"\u045F" }, + { u"dzigrarr", u"\u27FF" }, + { u"eDDot", u"\u2A77" }, + { u"eDot", u"\u2251" }, + { u"eacute", u"\u00E9" }, + { u"easter", u"\u2A6E" }, + { u"ecaron", u"\u011B" }, + { u"ecir", u"\u2256" }, + { u"ecirc", u"\u00EA" }, + { u"ecolon", u"\u2255" }, + { u"ecy", u"\u044D" }, + { u"edot", u"\u0117" }, + { u"ee", u"\u2147" }, + { u"efDot", u"\u2252" }, + { u"efr", u"\U0001D522" }, + { u"eg", u"\u2A9A" }, + { u"egrave", u"\u00E8" }, + { u"egs", u"\u2A96" }, + { u"egsdot", u"\u2A98" }, + { u"el", u"\u2A99" }, + { u"elinters", u"\u23E7" }, + { u"ell", u"\u2113" }, + { u"els", u"\u2A95" }, + { u"elsdot", u"\u2A97" }, + { u"emacr", u"\u0113" }, + { u"empty", u"\u2205" }, + { u"emptyset", u"\u2205" }, + { u"emptyv", u"\u2205" }, + { u"emsp", u"\u2003" }, + { u"emsp13", u"\u2004" }, + { u"emsp14", u"\u2005" }, + { u"eng", u"\u014B" }, + { u"ensp", u"\u2002" }, + { u"eogon", u"\u0119" }, + { u"eopf", u"\U0001D556" }, + { u"epar", u"\u22D5" }, + { u"eparsl", u"\u29E3" }, + { u"eplus", u"\u2A71" }, + { u"epsi", u"\u03B5" }, + { u"epsilon", u"\u03B5" }, + { u"epsiv", u"\u03F5" }, + { u"eqcirc", u"\u2256" }, + { u"eqcolon", u"\u2255" }, + { u"eqsim", u"\u2242" }, + { u"eqslantgtr", u"\u2A96" }, + { u"eqslantless", u"\u2A95" }, + { u"equals", u"\u003D" }, + { u"equest", u"\u225F" }, + { u"equiv", u"\u2261" }, + { u"equivDD", u"\u2A78" }, + { u"eqvparsl", u"\u29E5" }, + { u"erDot", u"\u2253" }, + { u"erarr", u"\u2971" }, + { u"escr", u"\u212F" }, + { u"esdot", u"\u2250" }, + { u"esim", u"\u2242" }, + { u"eta", u"\u03B7" }, + { u"eth", u"\u00F0" }, + { u"euml", u"\u00EB" }, + { u"euro", u"\u20AC" }, + { u"excl", u"\u0021" }, + { u"exist", u"\u2203" }, + { u"expectation", u"\u2130" }, + { u"exponentiale", u"\u2147" }, + { u"fallingdotseq", u"\u2252" }, + { u"fcy", u"\u0444" }, + { u"female", u"\u2640" }, + { u"ffilig", u"\uFB03" }, + { u"fflig", u"\uFB00" }, + { u"ffllig", u"\uFB04" }, + { u"ffr", u"\U0001D523" }, + { u"filig", u"\uFB01" }, + { u"fjlig", u"\u0066\u006A" }, + { u"flat", u"\u266D" }, + { u"fllig", u"\uFB02" }, + { u"fltns", u"\u25B1" }, + { u"fnof", u"\u0192" }, + { u"fopf", u"\U0001D557" }, + { u"forall", u"\u2200" }, + { u"fork", u"\u22D4" }, + { u"forkv", u"\u2AD9" }, + { u"fpartint", u"\u2A0D" }, + { u"frac12", u"\u00BD" }, + { u"frac13", u"\u2153" }, + { u"frac14", u"\u00BC" }, + { u"frac15", u"\u2155" }, + { u"frac16", u"\u2159" }, + { u"frac18", u"\u215B" }, + { u"frac23", u"\u2154" }, + { u"frac25", u"\u2156" }, + { u"frac34", u"\u00BE" }, + { u"frac35", u"\u2157" }, + { u"frac38", u"\u215C" }, + { u"frac45", u"\u2158" }, + { u"frac56", u"\u215A" }, + { u"frac58", u"\u215D" }, + { u"frac78", u"\u215E" }, + { u"frasl", u"\u2044" }, + { u"frown", u"\u2322" }, + { u"fscr", u"\U0001D4BB" }, + { u"gE", u"\u2267" }, + { u"gEl", u"\u2A8C" }, + { u"gacute", u"\u01F5" }, + { u"gamma", u"\u03B3" }, + { u"gammad", u"\u03DD" }, + { u"gap", u"\u2A86" }, + { u"gbreve", u"\u011F" }, + { u"gcirc", u"\u011D" }, + { u"gcy", u"\u0433" }, + { u"gdot", u"\u0121" }, + { u"ge", u"\u2265" }, + { u"gel", u"\u22DB" }, + { u"geq", u"\u2265" }, + { u"geqq", u"\u2267" }, + { u"geqslant", u"\u2A7E" }, + { u"ges", u"\u2A7E" }, + { u"gescc", u"\u2AA9" }, + { u"gesdot", u"\u2A80" }, + { u"gesdoto", u"\u2A82" }, + { u"gesdotol", u"\u2A84" }, + { u"gesl", u"\u22DB\uFE00" }, + { u"gesles", u"\u2A94" }, + { u"gfr", u"\U0001D524" }, + { u"gg", u"\u226B" }, + { u"ggg", u"\u22D9" }, + { u"gimel", u"\u2137" }, + { u"gjcy", u"\u0453" }, + { u"gl", u"\u2277" }, + { u"glE", u"\u2A92" }, + { u"gla", u"\u2AA5" }, + { u"glj", u"\u2AA4" }, + { u"gnE", u"\u2269" }, + { u"gnap", u"\u2A8A" }, + { u"gnapprox", u"\u2A8A" }, + { u"gne", u"\u2A88" }, + { u"gneq", u"\u2A88" }, + { u"gneqq", u"\u2269" }, + { u"gnsim", u"\u22E7" }, + { u"gopf", u"\U0001D558" }, + { u"grave", u"\u0060" }, + { u"gscr", u"\u210A" }, + { u"gsim", u"\u2273" }, + { u"gsime", u"\u2A8E" }, + { u"gsiml", u"\u2A90" }, + { u"gt", u"\u003E" }, + { u"gtcc", u"\u2AA7" }, + { u"gtcir", u"\u2A7A" }, + { u"gtdot", u"\u22D7" }, + { u"gtlPar", u"\u2995" }, + { u"gtquest", u"\u2A7C" }, + { u"gtrapprox", u"\u2A86" }, + { u"gtrarr", u"\u2978" }, + { u"gtrdot", u"\u22D7" }, + { u"gtreqless", u"\u22DB" }, + { u"gtreqqless", u"\u2A8C" }, + { u"gtrless", u"\u2277" }, + { u"gtrsim", u"\u2273" }, + { u"gvertneqq", u"\u2269\uFE00" }, + { u"gvnE", u"\u2269\uFE00" }, + { u"hArr", u"\u21D4" }, + { u"hairsp", u"\u200A" }, + { u"half", u"\u00BD" }, + { u"hamilt", u"\u210B" }, + { u"hardcy", u"\u044A" }, + { u"harr", u"\u2194" }, + { u"harrcir", u"\u2948" }, + { u"harrw", u"\u21AD" }, + { u"hbar", u"\u210F" }, + { u"hcirc", u"\u0125" }, + { u"hearts", u"\u2665" }, + { u"heartsuit", u"\u2665" }, + { u"hellip", u"\u2026" }, + { u"hercon", u"\u22B9" }, + { u"hfr", u"\U0001D525" }, + { u"hksearow", u"\u2925" }, + { u"hkswarow", u"\u2926" }, + { u"hoarr", u"\u21FF" }, + { u"homtht", u"\u223B" }, + { u"hookleftarrow", u"\u21A9" }, + { u"hookrightarrow", u"\u21AA" }, + { u"hopf", u"\U0001D559" }, + { u"horbar", u"\u2015" }, + { u"hscr", u"\U0001D4BD" }, + { u"hslash", u"\u210F" }, + { u"hstrok", u"\u0127" }, + { u"hybull", u"\u2043" }, + { u"hyphen", u"\u2010" }, + { u"iacute", u"\u00ED" }, + { u"ic", u"\u2063" }, + { u"icirc", u"\u00EE" }, + { u"icy", u"\u0438" }, + { u"iecy", u"\u0435" }, + { u"iexcl", u"\u00A1" }, + { u"iff", u"\u21D4" }, + { u"ifr", u"\U0001D526" }, + { u"igrave", u"\u00EC" }, + { u"ii", u"\u2148" }, + { u"iiiint", u"\u2A0C" }, + { u"iiint", u"\u222D" }, + { u"iinfin", u"\u29DC" }, + { u"iiota", u"\u2129" }, + { u"ijlig", u"\u0133" }, + { u"imacr", u"\u012B" }, + { u"image", u"\u2111" }, + { u"imagline", u"\u2110" }, + { u"imagpart", u"\u2111" }, + { u"imath", u"\u0131" }, + { u"imof", u"\u22B7" }, + { u"imped", u"\u01B5" }, + { u"in", u"\u2208" }, + { u"incare", u"\u2105" }, + { u"infin", u"\u221E" }, + { u"infintie", u"\u29DD" }, + { u"inodot", u"\u0131" }, + { u"int", u"\u222B" }, + { u"intcal", u"\u22BA" }, + { u"integers", u"\u2124" }, + { u"intercal", u"\u22BA" }, + { u"intlarhk", u"\u2A17" }, + { u"intprod", u"\u2A3C" }, + { u"iocy", u"\u0451" }, + { u"iogon", u"\u012F" }, + { u"iopf", u"\U0001D55A" }, + { u"iota", u"\u03B9" }, + { u"iprod", u"\u2A3C" }, + { u"iquest", u"\u00BF" }, + { u"iscr", u"\U0001D4BE" }, + { u"isin", u"\u2208" }, + { u"isinE", u"\u22F9" }, + { u"isindot", u"\u22F5" }, + { u"isins", u"\u22F4" }, + { u"isinsv", u"\u22F3" }, + { u"isinv", u"\u2208" }, + { u"it", u"\u2062" }, + { u"itilde", u"\u0129" }, + { u"iukcy", u"\u0456" }, + { u"iuml", u"\u00EF" }, + { u"jcirc", u"\u0135" }, + { u"jcy", u"\u0439" }, + { u"jfr", u"\U0001D527" }, + { u"jmath", u"\u0237" }, + { u"jopf", u"\U0001D55B" }, + { u"jscr", u"\U0001D4BF" }, + { u"jsercy", u"\u0458" }, + { u"jukcy", u"\u0454" }, + { u"kappa", u"\u03BA" }, + { u"kappav", u"\u03F0" }, + { u"kcedil", u"\u0137" }, + { u"kcy", u"\u043A" }, + { u"kfr", u"\U0001D528" }, + { u"kgreen", u"\u0138" }, + { u"khcy", u"\u0445" }, + { u"kjcy", u"\u045C" }, + { u"kopf", u"\U0001D55C" }, + { u"kscr", u"\U0001D4C0" }, + { u"lAarr", u"\u21DA" }, + { u"lArr", u"\u21D0" }, + { u"lAtail", u"\u291B" }, + { u"lBarr", u"\u290E" }, + { u"lE", u"\u2266" }, + { u"lEg", u"\u2A8B" }, + { u"lHar", u"\u2962" }, + { u"lacute", u"\u013A" }, + { u"laemptyv", u"\u29B4" }, + { u"lagran", u"\u2112" }, + { u"lambda", u"\u03BB" }, + { u"lang", u"\u27E8" }, + { u"langd", u"\u2991" }, + { u"langle", u"\u27E8" }, + { u"lap", u"\u2A85" }, + { u"laquo", u"\u00AB" }, + { u"larr", u"\u2190" }, + { u"larrb", u"\u21E4" }, + { u"larrbfs", u"\u291F" }, + { u"larrfs", u"\u291D" }, + { u"larrhk", u"\u21A9" }, + { u"larrlp", u"\u21AB" }, + { u"larrpl", u"\u2939" }, + { u"larrsim", u"\u2973" }, + { u"larrtl", u"\u21A2" }, + { u"lat", u"\u2AAB" }, + { u"latail", u"\u2919" }, + { u"late", u"\u2AAD" }, + { u"lates", u"\u2AAD\uFE00" }, + { u"lbarr", u"\u290C" }, + { u"lbbrk", u"\u2772" }, + { u"lbrace", u"\u007B" }, + { u"lbrack", u"\u005B" }, + { u"lbrke", u"\u298B" }, + { u"lbrksld", u"\u298F" }, + { u"lbrkslu", u"\u298D" }, + { u"lcaron", u"\u013E" }, + { u"lcedil", u"\u013C" }, + { u"lceil", u"\u2308" }, + { u"lcub", u"\u007B" }, + { u"lcy", u"\u043B" }, + { u"ldca", u"\u2936" }, + { u"ldquo", u"\u201C" }, + { u"ldquor", u"\u201E" }, + { u"ldrdhar", u"\u2967" }, + { u"ldrushar", u"\u294B" }, + { u"ldsh", u"\u21B2" }, + { u"le", u"\u2264" }, + { u"leftarrow", u"\u2190" }, + { u"leftarrowtail", u"\u21A2" }, + { u"leftharpoondown", u"\u21BD" }, + { u"leftharpoonup", u"\u21BC" }, + { u"leftleftarrows", u"\u21C7" }, + { u"leftrightarrow", u"\u2194" }, + { u"leftrightarrows", u"\u21C6" }, + { u"leftrightharpoons", u"\u21CB" }, + { u"leftrightsquigarrow", u"\u21AD" }, + { u"leftthreetimes", u"\u22CB" }, + { u"leg", u"\u22DA" }, + { u"leq", u"\u2264" }, + { u"leqq", u"\u2266" }, + { u"leqslant", u"\u2A7D" }, + { u"les", u"\u2A7D" }, + { u"lescc", u"\u2AA8" }, + { u"lesdot", u"\u2A7F" }, + { u"lesdoto", u"\u2A81" }, + { u"lesdotor", u"\u2A83" }, + { u"lesg", u"\u22DA\uFE00" }, + { u"lesges", u"\u2A93" }, + { u"lessapprox", u"\u2A85" }, + { u"lessdot", u"\u22D6" }, + { u"lesseqgtr", u"\u22DA" }, + { u"lesseqqgtr", u"\u2A8B" }, + { u"lessgtr", u"\u2276" }, + { u"lesssim", u"\u2272" }, + { u"lfisht", u"\u297C" }, + { u"lfloor", u"\u230A" }, + { u"lfr", u"\U0001D529" }, + { u"lg", u"\u2276" }, + { u"lgE", u"\u2A91" }, + { u"lhard", u"\u21BD" }, + { u"lharu", u"\u21BC" }, + { u"lharul", u"\u296A" }, + { u"lhblk", u"\u2584" }, + { u"ljcy", u"\u0459" }, + { u"ll", u"\u226A" }, + { u"llarr", u"\u21C7" }, + { u"llcorner", u"\u231E" }, + { u"llhard", u"\u296B" }, + { u"lltri", u"\u25FA" }, + { u"lmidot", u"\u0140" }, + { u"lmoust", u"\u23B0" }, + { u"lmoustache", u"\u23B0" }, + { u"lnE", u"\u2268" }, + { u"lnap", u"\u2A89" }, + { u"lnapprox", u"\u2A89" }, + { u"lne", u"\u2A87" }, + { u"lneq", u"\u2A87" }, + { u"lneqq", u"\u2268" }, + { u"lnsim", u"\u22E6" }, + { u"loang", u"\u27EC" }, + { u"loarr", u"\u21FD" }, + { u"lobrk", u"\u27E6" }, + { u"longleftarrow", u"\u27F5" }, + { u"longleftrightarrow", u"\u27F7" }, + { u"longmapsto", u"\u27FC" }, + { u"longrightarrow", u"\u27F6" }, + { u"looparrowleft", u"\u21AB" }, + { u"looparrowright", u"\u21AC" }, + { u"lopar", u"\u2985" }, + { u"lopf", u"\U0001D55D" }, + { u"loplus", u"\u2A2D" }, + { u"lotimes", u"\u2A34" }, + { u"lowast", u"\u2217" }, + { u"lowbar", u"\u005F" }, + { u"loz", u"\u25CA" }, + { u"lozenge", u"\u25CA" }, + { u"lozf", u"\u29EB" }, + { u"lpar", u"\u0028" }, + { u"lparlt", u"\u2993" }, + { u"lrarr", u"\u21C6" }, + { u"lrcorner", u"\u231F" }, + { u"lrhar", u"\u21CB" }, + { u"lrhard", u"\u296D" }, + { u"lrm", u"\u200E" }, + { u"lrtri", u"\u22BF" }, + { u"lsaquo", u"\u2039" }, + { u"lscr", u"\U0001D4C1" }, + { u"lsh", u"\u21B0" }, + { u"lsim", u"\u2272" }, + { u"lsime", u"\u2A8D" }, + { u"lsimg", u"\u2A8F" }, + { u"lsqb", u"\u005B" }, + { u"lsquo", u"\u2018" }, + { u"lsquor", u"\u201A" }, + { u"lstrok", u"\u0142" }, + { u"lt", u"\u003C" }, + { u"ltcc", u"\u2AA6" }, + { u"ltcir", u"\u2A79" }, + { u"ltdot", u"\u22D6" }, + { u"lthree", u"\u22CB" }, + { u"ltimes", u"\u22C9" }, + { u"ltlarr", u"\u2976" }, + { u"ltquest", u"\u2A7B" }, + { u"ltrPar", u"\u2996" }, + { u"ltri", u"\u25C3" }, + { u"ltrie", u"\u22B4" }, + { u"ltrif", u"\u25C2" }, + { u"lurdshar", u"\u294A" }, + { u"luruhar", u"\u2966" }, + { u"lvertneqq", u"\u2268\uFE00" }, + { u"lvnE", u"\u2268\uFE00" }, + { u"mDDot", u"\u223A" }, + { u"macr", u"\u00AF" }, + { u"male", u"\u2642" }, + { u"malt", u"\u2720" }, + { u"maltese", u"\u2720" }, + { u"map", u"\u21A6" }, + { u"mapsto", u"\u21A6" }, + { u"mapstodown", u"\u21A7" }, + { u"mapstoleft", u"\u21A4" }, + { u"mapstoup", u"\u21A5" }, + { u"marker", u"\u25AE" }, + { u"mcomma", u"\u2A29" }, + { u"mcy", u"\u043C" }, + { u"mdash", u"\u2014" }, + { u"measuredangle", u"\u2221" }, + { u"mfr", u"\U0001D52A" }, + { u"mho", u"\u2127" }, + { u"micro", u"\u00B5" }, + { u"mid", u"\u2223" }, + { u"midast", u"\u002A" }, + { u"midcir", u"\u2AF0" }, + { u"middot", u"\u00B7" }, + { u"minus", u"\u2212" }, + { u"minusb", u"\u229F" }, + { u"minusd", u"\u2238" }, + { u"minusdu", u"\u2A2A" }, + { u"mlcp", u"\u2ADB" }, + { u"mldr", u"\u2026" }, + { u"mnplus", u"\u2213" }, + { u"models", u"\u22A7" }, + { u"mopf", u"\U0001D55E" }, + { u"mp", u"\u2213" }, + { u"mscr", u"\U0001D4C2" }, + { u"mstpos", u"\u223E" }, + { u"mu", u"\u03BC" }, + { u"multimap", u"\u22B8" }, + { u"mumap", u"\u22B8" }, + { u"nGg", u"\u22D9\u0338" }, + { u"nGt", u"\u226B\u20D2" }, + { u"nGtv", u"\u226B\u0338" }, + { u"nLeftarrow", u"\u21CD" }, + { u"nLeftrightarrow", u"\u21CE" }, + { u"nLl", u"\u22D8\u0338" }, + { u"nLt", u"\u226A\u20D2" }, + { u"nLtv", u"\u226A\u0338" }, + { u"nRightarrow", u"\u21CF" }, + { u"nVDash", u"\u22AF" }, + { u"nVdash", u"\u22AE" }, + { u"nabla", u"\u2207" }, + { u"nacute", u"\u0144" }, + { u"nang", u"\u2220\u20D2" }, + { u"nap", u"\u2249" }, + { u"napE", u"\u2A70\u0338" }, + { u"napid", u"\u224B\u0338" }, + { u"napos", u"\u0149" }, + { u"napprox", u"\u2249" }, + { u"natur", u"\u266E" }, + { u"natural", u"\u266E" }, + { u"naturals", u"\u2115" }, + { u"nbsp", u"\u00A0" }, + { u"nbump", u"\u224E\u0338" }, + { u"nbumpe", u"\u224F\u0338" }, + { u"ncap", u"\u2A43" }, + { u"ncaron", u"\u0148" }, + { u"ncedil", u"\u0146" }, + { u"ncong", u"\u2247" }, + { u"ncongdot", u"\u2A6D\u0338" }, + { u"ncup", u"\u2A42" }, + { u"ncy", u"\u043D" }, + { u"ndash", u"\u2013" }, + { u"ne", u"\u2260" }, + { u"neArr", u"\u21D7" }, + { u"nearhk", u"\u2924" }, + { u"nearr", u"\u2197" }, + { u"nearrow", u"\u2197" }, + { u"nedot", u"\u2250\u0338" }, + { u"nequiv", u"\u2262" }, + { u"nesear", u"\u2928" }, + { u"nesim", u"\u2242\u0338" }, + { u"nexist", u"\u2204" }, + { u"nexists", u"\u2204" }, + { u"nfr", u"\U0001D52B" }, + { u"ngE", u"\u2267\u0338" }, + { u"nge", u"\u2271" }, + { u"ngeq", u"\u2271" }, + { u"ngeqq", u"\u2267\u0338" }, + { u"ngeqslant", u"\u2A7E\u0338" }, + { u"nges", u"\u2A7E\u0338" }, + { u"ngsim", u"\u2275" }, + { u"ngt", u"\u226F" }, + { u"ngtr", u"\u226F" }, + { u"nhArr", u"\u21CE" }, + { u"nharr", u"\u21AE" }, + { u"nhpar", u"\u2AF2" }, + { u"ni", u"\u220B" }, + { u"nis", u"\u22FC" }, + { u"nisd", u"\u22FA" }, + { u"niv", u"\u220B" }, + { u"njcy", u"\u045A" }, + { u"nlArr", u"\u21CD" }, + { u"nlE", u"\u2266\u0338" }, + { u"nlarr", u"\u219A" }, + { u"nldr", u"\u2025" }, + { u"nle", u"\u2270" }, + { u"nleftarrow", u"\u219A" }, + { u"nleftrightarrow", u"\u21AE" }, + { u"nleq", u"\u2270" }, + { u"nleqq", u"\u2266\u0338" }, + { u"nleqslant", u"\u2A7D\u0338" }, + { u"nles", u"\u2A7D\u0338" }, + { u"nless", u"\u226E" }, + { u"nlsim", u"\u2274" }, + { u"nlt", u"\u226E" }, + { u"nltri", u"\u22EA" }, + { u"nltrie", u"\u22EC" }, + { u"nmid", u"\u2224" }, + { u"nopf", u"\U0001D55F" }, + { u"not", u"\u00AC" }, + { u"notin", u"\u2209" }, + { u"notinE", u"\u22F9\u0338" }, + { u"notindot", u"\u22F5\u0338" }, + { u"notinva", u"\u2209" }, + { u"notinvb", u"\u22F7" }, + { u"notinvc", u"\u22F6" }, + { u"notni", u"\u220C" }, + { u"notniva", u"\u220C" }, + { u"notnivb", u"\u22FE" }, + { u"notnivc", u"\u22FD" }, + { u"npar", u"\u2226" }, + { u"nparallel", u"\u2226" }, + { u"nparsl", u"\u2AFD\u20E5" }, + { u"npart", u"\u2202\u0338" }, + { u"npolint", u"\u2A14" }, + { u"npr", u"\u2280" }, + { u"nprcue", u"\u22E0" }, + { u"npre", u"\u2AAF\u0338" }, + { u"nprec", u"\u2280" }, + { u"npreceq", u"\u2AAF\u0338" }, + { u"nrArr", u"\u21CF" }, + { u"nrarr", u"\u219B" }, + { u"nrarrc", u"\u2933\u0338" }, + { u"nrarrw", u"\u219D\u0338" }, + { u"nrightarrow", u"\u219B" }, + { u"nrtri", u"\u22EB" }, + { u"nrtrie", u"\u22ED" }, + { u"nsc", u"\u2281" }, + { u"nsccue", u"\u22E1" }, + { u"nsce", u"\u2AB0\u0338" }, + { u"nscr", u"\U0001D4C3" }, + { u"nshortmid", u"\u2224" }, + { u"nshortparallel", u"\u2226" }, + { u"nsim", u"\u2241" }, + { u"nsime", u"\u2244" }, + { u"nsimeq", u"\u2244" }, + { u"nsmid", u"\u2224" }, + { u"nspar", u"\u2226" }, + { u"nsqsube", u"\u22E2" }, + { u"nsqsupe", u"\u22E3" }, + { u"nsub", u"\u2284" }, + { u"nsubE", u"\u2AC5\u0338" }, + { u"nsube", u"\u2288" }, + { u"nsubset", u"\u2282\u20D2" }, + { u"nsubseteq", u"\u2288" }, + { u"nsubseteqq", u"\u2AC5\u0338" }, + { u"nsucc", u"\u2281" }, + { u"nsucceq", u"\u2AB0\u0338" }, + { u"nsup", u"\u2285" }, + { u"nsupE", u"\u2AC6\u0338" }, + { u"nsupe", u"\u2289" }, + { u"nsupset", u"\u2283\u20D2" }, + { u"nsupseteq", u"\u2289" }, + { u"nsupseteqq", u"\u2AC6\u0338" }, + { u"ntgl", u"\u2279" }, + { u"ntilde", u"\u00F1" }, + { u"ntlg", u"\u2278" }, + { u"ntriangleleft", u"\u22EA" }, + { u"ntrianglelefteq", u"\u22EC" }, + { u"ntriangleright", u"\u22EB" }, + { u"ntrianglerighteq", u"\u22ED" }, + { u"nu", u"\u03BD" }, + { u"num", u"\u0023" }, + { u"numero", u"\u2116" }, + { u"numsp", u"\u2007" }, + { u"nvDash", u"\u22AD" }, + { u"nvHarr", u"\u2904" }, + { u"nvap", u"\u224D\u20D2" }, + { u"nvdash", u"\u22AC" }, + { u"nvge", u"\u2265\u20D2" }, + { u"nvgt", u"\u003E\u20D2" }, + { u"nvinfin", u"\u29DE" }, + { u"nvlArr", u"\u2902" }, + { u"nvle", u"\u2264\u20D2" }, + { u"nvlt", u"\u003C\u20D2" }, + { u"nvltrie", u"\u22B4\u20D2" }, + { u"nvrArr", u"\u2903" }, + { u"nvrtrie", u"\u22B5\u20D2" }, + { u"nvsim", u"\u223C\u20D2" }, + { u"nwArr", u"\u21D6" }, + { u"nwarhk", u"\u2923" }, + { u"nwarr", u"\u2196" }, + { u"nwarrow", u"\u2196" }, + { u"nwnear", u"\u2927" }, + { u"oS", u"\u24C8" }, + { u"oacute", u"\u00F3" }, + { u"oast", u"\u229B" }, + { u"ocir", u"\u229A" }, + { u"ocirc", u"\u00F4" }, + { u"ocy", u"\u043E" }, + { u"odash", u"\u229D" }, + { u"odblac", u"\u0151" }, + { u"odiv", u"\u2A38" }, + { u"odot", u"\u2299" }, + { u"odsold", u"\u29BC" }, + { u"oelig", u"\u0153" }, + { u"ofcir", u"\u29BF" }, + { u"ofr", u"\U0001D52C" }, + { u"ogon", u"\u02DB" }, + { u"ograve", u"\u00F2" }, + { u"ogt", u"\u29C1" }, + { u"ohbar", u"\u29B5" }, + { u"ohm", u"\u03A9" }, + { u"oint", u"\u222E" }, + { u"olarr", u"\u21BA" }, + { u"olcir", u"\u29BE" }, + { u"olcross", u"\u29BB" }, + { u"oline", u"\u203E" }, + { u"olt", u"\u29C0" }, + { u"omacr", u"\u014D" }, + { u"omega", u"\u03C9" }, + { u"omicron", u"\u03BF" }, + { u"omid", u"\u29B6" }, + { u"ominus", u"\u2296" }, + { u"oopf", u"\U0001D560" }, + { u"opar", u"\u29B7" }, + { u"operp", u"\u29B9" }, + { u"oplus", u"\u2295" }, + { u"or", u"\u2228" }, + { u"orarr", u"\u21BB" }, + { u"ord", u"\u2A5D" }, + { u"order", u"\u2134" }, + { u"orderof", u"\u2134" }, + { u"ordf", u"\u00AA" }, + { u"ordm", u"\u00BA" }, + { u"origof", u"\u22B6" }, + { u"oror", u"\u2A56" }, + { u"orslope", u"\u2A57" }, + { u"orv", u"\u2A5B" }, + { u"oscr", u"\u2134" }, + { u"oslash", u"\u00F8" }, + { u"osol", u"\u2298" }, + { u"otilde", u"\u00F5" }, + { u"otimes", u"\u2297" }, + { u"otimesas", u"\u2A36" }, + { u"ouml", u"\u00F6" }, + { u"ovbar", u"\u233D" }, + { u"par", u"\u2225" }, + { u"para", u"\u00B6" }, + { u"parallel", u"\u2225" }, + { u"parsim", u"\u2AF3" }, + { u"parsl", u"\u2AFD" }, + { u"part", u"\u2202" }, + { u"pcy", u"\u043F" }, + { u"percnt", u"\u0025" }, + { u"period", u"\u002E" }, + { u"permil", u"\u2030" }, + { u"perp", u"\u22A5" }, + { u"pertenk", u"\u2031" }, + { u"pfr", u"\U0001D52D" }, + { u"phi", u"\u03C6" }, + { u"phiv", u"\u03D5" }, + { u"phmmat", u"\u2133" }, + { u"phone", u"\u260E" }, + { u"pi", u"\u03C0" }, + { u"pitchfork", u"\u22D4" }, + { u"piv", u"\u03D6" }, + { u"planck", u"\u210F" }, + { u"planckh", u"\u210E" }, + { u"plankv", u"\u210F" }, + { u"plus", u"\u002B" }, + { u"plusacir", u"\u2A23" }, + { u"plusb", u"\u229E" }, + { u"pluscir", u"\u2A22" }, + { u"plusdo", u"\u2214" }, + { u"plusdu", u"\u2A25" }, + { u"pluse", u"\u2A72" }, + { u"plusmn", u"\u00B1" }, + { u"plussim", u"\u2A26" }, + { u"plustwo", u"\u2A27" }, + { u"pm", u"\u00B1" }, + { u"pointint", u"\u2A15" }, + { u"popf", u"\U0001D561" }, + { u"pound", u"\u00A3" }, + { u"pr", u"\u227A" }, + { u"prE", u"\u2AB3" }, + { u"prap", u"\u2AB7" }, + { u"prcue", u"\u227C" }, + { u"pre", u"\u2AAF" }, + { u"prec", u"\u227A" }, + { u"precapprox", u"\u2AB7" }, + { u"preccurlyeq", u"\u227C" }, + { u"preceq", u"\u2AAF" }, + { u"precnapprox", u"\u2AB9" }, + { u"precneqq", u"\u2AB5" }, + { u"precnsim", u"\u22E8" }, + { u"precsim", u"\u227E" }, + { u"prime", u"\u2032" }, + { u"primes", u"\u2119" }, + { u"prnE", u"\u2AB5" }, + { u"prnap", u"\u2AB9" }, + { u"prnsim", u"\u22E8" }, + { u"prod", u"\u220F" }, + { u"profalar", u"\u232E" }, + { u"profline", u"\u2312" }, + { u"profsurf", u"\u2313" }, + { u"prop", u"\u221D" }, + { u"propto", u"\u221D" }, + { u"prsim", u"\u227E" }, + { u"prurel", u"\u22B0" }, + { u"pscr", u"\U0001D4C5" }, + { u"psi", u"\u03C8" }, + { u"puncsp", u"\u2008" }, + { u"qfr", u"\U0001D52E" }, + { u"qint", u"\u2A0C" }, + { u"qopf", u"\U0001D562" }, + { u"qprime", u"\u2057" }, + { u"qscr", u"\U0001D4C6" }, + { u"quaternions", u"\u210D" }, + { u"quatint", u"\u2A16" }, + { u"quest", u"\u003F" }, + { u"questeq", u"\u225F" }, + { u"quot", u"\u0022" }, + { u"rAarr", u"\u21DB" }, + { u"rArr", u"\u21D2" }, + { u"rAtail", u"\u291C" }, + { u"rBarr", u"\u290F" }, + { u"rHar", u"\u2964" }, + { u"race", u"\u223D\u0331" }, + { u"racute", u"\u0155" }, + { u"radic", u"\u221A" }, + { u"raemptyv", u"\u29B3" }, + { u"rang", u"\u27E9" }, + { u"rangd", u"\u2992" }, + { u"range", u"\u29A5" }, + { u"rangle", u"\u27E9" }, + { u"raquo", u"\u00BB" }, + { u"rarr", u"\u2192" }, + { u"rarrap", u"\u2975" }, + { u"rarrb", u"\u21E5" }, + { u"rarrbfs", u"\u2920" }, + { u"rarrc", u"\u2933" }, + { u"rarrfs", u"\u291E" }, + { u"rarrhk", u"\u21AA" }, + { u"rarrlp", u"\u21AC" }, + { u"rarrpl", u"\u2945" }, + { u"rarrsim", u"\u2974" }, + { u"rarrtl", u"\u21A3" }, + { u"rarrw", u"\u219D" }, + { u"ratail", u"\u291A" }, + { u"ratio", u"\u2236" }, + { u"rationals", u"\u211A" }, + { u"rbarr", u"\u290D" }, + { u"rbbrk", u"\u2773" }, + { u"rbrace", u"\u007D" }, + { u"rbrack", u"\u005D" }, + { u"rbrke", u"\u298C" }, + { u"rbrksld", u"\u298E" }, + { u"rbrkslu", u"\u2990" }, + { u"rcaron", u"\u0159" }, + { u"rcedil", u"\u0157" }, + { u"rceil", u"\u2309" }, + { u"rcub", u"\u007D" }, + { u"rcy", u"\u0440" }, + { u"rdca", u"\u2937" }, + { u"rdldhar", u"\u2969" }, + { u"rdquo", u"\u201D" }, + { u"rdquor", u"\u201D" }, + { u"rdsh", u"\u21B3" }, + { u"real", u"\u211C" }, + { u"realine", u"\u211B" }, + { u"realpart", u"\u211C" }, + { u"reals", u"\u211D" }, + { u"rect", u"\u25AD" }, + { u"reg", u"\u00AE" }, + { u"rfisht", u"\u297D" }, + { u"rfloor", u"\u230B" }, + { u"rfr", u"\U0001D52F" }, + { u"rhard", u"\u21C1" }, + { u"rharu", u"\u21C0" }, + { u"rharul", u"\u296C" }, + { u"rho", u"\u03C1" }, + { u"rhov", u"\u03F1" }, + { u"rightarrow", u"\u2192" }, + { u"rightarrowtail", u"\u21A3" }, + { u"rightharpoondown", u"\u21C1" }, + { u"rightharpoonup", u"\u21C0" }, + { u"rightleftarrows", u"\u21C4" }, + { u"rightleftharpoons", u"\u21CC" }, + { u"rightrightarrows", u"\u21C9" }, + { u"rightsquigarrow", u"\u219D" }, + { u"rightthreetimes", u"\u22CC" }, + { u"ring", u"\u02DA" }, + { u"risingdotseq", u"\u2253" }, + { u"rlarr", u"\u21C4" }, + { u"rlhar", u"\u21CC" }, + { u"rlm", u"\u200F" }, + { u"rmoust", u"\u23B1" }, + { u"rmoustache", u"\u23B1" }, + { u"rnmid", u"\u2AEE" }, + { u"roang", u"\u27ED" }, + { u"roarr", u"\u21FE" }, + { u"robrk", u"\u27E7" }, + { u"ropar", u"\u2986" }, + { u"ropf", u"\U0001D563" }, + { u"roplus", u"\u2A2E" }, + { u"rotimes", u"\u2A35" }, + { u"rpar", u"\u0029" }, + { u"rpargt", u"\u2994" }, + { u"rppolint", u"\u2A12" }, + { u"rrarr", u"\u21C9" }, + { u"rsaquo", u"\u203A" }, + { u"rscr", u"\U0001D4C7" }, + { u"rsh", u"\u21B1" }, + { u"rsqb", u"\u005D" }, + { u"rsquo", u"\u2019" }, + { u"rsquor", u"\u2019" }, + { u"rthree", u"\u22CC" }, + { u"rtimes", u"\u22CA" }, + { u"rtri", u"\u25B9" }, + { u"rtrie", u"\u22B5" }, + { u"rtrif", u"\u25B8" }, + { u"rtriltri", u"\u29CE" }, + { u"ruluhar", u"\u2968" }, + { u"rx", u"\u211E" }, + { u"sacute", u"\u015B" }, + { u"sbquo", u"\u201A" }, + { u"sc", u"\u227B" }, + { u"scE", u"\u2AB4" }, + { u"scap", u"\u2AB8" }, + { u"scaron", u"\u0161" }, + { u"sccue", u"\u227D" }, + { u"sce", u"\u2AB0" }, + { u"scedil", u"\u015F" }, + { u"scirc", u"\u015D" }, + { u"scnE", u"\u2AB6" }, + { u"scnap", u"\u2ABA" }, + { u"scnsim", u"\u22E9" }, + { u"scpolint", u"\u2A13" }, + { u"scsim", u"\u227F" }, + { u"scy", u"\u0441" }, + { u"sdot", u"\u22C5" }, + { u"sdotb", u"\u22A1" }, + { u"sdote", u"\u2A66" }, + { u"seArr", u"\u21D8" }, + { u"searhk", u"\u2925" }, + { u"searr", u"\u2198" }, + { u"searrow", u"\u2198" }, + { u"sect", u"\u00A7" }, + { u"semi", u"\u003B" }, + { u"seswar", u"\u2929" }, + { u"setminus", u"\u2216" }, + { u"setmn", u"\u2216" }, + { u"sext", u"\u2736" }, + { u"sfr", u"\U0001D530" }, + { u"sfrown", u"\u2322" }, + { u"sharp", u"\u266F" }, + { u"shchcy", u"\u0449" }, + { u"shcy", u"\u0448" }, + { u"shortmid", u"\u2223" }, + { u"shortparallel", u"\u2225" }, + { u"shy", u"\u00AD" }, + { u"sigma", u"\u03C3" }, + { u"sigmaf", u"\u03C2" }, + { u"sigmav", u"\u03C2" }, + { u"sim", u"\u223C" }, + { u"simdot", u"\u2A6A" }, + { u"sime", u"\u2243" }, + { u"simeq", u"\u2243" }, + { u"simg", u"\u2A9E" }, + { u"simgE", u"\u2AA0" }, + { u"siml", u"\u2A9D" }, + { u"simlE", u"\u2A9F" }, + { u"simne", u"\u2246" }, + { u"simplus", u"\u2A24" }, + { u"simrarr", u"\u2972" }, + { u"slarr", u"\u2190" }, + { u"smallsetminus", u"\u2216" }, + { u"smashp", u"\u2A33" }, + { u"smeparsl", u"\u29E4" }, + { u"smid", u"\u2223" }, + { u"smile", u"\u2323" }, + { u"smt", u"\u2AAA" }, + { u"smte", u"\u2AAC" }, + { u"smtes", u"\u2AAC\uFE00" }, + { u"softcy", u"\u044C" }, + { u"sol", u"\u002F" }, + { u"solb", u"\u29C4" }, + { u"solbar", u"\u233F" }, + { u"sopf", u"\U0001D564" }, + { u"spades", u"\u2660" }, + { u"spadesuit", u"\u2660" }, + { u"spar", u"\u2225" }, + { u"sqcap", u"\u2293" }, + { u"sqcaps", u"\u2293\uFE00" }, + { u"sqcup", u"\u2294" }, + { u"sqcups", u"\u2294\uFE00" }, + { u"sqsub", u"\u228F" }, + { u"sqsube", u"\u2291" }, + { u"sqsubset", u"\u228F" }, + { u"sqsubseteq", u"\u2291" }, + { u"sqsup", u"\u2290" }, + { u"sqsupe", u"\u2292" }, + { u"sqsupset", u"\u2290" }, + { u"sqsupseteq", u"\u2292" }, + { u"squ", u"\u25A1" }, + { u"square", u"\u25A1" }, + { u"squarf", u"\u25AA" }, + { u"squf", u"\u25AA" }, + { u"srarr", u"\u2192" }, + { u"sscr", u"\U0001D4C8" }, + { u"ssetmn", u"\u2216" }, + { u"ssmile", u"\u2323" }, + { u"sstarf", u"\u22C6" }, + { u"star", u"\u2606" }, + { u"starf", u"\u2605" }, + { u"straightepsilon", u"\u03F5" }, + { u"straightphi", u"\u03D5" }, + { u"strns", u"\u00AF" }, + { u"sub", u"\u2282" }, + { u"subE", u"\u2AC5" }, + { u"subdot", u"\u2ABD" }, + { u"sube", u"\u2286" }, + { u"subedot", u"\u2AC3" }, + { u"submult", u"\u2AC1" }, + { u"subnE", u"\u2ACB" }, + { u"subne", u"\u228A" }, + { u"subplus", u"\u2ABF" }, + { u"subrarr", u"\u2979" }, + { u"subset", u"\u2282" }, + { u"subseteq", u"\u2286" }, + { u"subseteqq", u"\u2AC5" }, + { u"subsetneq", u"\u228A" }, + { u"subsetneqq", u"\u2ACB" }, + { u"subsim", u"\u2AC7" }, + { u"subsub", u"\u2AD5" }, + { u"subsup", u"\u2AD3" }, + { u"succ", u"\u227B" }, + { u"succapprox", u"\u2AB8" }, + { u"succcurlyeq", u"\u227D" }, + { u"succeq", u"\u2AB0" }, + { u"succnapprox", u"\u2ABA" }, + { u"succneqq", u"\u2AB6" }, + { u"succnsim", u"\u22E9" }, + { u"succsim", u"\u227F" }, + { u"sum", u"\u2211" }, + { u"sung", u"\u266A" }, + { u"sup", u"\u2283" }, + { u"sup1", u"\u00B9" }, + { u"sup2", u"\u00B2" }, + { u"sup3", u"\u00B3" }, + { u"supE", u"\u2AC6" }, + { u"supdot", u"\u2ABE" }, + { u"supdsub", u"\u2AD8" }, + { u"supe", u"\u2287" }, + { u"supedot", u"\u2AC4" }, + { u"suphsol", u"\u27C9" }, + { u"suphsub", u"\u2AD7" }, + { u"suplarr", u"\u297B" }, + { u"supmult", u"\u2AC2" }, + { u"supnE", u"\u2ACC" }, + { u"supne", u"\u228B" }, + { u"supplus", u"\u2AC0" }, + { u"supset", u"\u2283" }, + { u"supseteq", u"\u2287" }, + { u"supseteqq", u"\u2AC6" }, + { u"supsetneq", u"\u228B" }, + { u"supsetneqq", u"\u2ACC" }, + { u"supsim", u"\u2AC8" }, + { u"supsub", u"\u2AD4" }, + { u"supsup", u"\u2AD6" }, + { u"swArr", u"\u21D9" }, + { u"swarhk", u"\u2926" }, + { u"swarr", u"\u2199" }, + { u"swarrow", u"\u2199" }, + { u"swnwar", u"\u292A" }, + { u"szlig", u"\u00DF" }, + { u"target", u"\u2316" }, + { u"tau", u"\u03C4" }, + { u"tbrk", u"\u23B4" }, + { u"tcaron", u"\u0165" }, + { u"tcedil", u"\u0163" }, + { u"tcy", u"\u0442" }, + { u"tdot", u"\u20DB" }, + { u"telrec", u"\u2315" }, + { u"tfr", u"\U0001D531" }, + { u"there4", u"\u2234" }, + { u"therefore", u"\u2234" }, + { u"theta", u"\u03B8" }, + { u"thetasym", u"\u03D1" }, + { u"thetav", u"\u03D1" }, + { u"thickapprox", u"\u2248" }, + { u"thicksim", u"\u223C" }, + { u"thinsp", u"\u2009" }, + { u"thkap", u"\u2248" }, + { u"thksim", u"\u223C" }, + { u"thorn", u"\u00FE" }, + { u"tilde", u"\u02DC" }, + { u"times", u"\u00D7" }, + { u"timesb", u"\u22A0" }, + { u"timesbar", u"\u2A31" }, + { u"timesd", u"\u2A30" }, + { u"tint", u"\u222D" }, + { u"toea", u"\u2928" }, + { u"top", u"\u22A4" }, + { u"topbot", u"\u2336" }, + { u"topcir", u"\u2AF1" }, + { u"topf", u"\U0001D565" }, + { u"topfork", u"\u2ADA" }, + { u"tosa", u"\u2929" }, + { u"tprime", u"\u2034" }, + { u"trade", u"\u2122" }, + { u"triangle", u"\u25B5" }, + { u"triangledown", u"\u25BF" }, + { u"triangleleft", u"\u25C3" }, + { u"trianglelefteq", u"\u22B4" }, + { u"triangleq", u"\u225C" }, + { u"triangleright", u"\u25B9" }, + { u"trianglerighteq", u"\u22B5" }, + { u"tridot", u"\u25EC" }, + { u"trie", u"\u225C" }, + { u"triminus", u"\u2A3A" }, + { u"triplus", u"\u2A39" }, + { u"trisb", u"\u29CD" }, + { u"tritime", u"\u2A3B" }, + { u"trpezium", u"\u23E2" }, + { u"tscr", u"\U0001D4C9" }, + { u"tscy", u"\u0446" }, + { u"tshcy", u"\u045B" }, + { u"tstrok", u"\u0167" }, + { u"twixt", u"\u226C" }, + { u"twoheadleftarrow", u"\u219E" }, + { u"twoheadrightarrow", u"\u21A0" }, + { u"uArr", u"\u21D1" }, + { u"uHar", u"\u2963" }, + { u"uacute", u"\u00FA" }, + { u"uarr", u"\u2191" }, + { u"ubrcy", u"\u045E" }, + { u"ubreve", u"\u016D" }, + { u"ucirc", u"\u00FB" }, + { u"ucy", u"\u0443" }, + { u"udarr", u"\u21C5" }, + { u"udblac", u"\u0171" }, + { u"udhar", u"\u296E" }, + { u"ufisht", u"\u297E" }, + { u"ufr", u"\U0001D532" }, + { u"ugrave", u"\u00F9" }, + { u"uharl", u"\u21BF" }, + { u"uharr", u"\u21BE" }, + { u"uhblk", u"\u2580" }, + { u"ulcorn", u"\u231C" }, + { u"ulcorner", u"\u231C" }, + { u"ulcrop", u"\u230F" }, + { u"ultri", u"\u25F8" }, + { u"umacr", u"\u016B" }, + { u"uml", u"\u00A8" }, + { u"uogon", u"\u0173" }, + { u"uopf", u"\U0001D566" }, + { u"uparrow", u"\u2191" }, + { u"updownarrow", u"\u2195" }, + { u"upharpoonleft", u"\u21BF" }, + { u"upharpoonright", u"\u21BE" }, + { u"uplus", u"\u228E" }, + { u"upsi", u"\u03C5" }, + { u"upsih", u"\u03D2" }, + { u"upsilon", u"\u03C5" }, + { u"upuparrows", u"\u21C8" }, + { u"urcorn", u"\u231D" }, + { u"urcorner", u"\u231D" }, + { u"urcrop", u"\u230E" }, + { u"uring", u"\u016F" }, + { u"urtri", u"\u25F9" }, + { u"uscr", u"\U0001D4CA" }, + { u"utdot", u"\u22F0" }, + { u"utilde", u"\u0169" }, + { u"utri", u"\u25B5" }, + { u"utrif", u"\u25B4" }, + { u"uuarr", u"\u21C8" }, + { u"uuml", u"\u00FC" }, + { u"uwangle", u"\u29A7" }, + { u"vArr", u"\u21D5" }, + { u"vBar", u"\u2AE8" }, + { u"vBarv", u"\u2AE9" }, + { u"vDash", u"\u22A8" }, + { u"vangrt", u"\u299C" }, + { u"varepsilon", u"\u03F5" }, + { u"varkappa", u"\u03F0" }, + { u"varnothing", u"\u2205" }, + { u"varphi", u"\u03D5" }, + { u"varpi", u"\u03D6" }, + { u"varpropto", u"\u221D" }, + { u"varr", u"\u2195" }, + { u"varrho", u"\u03F1" }, + { u"varsigma", u"\u03C2" }, + { u"varsubsetneq", u"\u228A\uFE00" }, + { u"varsubsetneqq", u"\u2ACB\uFE00" }, + { u"varsupsetneq", u"\u228B\uFE00" }, + { u"varsupsetneqq", u"\u2ACC\uFE00" }, + { u"vartheta", u"\u03D1" }, + { u"vartriangleleft", u"\u22B2" }, + { u"vartriangleright", u"\u22B3" }, + { u"vcy", u"\u0432" }, + { u"vdash", u"\u22A2" }, + { u"vee", u"\u2228" }, + { u"veebar", u"\u22BB" }, + { u"veeeq", u"\u225A" }, + { u"vellip", u"\u22EE" }, + { u"verbar", u"\u007C" }, + { u"vert", u"\u007C" }, + { u"vfr", u"\U0001D533" }, + { u"vltri", u"\u22B2" }, + { u"vnsub", u"\u2282\u20D2" }, + { u"vnsup", u"\u2283\u20D2" }, + { u"vopf", u"\U0001D567" }, + { u"vprop", u"\u221D" }, + { u"vrtri", u"\u22B3" }, + { u"vscr", u"\U0001D4CB" }, + { u"vsubnE", u"\u2ACB\uFE00" }, + { u"vsubne", u"\u228A\uFE00" }, + { u"vsupnE", u"\u2ACC\uFE00" }, + { u"vsupne", u"\u228B\uFE00" }, + { u"vzigzag", u"\u299A" }, + { u"wcirc", u"\u0175" }, + { u"wedbar", u"\u2A5F" }, + { u"wedge", u"\u2227" }, + { u"wedgeq", u"\u2259" }, + { u"weierp", u"\u2118" }, + { u"wfr", u"\U0001D534" }, + { u"wopf", u"\U0001D568" }, + { u"wp", u"\u2118" }, + { u"wr", u"\u2240" }, + { u"wreath", u"\u2240" }, + { u"wscr", u"\U0001D4CC" }, + { u"xcap", u"\u22C2" }, + { u"xcirc", u"\u25EF" }, + { u"xcup", u"\u22C3" }, + { u"xdtri", u"\u25BD" }, + { u"xfr", u"\U0001D535" }, + { u"xhArr", u"\u27FA" }, + { u"xharr", u"\u27F7" }, + { u"xi", u"\u03BE" }, + { u"xlArr", u"\u27F8" }, + { u"xlarr", u"\u27F5" }, + { u"xmap", u"\u27FC" }, + { u"xnis", u"\u22FB" }, + { u"xodot", u"\u2A00" }, + { u"xopf", u"\U0001D569" }, + { u"xoplus", u"\u2A01" }, + { u"xotime", u"\u2A02" }, + { u"xrArr", u"\u27F9" }, + { u"xrarr", u"\u27F6" }, + { u"xscr", u"\U0001D4CD" }, + { u"xsqcup", u"\u2A06" }, + { u"xuplus", u"\u2A04" }, + { u"xutri", u"\u25B3" }, + { u"xvee", u"\u22C1" }, + { u"xwedge", u"\u22C0" }, + { u"yacute", u"\u00FD" }, + { u"yacy", u"\u044F" }, + { u"ycirc", u"\u0177" }, + { u"ycy", u"\u044B" }, + { u"yen", u"\u00A5" }, + { u"yfr", u"\U0001D536" }, + { u"yicy", u"\u0457" }, + { u"yopf", u"\U0001D56A" }, + { u"yscr", u"\U0001D4CE" }, + { u"yucy", u"\u044E" }, + { u"yuml", u"\u00FF" }, + { u"zacute", u"\u017A" }, + { u"zcaron", u"\u017E" }, + { u"zcy", u"\u0437" }, + { u"zdot", u"\u017C" }, + { u"zeetrf", u"\u2128" }, + { u"zeta", u"\u03B6" }, + { u"zfr", u"\U0001D537" }, + { u"zhcy", u"\u0436" }, + { u"zigrarr", u"\u21DD" }, + { u"zopf", u"\U0001D56B" }, + { u"zscr", u"\U0001D4CF" }, + { u"zwj", u"\u200D" }, + { u"zwnj", u"\u200C" } + // clang-format on + }; + +const ::css::uno::Sequence<::css::beans::Pair<OUString, OUString>> + starmathdatabase::icustomMathmlHtmlEntities( + icustomMathmlHtmlEntitiesData, starmathdatabase::STARMATH_MATHMLHTML_ENTITY_NUMBER); + +static ::css::beans::Pair<::rtl::OUString, ::rtl::OUString> + icustomMathmlHtmlEntitiesNamesExportData[2] = { + // clang-format off + { u"σ", u"\u03C3"}, + { u"∞", u"\u221E"} + // clang-format on + }; +const ::css::uno::Sequence<::css::beans::Pair<::rtl::OUString, ::rtl::OUString>> + starmathdatabase::icustomMathmlHtmlEntitiesExport(icustomMathmlHtmlEntitiesNamesExportData, 2); + +/* 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..518f29fc5 --- /dev/null +++ b/starmath/source/mathtype.cxx @@ -0,0 +1,3354 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 "eqnolefilehdr.hxx" + +#include <filter/msfilter/classids.hxx> +#include <osl/diagnose.h> +#include <sfx2/docfile.hxx> +#include <sot/storage.hxx> +#include <array> + +//These are the default MathType sizes +constexpr std::array<sal_Int16, 7> aSizeTable { + 12, + 8, + 6, + 24, + 10, + 12, + 12, +}; + +void MathType::Init() +{ + /* + 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; + aUserStyles.reserve(11); + 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 0x03F6: + pC = " backepsilon "; + break; + case 0x2208: // in + case 0x2209: // notin + rRet.append(" func " + OUStringChar(nChar) + " "); + 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 " + OUStringChar(nChar) + " "); + 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); + auto 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 (!pS->good() || 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(0); + pS->ReadUChar(nXNudge); + sal_uInt8 nYNudge(0); + pS->ReadUChar(nYNudge); + if (nXNudge == 128 && nYNudge == 128) + { + sal_uInt16 nXLongNudge(0); + sal_uInt16 nYLongNudge(0); + 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; + 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 " + 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: + { + sal_uInt8 nTabStops(0); + 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) +{ + bool bRet=false; + if (nLstSize < 0) + { + const sal_Int16 nDefaultSize = 12; + 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(static_cast<sal_Int32>(-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(static_cast<sal_Int32>(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::Attribute: + 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 ); + if (!pS->good()) + return false; + 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(0); + 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 + "{"); + } + 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("}}" + 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 + //ATTRIBUTE 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..cd06cfcdd --- /dev/null +++ b/starmath/source/mathtype.hxx @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <node.hxx> + +#include <o3tl/sorted_vector.hxx> + +class SfxMedium; +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 o3tl::sorted_vector< 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; + + 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); +}; + +/* 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..c4c013b15 --- /dev/null +++ b/starmath/source/node.cxx @@ -0,0 +1,2486 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <symbol.hxx> +#include <smmod.hxx> +#include "tmpdevice.hxx" +#include <visitors.hxx> +#include <vcl/metric.hxx> +#include <osl/diagnose.h> +#include <basegfx/numeric/ftools.hxx> + +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::SetAttribute(FontAttribute nAttrib) +{ + if ( + (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) || + (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic)) + ) + { + mnAttributes |= nAttrib; + } + + ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->SetAttribute(nAttrib);}); +} + + +void SmNode::ClearAttribute(FontAttribute nAttrib) +{ + if ( + (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) || + (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic)) + ) + { + mnAttributes &= ~nAttrib; + } + + ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->ClearAttribute(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()); + tools::Long nHeight = static_cast<tools::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<tools::Long>(Fraction(aFntSize.Height()) * rSize) ); + break; + + case FontSizeType::DIVIDE: + if (rSize != Fraction(0)) + aFntSize.setHeight( static_cast<tools::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& rVector) +{ + if (rVector.X() == 0 && rVector.Y() == 0) + return; + + SmRect::Move(rVector); + + ForEachNonNull(this, [&rVector](SmNode *pNode){pNode->Move(rVector);}); +} + +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 == GetSelection().nStartPara + && nCol >= GetSelection().nStartPos && nCol <= GetSelection().nEndPos ) + 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 +{ + tools::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) + { + tools::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(SmNode* pFirst, SmNode* pSecond, SmNode* pThird) +{ + size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0)); + maSubNodes.resize( nSize ); + if (pFirst) + maSubNodes[0] = pFirst; + if (pSecond) + maSubNodes[1] = pSecond; + if (pThird) + maSubNodes[2] = pThird; + + ClaimPaternity(); +} + +void SmStructureNode::SetSubNodesBinMo(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird) +{ + if(GetType()==SmNodeType::BinDiagonal) + { + size_t nSize = pSecond ? 3 : (pThird ? 2 : (pFirst ? 1 : 0)); + maSubNodes.resize( nSize ); + if (pFirst) + maSubNodes[0] = pFirst.release(); + if (pSecond) + maSubNodes[2] = pSecond.release(); + if (pThird) + maSubNodes[1] = pThird.release(); + } + else + { + 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::SetSubNodesBinMo(SmNode* pFirst, SmNode* pSecond, SmNode* pThird) +{ + if(GetType()==SmNodeType::BinDiagonal) + { + size_t nSize = pSecond ? 3 : (pThird ? 2 : (pFirst ? 1 : 0)); + maSubNodes.resize( nSize ); + if (pFirst) + maSubNodes[0] = pFirst; + if (pSecond) + maSubNodes[2] = pSecond; + if (pThird) + maSubNodes[1] = pThird; + } + else + { + size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0)); + maSubNodes.resize( nSize ); + if (pFirst) + maSubNodes[0] = pFirst; + if (pSecond) + maSubNodes[1] = pSecond; + if (pThird) + maSubNodes[2] = pThird; + } + 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]; +} + +SmNode* SmStructureNode::GetSubNodeBinMo(size_t nIndex) const +{ + if(GetType()==SmNodeType::BinDiagonal) + { + if (nIndex==1) + nIndex = 2; + else if (nIndex==2) + nIndex = 1; + } + 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);}); +} + +int SmStructureNode::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 SmStructureNode::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); +} + +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 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 + tools::Long nDist = +(rFormat.GetDistance(DIS_VERTICAL) + * GetFont().GetFontSize().Height()) / 100; + + if (nSize < 1) + return; + + // arrange subnodes and get maximum width of them + tools::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; +} + + +tools::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 + tools::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); + + tools::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, + tools::Long &rHeight, tools::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) + tools::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); + + tools::Long nHeight, + nVerOffset; + lcl_GetHeightVerOffset(*pBody, nHeight, nVerOffset); + nHeight += rFormat.GetDistance(DIS_ROOT) + * GetFont().GetFontSize().Height() / 100; + + if (nHeight < 0) + { + SAL_WARN("starmath", "negative height"); + nHeight = 0; + } + + // 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 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(); + + tools::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); + + tools::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()); +} + +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 (std::abs(rHeading2.X()) > std::abs(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<tools::Long>(fLambda * rHeading1.X()), + rPoint1.Y() + static_cast<tools::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 + +{ + double fAngleRad = basegfx::deg2rad(fAngleDeg); + tools::Long nRectLeft = GetItalicLeft(), + nRectRight = GetItalicRight(), + nRectTop = GetTop(), + nRectBottom = GetBottom(); + Point aRightHdg (100, 0), + aDownHdg (0, 100), + aDiagHdg ( static_cast<tools::Long>(100.0 * cos(fAngleRad)), + static_cast<tools::Long>(-100.0 * sin(fAngleRad)) ); + + tools::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); + + tools::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 + tools::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); + + tools::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 + tools::Long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4); + + Point aPos; + tools::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 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; + + tools::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 + tools::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); + tools::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[0]; + if (cChar != MS_LINE && cChar != MS_DLINE && + cChar != MS_VERTLINE && cChar != MS_DVERTLINE) + pLeft ->GetFont().SetSize(aTmpSize); + + cChar = pRight->GetToken().cMathChar[0]; + 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(); + tools::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 + tools::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) ); + + tools::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; + tools::Long nFontHeight = pBody->GetFont().GetFontSize().Height(); + tools::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; +} + + +tools::Long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol, + const SmFormat &rFormat) const + // returns the font height to be used for operator-symbol +{ + tools::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) + { + tools::Long nBodyHeight = pBody->GetHeight(); + tools::Long nFontHeight = pSymbol->GetFont().GetFontSize().Height(); + if (nFontHeight < nBodyHeight) + { + pSymbol->SetSize(Fraction(nBodyHeight, nFontHeight)); + bDynamicallySized = true; + } + } + pOper->Arrange(rDev, rFormat); + + tools::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 SmAttributeNode::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; + tools::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::Attribute) + 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::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_uInt32 nc; + + 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 : SetAttribute(FontAttribute::Bold); break; + case TITALIC : SetAttribute(FontAttribute::Italic); break; + case TNBOLD : ClearAttribute(FontAttribute::Bold); break; + case TNITALIC : ClearAttribute(FontAttribute::Italic); break; + + // Using HTML CSS Level 1 standard + case TRGB : + case TRGBA : + case THTMLCOL : + case TMATHMLCOL : + case TDVIPSNAMESCOL: + case TICONICCOL : + case THEX : + nc = GetToken().cMathChar.toUInt32(16); + SetColor(Color(ColorTransparency, nc)); + break; + + default: + SAL_WARN("starmath", "unknown case"); + } + + pNode->Arrange(rDev, rFormat); + + SmRect::operator = (pNode->GetRect()); +} + +/**************************************************************************/ + + +SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken) + : SmGraphicNode(SmNodeType::PolyLine, rNodeToken) + , maPoly(2) + , 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()); + + tools::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); + + tools::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*/) +{ + tools::Long nFontHeight = GetFont().GetFontSize().Height(); + tools::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::ChangeText(const OUString &rText) { + maText = rText; + GetToken().aText = rText; + AdjustFontDesc(); +} + +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::GetAccessibleText( OUStringBuffer &rText ) const +{ + rText.append(maText); +} + +void SmTextNode::AdjustFontDesc() +{ + if (GetToken().nGroup == TG::Function) mnFontDesc = FNT_FUNCTION; + else if (GetToken().eType == TTEXT) mnFontDesc = FNT_TEXT; + else { + sal_Unicode firstChar = maText[0]; + if( ('0' <= firstChar && firstChar <= '9') || firstChar == '.' || firstChar == ',') + mnFontDesc = FNT_NUMBER; + else mnFontDesc = FNT_VARIABLE; + } +} + +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::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<tools::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 tools::Long nNormDist = 3 * GetFont().GetFontSize().Height(); + + // define horizontal and vertical minimal distances that separate + // the elements + tools::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<tools::Long> aColLeft(mnNumCols); + tools::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); + } +} + +const SmNode * SmMatrixNode::GetLeftMost() const +{ + return this; +} + + +/**************************************************************************/ + + +SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken) +: SmSpecialNode(SmNodeType::Math, rNodeToken, FNT_MATH) +{ + SetText(GetToken().cMathChar); +} + +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 + tools::Long nTmpBorderWidth = GetFont().GetBorderWidth(); + tools::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(vcl::PushFlags::FONT | vcl::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 + tools::Long nTmpBorderWidth = GetFont().GetBorderWidth(); + tools::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())); +} + +/**************************************************************************/ + +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() )) + SetAttribute(FontAttribute::Italic); + if (IsBold( GetFont() )) + SetAttribute(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}) + tools::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); +} + +/**************************************************************************/ +//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 SmAttributeNode::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..8a94b8908 --- /dev/null +++ b/starmath/source/ooxmlexport.cxx @@ -0,0 +1,598 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <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( GetTree() == 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(GetTree(), 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( GetTree(), 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 + //ATTRIBUTE 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 SmAttributeNode* 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(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( OUStringChar( 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..cd2e9d446 --- /dev/null +++ b/starmath/source/ooxmlexport.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "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 SmAttributeNode* 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; +}; + +/* 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..b096d80ba --- /dev/null +++ b/starmath/source/ooxmlimport.cxx @@ -0,0 +1,686 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <string_view> + +#include "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> +#include <o3tl/string_view.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 = OUString::Concat("lim from {") + fname.subView( 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.subView( 0, e.getLength() - 2 ) + lim + "}"; + if( limlowupp == LimLow && e.endsWith( " underbrace { }" )) + return e.subView( 0, e.getLength() - 2 ) + lim + "}"; + return e + + ( limlowupp == LimLow + ? std::u16string_view( u" csub {" ) : std::u16string_view( u" 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(o3tl::trim(rtag.text)); + 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..4f8df14bf --- /dev/null +++ b/starmath/source/ooxmlimport.hxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <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; +}; + +/* 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..66daec9d2 --- /dev/null +++ b/starmath/source/parse.cxx @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <parse.hxx> +#include <parse5.hxx> +#include <smmod.hxx> +#include <cfgitem.hxx> + +AbstractSmParser* starmathdatabase::GetDefaultSmParser() +{ + switch(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion()) + { + case 5: + { + AbstractSmParser* aParser = new SmParser5(); + return aParser; + } + default: + throw std::range_error("parser version limit"); + } +} + +AbstractSmParser* starmathdatabase::GetVersionSmParser(sal_uInt16 nVersion) +{ + switch(nVersion) + { + case 5: + { + AbstractSmParser* aParser = new SmParser5(); + return aParser; + } + default: + throw std::range_error("parser version limit"); + } +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/source/parse5.cxx b/starmath/source/parse5.cxx new file mode 100644 index 000000000..06aa373aa --- /dev/null +++ b/starmath/source/parse5.cxx @@ -0,0 +1,2773 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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/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 <osl/diagnose.h> +#include <rtl/character.hxx> +#include <parse5.hxx> +#include <strings.hrc> +#include <smmod.hxx> +#include <cfgitem.hxx> +#include <starmathdatabase.hxx> + +#include <stack> + +using namespace ::com::sun::star::i18n; + +//Definition of math keywords +const SmTokenTableEntry aTokenTable[] + = { { u"abs", TABS, '\0', TG::UnOper, 13 }, + { u"acute", TACUTE, MS_ACUTE, TG::Attribute, 5 }, + { u"aleph", TALEPH, MS_ALEPH, TG::Standalone, 5 }, + { u"alignb", TALIGNC, '\0', TG::Align, 0 }, + { u"alignc", TALIGNC, '\0', TG::Align, 0 }, + { u"alignl", TALIGNL, '\0', TG::Align, 0 }, + { u"alignm", TALIGNC, '\0', TG::Align, 0 }, + { u"alignr", TALIGNR, '\0', TG::Align, 0 }, + { u"alignt", TALIGNC, '\0', TG::Align, 0 }, + { u"and", TAND, MS_AND, TG::Product, 0 }, + { u"approx", TAPPROX, MS_APPROX, TG::Relation, 0 }, + { u"arccos", TACOS, '\0', TG::Function, 5 }, + { u"arccot", TACOT, '\0', TG::Function, 5 }, + { u"arcosh", TACOSH, '\0', TG::Function, 5 }, + { u"arcoth", TACOTH, '\0', TG::Function, 5 }, + { u"arcsin", TASIN, '\0', TG::Function, 5 }, + { u"arctan", TATAN, '\0', TG::Function, 5 }, + { u"arsinh", TASINH, '\0', TG::Function, 5 }, + { u"artanh", TATANH, '\0', TG::Function, 5 }, + { u"backepsilon", TBACKEPSILON, MS_BACKEPSILON, TG::Standalone, 5 }, + { u"bar", TBAR, MS_BAR, TG::Attribute, 5 }, + { u"binom", TBINOM, '\0', TG::NONE, 5 }, + { u"bold", TBOLD, '\0', TG::FontAttr, 5 }, + { u"boper", TBOPER, '\0', TG::Product, 0 }, + { u"breve", TBREVE, MS_BREVE, TG::Attribute, 5 }, + { u"bslash", TBACKSLASH, MS_BACKSLASH, TG::Product, 0 }, + { u"cdot", TCDOT, MS_CDOT, TG::Product, 0 }, + { u"check", TCHECK, MS_CHECK, TG::Attribute, 5 }, + { u"circ", TCIRC, MS_CIRC, TG::Standalone, 5 }, + { u"circle", TCIRCLE, MS_CIRCLE, TG::Attribute, 5 }, + { u"color", TCOLOR, '\0', TG::FontAttr, 5 }, + { u"coprod", TCOPROD, MS_COPROD, TG::Oper, 5 }, + { u"cos", TCOS, '\0', TG::Function, 5 }, + { u"cosh", TCOSH, '\0', TG::Function, 5 }, + { u"cot", TCOT, '\0', TG::Function, 5 }, + { u"coth", TCOTH, '\0', TG::Function, 5 }, + { u"csub", TCSUB, '\0', TG::Power, 0 }, + { u"csup", TCSUP, '\0', TG::Power, 0 }, + { u"dddot", TDDDOT, MS_DDDOT, TG::Attribute, 5 }, + { u"ddot", TDDOT, MS_DDOT, TG::Attribute, 5 }, + { u"def", TDEF, MS_DEF, TG::Relation, 0 }, + { u"div", TDIV, MS_DIV, TG::Product, 0 }, + { u"divides", TDIVIDES, MS_LINE, TG::Relation, 0 }, + { u"dlarrow", TDLARROW, MS_DLARROW, TG::Standalone, 5 }, + { u"dlrarrow", TDLRARROW, MS_DLRARROW, TG::Standalone, 5 }, + { u"dot", TDOT, MS_DOT, TG::Attribute, 5 }, + { u"dotsaxis", TDOTSAXIS, MS_DOTSAXIS, TG::Standalone, 5 }, // 5 to continue expression + { u"dotsdiag", TDOTSDIAG, MS_DOTSUP, TG::Standalone, 5 }, + { u"dotsdown", TDOTSDOWN, MS_DOTSDOWN, TG::Standalone, 5 }, + { u"dotslow", TDOTSLOW, MS_DOTSLOW, TG::Standalone, 5 }, + { u"dotsup", TDOTSUP, MS_DOTSUP, TG::Standalone, 5 }, + { u"dotsvert", TDOTSVERT, MS_DOTSVERT, TG::Standalone, 5 }, + { u"downarrow", TDOWNARROW, MS_DOWNARROW, TG::Standalone, 5 }, + { u"drarrow", TDRARROW, MS_DRARROW, TG::Standalone, 5 }, + { u"emptyset", TEMPTYSET, MS_EMPTYSET, TG::Standalone, 5 }, + { u"equiv", TEQUIV, MS_EQUIV, TG::Relation, 0 }, + { u"evaluate", TEVALUATE, '\0', TG::NONE, 0 }, + { u"exists", TEXISTS, MS_EXISTS, TG::Standalone, 5 }, + { u"exp", TEXP, '\0', TG::Function, 5 }, + { u"fact", TFACT, MS_FACT, TG::UnOper, 5 }, + { u"fixed", TFIXED, '\0', TG::Font, 0 }, + { u"font", TFONT, '\0', TG::FontAttr, 5 }, + { u"forall", TFORALL, MS_FORALL, TG::Standalone, 5 }, + { u"fourier", TFOURIER, MS_FOURIER, TG::Standalone, 5 }, + { u"frac", TFRAC, '\0', TG::NONE, 5 }, + { u"from", TFROM, '\0', TG::Limit, 0 }, + { u"func", TFUNC, '\0', TG::Function, 5 }, + { u"ge", TGE, MS_GE, TG::Relation, 0 }, + { u"geslant", TGESLANT, MS_GESLANT, TG::Relation, 0 }, + { u"gg", TGG, MS_GG, TG::Relation, 0 }, + { u"grave", TGRAVE, MS_GRAVE, TG::Attribute, 5 }, + { u"gt", TGT, MS_GT, TG::Relation, 0 }, + { u"harpoon", THARPOON, MS_HARPOON, TG::Attribute, 5 }, + { u"hat", THAT, MS_HAT, TG::Attribute, 5 }, + { u"hbar", THBAR, MS_HBAR, TG::Standalone, 5 }, + { u"hex", THEX, '\0', TG::NONE, 5 }, + { u"iiint", TIIINT, MS_IIINT, TG::Oper, 5 }, + { u"iint", TIINT, MS_IINT, TG::Oper, 5 }, + { u"im", TIM, MS_IM, TG::Standalone, 5 }, + { u"in", TIN, MS_IN, TG::Relation, 0 }, + { u"infinity", TINFINITY, MS_INFINITY, TG::Standalone, 5 }, + { u"infty", TINFINITY, MS_INFINITY, TG::Standalone, 5 }, + { u"int", TINT, MS_INT, TG::Oper, 5 }, + { u"intd", TINTD, MS_INT, TG::Oper, 5 }, + { u"intersection", TINTERSECT, MS_INTERSECT, TG::Product, 0 }, + { u"it", TIT, '\0', TG::Product, 0 }, + { u"ital", TITALIC, '\0', TG::FontAttr, 5 }, + { u"italic", TITALIC, '\0', TG::FontAttr, 5 }, + { u"lambdabar", TLAMBDABAR, MS_LAMBDABAR, TG::Standalone, 5 }, + { u"langle", TLANGLE, MS_LMATHANGLE, TG::LBrace, 5 }, + { u"laplace", TLAPLACE, MS_LAPLACE, TG::Standalone, 5 }, + { u"lbrace", TLBRACE, MS_LBRACE, TG::LBrace, 5 }, + { u"lceil", TLCEIL, MS_LCEIL, TG::LBrace, 5 }, + { u"ldbracket", TLDBRACKET, MS_LDBRACKET, TG::LBrace, 5 }, + { u"ldline", TLDLINE, MS_DVERTLINE, TG::LBrace, 5 }, + { u"le", TLE, MS_LE, TG::Relation, 0 }, + { u"left", TLEFT, '\0', TG::NONE, 5 }, + { u"leftarrow", TLEFTARROW, MS_LEFTARROW, TG::Standalone, 5 }, + { u"leslant", TLESLANT, MS_LESLANT, TG::Relation, 0 }, + { u"lfloor", TLFLOOR, MS_LFLOOR, TG::LBrace, 5 }, + { u"lim", TLIM, '\0', TG::Oper, 5 }, + { u"liminf", TLIMINF, '\0', TG::Oper, 5 }, + { u"limsup", TLIMSUP, '\0', TG::Oper, 5 }, + { u"lint", TLINT, MS_LINT, TG::Oper, 5 }, + { u"ll", TLL, MS_LL, TG::Relation, 0 }, + { u"lline", TLLINE, MS_VERTLINE, TG::LBrace, 5 }, + { u"llint", TLLINT, MS_LLINT, TG::Oper, 5 }, + { u"lllint", TLLLINT, MS_LLLINT, TG::Oper, 5 }, + { u"ln", TLN, '\0', TG::Function, 5 }, + { u"log", TLOG, '\0', TG::Function, 5 }, + { u"lrline", TLRLINE, MS_VERTLINE, TG::LBrace | TG::RBrace, 5 }, + { u"lrdline", TLRDLINE, MS_VERTLINE, TG::LBrace | TG::RBrace, 5 }, + { u"lsub", TLSUB, '\0', TG::Power, 0 }, + { u"lsup", TLSUP, '\0', TG::Power, 0 }, + { u"lt", TLT, MS_LT, TG::Relation, 0 }, + { u"matrix", TMATRIX, '\0', TG::NONE, 5 }, + { u"minusplus", TMINUSPLUS, MS_MINUSPLUS, TG::UnOper | TG::Sum, 5 }, + { u"mline", TMLINE, MS_VERTLINE, TG::NONE, 0 }, //! not in TG::RBrace, Level 0 + { u"nabla", TNABLA, MS_NABLA, TG::Standalone, 5 }, + { u"nbold", TNBOLD, '\0', TG::FontAttr, 5 }, + { u"ndivides", TNDIVIDES, MS_NDIVIDES, TG::Relation, 0 }, + { u"neg", TNEG, MS_NEG, TG::UnOper, 5 }, + { u"neq", TNEQ, MS_NEQ, TG::Relation, 0 }, + { u"newline", TNEWLINE, '\0', TG::NONE, 0 }, + { u"ni", TNI, MS_NI, TG::Relation, 0 }, + { u"nitalic", TNITALIC, '\0', TG::FontAttr, 5 }, + { u"none", TNONE, '\0', TG::LBrace | TG::RBrace, 0 }, + { u"nospace", TNOSPACE, '\0', TG::Standalone, 5 }, + { u"notexists", TNOTEXISTS, MS_NOTEXISTS, TG::Standalone, 5 }, + { u"notin", TNOTIN, MS_NOTIN, TG::Relation, 0 }, + { u"nprec", TNOTPRECEDES, MS_NOTPRECEDES, TG::Relation, 0 }, + { u"nroot", TNROOT, MS_SQRT, TG::UnOper, 5 }, + { u"nsubset", TNSUBSET, MS_NSUBSET, TG::Relation, 0 }, + { u"nsubseteq", TNSUBSETEQ, MS_NSUBSETEQ, TG::Relation, 0 }, + { u"nsucc", TNOTSUCCEEDS, MS_NOTSUCCEEDS, TG::Relation, 0 }, + { u"nsupset", TNSUPSET, MS_NSUPSET, TG::Relation, 0 }, + { u"nsupseteq", TNSUPSETEQ, MS_NSUPSETEQ, TG::Relation, 0 }, + { u"odivide", TODIVIDE, MS_ODIVIDE, TG::Product, 0 }, + { u"odot", TODOT, MS_ODOT, TG::Product, 0 }, + { u"ominus", TOMINUS, MS_OMINUS, TG::Sum, 0 }, + { u"oper", TOPER, '\0', TG::Oper, 5 }, + { u"oplus", TOPLUS, MS_OPLUS, TG::Sum, 0 }, + { u"or", TOR, MS_OR, TG::Sum, 0 }, + { u"ortho", TORTHO, MS_ORTHO, TG::Relation, 0 }, + { u"otimes", TOTIMES, MS_OTIMES, TG::Product, 0 }, + { u"over", TOVER, '\0', TG::Product, 0 }, + { u"overbrace", TOVERBRACE, MS_OVERBRACE, TG::Product, 5 }, + { u"overline", TOVERLINE, '\0', TG::Attribute, 5 }, + { u"overstrike", TOVERSTRIKE, '\0', TG::Attribute, 5 }, + { u"owns", TNI, MS_NI, TG::Relation, 0 }, + { u"parallel", TPARALLEL, MS_DLINE, TG::Relation, 0 }, + { u"partial", TPARTIAL, MS_PARTIAL, TG::Standalone, 5 }, + { u"phantom", TPHANTOM, '\0', TG::FontAttr, 5 }, + { u"plusminus", TPLUSMINUS, MS_PLUSMINUS, TG::UnOper | TG::Sum, 5 }, + { u"prec", TPRECEDES, MS_PRECEDES, TG::Relation, 0 }, + { u"preccurlyeq", TPRECEDESEQUAL, MS_PRECEDESEQUAL, TG::Relation, 0 }, + { u"precsim", TPRECEDESEQUIV, MS_PRECEDESEQUIV, TG::Relation, 0 }, + { u"prod", TPROD, MS_PROD, TG::Oper, 5 }, + { u"prop", TPROP, MS_PROP, TG::Relation, 0 }, + { u"rangle", TRANGLE, MS_RMATHANGLE, TG::RBrace, 0 }, //! 0 to terminate expression + { u"rbrace", TRBRACE, MS_RBRACE, TG::RBrace, 0 }, + { u"rceil", TRCEIL, MS_RCEIL, TG::RBrace, 0 }, + { u"rdbracket", TRDBRACKET, MS_RDBRACKET, TG::RBrace, 0 }, + { u"rdline", TRDLINE, MS_DVERTLINE, TG::RBrace, 0 }, + { u"re", TRE, MS_RE, TG::Standalone, 5 }, + { u"rfloor", TRFLOOR, MS_RFLOOR, TG::RBrace, 0 }, //! 0 to terminate expression + { u"right", TRIGHT, '\0', TG::NONE, 0 }, + { u"rightarrow", TRIGHTARROW, MS_RIGHTARROW, TG::Standalone, 5 }, + { u"rline", TRLINE, MS_VERTLINE, TG::RBrace, 0 }, //! 0 to terminate expression + { u"rsub", TRSUB, '\0', TG::Power, 0 }, + { u"rsup", TRSUP, '\0', TG::Power, 0 }, + { u"sans", TSANS, '\0', TG::Font, 0 }, + { u"serif", TSERIF, '\0', TG::Font, 0 }, + { u"setC", TSETC, MS_SETC, TG::Standalone, 5 }, + { u"setminus", TSETMINUS, MS_BACKSLASH, TG::Product, 0 }, + { u"setN", TSETN, MS_SETN, TG::Standalone, 5 }, + { u"setQ", TSETQ, MS_SETQ, TG::Standalone, 5 }, + { u"setquotient", TSETQUOTIENT, MS_SLASH, TG::Product, 0 }, + { u"setR", TSETR, MS_SETR, TG::Standalone, 5 }, + { u"setZ", TSETZ, MS_SETZ, TG::Standalone, 5 }, + { u"sim", TSIM, MS_SIM, TG::Relation, 0 }, + { u"simeq", TSIMEQ, MS_SIMEQ, TG::Relation, 0 }, + { u"sin", TSIN, '\0', TG::Function, 5 }, + { u"sinh", TSINH, '\0', TG::Function, 5 }, + { u"size", TSIZE, '\0', TG::FontAttr, 5 }, + { u"slash", TSLASH, MS_SLASH, TG::Product, 0 }, + { u"sqrt", TSQRT, MS_SQRT, TG::UnOper, 5 }, + { u"stack", TSTACK, '\0', TG::NONE, 5 }, + { u"sub", TRSUB, '\0', TG::Power, 0 }, + { u"subset", TSUBSET, MS_SUBSET, TG::Relation, 0 }, + { u"subseteq", TSUBSETEQ, MS_SUBSETEQ, TG::Relation, 0 }, + { u"succ", TSUCCEEDS, MS_SUCCEEDS, TG::Relation, 0 }, + { u"succcurlyeq", TSUCCEEDSEQUAL, MS_SUCCEEDSEQUAL, TG::Relation, 0 }, + { u"succsim", TSUCCEEDSEQUIV, MS_SUCCEEDSEQUIV, TG::Relation, 0 }, + { u"sum", TSUM, MS_SUM, TG::Oper, 5 }, + { u"sup", TRSUP, '\0', TG::Power, 0 }, + { u"supset", TSUPSET, MS_SUPSET, TG::Relation, 0 }, + { u"supseteq", TSUPSETEQ, MS_SUPSETEQ, TG::Relation, 0 }, + { u"tan", TTAN, '\0', TG::Function, 5 }, + { u"tanh", TTANH, '\0', TG::Function, 5 }, + { u"tilde", TTILDE, MS_TILDE, TG::Attribute, 5 }, + { u"times", TTIMES, MS_TIMES, TG::Product, 0 }, + { u"to", TTO, '\0', TG::Limit, 0 }, + { u"toward", TTOWARD, MS_RIGHTARROW, TG::Relation, 0 }, + { u"transl", TTRANSL, MS_TRANSL, TG::Relation, 0 }, + { u"transr", TTRANSR, MS_TRANSR, TG::Relation, 0 }, + { u"underbrace", TUNDERBRACE, MS_UNDERBRACE, TG::Product, 5 }, + { u"underline", TUNDERLINE, '\0', TG::Attribute, 5 }, + { u"union", TUNION, MS_UNION, TG::Sum, 0 }, + { u"uoper", TUOPER, '\0', TG::UnOper, 5 }, + { u"uparrow", TUPARROW, MS_UPARROW, TG::Standalone, 5 }, + { u"vec", TVEC, MS_VEC, TG::Attribute, 5 }, + { u"widebslash", TWIDEBACKSLASH, MS_BACKSLASH, TG::Product, 0 }, + { u"wideharpoon", TWIDEHARPOON, MS_HARPOON, TG::Attribute, 5 }, + { u"widehat", TWIDEHAT, MS_HAT, TG::Attribute, 5 }, + { u"wideslash", TWIDESLASH, MS_SLASH, TG::Product, 0 }, + { u"widetilde", TWIDETILDE, MS_TILDE, TG::Attribute, 5 }, + { u"widevec", TWIDEVEC, MS_VEC, TG::Attribute, 5 }, + { u"wp", TWP, MS_WP, TG::Standalone, 5 } }; + +// First character may be any alphabetic +const sal_Int32 coStartFlags = KParseTokens::ANY_LETTER | KParseTokens::IGNORE_LEADING_WS; + +// Continuing characters may be any alphabetic +const sal_Int32 coContFlags = (coStartFlags & ~KParseTokens::IGNORE_LEADING_WS) + | KParseTokens::TWO_DOUBLE_QUOTES_BREAK_STRING; +// First character for numbers, may be any numeric or dot +const sal_Int32 coNumStartFlags + = KParseTokens::ASC_DIGIT | KParseTokens::ASC_DOT | KParseTokens::IGNORE_LEADING_WS; +// Continuing characters for numbers, may be any numeric or dot or comma. +// 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). +const sal_Int32 coNumContFlags = (coNumStartFlags & ~KParseTokens::IGNORE_LEADING_WS) + | KParseTokens::GROUP_SEPARATOR_IN_NUMBER; +// First character for numbers hexadecimal +const sal_Int32 coNum16StartFlags + = KParseTokens::ASC_DIGIT | KParseTokens::ASC_UPALPHA | KParseTokens::IGNORE_LEADING_WS; + +// Continuing characters for numbers hexadecimal +const sal_Int32 coNum16ContFlags = (coNum16StartFlags & ~KParseTokens::IGNORE_LEADING_WS); +// user-defined char continuing characters may be any alphanumeric or dot. +const sal_Int32 coUserDefinedCharContFlags = KParseTokens::ANY_LETTER_OR_NUMBER + | KParseTokens::ASC_DOT + | KParseTokens::TWO_DOUBLE_QUOTES_BREAK_STRING; + +//Checks if keyword is in the list. +static inline bool findCompare(const SmTokenTableEntry& lhs, const OUString& s) +{ + return s.compareToIgnoreAsciiCase(lhs.aIdent) > 0; +} + +//Returns the SmTokenTableEntry for a keyword +static const SmTokenTableEntry* GetTokenTableEntry(const OUString& rName) +{ + 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.equalsIgnoreAsciiCase(findIter->aIdent)) + return &*findIter; //check is equal + return nullptr; //not found +} + +static 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); +} + +// checks 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; +} +// checks 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_IsNotWholeNumber(const OUString& rText) +{ + const sal_Unicode* pBuffer = rText.getStr(); + for (sal_Int32 nPos = 0; nPos < rText.getLength(); nPos++, pBuffer++) + if (!rtl::isAsciiDigit(*pBuffer)) + return true; + return false; +} +// checks hex number used as arguments in Math formulas (e.g. 'hex' command) +// Format: no negative numbers, must start with a digit, no exponent notation, ... +static bool lcl_IsNotWholeNumber16(const OUString& rText) +{ + const sal_Unicode* pBuffer = rText.getStr(); + for (sal_Int32 nPos = 0; nPos < rText.getLength(); nPos++, pBuffer++) + if (!rtl::isAsciiCanonicHexDigit(*pBuffer)) + return true; + return false; +} + +//Text replace onto m_aBufferString +void SmParser5::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 SmParser5::NextToken() //Central part of the parser +{ + 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; + sal_uInt32 nCol = nRealStart - m_nColOff; + + bool bHandled = true; + if (nRealStart >= nBufLen) + { + m_aCurToken.eType = TEND; + m_aCurToken.cMathChar = u""; + 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 = u""; + 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 = u""; + m_aCurToken.nGroup = TG::NONE; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = aRes.DequotedNameOrString; + nCol++; + } + 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.setChar(pEntry->cMathChar); + m_aCurToken.nGroup = pEntry->nGroup; + m_aCurToken.nLevel = pEntry->nLevel; + m_aCurToken.aText = pEntry->aIdent; + } + else + { + m_aCurToken.eType = TIDENT; + m_aCurToken.cMathChar = u""; + 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 = u""; + 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.setChar(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.setChar(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.setChar(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.setChar(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.setChar(MS_PLACE); + m_aCurToken.nGroup = TG::NONE; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = "<?>"; + + rnEndPos = nRealStart + 3; + } + else + { + m_aCurToken.eType = TLT; + m_aCurToken.setChar(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.setChar(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.setChar(MS_GG); + m_aCurToken.nGroup = TG::Relation; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = ">>"; + + rnEndPos = nRealStart + 2; + } + else + { + m_aCurToken.eType = TGT; + m_aCurToken.setChar(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 = u""; + m_aCurToken.nGroup = TG::NONE; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = "%"; + + 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.setChar(MS_LBRACKET); + m_aCurToken.nGroup = TG::LBrace; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = "["; + } + break; + case '\\': + { + m_aCurToken.eType = TESCAPE; + m_aCurToken.cMathChar = u""; + m_aCurToken.nGroup = TG::NONE; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = "\\"; + } + break; + case ']': + { + m_aCurToken.eType = TRBRACKET; + m_aCurToken.setChar(MS_RBRACKET); + m_aCurToken.nGroup = TG::RBrace; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "]"; + } + break; + case '^': + { + m_aCurToken.eType = TRSUP; + m_aCurToken.cMathChar = u""; + m_aCurToken.nGroup = TG::Power; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "^"; + } + break; + case '`': + { + m_aCurToken.eType = TSBLANK; + m_aCurToken.cMathChar = u""; + m_aCurToken.nGroup = TG::Blank; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = "`"; + } + break; + case '{': + { + m_aCurToken.eType = TLGROUP; + m_aCurToken.setChar(MS_LBRACE); + m_aCurToken.nGroup = TG::NONE; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = "{"; + } + break; + case '|': + { + m_aCurToken.eType = TOR; + m_aCurToken.setChar(MS_OR); + m_aCurToken.nGroup = TG::Sum; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "|"; + } + break; + case '}': + { + m_aCurToken.eType = TRGROUP; + m_aCurToken.setChar(MS_RBRACE); + m_aCurToken.nGroup = TG::NONE; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "}"; + } + break; + case '~': + { + m_aCurToken.eType = TBLANK; + m_aCurToken.cMathChar = u""; + 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 = u""; + m_aCurToken.nGroup = TG::NONE; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "##"; + + rnEndPos = nRealStart + 2; + } + else + { + m_aCurToken.eType = TPOUND; + m_aCurToken.cMathChar = u""; + m_aCurToken.nGroup = TG::NONE; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "#"; + } + } + break; + case '&': + { + m_aCurToken.eType = TAND; + m_aCurToken.setChar(MS_AND); + m_aCurToken.nGroup = TG::Product; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "&"; + } + break; + case '(': + { + m_aCurToken.eType = TLPARENT; + m_aCurToken.setChar(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.setChar(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.setChar(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.setChar(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.setChar(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.setChar(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.setChar(MS_RIGHTARROW); + m_aCurToken.nGroup = TG::Standalone; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = "->"; + + rnEndPos = nRealStart + 2; + } + else + { + m_aCurToken.eType = TMINUS; + m_aCurToken.setChar(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 = u""; + 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.setChar(MS_SLASH); + m_aCurToken.nGroup = TG::Product; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "/"; + } + break; + case '=': + { + m_aCurToken.eType = TASSIGN; + m_aCurToken.setChar(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 = u""; + 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; + } + m_aCurESelection = ESelection(m_nRow, nCol, m_nRow, nCol + m_aCurToken.aText.getLength()); + + if (TEND != m_aCurToken.eType) + m_nBufferIndex = aRes.EndPos; +} + +void SmParser5::NextTokenColor(SmTokenType dvipload) +{ + 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; + //parse, there are few options, so less strict. + 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; + sal_uInt32 nCol = nRealStart - m_nColOff; + + if (nRealStart >= nBufLen) + m_aCurToken.eType = TEND; + else if (aRes.TokenType & KParseType::IDENTNAME) + { + sal_Int32 n = aRes.EndPos - nRealStart; + assert(n >= 0); + OUString aName(m_aBufferString.copy(nRealStart, n)); + switch (dvipload) + { + case TCOLOR: + m_aCurToken = starmathdatabase::Identify_ColorName_Parser(aName); + break; + case TDVIPSNAMESCOL: + m_aCurToken = starmathdatabase::Identify_ColorName_DVIPSNAMES(aName); + break; + default: + m_aCurToken = starmathdatabase::Identify_ColorName_Parser(aName); + break; + } + } + else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR) + { + if (m_aBufferString[nRealStart] == '#' && !m_aBufferString.match("##", nRealStart)) + { + m_aCurToken.eType = THEX; + m_aCurToken.cMathChar = u""; + m_aCurToken.nGroup = TG::Color; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "hex"; + } + } + else + m_aCurToken.eType = TNONE; + + m_aCurESelection = ESelection(m_nRow, nCol, m_nRow, nCol + m_aCurToken.aText.getLength()); + if (TEND != m_aCurToken.eType) + m_nBufferIndex = aRes.EndPos; +} + +void SmParser5::NextTokenFontSize() +{ + sal_Int32 nBufLen = m_aBufferString.getLength(); + ParseResult aRes; + sal_Int32 nRealStart; + bool bCont; + bool hex = false; + + do + { + // skip white spaces + while (UnicodeType::SPACE_SEPARATOR == m_pSysCC->getType(m_aBufferString, m_nBufferIndex)) + ++m_nBufferIndex; + //hexadecimal parser + aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex, coNum16StartFlags, ".", + coNum16ContFlags, ".,"); + if (aRes.TokenType == 0) + { + // Try again with the default token parsing. + aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex, coStartFlags, "", + coContFlags, ""); + } + else + hex = true; + 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; + sal_uInt32 nCol = nRealStart - m_nColOff; + + if (nRealStart >= nBufLen) + m_aCurToken.eType = TEND; + else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR) + { + if (aRes.EndPos - nRealStart == 1) + { + switch (m_aBufferString[nRealStart]) + { + case '*': + m_aCurToken.eType = TMULTIPLY; + m_aCurToken.setChar(MS_MULTIPLY); + m_aCurToken.nGroup = TG::Product; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "*"; + break; + case '+': + m_aCurToken.eType = TPLUS; + m_aCurToken.setChar(MS_PLUS); + m_aCurToken.nGroup = TG::UnOper | TG::Sum; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = "+"; + break; + case '-': + m_aCurToken.eType = TMINUS; + m_aCurToken.setChar(MS_MINUS); + m_aCurToken.nGroup = TG::UnOper | TG::Sum; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = "-"; + break; + case '/': + m_aCurToken.eType = TDIVIDEBY; + m_aCurToken.setChar(MS_SLASH); + m_aCurToken.nGroup = TG::Product; + m_aCurToken.nLevel = 0; + m_aCurToken.aText = "/"; + break; + default: + m_aCurToken.eType = TNONE; + break; + } + } + else + m_aCurToken.eType = TNONE; + } + else if (hex) + { + assert(aRes.EndPos > 0); + sal_Int32 n = aRes.EndPos - nRealStart; + assert(n >= 0); + m_aCurToken.eType = THEX; + m_aCurToken.cMathChar = u""; + m_aCurToken.nGroup = TG::NONE; + m_aCurToken.nLevel = 5; + m_aCurToken.aText = m_aBufferString.copy(nRealStart, n); + } + else + m_aCurToken.eType = TNONE; + + m_aCurESelection = ESelection(m_nRow, nCol, m_nRow, nCol + m_aCurToken.aText.getLength()); + 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> SmParser5::DoTable() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + 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->SetSelection(m_aCurESelection); + xSNode->SetSubNodes(buildNodeArray(aLineArray)); + return xSNode; +} + +std::unique_ptr<SmNode> SmParser5::DoAlign(bool bUseExtraSpaces) +// parse alignment info (if any), then go on with rest of expression +{ + DepthProtect aDepthGuard(m_nParseDepth); + + std::unique_ptr<SmStructureNode> xSNode; + + if (TokenInGroup(TG::Align)) + { + xSNode.reset(new SmAlignNode(m_aCurToken)); + xSNode->SetSelection(m_aCurESelection); + + 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> SmParser5::DoLine() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + 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->SetSelection(m_aCurESelection); + xSNode->SetSubNodes(buildNodeArray(ExpressionArray)); + return xSNode; +} + +std::unique_ptr<SmNode> SmParser5::DoExpression(bool bUseExtraSpaces) +{ + DepthProtect aDepthGuard(m_nParseDepth); + + 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> SmParser5::DoRelation() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + int nDepthLimit = m_nParseDepth; + + auto xFirst = DoSum(); + while (TokenInGroup(TG::Relation)) + { + std::unique_ptr<SmStructureNode> xSNode(new SmBinHorNode(m_aCurToken)); + xSNode->SetSelection(m_aCurESelection); + auto xSecond = DoOpSubSup(); + auto xThird = DoSum(); + xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond), std::move(xThird)); + xFirst = std::move(xSNode); + + ++m_nParseDepth; + DepthProtect bDepthGuard(m_nParseDepth); + } + + m_nParseDepth = nDepthLimit; + + return xFirst; +} + +std::unique_ptr<SmNode> SmParser5::DoSum() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + int nDepthLimit = m_nParseDepth; + + auto xFirst = DoProduct(); + while (TokenInGroup(TG::Sum)) + { + std::unique_ptr<SmStructureNode> xSNode(new SmBinHorNode(m_aCurToken)); + xSNode->SetSelection(m_aCurESelection); + auto xSecond = DoOpSubSup(); + auto xThird = DoProduct(); + xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond), std::move(xThird)); + xFirst = std::move(xSNode); + + ++m_nParseDepth; + DepthProtect bDepthGuard(m_nParseDepth); + } + + m_nParseDepth = nDepthLimit; + + return xFirst; +} + +std::unique_ptr<SmNode> SmParser5::DoProduct() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + 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 (m_nParseDepth + nDepthLimit > DEPTH_LIMIT) + throw std::range_error("parser depth limit"); + + std::unique_ptr<SmStructureNode> xSNode; + std::unique_ptr<SmNode> xOper; + + SmTokenType eType = m_aCurToken.eType; + switch (eType) + { + case TOVER: + xSNode.reset(new SmBinVerNode(m_aCurToken)); + xSNode->SetSelection(m_aCurESelection); + xOper.reset(new SmRectangleNode(m_aCurToken)); + xOper->SetSelection(m_aCurESelection); + 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)); + xSNode->SetSelection(m_aCurESelection); + xOper.reset(new SmMathSymbolNode(m_aCurToken)); + xOper->SetSelection(m_aCurESelection); + + 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)); + xOper->SetSelection(m_aCurESelection); + NextToken(); + + break; + } + + default: + xSNode.reset(new SmBinHorNode(m_aCurToken)); + xSNode->SetSelection(m_aCurESelection); + + xOper = DoOpSubSup(); + } + + auto xArg = DoPower(); + xSNode->SetSubNodesBinMo(std::move(xFirst), std::move(xOper), std::move(xArg)); + xFirst = std::move(xSNode); + ++nDepthLimit; + } + return xFirst; +} + +std::unique_ptr<SmNode> SmParser5::DoSubSup(TG nActiveGroup, std::unique_ptr<SmNode> xGivenNode) +{ + DepthProtect aDepthGuard(m_nParseDepth); + + assert(nActiveGroup == TG::Power || nActiveGroup == TG::Limit); + assert(m_aCurToken.nGroup == nActiveGroup); + + std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(m_aCurToken)); + pNode->SetSelection(m_aCurESelection); + //! 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> SmParser5::DoSubSupEvaluate(std::unique_ptr<SmNode> xGivenNode) +{ + DepthProtect aDepthGuard(m_nParseDepth); + + std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(m_aCurToken)); + pNode->SetSelection(m_aCurESelection); + pNode->SetUseLimits(true); + + // 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(TG::Limit)) + { + SmTokenType eType(m_aCurToken.eType); + + switch (eType) + { + case TFROM: + nIndex = static_cast<int>(RSUB); + break; + case TTO: + nIndex = static_cast<int>(RSUP); + 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 + NextToken(); // skip sub-/supscript token + + // get sub-/supscript node + std::unique_ptr<SmNode> xSNode; + xSNode = DoTerm(true); + + aSubNodes[nIndex] = std::move(xENode ? xENode : xSNode); + } + + pNode->SetSubNodes(buildNodeArray(aSubNodes)); + return pNode; +} + +std::unique_ptr<SmNode> SmParser5::DoOpSubSup() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + // get operator symbol + auto xNode = std::make_unique<SmMathSymbolNode>(m_aCurToken); + xNode->SetSelection(m_aCurESelection); + // skip operator token + NextToken(); + // get sub- supscripts if any + if (m_aCurToken.nGroup == TG::Power) + return DoSubSup(TG::Power, std::move(xNode)); + return xNode; +} + +std::unique_ptr<SmNode> SmParser5::DoPower() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + // 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, std::move(xNode)); + return xNode; +} + +std::unique_ptr<SmBlankNode> SmParser5::DoBlank() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + assert(TokenInGroup(TG::Blank)); + std::unique_ptr<SmBlankNode> pBlankNode(new SmBlankNode(m_aCurToken)); + pBlankNode->SetSelection(m_aCurESelection); + + 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> SmParser5::DoTerm(bool bGroupNumberIdent) +{ + DepthProtect aDepthGuard(m_nParseDepth); + + 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->SetSelection(m_aCurESelection); + 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); + xSNode->SetSelection(m_aCurESelection); + 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 TEVALUATE: + return DoEvaluate(); + + case TBLANK: + case TSBLANK: + return DoBlank(); + + case TTEXT: + { + auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_TEXT); + pNode->SetSelection(m_aCurESelection); + NextToken(); + return std::unique_ptr<SmNode>(pNode.release()); + } + case TCHARACTER: + { + auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_VARIABLE); + pNode->SetSelection(m_aCurESelection); + 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); + pTextNode->SetSelection(m_aCurESelection); + 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 TFOURIER: + case TTOWARD: + case TDOTSAXIS: + case TDOTSDIAG: + case TDOTSDOWN: + case TDOTSLOW: + case TDOTSUP: + case TDOTSVERT: + { + auto pNode = std::make_unique<SmMathSymbolNode>(m_aCurToken); + pNode->SetSelection(m_aCurESelection); + 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); + pNode->SetSelection(m_aCurESelection); + NextToken(); + return std::unique_ptr<SmNode>(pNode.release()); + } + + case TPLACE: + { + auto pNode = std::make_unique<SmPlaceNode>(m_aCurToken); + pNode->SetSelection(m_aCurESelection); + NextToken(); + return std::unique_ptr<SmNode>(pNode.release()); + } + + case TSPECIAL: + return DoSpecial(); + + case TBINOM: + return DoBinom(); + + case TFRAC: + return DoFrac(); + + case TSTACK: + return DoStack(); + + case TMATRIX: + return DoMatrix(); + + case THEX: + NextTokenFontSize(); + if (m_aCurToken.eType == THEX) + { + auto pTextNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_NUMBER); + pTextNode->SetSelection(m_aCurESelection); + NextToken(); + return pTextNode; + } + else + return DoError(SmParseError::NumberExpected); + 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>, + std::vector<std::unique_ptr<SmStructureNode>>> + aStack; + bool bIsAttr; + for (;;) + { + bIsAttr = TokenInGroup(TG::Attribute); + if (!bIsAttr && !TokenInGroup(TG::FontAttr)) + break; + aStack.push(bIsAttr ? DoAttribute() : DoFontAttribute()); + } + + 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> SmParser5::DoEscape() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + 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); + pNode->SetSelection(m_aCurESelection); + NextToken(); + return std::unique_ptr<SmNode>(pNode.release()); + } + default: + return DoError(SmParseError::UnexpectedToken); + } +} + +std::unique_ptr<SmOperNode> SmParser5::DoOperator() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + assert(TokenInGroup(TG::Oper)); + + auto xSNode = std::make_unique<SmOperNode>(m_aCurToken); + xSNode->SetSelection(m_aCurESelection); + + // get operator + auto xOperator = DoOper(); + + if (m_aCurToken.nGroup == TG::Limit || m_aCurToken.nGroup == TG::Power) + xOperator = DoSubSup(m_aCurToken.nGroup, std::move(xOperator)); + + // get argument + auto xArg = DoPower(); + + xSNode->SetSubNodes(std::move(xOperator), std::move(xArg)); + return xSNode; +} + +std::unique_ptr<SmNode> SmParser5::DoOper() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + 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)); + pNode->SetSelection(m_aCurESelection); + break; + + case TLIM: + case TLIMSUP: + case TLIMINF: + m_aCurToken.aText + = eType == TLIMSUP ? u"lim sup" : eType == TLIMINF ? u"lim inf" : u"lim"; + pNode.reset(new SmTextNode(m_aCurToken, FNT_TEXT)); + pNode->SetSelection(m_aCurESelection); + break; + + case TOPER: + NextToken(); + OSL_ENSURE(m_aCurToken.eType == TSPECIAL, "Sm: wrong token"); + m_aCurToken.eType = TOPER; + pNode.reset(new SmGlyphSpecialNode(m_aCurToken)); + pNode->SetSelection(m_aCurESelection); + break; + + default: + assert(false && "unknown case"); + } + + NextToken(); + return pNode; +} + +std::unique_ptr<SmStructureNode> SmParser5::DoUnOper() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + assert(TokenInGroup(TG::UnOper)); + + SmToken aNodeToken = m_aCurToken; + ESelection aESelection = m_aCurESelection; + 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->SetSelection(aESelection); + 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.setChar(MS_VERTLINE); + std::unique_ptr<SmNode> xLeft(new SmMathSymbolNode(aNodeToken)); + xLeft->SetSelection(aESelection); + std::unique_ptr<SmNode> xRight(new SmMathSymbolNode(aNodeToken)); + xRight->SetSelection(aESelection); + + xSNode->SetSubNodes(std::move(xLeft), std::move(xArg), std::move(xRight)); + } + else if (eType == TSQRT || eType == TNROOT) + { + xSNode.reset(new SmRootNode(aNodeToken)); + xSNode->SetSelection(aESelection); + xOper.reset(new SmRootSymbolNode(aNodeToken)); + xOper->SetSelection(aESelection); + xSNode->SetSubNodes(std::move(xExtra), std::move(xOper), std::move(xArg)); + } + else + { + xSNode.reset(new SmUnHorNode(aNodeToken)); + xSNode->SetSelection(aESelection); + 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> SmParser5::DoAttribute() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + assert(TokenInGroup(TG::Attribute)); + + auto xSNode = std::make_unique<SmAttributeNode>(m_aCurToken); + xSNode->SetSelection(m_aCurESelection); + 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)); + xAttr->SetSelection(m_aCurESelection); + eScaleMode = SmScaleMode::Width; + break; + + case TWIDEVEC: + case TWIDEHARPOON: + case TWIDEHAT: + case TWIDETILDE: + xAttr.reset(new SmMathSymbolNode(m_aCurToken)); + xAttr->SetSelection(m_aCurESelection); + eScaleMode = SmScaleMode::Width; + break; + + default: + xAttr.reset(new SmMathSymbolNode(m_aCurToken)); + xAttr->SetSelection(m_aCurESelection); + } + + NextToken(); + + xSNode->SetSubNodes(std::move(xAttr), nullptr); // the body will be filled later + xSNode->SetScaleMode(eScaleMode); + return xSNode; +} + +std::unique_ptr<SmStructureNode> SmParser5::DoFontAttribute() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + 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); + pNode->SetSelection(m_aCurESelection); + NextToken(); + return pNode; + } + + case TSIZE: + return DoFontSize(); + + case TFONT: + return DoFont(); + + case TCOLOR: + return DoColor(); + + default: + assert(false); + return {}; + } +} + +std::unique_ptr<SmStructureNode> SmParser5::DoColor() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + assert(m_aCurToken.eType == TCOLOR); + sal_Int32 nBufferIndex = m_nBufferIndex; + NextTokenColor(TCOLOR); + SmToken aToken; + ESelection aESelection; + + if (m_aCurToken.eType == TDVIPSNAMESCOL) + NextTokenColor(TDVIPSNAMESCOL); + if (m_aCurToken.eType == TERROR) + return DoError(SmParseError::ColorExpected); + if (TokenInGroup(TG::Color)) + { + aToken = m_aCurToken; + aESelection = m_aCurESelection; + if (m_aCurToken.eType == TRGB) //loads r, g and b + { + sal_uInt32 nr, ng, nb, nc; + NextTokenFontSize(); + if (lcl_IsNotWholeNumber(m_aCurToken.aText)) + return DoError(SmParseError::ColorExpected); + nr = m_aCurToken.aText.toUInt32(); + if (nr > 255) + return DoError(SmParseError::ColorExpected); + NextTokenFontSize(); + if (lcl_IsNotWholeNumber(m_aCurToken.aText)) + return DoError(SmParseError::ColorExpected); + ng = m_aCurToken.aText.toUInt32(); + if (ng > 255) + return DoError(SmParseError::ColorExpected); + NextTokenFontSize(); + if (lcl_IsNotWholeNumber(m_aCurToken.aText)) + return DoError(SmParseError::ColorExpected); + nb = m_aCurToken.aText.toUInt32(); + if (nb > 255) + return DoError(SmParseError::ColorExpected); + nc = nb | ng << 8 | nr << 16 | sal_uInt32(0) << 24; + aToken.cMathChar = OUString::number(nc, 16); + } + else if (m_aCurToken.eType == TRGBA) //loads r, g and b + { + sal_uInt32 nr, na, ng, nb, nc; + NextTokenFontSize(); + if (lcl_IsNotWholeNumber(m_aCurToken.aText)) + return DoError(SmParseError::ColorExpected); + nr = m_aCurToken.aText.toUInt32(); + if (nr > 255) + return DoError(SmParseError::ColorExpected); + NextTokenFontSize(); + if (lcl_IsNotWholeNumber(m_aCurToken.aText)) + return DoError(SmParseError::ColorExpected); + ng = m_aCurToken.aText.toUInt32(); + if (ng > 255) + return DoError(SmParseError::ColorExpected); + NextTokenFontSize(); + if (lcl_IsNotWholeNumber(m_aCurToken.aText)) + return DoError(SmParseError::ColorExpected); + nb = m_aCurToken.aText.toUInt32(); + if (nb > 255) + return DoError(SmParseError::ColorExpected); + NextTokenFontSize(); + if (lcl_IsNotWholeNumber(m_aCurToken.aText)) + return DoError(SmParseError::ColorExpected); + na = m_aCurToken.aText.toUInt32(); + if (na > 255) + return DoError(SmParseError::ColorExpected); + nc = nb | ng << 8 | nr << 16 | na << 24; + aToken.cMathChar = OUString::number(nc, 16); + } + else if (m_aCurToken.eType == THEX) //loads hex code + { + sal_uInt32 nc; + NextTokenFontSize(); + if (lcl_IsNotWholeNumber16(m_aCurToken.aText)) + return DoError(SmParseError::ColorExpected); + nc = m_aCurToken.aText.toUInt32(16); + aToken.cMathChar = OUString::number(nc, 16); + } + aToken.aText = m_aBufferString.subView(nBufferIndex, m_nBufferIndex - nBufferIndex); + NextToken(); + } + else + return DoError(SmParseError::ColorExpected); + + std::unique_ptr<SmStructureNode> xNode; + xNode.reset(new SmFontNode(aToken)); + xNode->SetSelection(aESelection); + return xNode; +} + +std::unique_ptr<SmStructureNode> SmParser5::DoFont() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + assert(m_aCurToken.eType == TFONT); + + std::unique_ptr<SmStructureNode> xNode; + // last font rules, get that one + SmToken aToken; + ESelection aESelection = m_aCurESelection; + 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)); + xNode->SetSelection(aESelection); + return xNode; +} + +std::unique_ptr<SmStructureNode> SmParser5::DoFontSize() +{ + DepthProtect aDepthGuard(m_nParseDepth); + std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(m_aCurToken)); + pFontNode->SetSelection(m_aCurESelection); + NextTokenFontSize(); + FontSizeType Type; + + switch (m_aCurToken.eType) + { + case THEX: + 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) + { + NextTokenFontSize(); + if (m_aCurToken.eType != THEX) + return DoError(SmParseError::SizeExpected); + } + + // get number argument + Fraction aValue(1); + if (lcl_IsNumber(m_aCurToken.aText)) + { + aValue = m_aCurToken.aText.toDouble(); + //!! Reduce values in order to avoid numerical errors + if (aValue.GetDenominator() > 1000) + { + tools::Long nNum = aValue.GetNumerator(); + tools::Long nDenom = aValue.GetDenominator(); + while (nDenom > 1000) //remove big denominator + { + nNum /= 10; + nDenom /= 10; + } + aValue = Fraction(nNum, nDenom); + } + } + else + return DoError(SmParseError::SizeExpected); + + pFontNode->SetSizeParameter(aValue, Type); + NextToken(); + return pFontNode; +} + +std::unique_ptr<SmStructureNode> SmParser5::DoBrace() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + assert(m_aCurToken.eType == TLEFT || TokenInGroup(TG::LBrace)); + + std::unique_ptr<SmStructureNode> xSNode(new SmBraceNode(m_aCurToken)); + xSNode->SetSelection(m_aCurESelection); + 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)); + pLeft->SetSelection(m_aCurESelection); + + 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)); + pRight->SetSelection(m_aCurESelection); + NextToken(); + } + else + eError = SmParseError::RbraceExpected; + } + else + eError = SmParseError::RightExpected; + } + else + eError = SmParseError::LbraceExpected; + } + else + { + assert(TokenInGroup(TG::LBrace)); + + pLeft.reset(new SmMathSymbolNode(m_aCurToken)); + pLeft->SetSelection(m_aCurESelection); + + 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; + case TLRLINE: + eExpectedType = TLRLINE; + break; + case TLRDLINE: + eExpectedType = TLRDLINE; + break; + default: + SAL_WARN("starmath", "unknown case"); + } + + if (m_aCurToken.eType == eExpectedType) + { + pRight.reset(new SmMathSymbolNode(m_aCurToken)); + pRight->SetSelection(m_aCurESelection); + 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> SmParser5::DoBracebody(bool bIsLeftRight) +{ + DepthProtect aDepthGuard(m_nParseDepth); + + auto pBody = std::make_unique<SmBracebodyNode>(m_aCurToken); + pBody->SetSelection(m_aCurESelection); + + std::vector<std::unique_ptr<SmNode>> aNodes; + // get body if any + if (bIsLeftRight) + { + do + { + if (m_aCurToken.eType == TMLINE) + { + SmMathSymbolNode* pTempNode = new SmMathSymbolNode(m_aCurToken); + pTempNode->SetSelection(m_aCurESelection); + aNodes.emplace_back(std::unique_ptr<SmMathSymbolNode>(pTempNode)); + 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) + { + SmMathSymbolNode* pTempNode = new SmMathSymbolNode(m_aCurToken); + pTempNode->SetSelection(m_aCurESelection); + aNodes.emplace_back(std::unique_ptr<SmMathSymbolNode>(pTempNode)); + 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<SmNode> SmParser5::DoEvaluate() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + // Create node + std::unique_ptr<SmStructureNode> xSNode(new SmBraceNode(m_aCurToken)); + xSNode->SetSelection(m_aCurESelection); + SmToken aToken(TRLINE, MS_VERTLINE, "evaluate", TG::RBrace, 5); + + // Parse body && left none + NextToken(); + std::unique_ptr<SmNode> pBody = DoPower(); + SmToken bToken(TNONE, '\0', "", TG::LBrace, 5); + std::unique_ptr<SmNode> pLeft; + pLeft.reset(new SmMathSymbolNode(bToken)); + + // Mount nodes + std::unique_ptr<SmNode> pRight; + pRight.reset(new SmMathSymbolNode(aToken)); + xSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight)); + xSNode->SetScaleMode(SmScaleMode::Height); // scalable line + + // Parse from to + if (m_aCurToken.nGroup == TG::Limit) + { + std::unique_ptr<SmNode> rSNode; + rSNode = DoSubSupEvaluate(std::move(xSNode)); + rSNode->GetToken().eType = TEVALUATE; + return rSNode; + } + + return xSNode; +} + +std::unique_ptr<SmTextNode> SmParser5::DoFunction() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + if (m_aCurToken.eType == TFUNC) + { + NextToken(); // skip "FUNC"-statement + m_aCurToken.eType = TFUNC; + m_aCurToken.nGroup = TG::Function; + } + auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_FUNCTION); + pNode->SetSelection(m_aCurESelection); + NextToken(); + return pNode; +} + +std::unique_ptr<SmTableNode> SmParser5::DoBinom() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + auto xSNode = std::make_unique<SmTableNode>(m_aCurToken); + xSNode->SetSelection(m_aCurESelection); + + NextToken(); + + auto xFirst = DoSum(); + auto xSecond = DoSum(); + xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond)); + return xSNode; +} + +std::unique_ptr<SmBinVerNode> SmParser5::DoFrac() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + std::unique_ptr<SmBinVerNode> xSNode = std::make_unique<SmBinVerNode>(m_aCurToken); + xSNode->SetSelection(m_aCurESelection); + std::unique_ptr<SmNode> xOper = std::make_unique<SmRectangleNode>(m_aCurToken); + xOper->SetSelection(m_aCurESelection); + + NextToken(); + + auto xFirst = DoSum(); + auto xSecond = DoSum(); + xSNode->SetSubNodes(std::move(xFirst), std::move(xOper), std::move(xSecond)); + return xSNode; +} + +std::unique_ptr<SmStructureNode> SmParser5::DoStack() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + std::unique_ptr<SmStructureNode> xSNode(new SmTableNode(m_aCurToken)); + xSNode->SetSelection(m_aCurESelection); + 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> SmParser5::DoMatrix() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + std::unique_ptr<SmMatrixNode> xMNode(new SmMatrixNode(m_aCurToken)); + xMNode->SetSelection(m_aCurESelection); + 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> SmParser5::DoSpecial() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + 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.subView(1)); + bReplace = true; + } + else if (IsExportSymbolNames()) + { + aNewName = SmLocalizedSymbolData::GetExportSymbolName(rName.subView(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); + pNode->SetSelection(m_aCurESelection); + NextToken(); + return pNode; +} + +std::unique_ptr<SmGlyphSpecialNode> SmParser5::DoGlyphSpecial() +{ + DepthProtect aDepthGuard(m_nParseDepth); + + auto pNode = std::make_unique<SmGlyphSpecialNode>(m_aCurToken); + NextToken(); + return pNode; +} + +std::unique_ptr<SmExpressionNode> SmParser5::DoError(SmParseError eError) +{ + DepthProtect aDepthGuard(m_nParseDepth); + + // Identify error message + OUString sStrBuf(SmResId(RID_ERR_IDENT) + starmathdatabase::getParseErrorDesc(eError)); + + // Generate error node + m_aCurToken.eType = TERROR; + m_aCurToken.cMathChar = sStrBuf; + auto xSNode = std::make_unique<SmExpressionNode>(m_aCurToken); + SmErrorNode* pErr(new SmErrorNode(m_aCurToken)); + pErr->SetSelection(m_aCurESelection); + xSNode->SetSubNode(0, pErr); + + // Append error to the error list + SmErrorDesc aErrDesc(eError, xSNode.get(), m_aCurToken.cMathChar); + m_aErrDescList.push_back(aErrDesc); + + NextToken(); + + return xSNode; +} + +// end grammar + +SmParser5::SmParser5() + : 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().GetCharClass()) +{ +} + +SmParser5::~SmParser5() {} + +std::unique_ptr<SmTableNode> SmParser5::Parse(const OUString& rBuffer) +{ + m_aUsedSymbols.clear(); + + m_aBufferString = convertLineEnd(rBuffer, LINEEND_LF); + m_nBufferIndex = 0; + m_nTokenIndex = 0; + m_nRow = 0; + m_nColOff = 0; + m_nCurError = -1; + + m_aErrDescList.clear(); + + NextToken(); + return DoTable(); +} + +std::unique_ptr<SmNode> SmParser5::ParseExpression(const OUString& rBuffer) +{ + m_aBufferString = convertLineEnd(rBuffer, LINEEND_LF); + m_nBufferIndex = 0; + m_nTokenIndex = 0; + m_nRow = 0; + m_nColOff = 0; + m_nCurError = -1; + + m_aErrDescList.clear(); + + NextToken(); + return DoExpression(); +} + +const SmErrorDesc* SmParser5::NextError() +{ + if (!m_aErrDescList.empty()) + if (m_nCurError > 0) + return &m_aErrDescList[--m_nCurError]; + else + { + m_nCurError = 0; + return &m_aErrDescList[m_nCurError]; + } + else + return nullptr; +} + +const SmErrorDesc* SmParser5::PrevError() +{ + if (!m_aErrDescList.empty()) + if (m_nCurError < static_cast<int>(m_aErrDescList.size() - 1)) + return &m_aErrDescList[++m_nCurError]; + else + { + m_nCurError = static_cast<int>(m_aErrDescList.size() - 1); + return &m_aErrDescList[m_nCurError]; + } + else + return nullptr; +} + +const SmErrorDesc* SmParser5::GetError() const +{ + if (m_aErrDescList.empty()) + return nullptr; + return &m_aErrDescList.front(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/starmath/source/parsebase.cxx b/starmath/source/parsebase.cxx new file mode 100644 index 000000000..31c1e40c9 --- /dev/null +++ b/starmath/source/parsebase.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 <parsebase.hxx> +#include <strings.hrc> +#include <smmod.hxx> + +const TranslateId starmathdatabase::SmParseErrorDesc[] = { + // clang-format off + RID_ERR_NONE, + RID_ERR_UNEXPECTEDCHARACTER, + RID_ERR_UNEXPECTEDTOKEN, + RID_ERR_POUNDEXPECTED, + RID_ERR_COLOREXPECTED, + RID_ERR_LGROUPEXPECTED, + RID_ERR_RGROUPEXPECTED, + RID_ERR_LBRACEEXPECTED, + RID_ERR_RBRACEEXPECTED, + RID_ERR_PARENTMISMATCH, + RID_ERR_RIGHTEXPECTED, + RID_ERR_FONTEXPECTED, + RID_ERR_SIZEEXPECTED, + RID_ERR_DOUBLEALIGN, + RID_ERR_DOUBLESUBSUPSCRIPT, + RID_ERR_NUMBEREXPECTED + // clang-format on +}; + +OUString starmathdatabase::getParseErrorDesc(SmParseError err) +{ + return SmResId(starmathdatabase::SmParseErrorDesc[static_cast<uint_fast8_t>(err)]); +} + +/* 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..e7b761a46 --- /dev/null +++ b/starmath/source/rect.cxx @@ -0,0 +1,621 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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> + +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(vcl::PushFlags::FONT | vcl::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 + tools::Long nScaleFactor = 1; + while( aFntSize.Height() > 2000 * nScaleFactor ) + nScaleFactor *= 2; + + aFnt.SetFontSize( Size( aFntSize.Width() / nScaleFactor, aFntSize.Height() / nScaleFactor ) ); + pGlyphDev->SetFont(aFnt); + + tools::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... */ + { + tools::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) + tools::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 tools::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(vcl::PushFlags::MAPMODE | vcl::PushFlags::FONT); + + pWindow->SetMapMode(rDev.GetMapMode()); + pWindow->SetFont(rDev.GetFontMetric()); + + tools::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; + + tools::Long nDist = 0; + if (pFormat) + nDist = (rDev.GetFont().GetFontSize().Height() + * pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100; + + nHiAttrFence = aGlyphRect.Top() - 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(tools::Long nWidth, tools::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(tools::Long nLeft) +{ + if (nLeft <= GetRight()) + { aSize.setWidth( GetRight() - nLeft + 1 ); + aTopLeft.setX( nLeft ); + } +} + + +void SmRect::SetRight(tools::Long nRight) +{ + if (nRight >= GetLeft()) + aSize.setWidth( nRight - GetLeft() + 1 ); +} + + +void SmRect::SetBottom(tools::Long nBottom) +{ + if (nBottom >= GetTop()) + aSize.setHeight( nBottom - GetTop() + 1 ); +} + + +void SmRect::SetTop(tools::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; + + tools::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; + + tools::Long nL = rRect.GetLeft(), + nR = rRect.GetRight(), + nT = rRect.GetTop(), + nB = rRect.GetBottom(), + nGT = rRect.nGlyphTop, + nGB = rRect.nGlyphBottom; + if (!IsEmpty()) + { tools::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) ! + tools::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, + tools::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.) +{ + tools::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; +} + + +tools::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); + + tools::Long nAbsX = std::abs(aDist.X()), + nAbsY = std::abs(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/rtfexport.cxx b/starmath/source/rtfexport.cxx new file mode 100644 index 000000000..ee5c486ea --- /dev/null +++ b/starmath/source/rtfexport.cxx @@ -0,0 +1,502 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <svtools/rtfkeywd.hxx> +#include <filter/msfilter/rtfutil.hxx> + +SmRtfExport::SmRtfExport(const SmNode* pIn) + : SmWordExportBase(pIn) + , m_pBuffer(nullptr) + , m_nEncoding(RTL_TEXTENCODING_DONTKNOW) +{ +} + +void SmRtfExport::ConvertFromStarMath(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding) +{ + if (!GetTree()) + return; + m_pBuffer = &rBuffer; + m_nEncoding = nEncoding; + m_pBuffer->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE LO_STRING_SVTOOLS_RTF_MOMATH " "); + HandleNode(GetTree(), 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 SmAttributeNode* 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 {}; + 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: " << __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: " << __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: " << __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..e8c38aaa6 --- /dev/null +++ b/starmath/source/rtfexport.hxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "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 SmAttributeNode* 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; +}; + +/* 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..f17ec7864 --- /dev/null +++ b/starmath/source/smdetect.cxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <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..81a68d909 --- /dev/null +++ b/starmath/source/smdetect.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <com/sun/star/document/XExtendedFilterDetection.hpp> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> + + +namespace com::sun::star::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; +}; + +/* 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..f10e9d9db --- /dev/null +++ b/starmath/source/smdll.cxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <svx/svxids.hrc> +#include <svx/modctrl.hxx> +#include <svx/zoomctrl.hxx> +#include <svx/zoomsliderctrl.hxx> +#include <sfx2/docfac.hxx> +#include <sfx2/app.hxx> +#include <sfx2/sidebar/SidebarChildWindow.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); + + sfx2::sidebar::SidebarChildWindow::RegisterChildWindow(false, 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/smediteng.cxx b/starmath/source/smediteng.cxx new file mode 100644 index 000000000..5560d501e --- /dev/null +++ b/starmath/source/smediteng.cxx @@ -0,0 +1,158 @@ +/* -*- 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 <smediteng.hxx> +#include <smmod.hxx> +#include <cfgitem.hxx> + +#include <vcl/settings.hxx> +#include <editeng/editview.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/fontitem.hxx> +#include <svl/itempool.hxx> +#include <svl/itemset.hxx> +#include <vcl/outdev.hxx> +#include <vcl/svapp.hxx> + +SmEditEngine::SmEditEngine(SfxItemPool* pItemPool) + : EditEngine(pItemPool) + , m_nOldZoom(100) + , m_nNewZoom(100) + , m_nDefaultFontSize(0) + , m_aAllSelection(0, 0, 0, 0) +{ + SetText(u""); + + // Add external text leading + SetAddExtLeading(true); + + // Allow to undo changes ( Ctrl + z ) + EnableUndo(true); + + // Length in pixel of a tabulation + SetDefTab(sal_uInt16(Application::GetDefaultDevice()->GetTextWidth("XXXX"))); + + // Set default background color by theme + SetBackgroundColor( + Application::GetDefaultDevice()->GetSettings().GetStyleSettings().GetFieldColor()); + + // Control words + SetControlWord((GetControlWord() | EEControlBits::AUTOINDENTING) + & EEControlBits(~EEControlBits::UNDOATTRIBS) + & EEControlBits(~EEControlBits::PASTESPECIAL)); + + // Word delimiters for auto word selection by double click + SetWordDelimiters(" .=+-*/(){}[];\""); + + // Default mapping mode + SetRefMapMode(MapMode(MapUnit::MapPixel)); + + // Default size of the box + SetPaperSize(Size(1000, 0)); +} + +bool SmEditEngine::checkZoom() +{ + return m_nOldZoom != (m_nNewZoom = SM_MOD()->GetConfig()->GetSmEditWindowZoomFactor()); +} + +void SmEditEngine::executeZoom(EditView* pEditView) +{ + if (checkZoom()) + { + updateZoom(); + if (pEditView) + { + FormatAndLayout(pEditView); + pEditView->SetSelection(pEditView->GetSelection()); + } + } +} + +void SmEditEngine::updateZoom() +{ + // In first run get font size as base to scale + if (m_nDefaultFontSize == 0) + { + SfxItemSet sfxatts = GetAttribs(0, 0, 0, GetAttribsFlags::CHARATTRIBS); + const SvxFontHeightItem* sfxattsh = sfxatts.GetItem(EE_CHAR_FONTHEIGHT); + m_nDefaultFontSize = sfxattsh->GetHeight(); + } + + // Now we calculate the new font size + sal_Int32 nNewFontSize = m_nDefaultFontSize * m_nNewZoom / 100; + + // We apply the new font size to all the text + updateAllESelection(); // Update length of the text + SfxItemSet aSet = GetEmptyItemSet(); + aSet.Put(SvxFontHeightItem(nNewFontSize, 100, EE_CHAR_FONTHEIGHT)); + QuickSetAttribs(aSet, m_aAllSelection); + + // We don't forget to equalize the zoomvalues + m_nOldZoom = m_nNewZoom; +} + +void SmEditEngine::updateAllESelection() +{ + sal_Int32 paracount = GetParagraphCount(); + m_aAllSelection.nEndPara = paracount > 0 ? paracount - 1 : 0; + sal_Int32 textlength = GetTextLen(m_aAllSelection.nEndPara); + m_aAllSelection.nEndPos = textlength > 0 ? textlength : 0; +} + +void SmEditEngine::setSmItemPool(SfxItemPool* mpItemPool, const SvtLinguOptions& maLangOptions) +{ + // Set fonts to be used + struct FontData + { + LanguageType nFallbackLang; + LanguageType nLang; + DefaultFontType nFontType; + sal_uInt16 nFontInfoId; + }; + + FontData aFontDataTable[3] + = { // info to get western font to be used + { LANGUAGE_ENGLISH_US, maLangOptions.nDefaultLanguage, DefaultFontType::FIXED, + EE_CHAR_FONTINFO }, + // info to get CJK font to be used + { LANGUAGE_JAPANESE, maLangOptions.nDefaultLanguage_CJK, DefaultFontType::CJK_TEXT, + EE_CHAR_FONTINFO_CJK }, + // info to get CTL font to be used + { LANGUAGE_ARABIC_SAUDI_ARABIA, maLangOptions.nDefaultLanguage_CTL, + DefaultFontType::CTL_TEXT, EE_CHAR_FONTINFO_CTL } + }; + + // Text color + auto aDefaultDevice = Application::GetDefaultDevice(); + Color aTextColor = aDefaultDevice->GetSettings().GetStyleSettings().GetFieldTextColor(); + for (const FontData& aFontData : aFontDataTable) + { + LanguageType nLang + = (LANGUAGE_NONE == aFontData.nLang) ? aFontData.nFallbackLang : aFontData.nLang; + vcl::Font aFont = OutputDevice::GetDefaultFont(aFontData.nFontType, nLang, + GetDefaultFontFlags::OnlyOne); + aFont.SetColor(aTextColor); + mpItemPool->SetPoolDefaultItem(SvxFontItem(aFont.GetFamilyType(), aFont.GetFamilyName(), + aFont.GetStyleName(), aFont.GetPitch(), + aFont.GetCharSet(), aFontData.nFontInfoId)); + } + + // Set font heights + SvxFontHeightItem aFontHeight( + aDefaultDevice->LogicToPixel(Size(0, 11), MapMode(MapUnit::MapPoint)).Height(), 100, + EE_CHAR_FONTHEIGHT); + mpItemPool->SetPoolDefaultItem(aFontHeight); + aFontHeight.SetWhich(EE_CHAR_FONTHEIGHT_CJK); + mpItemPool->SetPoolDefaultItem(aFontHeight); + aFontHeight.SetWhich(EE_CHAR_FONTHEIGHT_CTL); + mpItemPool->SetPoolDefaultItem(aFontHeight); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/starmath/source/smmod.cxx b/starmath/source/smmod.cxx new file mode 100644 index 000000000..783965844 --- /dev/null +++ b/starmath/source/smmod.cxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <o3tl/string_view.hxx> +#include <sfx2/objface.hxx> +#include <svl/whiter.hxx> +#include <sfx2/viewsh.hxx> +#include <svx/svxids.hrc> +#include <vcl/virdev.hxx> +#include <unotools/syslocale.hxx> +#include <smmod.hxx> +#include <cfgitem.hxx> +#include <dialog.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(TranslateId aId) +{ + return Translate::get(aId, SM_MOD()->GetResLocale()); +} + +OUString SmLocalizedSymbolData::GetUiSymbolName( std::u16string_view rExportName ) +{ + OUString aRes; + + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UI_SYMBOL_NAMES); ++i) + { + if (o3tl::equalsAscii(rExportName, RID_UI_SYMBOL_NAMES[i].mpId)) + { + aRes = SmResId(RID_UI_SYMBOL_NAMES[i]); + break; + } + } + + return aRes; +} + +OUString SmLocalizedSymbolData::GetExportSymbolName( std::u16string_view 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 = RID_UI_SYMBOL_NAMES[i].mpId; + aRes = OUString(pKey, strlen(pKey), RTL_TEXTENCODING_UTF8); + break; + } + } + + return aRes; +} + +OUString SmLocalizedSymbolData::GetUiSymbolSetName( std::u16string_view rExportName ) +{ + OUString aRes; + + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UI_SYMBOLSET_NAMES); ++i) + { + if (o3tl::equalsAscii(rExportName, RID_UI_SYMBOLSET_NAMES[i].mpId)) + { + aRes = SmResId(RID_UI_SYMBOLSET_NAMES[i]); + break; + } + } + + return aRes; +} + +OUString SmLocalizedSymbolData::GetExportSymbolSetName( std::u16string_view 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 = RID_UI_SYMBOLSET_NAMES[i].mpId; + 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()) + return; + + SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + while (pViewShell) + { + // FIXME: What if pViewShell is for a different document, + // but OTOH Math is presumably never used through + // LibreOfficeKit, so maybe an irrelevant concern? + 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::optional<SfxItemSet> SmModule::CreateItemSet( sal_uInt16 nId ) +{ + std::optional<SfxItemSet> pRet; + if(nId == SID_SM_EDITOPTIONS) + { + pRet.emplace( + GetPool(), + svl::Items< //TP_SMPRINT + SID_PRINTTITLE, SID_PRINTZOOM, + SID_NO_RIGHT_SPACES, SID_SAVE_ONLY_USED_SYMBOLS, + SID_AUTO_CLOSE_BRACKETS, SID_SMEDITWINDOWZOOM>); + + 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..5dcf17309 --- /dev/null +++ b/starmath/source/symbol.cxx @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <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; + aRes.reserve(m_aSymbols.size()); + 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( std::u16string_view rSymbolSetName ) +{ + SymbolPtrVec_t aRes; + if (!rSymbolSetName.empty()) + { + 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(u"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(u"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..fccb20f72 --- /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 <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(vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE | + vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR | vcl::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; + Color aConfigDocColor = SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor; + return rOutDev.GetReadableFontColor(aConfigFontColor, aConfigDocColor); + } + + 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..9c15fe035 --- /dev/null +++ b/starmath/source/tmpdevice.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <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; } +}; + +/* 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/unodoc.cxx b/starmath/source/unodoc.cxx new file mode 100644 index 000000000..3c125077c --- /dev/null +++ b/starmath/source/unodoc.cxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <smdll.hxx> +#include <document.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +Math_FormulaDocument_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const& args) +{ + SolarMutexGuard aGuard; + SmGlobals::ensure(); + css::uno::Reference<css::uno::XInterface> xInterface = sfx2::createSfxModelInstance(args, + [](SfxModelFlags _nCreationFlags) + { + SfxObjectShell* pShell = new SmDocShell( _nCreationFlags ); + return pShell->GetModel(); + }); + xInterface->acquire(); + return xInterface.get(); +} + + +/* 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..fd150005d --- /dev/null +++ b/starmath/source/unofilter.cxx @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#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() +{ + return { "com.sun.star.document.ImportFilter" }; +} + +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..42f377f27 --- /dev/null +++ b/starmath/source/unomodel.cxx @@ -0,0 +1,1087 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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/propertyvalue.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 aHintNoLayoutPage{ comphelper::makePropertyValue("HintNoLayoutPage", 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, + HANDLE_STARMATH_VERSION +}; + +} + +static const rtl::Reference<PropertySetInfo> & lcl_createModelPropertyInfo () +{ + static const 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("SyntaxVersion") , HANDLE_STARMATH_VERSION , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, + }; + static const rtl::Reference<PropertySetInfo> PROPS_INFO = new PropertySetInfo ( aModelPropertyInfoMap ); + return PROPS_INFO; +} + +SmModel::SmModel( SfxObjectShell *pObjSh ) +: SfxBaseModel(pObjSh) +, PropertySetHelper ( lcl_createModelPropertyInfo () ) +{ +} + +SmModel::~SmModel() noexcept +{ +} + +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() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL SmModel::release() noexcept +{ + 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() }); +} + +const uno::Sequence< sal_Int8 > & SmModel::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theSmModelUnoTunnelId; + return theSmModelUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL SmModel::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf<SfxBaseModel>{}); +} + +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() +{ + static constexpr OUStringLiteral service1 = u"com.sun.star.document.OfficeDocument"; + static constexpr OUStringLiteral service2 = u"com.sun.star.formula.FormulaProperties"; + return uno::Sequence<OUString>{ service1, service2 }; +} + +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 ); + auto pItemSet = std::make_unique<SfxItemSetFixed< + SID_PRINTTITLE, SID_PRINTTITLE, + SID_PRINTTEXT, SID_PRINTTEXT, + SID_PRINTFRAME, SID_PRINTFRAME, + SID_PRINTSIZE, SID_PRINTSIZE, + SID_PRINTZOOM, SID_PRINTZOOM, + SID_NO_RIGHT_SPACES, SID_NO_RIGHT_SPACES, + SID_SAVE_ONLY_USED_SYMBOLS, SID_SAVE_ONLY_USED_SYMBOLS, + SID_AUTO_CLOSE_BRACKETS, SID_SMEDITWINDOWZOOM>> ( SmDocShell::GetPool() ); + 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; + case HANDLE_STARMATH_VERSION: + pDocSh->SetSmSyntaxVersion(pValues->get<sal_uInt16>()); + 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; + case HANDLE_STARMATH_VERSION: + *pValue <<= pDocSh->GetSmSyntaxVersion(); + 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::getFromUnoTunnel<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<tools::Long>(aPrtPaperSize.Width() * 0.941), + static_cast<tools::Long>(aPrtPaperSize.Height() * 0.961)); + aPrtPageOffset = Point( static_cast<tools::Long>(aPrtPaperSize.Width() * 0.0250), + static_cast<tools::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 ); + if (SfxObjectShell* pDoc = SfxObjectShell::GetShellFromComponent(xParent)) + 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..e8406c103 --- /dev/null +++ b/starmath/source/utility.cxx @@ -0,0 +1,247 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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> + +#include <comphelper/lok.hxx> +#include <sfx2/lokcomponenthelpers.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(); + SmViewShell* pSmView = dynamic_cast<SmViewShell*>(pView); + if (!pSmView && comphelper::LibreOfficeKit::isActive()) + { + auto* pWindow = static_cast<SmGraphicWindow*>(LokStarMathHelper(pView).GetGraphicWindow()); + if (pWindow) + pSmView = &pWindow->GetGraphicWidget().GetView(); + } + return pSmView; +} + + +/**************************************************************************/ + +void SmFontPickList::Clear() +{ + aFontVec.clear(); +} + +SmFontPickList& SmFontPickList::operator = (const SmFontPickList& rList) +{ + Clear(); + nMaxItems = rList.nMaxItems; + aFontVec = rList.aFontVec; + + 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) +{ + const int nPos = m_xWidget->get_active(); + if (nPos != 0) + { + SmFontPickList::Insert(Get(nPos)); + OUString 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); +} + + +tools::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(tools::Long(rFaceSize.Width() * rFrac), + tools::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..2ddb7a230 --- /dev/null +++ b/starmath/source/view.cxx @@ -0,0 +1,2269 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/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/lok.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/string.hxx> +#include <i18nutil/unicode.hxx> +#include <officecfg/Office/Common.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/sfxbasecontroller.hxx> +#include <sfx2/sidebar/SidebarChildWindow.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 <svl/whiter.hxx> +#include <svx/sidebar/SelectionChangeHandler.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/settings.hxx> +#include <vcl/virdev.hxx> +#include <sal/log.hxx> +#include <tools/svborder.hxx> +#include <o3tl/string_view.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> + +#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 3 +#define CMD_BOX_PADDING_TOP 11 + +#define ShellClass_SmViewShell +#include <smslots.hxx> + +using namespace css; +using namespace css::accessibility; +using namespace css::uno; + +SmGraphicWindow::SmGraphicWindow(SmViewShell& rShell) + : InterimItemWindow(&rShell.GetViewFrame()->GetWindow(), "modules/smath/ui/mathwindow.ui", "MathWindow") + , nLinePixH(GetSettings().GetStyleSettings().GetScrollBarSize()) + , nColumnPixW(nLinePixH) + , nZoom(100) + // continue to use user-scrolling to make this work equivalent to how it 'always' worked + , mxScrolledWindow(m_xBuilder->weld_scrolled_window("scrolledwindow", true)) + , mxGraphic(new SmGraphicWidget(rShell, *this)) + , mxGraphicWin(new weld::CustomWeld(*m_xBuilder, "mathview", *mxGraphic)) +{ + InitControlBase(mxGraphic->GetDrawingArea()); + + mxScrolledWindow->connect_hadjustment_changed(LINK(this, SmGraphicWindow, ScrollHdl)); + mxScrolledWindow->connect_vadjustment_changed(LINK(this, SmGraphicWindow, ScrollHdl)); + + // docking windows are usually hidden (often already done in the + // resource) and will be shown by the sfx framework. + Hide(); +} + +void SmGraphicWindow::dispose() +{ + InitControlBase(nullptr); + mxGraphicWin.reset(); + mxGraphic.reset(); + mxScrolledWindow.reset(); + InterimItemWindow::dispose(); +} + +SmGraphicWindow::~SmGraphicWindow() +{ + disposeOnce(); +} + +void SmGraphicWindow::Resize() +{ + InterimItemWindow::Resize(); + + // get the new output-size in pixel + Size aOutPixSz = GetOutputSizePixel(); + + // determine the size of the output-area and if we need scrollbars + const auto nScrSize = mxScrolledWindow->get_scroll_thickness(); + bool bVVisible = false; // by default no vertical-ScrollBar + bool bHVisible = false; // by default no horizontal-ScrollBar + bool bChanged; // determines if a visiblility was changed + do + { + bChanged = false; + + // does we need a vertical ScrollBar + if ( aOutPixSz.Width() < aTotPixSz.Width() && !bHVisible ) + { + bHVisible = true; + aOutPixSz.AdjustHeight( -nScrSize ); + bChanged = true; + } + + // does we need a horizontal ScrollBar + if ( aOutPixSz.Height() < aTotPixSz.Height() && !bVVisible ) + { + bVVisible = true; + aOutPixSz.AdjustWidth( -nScrSize ); + bChanged = true; + } + + } + while ( bChanged ); // until no visibility has changed + + // store the old offset and map-mode + MapMode aMap(GetGraphicMapMode()); + Point aOldPixOffset(aPixOffset); + + // justify (right/bottom borders should never exceed the virtual window) + Size aPixDelta; + if ( aPixOffset.X() < 0 && + aPixOffset.X() + aTotPixSz.Width() < aOutPixSz.Width() ) + aPixDelta.setWidth( + aOutPixSz.Width() - ( aPixOffset.X() + aTotPixSz.Width() ) ); + if ( aPixOffset.Y() < 0 && + aPixOffset.Y() + aTotPixSz.Height() < aOutPixSz.Height() ) + aPixDelta.setHeight( + aOutPixSz.Height() - ( aPixOffset.Y() + aTotPixSz.Height() ) ); + if ( aPixDelta.Width() || aPixDelta.Height() ) + { + aPixOffset.AdjustX(aPixDelta.Width() ); + aPixOffset.AdjustY(aPixDelta.Height() ); + } + + // for axis without scrollbar restore the origin + if ( !bVVisible || !bHVisible ) + { + aPixOffset = Point( + bHVisible + ? aPixOffset.X() + : (aOutPixSz.Width()-aTotPixSz.Width()) / 2, + bVVisible + ? aPixOffset.Y() + : (aOutPixSz.Height()-aTotPixSz.Height()) / 2 ); + } + if (bHVisible && mxScrolledWindow->get_hpolicy() == VclPolicyType::NEVER) + aPixOffset.setX( 0 ); + if (bVVisible && mxScrolledWindow->get_vpolicy() == VclPolicyType::NEVER) + aPixOffset.setY( 0 ); + + // select the shifted map-mode + if (aPixOffset != aOldPixOffset) + SetGraphicMapMode(aMap); + + // show or hide scrollbars + mxScrolledWindow->set_vpolicy(bVVisible ? VclPolicyType::ALWAYS : VclPolicyType::NEVER); + mxScrolledWindow->set_hpolicy(bHVisible ? VclPolicyType::ALWAYS : VclPolicyType::NEVER); + + // resize scrollbars and set their ranges + if ( bHVisible ) + { + mxScrolledWindow->hadjustment_configure(-aPixOffset.X(), 0, aTotPixSz.Width(), nColumnPixW, + aOutPixSz.Width(), aOutPixSz.Width()); + } + if ( bVVisible ) + { + mxScrolledWindow->vadjustment_configure(-aPixOffset.Y(), 0, aTotPixSz.Height(), nLinePixH, + aOutPixSz.Height(), aOutPixSz.Height()); + } +} + +IMPL_LINK_NOARG(SmGraphicWindow, ScrollHdl, weld::ScrolledWindow&, void) +{ + MapMode aMap(GetGraphicMapMode()); + Point aNewPixOffset(aPixOffset); + + // scrolling horizontally? + if (mxScrolledWindow->get_hpolicy() == VclPolicyType::ALWAYS) + aNewPixOffset.setX(-mxScrolledWindow->hadjustment_get_value()); + + // scrolling vertically? + if (mxScrolledWindow->get_vpolicy() == VclPolicyType::ALWAYS) + aNewPixOffset.setY(-mxScrolledWindow->vadjustment_get_value()); + + // scrolling? + if (aPixOffset == aNewPixOffset) + return; + + // recompute the logical scroll units + aPixOffset = aNewPixOffset; + + SetGraphicMapMode(aMap); +} + +void SmGraphicWindow::SetGraphicMapMode(const MapMode& rNewMapMode) +{ + OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device(); + MapMode aMap( rNewMapMode ); + aMap.SetOrigin( aMap.GetOrigin() + rDevice.PixelToLogic( aPixOffset, aMap ) ); + rDevice.SetMapMode( aMap ); + mxGraphic->Invalidate(); +} + +MapMode SmGraphicWindow::GetGraphicMapMode() const +{ + OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device(); + MapMode aMap(rDevice.GetMapMode()); + aMap.SetOrigin( aMap.GetOrigin() - rDevice.PixelToLogic( aPixOffset ) ); + return aMap; +} + +void SmGraphicWindow::SetTotalSize( const Size& rNewSize ) +{ + OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device(); + aTotPixSz = rDevice.LogicToPixel(rNewSize); + Resize(); +} + +Size SmGraphicWindow::GetTotalSize() const +{ + OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device(); + return rDevice.PixelToLogic(aTotPixSz); +} + +void SmGraphicWindow::ShowContextMenu(const CommandEvent& rCEvt) +{ + GetParent()->ToTop(); + Point aPos(5, 5); + if (rCEvt.IsMouseEvent()) + aPos = rCEvt.GetMousePosPixel(); + + // added for replaceability of context menus + SfxDispatcher::ExecutePopup( this, &aPos ); +} + +SmGraphicWidget::SmGraphicWidget(SmViewShell& rShell, SmGraphicWindow& rGraphicWindow) + : mrGraphicWindow(rGraphicWindow) + , bIsCursorVisible(false) + , bIsLineVisible(false) + , aCaretBlinkTimer("SmGraphicWidget aCaretBlinkTimer") + , mrViewShell(rShell) +{ +} + +void SmGraphicWidget::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + + rDevice.SetBackground(SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor); + + const Fraction aFraction(1, 1); + rDevice.SetMapMode(MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction)); + + SetTotalSize(); + + SetHelpId(HID_SMA_WIN_DOCUMENT); + + ShowLine(false); + CaretBlinkInit(); +} + +SmGraphicWidget::~SmGraphicWidget() +{ + if (mxAccessible.is()) + mxAccessible->ClearWin(); // make Accessible nonfunctional + mxAccessible.clear(); + CaretBlinkStop(); +} + +bool SmGraphicWidget::MouseButtonDown(const MouseEvent& 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 true; + + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + // get click position relative to formula + Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos()); + + const SmNode *pTree = mrViewShell.GetDoc()->GetFormulaTree(); + if (!pTree) + return true; + + if (SmViewShell::IsInlineEditEnabled()) { + mrViewShell.GetDoc()->GetCursor().MoveTo(&rDevice, aPos, !rMEvt.IsShift()); + return true; + } + 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 true; + + SmEditWindow* pEdit = mrViewShell.GetEditWindow(); + if (!pEdit) + return true; + + // set selection to the beginning of the token + pEdit->SetSelection(pNode->GetSelection()); + SetCursor(pNode); + + // allow for immediate editing and + //! implicitly synchronize the cursor position mark in this window + pEdit->GrabFocus(); + + return true; +} + +bool SmGraphicWidget::MouseMove(const MouseEvent &rMEvt) +{ + if (rMEvt.IsLeft() && SmViewShell::IsInlineEditEnabled()) + { + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos()); + mrViewShell.GetDoc()->GetCursor().MoveTo(&rDevice, aPos, false); + + CaretBlinkStop(); + SetIsCursorVisible(true); + CaretBlinkStart(); + RepaintViewShellDoc(); + } + return true; +} + +void SmGraphicWidget::GetFocus() +{ + if (!SmViewShell::IsInlineEditEnabled()) + return; + if (mrViewShell.GetEditWindow()) + mrViewShell.GetEditWindow()->Flush(); + //Let view shell know what insertions should be done in visual editor + mrViewShell.SetInsertIntoEditWindow(false); + SetIsCursorVisible(true); + ShowLine(true); + CaretBlinkStart(); + RepaintViewShellDoc(); +} + +void SmGraphicWidget::LoseFocus() +{ + if (mxAccessible.is()) + { + uno::Any aOldValue, aNewValue; + aOldValue <<= AccessibleStateType::FOCUSED; + // aNewValue remains empty + mxAccessible->LaunchEvent( AccessibleEventId::STATE_CHANGED, + aOldValue, aNewValue ); + } + if (!SmViewShell::IsInlineEditEnabled()) + return; + SetIsCursorVisible(false); + ShowLine(false); + CaretBlinkStop(); + RepaintViewShellDoc(); +} + +void SmGraphicWidget::RepaintViewShellDoc() +{ + SmDocShell* pDoc = mrViewShell.GetDoc(); + if (pDoc) + pDoc->Repaint(); +} + +IMPL_LINK_NOARG(SmGraphicWidget, CaretBlinkTimerHdl, Timer *, void) +{ + if (IsCursorVisible()) + SetIsCursorVisible(false); + else + SetIsCursorVisible(true); + + RepaintViewShellDoc(); +} + +void SmGraphicWidget::CaretBlinkInit() +{ + aCaretBlinkTimer.SetInvokeHandler(LINK(this, SmGraphicWidget, CaretBlinkTimerHdl)); + aCaretBlinkTimer.SetTimeout(Application::GetSettings().GetStyleSettings().GetCursorBlinkTime()); +} + +void SmGraphicWidget::CaretBlinkStart() +{ + if (!SmViewShell::IsInlineEditEnabled()) + return; + if (aCaretBlinkTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME) + aCaretBlinkTimer.Start(); +} + +void SmGraphicWidget::CaretBlinkStop() +{ + if (!SmViewShell::IsInlineEditEnabled()) + return; + aCaretBlinkTimer.Stop(); +} + +// shows or hides the formula-cursor depending on 'bShow' is true or not +void SmGraphicWidget::ShowCursor(bool bShow) +{ + if (SmViewShell::IsInlineEditEnabled()) + return; + + bool bInvert = bShow != IsCursorVisible(); + if (bInvert) + { + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + InvertFocusRect(rDevice, aCursorRect); + } + + SetIsCursorVisible(bShow); +} + +void SmGraphicWidget::ShowLine(bool bShow) +{ + if (!SmViewShell::IsInlineEditEnabled()) + return; + + bIsLineVisible = bShow; +} + +void SmGraphicWidget::SetCursor(const SmNode *pNode) +{ + if (SmViewShell::IsInlineEditEnabled()) + return; + + const SmNode *pTree = mrViewShell.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 SmGraphicWidget::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 (SmViewShell::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 * SmGraphicWidget::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 (SmViewShell::IsInlineEditEnabled()) + return nullptr; + + // find visible node with token at nRow, nCol + const SmNode *pTree = mrViewShell.GetDoc()->GetFormulaTree(), + *pNode = nullptr; + if (pTree) + pNode = pTree->FindTokenAt(nRow, nCol); + + if (pNode) + SetCursor(pNode); + else + ShowCursor(false); + + return pNode; +} + +void SmGraphicWidget::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + SmDocShell& rDoc = *mrViewShell.GetDoc(); + Point aPoint; + + rDoc.DrawFormula(rRenderContext, aPoint, true); //! modifies aPoint to be the topleft + //! corner of the formula + aFormulaDrawPos = aPoint; + if (SmViewShell::IsInlineEditEnabled()) + { + //Draw cursor if any... + if (mrViewShell.GetDoc()->HasCursor() && IsLineVisible()) + mrViewShell.GetDoc()->GetCursor().Draw(rRenderContext, aPoint, IsCursorVisible()); + } + else + { + SetIsCursorVisible(false); // (old) cursor must be drawn again + + const SmEditWindow* pEdit = mrViewShell.GetEditWindow(); + if (pEdit) + { // get new position for formula-cursor (for possible altered formula) + sal_Int32 nRow; + sal_uInt16 nCol; + SmGetLeftSelectionPart(pEdit->GetSelection(), nRow, nCol); + const SmNode *pFound = SetCursorPos(static_cast<sal_uInt16>(nRow), nCol); + + SmModule *pp = SM_MOD(); + if (pFound && pp->GetConfig()->IsShowFormulaCursor()) + ShowCursor(true); + } + } +} + +void SmGraphicWidget::SetTotalSize() +{ + SmDocShell &rDoc = *mrViewShell.GetDoc(); + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + const Size aTmp(rDevice.PixelToLogic(rDevice.LogicToPixel(rDoc.GetSize()))); + if (aTmp != mrGraphicWindow.GetTotalSize()) + mrGraphicWindow.SetTotalSize(aTmp); +} + +namespace +{ +SmBracketType BracketTypeOf(sal_uInt32 c) +{ + switch (c) + { + case '(': + case ')': + return SmBracketType::Round; + case '[': + case ']': + return SmBracketType::Square; + case '{': + case '}': + return SmBracketType::Curly; + } + assert(false); // Unreachable + return SmBracketType::Round; +} + +bool CharInput(sal_uInt32 c, SmCursor& rCursor, OutputDevice& rDevice) +{ + switch (c) + { + case 0: + return false; + case ' ': + rCursor.InsertElement(BlankElement); + break; + case '!': + rCursor.InsertElement(FactorialElement); + break; + case '%': + rCursor.InsertElement(PercentElement); + break; + case '*': + rCursor.InsertElement(CDotElement); + break; + case '+': + rCursor.InsertElement(PlusElement); + break; + case '-': + rCursor.InsertElement(MinusElement); + break; + case '<': + rCursor.InsertElement(LessThanElement); + break; + case '=': + rCursor.InsertElement(EqualElement); + break; + case '>': + rCursor.InsertElement(GreaterThanElement); + break; + case '^': + rCursor.InsertSubSup(RSUP); + break; + case '_': + rCursor.InsertSubSup(RSUB); + break; + case '/': + rCursor.InsertFraction(); + break; + case '(': + case '[': + case '{': + rCursor.InsertBrackets(BracketTypeOf(c)); + break; + case ')': + case ']': + case '}': + if (rCursor.IsAtTailOfBracket(BracketTypeOf(c))) + { + rCursor.Move(&rDevice, MoveRight); + break; + } + [[fallthrough]]; + default: + rCursor.InsertText(OUString(&c, 1)); + break; + } + return true; +} +} + +bool SmGraphicWidget::KeyInput(const KeyEvent& rKEvt) +{ + if (!SmViewShell::IsInlineEditEnabled()) + return mrViewShell.KeyInput(rKEvt); + + bool bConsumed = true; + + SmCursor& rCursor = mrViewShell.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 { + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + switch(nCode) + { + case KEY_LEFT: + { + rCursor.Move(&rDevice, MoveLeft, !rKEvt.GetKeyCode().IsShift()); + }break; + case KEY_RIGHT: + { + rCursor.Move(&rDevice, MoveRight, !rKEvt.GetKeyCode().IsShift()); + }break; + case KEY_UP: + { + rCursor.Move(&rDevice, MoveUp, !rKEvt.GetKeyCode().IsShift()); + }break; + case KEY_DOWN: + { + rCursor.Move(&rDevice, MoveDown, !rKEvt.GetKeyCode().IsShift()); + }break; + case KEY_RETURN: + { + if(!rKEvt.GetKeyCode().IsShift()) + rCursor.InsertRow(); + }break; + case KEY_DELETE: + { + if(!rCursor.HasSelection()){ + rCursor.Move(&rDevice, MoveRight, false); + if(rCursor.HasComplexSelection()) break; + } + rCursor.Delete(); + }break; + case KEY_BACKSPACE: + { + rCursor.DeletePrev(&rDevice); + }break; + default: + { + if (!CharInput(rKEvt.GetCharCode(), rCursor, rDevice)) + bConsumed = mrViewShell.KeyInput(rKEvt); + } + } + } + CaretBlinkStop(); + CaretBlinkStart(); + SetIsCursorVisible(true); + RepaintViewShellDoc(); + + return bConsumed; +} + +bool SmGraphicWidget::Command(const CommandEvent& rCEvt) +{ + bool bCallBase = true; + if (!mrViewShell.GetViewFrame()->GetFrame().IsInPlace()) + { + switch ( rCEvt.GetCommand() ) + { + case CommandEventId::ContextMenu: + // purely for "ExecutePopup" taking a vcl::Window and + // we assume SmGraphicWindow 0,0 is at SmEditWindow 0,0 + mrGraphicWindow.ShowContextMenu(rCEvt); + bCallBase = false; + break; + + case CommandEventId::Wheel: + { + const CommandWheelData* pWData = rCEvt.GetWheelData(); + if ( pWData && CommandWheelMode::ZOOM == pWData->GetMode() ) + { + sal_uInt16 nTmpZoom = mrGraphicWindow.GetZoom(); + if( 0 > pWData->GetDelta() ) + nTmpZoom -= 10; + else + nTmpZoom += 10; + mrGraphicWindow.SetZoom(nTmpZoom); + bCallBase = false; + } + } + break; + + default: break; + } + } + else + { + switch (rCEvt.GetCommand()) + { + case CommandEventId::ExtTextInput: + if (comphelper::LibreOfficeKit::isActive()) + { + const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData(); + assert(pData); + const OUString& rText = pData->GetText(); + SmCursor& rCursor = mrViewShell.GetDoc()->GetCursor(); + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + for (sal_Int32 i = 0; i < rText.getLength();) + CharInput(rText.iterateCodePoints(&i), rCursor, rDevice); + bCallBase = false; + } + break; + default: + break; + } + } + return !bCallBase; +} + +void SmGraphicWindow::SetZoom(sal_uInt16 Factor) +{ + nZoom = std::clamp(Factor, MINZOOM, MAXZOOM); + Fraction aFraction(nZoom, 100); + SetGraphicMapMode(MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction)); + mxGraphic->SetTotalSize(); + SmViewShell& rViewSh = mxGraphic->GetView(); + rViewSh.GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOM); + rViewSh.GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOMSLIDER); +} + +void SmGraphicWindow::ZoomToFitInWindow() +{ + SmViewShell& rViewSh = mxGraphic->GetView(); + SmDocShell& rDoc = *rViewSh.GetDoc(); + + // set defined mapmode before calling 'LogicToPixel' below + SetGraphicMapMode(MapMode(MapUnit::Map100thMM)); + + OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device(); + Size aSize(rDevice.LogicToPixel(rDoc.GetSize())); + Size aWindowSize(GetSizePixel()); + + if (!aSize.IsEmpty()) + { + tools::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 > SmGraphicWidget::CreateAccessible() +{ + if (!mxAccessible.is()) + { + mxAccessible = new SmGraphicAccessible( this ); + } + return mxAccessible; +} + +/**************************************************************************/ +SmGraphicController::SmGraphicController(SmGraphicWidget &rSmGraphic, + sal_uInt16 nId_, + SfxBindings &rBindings) : + SfxControllerItem(nId_, rBindings), + rGraphic(rSmGraphic) +{ +} + +void SmGraphicController::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState) +{ + rGraphic.SetTotalSize(); + rGraphic.Invalidate(); + SfxControllerItem::StateChangedAtToolBoxControl (nSID, eState, pState); +} + +/**************************************************************************/ +SmEditController::SmEditController(SmEditWindow &rSmEdit, + sal_uInt16 nId_, + SfxBindings &rBindings) : + SfxControllerItem(nId_, rBindings), + rEdit(rSmEdit) +{ +} + +void SmEditController::StateChangedAtToolBoxControl(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::StateChangedAtToolBoxControl (nSID, eState, pState); +} + +/**************************************************************************/ +SmCmdBoxWindow::SmCmdBoxWindow(SfxBindings *pBindings_, SfxChildWindow *pChildWindow, + vcl::Window *pParent) + : SfxDockingWindow(pBindings_, pChildWindow, pParent, "EditWindow", "modules/smath/ui/editwindow.ui") + , m_xEdit(new SmEditWindow(*this, *m_xBuilder)) + , aController(*m_xEdit, SID_TEXT, *pBindings_) + , bExiting(false) + , aInitialFocusTimer("SmCmdBoxWindow aInitialFocusTimer") +{ + set_id("math_edit"); + + SetHelpId( HID_SMA_COMMAND_WIN ); + SetSizePixel(LogicToPixel(Size(292 , 94), MapMode(MapUnit::MapAppFont))); + SetText(SmResId(STR_CMDBOXWINDOW)); + + Hide(); + + // Don't try to grab focus in LOK inline edit mode + if (!comphelper::LibreOfficeKit::isActive()) + { + aInitialFocusTimer.SetInvokeHandler(LINK(this, SmCmdBoxWindow, InitialFocusTimerHdl)); + aInitialFocusTimer.SetTimeout(100); + } +} + +Point SmCmdBoxWindow::WidgetToWindowPos(const weld::Widget& rWidget, const Point& rPos) +{ + Point aRet(rPos); + int x(0), y(0), width(0), height(0); + rWidget.get_extents_relative_to(*m_xContainer, x, y, width, height); + aRet.Move(x, y); + aRet.Move(m_xBox->GetPosPixel().X(), m_xBox->GetPosPixel().Y()); + return aRet; +} + +void SmCmdBoxWindow::ShowContextMenu(const Point& rPos) +{ + ToTop(); + SmViewShell *pViewSh = GetView(); + if (pViewSh) + pViewSh->GetViewFrame()->GetDispatcher()->ExecutePopup("edit", this, &rPos); +} + +void SmCmdBoxWindow::Command(const CommandEvent& rCEvt) +{ + if (rCEvt.GetCommand() == CommandEventId::ContextMenu) + { + ShowContextMenu(rCEvt.GetMousePosPixel()); + return; + } + + SfxDockingWindow::Command(rCEvt); +} + +SmCmdBoxWindow::~SmCmdBoxWindow () +{ + disposeOnce(); +} + +void SmCmdBoxWindow::dispose() +{ + aInitialFocusTimer.Stop(); + bExiting = true; + aController.dispose(); + m_xEdit.reset(); + SfxDockingWindow::dispose(); +} + +SmViewShell * SmCmdBoxWindow::GetView() +{ + SfxDispatcher *pDispatcher = GetBindings().GetDispatcher(); + SfxViewShell *pView = pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr; + return dynamic_cast<SmViewShell*>( pView); +} + +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() ); + + m_xEdit->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) + m_xEdit->GrabFocus(); +} + +SFX_IMPL_DOCKINGWINDOW_WITHID(SmCmdBoxWrapper, SID_CMDBOXWINDOW); + +SmCmdBoxWrapper::SmCmdBoxWrapper(vcl::Window *pParentWindow, sal_uInt16 nId, + SfxBindings *pBindings, + SfxChildWinInfo *pInfo) : + SfxChildWindow(pParentWindow, nId) +{ + VclPtrInstance<SmCmdBoxWindow> pDialog(pBindings, this, pParentWindow); + SetWindow(pDialog); + // make window docked to the bottom initially (after first start) + SetAlignment(SfxChildAlignment::BOTTOM); + pDialog->setDeferredProperties(); + pDialog->set_border_width(CMD_BOX_PADDING); + pDialog->set_margin_top(CMD_BOX_PADDING_TOP); + pDialog->Initialize(pInfo); +} + +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()); + + GetStaticInterface()->RegisterChildWindow(::sfx2::sidebar::SidebarChildWindow::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)); + Fraction aZoomX(aProvidedSize.Width(), aObjSize.Width()); + Fraction aZoomY(aProvidedSize.Height(), aObjSize.Height()); + MapMode aMap(mxGraphicWindow->GetGraphicMapMode()); + aMap.SetScaleX(aZoomX); + aMap.SetScaleY(aZoomY); + mxGraphicWindow->SetGraphicMapMode(aMap); + } + + SetBorderPixel( SvBorder() ); + mxGraphicWindow->SetPosSizePixel(rOfs, rSize); + GetGraphicWidget().SetTotalSize(); +} + +void SmViewShell::OuterResizePixel(const Point &rOfs, const Size &rSize) +{ + mxGraphicWindow->SetPosSizePixel(rOfs, rSize); + if (GetDoc()->IsPreview()) + mxGraphicWindow->ZoomToFitInWindow(); +} + +void SmViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const +{ + rRect.SetSize(mxGraphicWindow->GetSizePixel()); +} + +void SmViewShell::SetZoomFactor( const Fraction &rX, const Fraction &rY ) +{ + const Fraction &rFrac = std::min(rX, rY); + mxGraphicWindow->SetZoom(sal::static_int_cast<sal_uInt16>(tools::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 tools::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, std::u16string_view rText, tools::Long MaxWidth) +{ + Size aSize; + Size aTextSize; + if (rText.empty()) + return aTextSize; + + sal_Int32 nPos = 0; + do + { + OUString aLine( o3tl::getToken(rText, 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, u""); + aSize = GetTextLineSize(rDevice, aText); + aTextSize.AdjustHeight(aSize.Height() ); + aTextSize.setWidth( std::clamp(aSize.Width(), aTextSize.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 tools::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, std::u16string_view rText, sal_uInt16 MaxWidth) +{ + if (rText.empty()) + return; + + Point aPoint(rPosition); + Size aSize; + + sal_Int32 nPos = 0; + do + { + OUString aLine( o3tl::getToken(rText, 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, u""); + + 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(tools::Long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())), + tools::Long(Fraction(OutputSize.Height() * 100, GraphicSize.Height())))); + nZ -= 10; + Fraction aFraction (std::clamp(nZ, MINZOOM, sal_uInt16(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; +} + +SmElementsDockingWindow* SmViewShell::GetDockingWindow() +{ + auto eldockwinwrap = GetViewFrame()->GetChildWindow(SmElementsDockingWindowWrapper::GetChildWindowId()); + if(eldockwinwrap) + return dynamic_cast<SmElementsDockingWindow*>(eldockwinwrap->GetWindow()); + else + 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_GRAPHIC_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_GRAPHIC_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()) + GetGraphicWidget().ShowCursor(bVal); + break; + } + case SID_DRAW: + if (pWin) + { + GetDoc()->SetText( pWin->GetText() ); + SetStatusText(OUString()); + ShowError( nullptr ); + GetDoc()->Repaint(); + } + break; + + case SID_ZOOM_OPTIMAL: + mxGraphicWindow->ZoomToFitInWindow(); + break; + + case SID_ZOOMIN: + mxGraphicWindow->SetZoom(mxGraphicWindow->GetZoom() + 25); + break; + + case SID_ZOOMOUT: + SAL_WARN_IF( mxGraphicWindow->GetZoom() < 25, "starmath", "incorrect sal_uInt16 argument" ); + mxGraphicWindow->SetZoom(mxGraphicWindow->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::getFromUnoTunnel<TransferableHelper>(xTrans); + if (pTrans) + { + SmEditWindow *pEditWin = GetEditWindow(); + pTrans->CopyToClipboard(pEditWin->GetClipboard()); + } + } + } + break; + + case SID_PASTEOBJECT: + { + SmEditWindow *pEditWin = GetEditWindow(); + TransferableDataHelper aData(TransferableDataHelper::CreateFromClipboard(pEditWin->GetClipboard())); + 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 ) + { + SmEditWindow *pEditWin = GetEditWindow(); + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromClipboard( + pEditWin->GetClipboard())); + + 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()); + GetGraphicWidget().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: + { + mpRequest.reset(new SfxRequest( rReq )); + mpDocInserter.reset(new ::sfx2::DocumentInserter(pWin ? pWin->GetFrameWeld() : nullptr, + GetDoc()->GetFactory().GetFactoryName())); + mpDocInserter->StartExecuteModal( LINK( this, SmViewShell, DialogClosedHdl ) ); + break; + } + + case SID_IMPORT_MATHML_CLIPBOARD: + { + SmEditWindow *pEditWin = GetEditWindow(); + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromClipboard(pEditWin->GetClipboard())); + uno::Reference < io::XInputStream > xStrm; + if ( aDataHelper.GetTransferable().is() ) + { + SotClipboardFormatId nId = SotClipboardFormatId::MATHML; + if (aDataHelper.HasFormat(nId)) + { + xStrm = aDataHelper.GetInputStream(nId, ""); + if (xStrm.is()) + { + SfxMedium aClipboardMedium; + aClipboardMedium.GetItemSet(); //generate initial itemset, not sure if necessary + std::shared_ptr<const SfxFilter> pMathFilter = + SfxFilter::GetFilterByName(MATHML_XML); + aClipboardMedium.SetFilter(pMathFilter); + aClipboardMedium.setStreamToLoadFrom(xStrm, true /*bIsReadOnly*/); + InsertFrom(aClipboardMedium); + 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; + + SfxMedium aClipboardMedium; + aClipboardMedium.GetItemSet(); //generates initial itemset, not sure if necessary + std::shared_ptr<const SfxFilter> pMathFilter = + SfxFilter::GetFilterByName(MATHML_XML); + aClipboardMedium.SetFilter(pMathFilter); + + SvMemoryStream aStrm( const_cast<sal_Unicode *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode), StreamMode::READ); + uno::Reference<io::XInputStream> xStrm2( new ::utl::OInputStreamWrapper(aStrm) ); + aClipboardMedium.setStreamToLoadFrom(xStrm2, true /*bIsReadOnly*/); + InsertFrom(aClipboardMedium); + 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 + { + SfxItemSetFixed<SID_ATTR_ZOOM, SID_ATTR_ZOOM> aSet( SmDocShell::GetPool() ); + aSet.Put( SvxZoomItem( SvxZoomType::PERCENT, mxGraphicWindow->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(); + mxGraphicWindow->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->GetFontFaceCollectionCount() == 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::CreateFromClipboard( + pEditWin->GetClipboard()) ); + + 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, mxGraphicWindow->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 = mxGraphicWindow->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; + } + } +} + +namespace +{ +class SmController : public SfxBaseController +{ +public: + SmController(SfxViewShell& rViewShell) + : SfxBaseController(&rViewShell) + , mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler( + GetContextName, this, vcl::EnumContext::Context::Math)) + { + mpSelectionChangeHandler->Connect(); + rViewShell.SetContextName(GetContextName()); + } + ~SmController() { mpSelectionChangeHandler->Disconnect(); } + + // css::frame::XController + void SAL_CALL attachFrame(const css::uno::Reference<css::frame::XFrame>& xFrame) override + { + SfxBaseController::attachFrame(xFrame); + + mpSelectionChangeHandler->selectionChanged({}); // Installs the correct context + } + +private: + static OUString GetContextName() { return "Math"; } // Static constant for now + + rtl::Reference<svx::sidebar::SelectionChangeHandler> mpSelectionChangeHandler; +}; +} + +SmViewShell::SmViewShell(SfxViewFrame *pFrame_, SfxViewShell *) + : SfxViewShell(pFrame_, SfxViewShellFlags::HAS_PRINTOPTIONS) + , mxGraphicWindow(VclPtr<SmGraphicWindow>::Create(*this)) + , maGraphicController(mxGraphicWindow->GetGraphicWidget(), SID_GRAPHIC_SM, pFrame_->GetBindings()) + , mbPasteState(false) + , mbInsertIntoEditWindow(false) +{ + SetStatusText(OUString()); + SetWindow(mxGraphicWindow.get()); + SfxShell::SetName("SmView"); + SfxShell::SetUndoManager( &GetDoc()->GetEditEngine().GetUndoManager() ); + SetController(new SmController(*this)); +} + +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(); + mxGraphicWindow.disposeAndClear(); +} + +void SmViewShell::Deactivate( bool bIsMDIActivate ) +{ + SmEditWindow *pEdit = GetEditWindow(); + if ( pEdit ) + pEdit->Flush(); + + SfxViewShell::Deactivate( bIsMDIActivate ); +} + +void SmViewShell::Activate( bool bIsMDIActivate ) +{ + SfxViewShell::Activate( bIsMDIActivate ); + + if (comphelper::LibreOfficeKit::isActive()) + { + // In LOK, activate in-place editing + GetGraphicWidget().GrabFocus(); + } + else if (SmEditWindow *pEdit = GetEditWindow()) + { + //! 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(mpDocInserter && "ScDocShell::DialogClosedHdl(): no document inserter"); + + if ( ERRCODE_NONE == _pFileDlg->GetError() ) + { + std::unique_ptr<SfxMedium> pMedium = mpDocInserter->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_GRAPHIC_SM); + } + } + + mpRequest->SetReturnValue( SfxBoolItem( mpRequest->GetSlot(), true ) ); + mpRequest->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() +{ + return comphelper::LibreOfficeKit::isActive() + || officecfg::Office::Common::Misc::ExperimentalMode::get(); +} + +void SmViewShell::ZoomByItemSet(const SfxItemSet *pSet) +{ + assert(pSet); + const SvxZoomItem &rZoom = pSet->Get(SID_ATTR_ZOOM); + switch( rZoom.GetType() ) + { + case SvxZoomType::PERCENT: + mxGraphicWindow->SetZoom(sal::static_int_cast<sal_uInt16>(rZoom.GetValue ())); + break; + + case SvxZoomType::OPTIMAL: + mxGraphicWindow->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(tools::Long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())), + tools::Long(Fraction(OutputSize.Height() * 100, GraphicSize.Height())))); + mxGraphicWindow->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..980c6346c --- /dev/null +++ b/starmath/source/visitors.cxx @@ -0,0 +1,2727 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <starmathdatabase.hxx> + +// 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( SmAttributeNode* 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( vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE | vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::TEXTCOLOR ); + + maPos.pSelectedNode->Accept( this ); + //Restore device state + mrDev.Pop( ); +} + +void SmCaretDrawingVisitor::Visit( SmTextNode* pNode ) +{ + tools::Long i = maPos.nIndex; + + mrDev.SetFont( pNode->GetFont( ) ); + + //Find the line + SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode ); + + //Find coordinates + tools::Long left = pNode->GetLeft( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, i ) + maOffset.X( ); + tools::Long top = pLine->GetTop( ) + maOffset.Y( ); + tools::Long height = pLine->GetHeight( ); + tools::Long left_line = pLine->GetLeft( ) + maOffset.X( ); + tools::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 + tools::Long left = pNode->GetLeft( ) + maOffset.X( ) + ( maPos.nIndex == 1 ? pNode->GetWidth( ) : 0 ); + tools::Long top = pLine->GetTop( ) + maOffset.Y( ); + tools::Long height = pLine->GetHeight( ); + tools::Long left_line = pLine->GetLeft( ) + maOffset.X( ); + tools::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( vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR ); + + tools::Long i = maPos.nIndex; + + mpDev->SetFont( pNode->GetFont( ) ); + + //Find coordinates + tools::Long left = pNode->GetLeft( ) + mpDev->GetTextWidth( pNode->GetText( ), 0, i ); + tools::Long top = pNode->GetTop( ); + tools::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( SmAttributeNode* 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} ) + tools::Long nBarHeight = pNode->GetWidth( ) * 7 / 100; + tools::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; + + tools::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 ) { + tools::Long i1 = -1, + i2 = -1; + if( maStartPos.pSelectedNode == pNode ) + i1 = maStartPos.nIndex; + if( maEndPos.pSelectedNode == pNode ) + i2 = maEndPos.nIndex; + + tools::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; + + if( pSubSup ) { + SmNode* pChild = pSubSup->GetSubSup( LSUP ); + if( pChild ) { + //Create position in front of pChild + SmCaretPosGraphEntry *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 + SmCaretPosGraphEntry *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 + SmCaretPosGraphEntry *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 + SmCaretPosGraphEntry *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 + SmCaretPosGraphEntry *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 + SmCaretPosGraphEntry *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 SmAttributeNode + * + * Lines in an SmAttributeNode: + * \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 SmAttributeNode tells how the attribute should be + * scaled ). + */ +void SmCaretPosGraphBuildingVisitor::Visit( SmAttributeNode* 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( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmBraceNode* pNode ) +{ + SmBraceNode* pClone = new SmBraceNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmBracebodyNode* pNode ) +{ + SmBracebodyNode* pClone = new SmBracebodyNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmOperNode* pNode ) +{ + SmOperNode* pClone = new SmOperNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmAlignNode* pNode ) +{ + SmAlignNode* pClone = new SmAlignNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmAttributeNode* pNode ) +{ + SmAttributeNode* pClone = new SmAttributeNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmFontNode* pNode ) +{ + SmFontNode* pClone = new SmFontNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + 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( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmBinHorNode* pNode ) +{ + SmBinHorNode* pClone = new SmBinHorNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmBinVerNode* pNode ) +{ + SmBinVerNode* pClone = new SmBinVerNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmBinDiagonalNode* pNode ) +{ + SmBinDiagonalNode *pClone = new SmBinDiagonalNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + pClone->SetAscending( pNode->IsAscending( ) ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmSubSupNode* pNode ) +{ + SmSubSupNode *pClone = new SmSubSupNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + pClone->SetUseLimits( pNode->IsUseLimits( ) ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmMatrixNode* pNode ) +{ + SmMatrixNode *pClone = new SmMatrixNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + pClone->SetRowCol( pNode->GetNumRows( ), pNode->GetNumCols( ) ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmPlaceNode* pNode ) +{ + mpResult = new SmPlaceNode( pNode->GetToken( ) ); + mpResult->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, mpResult ); +} + +void SmCloningVisitor::Visit( SmTextNode* pNode ) +{ + SmTextNode* pClone = new SmTextNode( pNode->GetToken( ), pNode->GetFontDesc( ) ); + pClone->SetSelection( pNode->GetSelection() ); + pClone->ChangeText( pNode->GetText( ) ); + CloneNodeAttr( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmSpecialNode* pNode ) +{ + mpResult = new SmSpecialNode( pNode->GetToken( ) ); + mpResult->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, mpResult ); +} + +void SmCloningVisitor::Visit( SmGlyphSpecialNode* pNode ) +{ + mpResult = new SmGlyphSpecialNode( pNode->GetToken( ) ); + mpResult->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, mpResult ); +} + +void SmCloningVisitor::Visit( SmMathSymbolNode* pNode ) +{ + mpResult = new SmMathSymbolNode( pNode->GetToken( ) ); + mpResult->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, mpResult ); +} + +void SmCloningVisitor::Visit( SmBlankNode* pNode ) +{ + SmBlankNode* pClone = new SmBlankNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + pClone->SetBlankNum( pNode->GetBlankNum( ) ); + mpResult = pClone; + CloneNodeAttr( pNode, mpResult ); +} + +void SmCloningVisitor::Visit( SmErrorNode* pNode ) +{ + mpResult = new SmErrorNode( pNode->GetToken( ) ); + mpResult->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, mpResult ); +} + +void SmCloningVisitor::Visit( SmLineNode* pNode ) +{ + SmLineNode* pClone = new SmLineNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmExpressionNode* pNode ) +{ + SmExpressionNode* pClone = new SmExpressionNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmPolyLineNode* pNode ) +{ + mpResult = new SmPolyLineNode( pNode->GetToken( ) ); + mpResult->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, mpResult ); +} + +void SmCloningVisitor::Visit( SmRootNode* pNode ) +{ + SmRootNode* pClone = new SmRootNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, pClone ); + CloneKids( pNode, pClone ); + mpResult = pClone; +} + +void SmCloningVisitor::Visit( SmRootSymbolNode* pNode ) +{ + mpResult = new SmRootSymbolNode( pNode->GetToken( ) ); + mpResult->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, mpResult ); +} + +void SmCloningVisitor::Visit( SmRectangleNode* pNode ) +{ + mpResult = new SmRectangleNode( pNode->GetToken( ) ); + mpResult->SetSelection( pNode->GetSelection() ); + CloneNodeAttr( pNode, mpResult ); +} + +void SmCloningVisitor::Visit( SmVerticalBraceNode* pNode ) +{ + SmVerticalBraceNode* pClone = new SmVerticalBraceNode( pNode->GetToken( ) ); + pClone->SetSelection( pNode->GetSelection() ); + 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( vcl::PushFlags::LINECOLOR | vcl::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( vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::FONT ); + + mrDev.SetFont( pNode->GetFont( ) ); + Point Position = pNode->GetTopLeft( ); + tools::Long left = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionStart( ) ); + tools::Long right = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionEnd( ) ); + tools::Long top = Position.getY( ); + tools::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 ); + maCmdText.stripEnd(' '); + rText = maCmdText.makeStringAndClear(); +} + +void SmNodeToTextVisitor::Visit( SmTableNode* pNode ) +{ + if( pNode->GetToken( ).eType == TBINOM ) { + Append(u"{ binom"); + LineToText( pNode->GetSubNode( 0 ) ); + LineToText( pNode->GetSubNode( 1 ) ); + Append(u"} "); + } else if( pNode->GetToken( ).eType == TSTACK ) { + Append(u"stack{ "); + bool bFirst = true; + for( auto pChild : *pNode ) + { + if(!pChild) + continue; + if(bFirst) + bFirst = false; + else + { + Separate( ); + Append(u"# "); + } + LineToText( pChild ); + } + Separate( ); + Append(u"}"); + } 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(u"newline"); + } + Separate( ); + pChild->Accept( this ); + } + } +} + +void SmNodeToTextVisitor::Visit( SmBraceNode* pNode ) +{ + if ( pNode->GetToken().eType == TEVALUATE ) + { + SmNode *pBody = pNode->Body(); + Append(u"evaluate { "); + pBody->Accept( this ); + Append(u"} "); + } + else{ + SmNode *pLeftBrace = pNode->OpeningBrace(), + *pBody = pNode->Body(), + *pRightBrace = pNode->ClosingBrace(); + //Handle special case where it's absolute function + if( pNode->GetToken( ).eType == TABS ) { + Append(u"abs"); + LineToText( pBody ); + } else { + if( pNode->GetScaleMode( ) == SmScaleMode::Height ) + Append(u"left "); + pLeftBrace->Accept( this ); + Separate( ); + pBody->Accept( this ); + Separate( ); + if( pNode->GetScaleMode( ) == SmScaleMode::Height ) + Append(u"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(u"lsup { "); + LineToText( pChild ); + Append(u"} "); + } + pChild = pSubSup->GetSubSup( LSUB ); + if( pChild ) { + Separate( ); + Append(u"lsub { "); + LineToText( pChild ); + Append(u"} "); + } + pChild = pSubSup->GetSubSup( RSUP ); + if( pChild ) { + Separate( ); + Append(u"^ { "); + LineToText( pChild ); + Append(u"} "); + } + pChild = pSubSup->GetSubSup( RSUB ); + if( pChild ) { + Separate( ); + Append(u"_ { "); + LineToText( pChild ); + Append(u"} "); + } + pChild = pSubSup->GetSubSup( CSUP ); + if( pChild ) { + Separate( ); + if (pSubSup->IsUseLimits()) + Append(u"to { "); + else + Append(u"csup { "); + LineToText( pChild ); + Append(u"} "); + } + pChild = pSubSup->GetSubSup( CSUB ); + if( pChild ) { + Separate( ); + if (pSubSup->IsUseLimits()) + Append(u"from { "); + else + Append(u"csub { "); + LineToText( pChild ); + Append(u"} "); + } + } + LineToText( pNode->GetSubNode( 1 ) ); +} + +void SmNodeToTextVisitor::Visit( SmAlignNode* pNode ) +{ + Append( pNode->GetToken( ).aText ); + LineToText( pNode->GetSubNode( 0 ) ); +} + +void SmNodeToTextVisitor::Visit( SmAttributeNode* pNode ) +{ + Append( pNode->GetToken( ).aText ); + LineToText( pNode->Body() ); +} + +void SmNodeToTextVisitor::Visit( SmFontNode* pNode ) +{ + sal_uInt32 nc; + sal_uInt8 nr, ng, nb; + switch ( pNode->GetToken( ).eType ) + { + case TBOLD: + Append(u"bold "); + break; + case TNBOLD: + Append(u"nbold "); + break; + case TITALIC: + Append(u"italic "); + break; + case TNITALIC: + Append(u"nitalic "); + break; + case TPHANTOM: + Append(u"phantom "); + break; + case TSIZE: + { + Append(u"size "); + switch ( pNode->GetSizeType( ) ) + { + case FontSizeType::PLUS: + Append(u"+"); + break; + case FontSizeType::MINUS: + Append(u"-"); + break; + case FontSizeType::MULTIPLY: + Append(u"*"); + break; + case FontSizeType::DIVIDE: + Append(u"/"); + break; + case FontSizeType::ABSOLUT: + default: + break; + } + Append( ::rtl::math::doubleToUString( + static_cast<double>( pNode->GetSizeParameter( ) ), + rtl_math_StringFormat_Automatic, + rtl_math_DecimalPlaces_Max, '.', true ) ); + Separate( ); + } + break; + + case TDVIPSNAMESCOL: + Append(u"color dvip "); + nc = pNode->GetToken().cMathChar.toUInt32(16); + Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent ); + break; + case THTMLCOL: + case TMATHMLCOL: + case TICONICCOL: + Append(u"color "); + nc = pNode->GetToken().cMathChar.toUInt32(16); + Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent ); + break; + case TRGB: + nc = pNode->GetToken().cMathChar.toUInt32(16); + Append(u"color rgb "); + nb = nc % 256; + nc /= 256; + ng = nc % 256; + nc /= 256; + nr = nc % 256; + Append(OUString::number(nr)); + Separate(); + Append(OUString::number(ng)); + Separate(); + Append(OUString::number(nb)); + Separate(); + break; + case TRGBA: + Append(u"color rgba "); + nc = pNode->GetToken().cMathChar.toUInt32(16); + nb = nc % 256; + nc /= 256; + ng = nc % 256; + nc /= 256; + nr = nc % 256; + nc /= 256; + Append(OUString::number(nr)); + Separate(); + Append(OUString::number(ng)); + Separate(); + Append(OUString::number(nb)); + Separate(); + Append(OUString::number(nc)); + Separate(); + break; + case THEX: + Append(u"color hex "); + nc = pNode->GetToken().cMathChar.toUInt32(16); + Append(OUString::number(nc,16)); + Separate(); + break; + case TSANS: + Append(u"font sans "); + break; + case TSERIF: + Append(u"font serif "); + break; + case TFIXED: + Append(u"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(u"{ "); + pLeft->Accept( this ); + Separate( ); + pOper->Accept( this ); + Separate( ); + pRight->Accept( this ); + Separate( ); + if (bBraceNeeded) + Append(u"} "); +} + +void SmNodeToTextVisitor::Visit( SmBinVerNode* pNode ) +{ + if( pNode->GetToken().eType == TOVER ){ + SmNode *pNum = pNode->GetSubNode( 0 ), + *pDenom = pNode->GetSubNode( 2 ); + Append(u"{ "); + LineToText( pNum ); + Append(u"over"); + LineToText( pDenom ); + Append(u"} "); + } else{ + SmNode *pNum = pNode->GetSubNode( 0 ), + *pDenom = pNode->GetSubNode( 2 ); + Append(u"frac {"); + LineToText( pNum ); + Append(u"} {"); + LineToText( pDenom ); + Append(u"}"); + } +} + +void SmNodeToTextVisitor::Visit( SmBinDiagonalNode* pNode ) +{ + SmNode *pLeftOperand = pNode->GetSubNode( 0 ), + *pRightOperand = pNode->GetSubNode( 1 ); + Append(u"{ "); + LineToText( pLeftOperand ); + Separate( ); + Append(u"wideslash "); + LineToText( pRightOperand ); + Append(u"} "); +} + +void SmNodeToTextVisitor::Visit( SmSubSupNode* pNode ) +{ + if( pNode->GetToken().eType == TEVALUATE ) + { + Append(u"evaluate { "); + pNode->GetSubNode( 0 )->GetSubNode( 1 )->Accept(this); + Append(u"} "); + SmNode* pChild = pNode->GetSubSup( RSUP ); + if( pChild ) { + Separate( ); + Append(u"to { "); + LineToText( pChild ); + Append(u"} "); + } + pChild = pNode->GetSubSup( RSUB ); + if( pChild ) { + Separate( ); + Append(u"from { "); + LineToText( pChild ); + Append(u"} "); + } + } + else + { + LineToText( pNode->GetBody( ) ); + SmNode *pChild = pNode->GetSubSup( LSUP ); + if( pChild ) { + Separate( ); + Append(u"lsup "); + LineToText( pChild ); + } + pChild = pNode->GetSubSup( LSUB ); + if( pChild ) { + Separate( ); + Append(u"lsub "); + LineToText( pChild ); + } + pChild = pNode->GetSubSup( RSUP ); + if( pChild ) { + Separate( ); + Append(u"^ "); + LineToText( pChild ); + } + pChild = pNode->GetSubSup( RSUB ); + if( pChild ) { + Separate( ); + Append(u"_ "); + LineToText( pChild ); + } + pChild = pNode->GetSubSup( CSUP ); + if( pChild ) { + Separate( ); + if (pNode->IsUseLimits()) + Append(u"to "); + else + Append(u"csup "); + LineToText( pChild ); + } + pChild = pNode->GetSubSup( CSUB ); + if( pChild ) { + Separate( ); + if (pNode->IsUseLimits()) + Append(u"from "); + else + Append(u"csub "); + LineToText( pChild ); + } + } +} + +void SmNodeToTextVisitor::Visit( SmMatrixNode* pNode ) +{ + Append(u"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( ); + if (pSubNode) + pSubNode->Accept( this ); + Separate( ); + if (j != pNode->GetNumCols() - 1U) + Append(u"#"); + } + Separate( ); + if (i != pNode->GetNumRows() - 1U) + Append(u"##"); + } + Append(u"} "); +} + +void SmNodeToTextVisitor::Visit( SmPlaceNode* ) +{ + Append(u"<?>"); +} + +void SmNodeToTextVisitor::Visit( SmTextNode* pNode ) +{ + SmTokenType type = pNode->GetToken( ).eType; + switch(type){ + case TTEXT: + Append(u"\""); + Append( pNode->GetToken().aText ); + Append(u"\""); + break; + case TNUMBER: + Append( pNode->GetToken().aText ); + break; + case TIDENT: + Append( pNode->GetToken().aText ); + break; + case TFUNC: + Append(u"func "); + Append( pNode->GetToken().aText ); + break; + case THEX: + Append(u"hex "); + Append( pNode->GetToken().aText ); + break; + default: + Append( pNode->GetToken().aText ); + } + Separate( ); +} + +void SmNodeToTextVisitor::Visit( SmSpecialNode* pNode ) +{ + SmTokenType type = pNode->GetToken().eType; + switch(type){ + case TLIMSUP: + Append(u"lim sup "); + break; + case TLIMINF: + Append(u"lim inf "); + break; + default: + Append( pNode->GetToken().aText ); + break; + } +} + +void SmNodeToTextVisitor::Visit( SmGlyphSpecialNode* pNode ) +{ + if( pNode->GetToken( ).eType == TBOPER ) + Append(u"boper "); + else + Append(u"uoper "); + Append( pNode->GetToken( ).aText ); +} + +//TODO to improve this it is required to improve mathmlimport. +void SmNodeToTextVisitor::Visit( SmMathSymbolNode* pNode ) +{ + if ( ( pNode->GetToken().nGroup & TG::LBrace ) + || ( pNode->GetToken().nGroup & TG::RBrace ) + || ( pNode->GetToken().nGroup & TG::Sum ) + || ( pNode->GetToken().nGroup & TG::Product ) + || ( pNode->GetToken().nGroup & TG::Relation ) + || ( pNode->GetToken().nGroup & TG::UnOper ) + || ( pNode->GetToken().nGroup & TG::Oper ) + ) { + Append( pNode->GetToken().aText ); + return; + } + sal_Unicode cChar = pNode->GetToken().cMathChar[0]; + Separate( ); + switch(cChar){ + case MS_NONE: + Append(u"none"); + break; + case '{': + Append(u"{"); + break; + case '}': + Append(u"}"); + break; + case MS_VERTLINE: + Append(u"mline"); + break; + case MS_TILDE: + Append(u"\"~\""); + break; + case MS_RIGHTARROW: + if( pNode->GetToken().eType == TTOWARD ) Append(u"toward"); + else Append(u"rightarrow"); + break; + case MS_LEFTARROW: + Append(u"leftarrow"); + break; + case MS_UPARROW: + Append(u"uparrow"); + break; + case MS_DOWNARROW: + Append(u"downarrow"); + break; + case MS_LAMBDABAR: + Append(u"lambdabar"); + break; + case MS_DOTSLOW: + Append(u"dotslow"); + break; + case MS_SETC: + Append(u"setC"); + break; + case MS_HBAR: + Append(u"hbar"); + break; + case MS_IM: + Append(u"Im"); + break; + case MS_SETN: + Append(u"setN"); + break; + case MS_WP: + Append(u"wp"); + break; + case MS_LAPLACE: + Append(u"laplace"); + break; + case MS_SETQ: + Append(u"setQ"); + break; + case MS_RE: + Append(u"Re"); + break; + case MS_SETR: + Append(u"setR"); + break; + case MS_SETZ: + Append(u"setZ"); + break; + case MS_ALEPH: + Append(u"aleph"); + break; + case 0x0362: + Append(u"widevec"); + break; + case MS_DLARROW: + Append(u"dlarrow"); + break; + case MS_DRARROW: + Append(u"drarrow"); + break; + case MS_DLRARROW: + Append(u"dlrarrow"); + break; + case MS_FORALL: + Append(u"forall"); + break; + case MS_PARTIAL: + Append(u"partial"); + break; + case MS_EXISTS: + Append(u"exists"); + break; + case MS_NOTEXISTS: + Append(u"notexists"); + break; + case MS_EMPTYSET: + Append(u"emptyset"); + break; + case MS_NABLA: + Append(u"nabla"); + break; + case MS_BACKEPSILON: + Append(u"backepsilon"); + break; + case MS_CIRC: + Append(u"circ"); + break; + case MS_INFINITY: + Append(u"infinity"); + break; + case 0x22b2: // NORMAL SUBGROUP OF + Append(OUStringChar(cChar)); + break; + case 0x22b3: // CONTAINS AS NORMAL SUBGROUP + Append(OUStringChar(cChar)); + break; + case MS_ORTHO: + Append(u"ortho"); + break; + case MS_DOTSVERT: + Append(u"dotsvert"); + break; + case MS_DOTSAXIS: + Append(u"dotsaxis"); + break; + case MS_DOTSUP: + Append(u"dotsup"); + break; + case MS_DOTSDOWN: + Append(u"dotsdown"); + break; + case '^': + Append(u"^"); + break; + case 0xe091: + Append(u"widehat"); + break; + case 0xe096: + Append(u"widetilde"); + break; + case 0xe098: + Append(u"widevec"); + break; + case 0xeb01: //no space + case 0xeb08: //normal space + break; + case 0xef04: //tiny space + case 0xef05: //tiny space + case 0xeb02: //small space + case 0xeb04: //medium space + Append(u"`"); + break; + case 0xeb05: //large space + Append(u"~"); + break; + case 0x3a9: + Append(u"%OMEGA"); + break; + default: + Append(OUStringChar(cChar)); + break; + } +} + +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(u"~"); + for (sal_uInt16 i = 0; i < nNarrow; i++) + Append(u"`"); + Append(u" "); +} + +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(u"{ "); + } + for( auto pChild : *pNode ) + { + if(!pChild) + continue; + pChild->Accept( this ); + Separate( ); + } + if (bracketsNeeded) { + Append(u"} "); + } +} + +void SmNodeToTextVisitor::Visit( SmPolyLineNode* ) +{ +} + +void SmNodeToTextVisitor::Visit( SmRootNode* pNode ) +{ + SmNode *pExtra = pNode->GetSubNode( 0 ), + *pBody = pNode->GetSubNode( 2 ); + if( pExtra ) { + Append(u"nroot"); + LineToText( pExtra ); + } else + Append(u"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..adf1a275c --- /dev/null +++ b/starmath/source/wordexportbase.cxx @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <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::Attribute: + HandleAttribute(static_cast<const SmAttributeNode*>(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..33a179d05 --- /dev/null +++ b/starmath/source/wordexportbase.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <node.hxx> + +/** + 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 SmAttributeNode* 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* GetTree() const { return m_pTree; } + +private: + const SmNode* const m_pTree; +}; + +/* 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..434d4deeb --- /dev/null +++ b/starmath/uiconfig/smath/menubar/menubar.xml @@ -0,0 +1,171 @@ +<?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:Sidebar"/> + <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..c9626cc6a --- /dev/null +++ b/starmath/uiconfig/smath/popupmenu/edit.xml @@ -0,0 +1,270 @@ +<?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:menuitem menu:label="backepsilon" menu:id=".uno:InsertCommandText?Text:string=backepsilon "/> + <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..629d76c2d --- /dev/null +++ b/starmath/uiconfig/smath/ui/alignmentdialog.ui @@ -0,0 +1,210 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <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> + <child internal-child="accessible"> + <object class="AtkObject" id="default-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="alignmentdialog|extended_tip|default">Click here to save your changes as the default settings for new formulas.</property> + </object> + </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="ok"> + <property name="label" translatable="yes" context="stock">_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-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="cancel"> + <property name="label" translatable="yes" context="stock">_Cancel</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="help"> + <property name="label" translatable="yes" context="stock">_Help</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">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="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="spacing">6</property> + <property name="margin-start">12</property> + <property name="margin-top">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="active">True</property> + <property name="draw_indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="left-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="alignmentdialog|extended_tip|left">Aligns the selected elements of a formula to the left.</property> + </object> + </child> + </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="draw_indicator">True</property> + <property name="group">left</property> + <child internal-child="accessible"> + <object class="AtkObject" id="center-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="alignmentdialog|extended_tip|center">Aligns the elements of a formula to the center.</property> + </object> + </child> + </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="draw_indicator">True</property> + <property name="group">left</property> + <child internal-child="accessible"> + <object class="AtkObject" id="right-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="alignmentdialog|extended_tip|right">Aligns the elements of a formula to the right.</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </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 type="titlebar"> + <placeholder/> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="AlignmentDialog-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="alignmentdialog|extended_tip|AlignmentDialog">You can define the alignment of multi-line formulas as well as formulas with several elements in one line.</property> + </object> + </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..947311f09 --- /dev/null +++ b/starmath/uiconfig/smath/ui/catalogdialog.ui @@ -0,0 +1,250 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <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> + <child internal-child="accessible"> + <object class="AtkObject" id="edit-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="catalogdialog|extended_tip|edit">Click here to open the Edit Symbols dialog.</property> + </object> + </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="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> + <child internal-child="accessible"> + <object class="AtkObject" id="ok-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="catalogdialog|extended_tip|ok">Saves all changes and closes dialog.</property> + </object> + </child> + </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" translatable="yes" context="stock">_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> + </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" translatable="yes" context="stock">_Help</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">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> + <!-- n-columns=1 n-rows=1 --> + <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> + <child internal-child="accessible"> + <object class="AtkObject" id="symbolset-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="catalogdialog|extended_tip|symbolset">All symbols are organized into symbol sets. Select the desired symbol set from the list box. The corresponding group of symbols appear in the field below.</property> + </object> + </child> + </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> + <child internal-child="accessible"> + <object class="AtkObject" id="preview-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="catalogdialog|extended_tip|preview">Displays a preview of the current selection.</property> + </object> + </child> + </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 type="titlebar"> + <placeholder/> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="CatalogDialog-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="catalogdialog|extended_tip|CatalogDialog">Opens the Symbols dialog, in which you can select a symbol to insert in the formula.</property> + </object> + </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..604ffc063 --- /dev/null +++ b/starmath/uiconfig/smath/ui/dockingelements.ui @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.1 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <object class="GtkTreeStore" id="liststore1"> + <columns> + <!-- column-name expander --> + <column type="GdkPixbuf"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> + <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"> + <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="GtkIconView" id="elements"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="margin">6</property> + <property name="has-tooltip">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">liststore1</property> + <property name="pixbuf-column">0</property> + <property name="activate-on-single-click">True</property> + </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/editwindow.ui b/starmath/uiconfig/smath/ui/editwindow.ui new file mode 100644 index 000000000..be25ca274 --- /dev/null +++ b/starmath/uiconfig/smath/ui/editwindow.ui @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <object class="GtkBox" id="EditWindow"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="hexpand">True</property> + <property name="border-width">0</property> + <property name="hscrollbar-policy">never</property> + <property name="vscrollbar-policy">always</property> + <property name="shadow-type">etched-out</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkDrawingArea" id="editview"> + <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_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</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..084a8f8c5 --- /dev/null +++ b/starmath/uiconfig/smath/ui/fontdialog.ui @@ -0,0 +1,309 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <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 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" translatable="yes" context="stock">_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-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="cancel"> + <property name="label" translatable="yes" context="stock">_Cancel</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">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="help"> + <property name="label" translatable="yes" context="stock">_Help</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> + <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> + <!-- n-columns=1 n-rows=1 --> + <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> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="font-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontdialog|extended_tip|font">Select a font from the list.</property> + </object> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </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> + <!-- n-columns=1 n-rows=1 --> + <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> + <property name="margin-start">12</property> + <property name="margin-top">6</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="draw_indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="bold-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontdialog|extended_tip|bold">Check this box to assign the bold attribute to the font.</property> + </object> + </child> + </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="draw_indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="italic-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontdialog|extended_tip|italic">Check this box to assign the italic attribute to the font.</property> + </object> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </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> + <child type="titlebar"> + <placeholder/> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="FontDialog-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontdialog|extended_tip|FontDialog">Use this dialog to select the font for the respective category in the Fonts dialog.</property> + </object> + </child> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/fontsizedialog.ui b/starmath/uiconfig/smath/ui/fontsizedialog.ui new file mode 100644 index 000000000..37ead0b4a --- /dev/null +++ b/starmath/uiconfig/smath/ui/fontsizedialog.ui @@ -0,0 +1,421 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <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> + <child internal-child="accessible"> + <object class="AtkObject" id="default-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontsizedialog|extended_tip|default">Click this button to save your changes as a default for all new formulas.</property> + </object> + </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="ok"> + <property name="label" translatable="yes" context="stock">_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-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="cancel"> + <property name="label" translatable="yes" context="stock">_Cancel</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="help"> + <property name="label" translatable="yes" context="stock">_Help</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">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="GtkBox" id="box2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">12</property> + <property name="margin-start">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> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="spinB_baseSize-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontsizedialog|extended_tip|spinB_baseSize">All elements of a formula are proportionally scaled to the base size. To change the base size, select or type in the desired point (pt) size. You can also use other units of measure or other metrics, which are then automatically converted to points.</property> + </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> + <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> + <!-- n-columns=1 n-rows=1 --> + <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> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="spinB_function-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontsizedialog|extended_tip|spinB_function">Select the relative size for names and other function elements in a formula in proportion to the base size.</property> + </object> + </child> + </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> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="spinB_operator-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontsizedialog|extended_tip|spinB_operator">Select the relative size of the mathematical operators in a formula in proportion to the base size.</property> + </object> + </child> + </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> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="spinB_limit-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontsizedialog|extended_tip|spinB_limit">Select the relative size for the limits in a formula in proportion to the base size.</property> + </object> + </child> + </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> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="spinB_text-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontsizedialog|extended_tip|spinB_text">Select the size for text in a formula relative to the base size.</property> + </object> + </child> + </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> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="spinB_index-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontsizedialog|extended_tip|spinB_index">Select the relative size for the indexes in a formula in proportion to the base size.</property> + </object> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </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 type="titlebar"> + <placeholder/> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="FontSizeDialog-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fontsizedialog|extended_tip|FontSizeDialog">Use this dialog to specify the font sizes for your formula. Select a base size and all elements of the formula will be scaled in relation to this base.</property> + </object> + </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..e79ee4bf3 --- /dev/null +++ b/starmath/uiconfig/smath/ui/fonttypedialog.ui @@ -0,0 +1,520 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <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="GtkButton" id="help"> + <property name="label" translatable="yes" context="stock">_Help</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> + <property name="secondary">True</property> + </packing> + </child> + <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="draw-indicator">True</property> + <property name="popup">menu1</property> + <property name="use-popover">False</property> + <child> + <placeholder/> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="modify-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fonttypedialog|extended_tip|modify">Click one of the choices from this pop-up menu to access the Fonts dialog, where you can define the font and attributes for the respective formula and for custom fonts.</property> + </object> + </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="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> + <child internal-child="accessible"> + <object class="AtkObject" id="default-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fonttypedialog|extended_tip|default">Click this button to save your changes as the default for all new formulas.</property> + </object> + </child> + </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" translatable="yes" context="stock">_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-underline">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" translatable="yes" context="stock">_Cancel</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">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> + <!-- n-columns=2 n-rows=4 --> + <object class="GtkGrid" id="grid1"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <child internal-child="accessible"> + <object class="AtkObject" id="variableCB-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fonttypedialog|extended_tip|variableCB">You can select the font for the variables in your formula.</property> + </object> + </child> + </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> + <child internal-child="accessible"> + <object class="AtkObject" id="functionCB-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fonttypedialog|extended_tip|functionCB">Select the font for names and properties of functions.</property> + </object> + </child> + </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> + <child internal-child="accessible"> + <object class="AtkObject" id="numberCB-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fonttypedialog|extended_tip|numberCB">You can select the font for the numbers in your formula.</property> + </object> + </child> + </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> + <child internal-child="accessible"> + <object class="AtkObject" id="textCB-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fonttypedialog|extended_tip|textCB">Define the font for the text in your formula here.</property> + </object> + </child> + </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">3</property> + </packing> + </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> + <!-- n-columns=2 n-rows=3 --> + <object class="GtkGrid" id="grid2"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <child internal-child="accessible"> + <object class="AtkObject" id="serifCB-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fonttypedialog|extended_tip|serifCB">You can specify the font to be used as serif font.</property> + </object> + </child> + </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> + <child internal-child="accessible"> + <object class="AtkObject" id="sansCB-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fonttypedialog|extended_tip|sansCB">You can specify the font to be used for sans serif font.</property> + </object> + </child> + </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> + <child internal-child="accessible"> + <object class="AtkObject" id="fixedCB-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fonttypedialog|extended_tip|fixedCB">You can specify the font to be used for fixed-width font.</property> + </object> + </child> + </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">2</property> + </packing> + </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 internal-child="accessible"> + <object class="AtkObject" id="FontsDialog-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="fonttypedialog|extended_tip|FontsDialog">Defines the fonts that can be applied to formula elements.</property> + </object> + </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/mathwindow.ui b/starmath/uiconfig/smath/ui/mathwindow.ui new file mode 100644 index 000000000..527e436fd --- /dev/null +++ b/starmath/uiconfig/smath/ui/mathwindow.ui @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <object class="GtkBox" id="MathWindow"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="hexpand">True</property> + <property name="border-width">0</property> + <property name="shadow-type">etched-out</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkDrawingArea" id="mathview"> + <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_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_SCROLL_MASK</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/printeroptions.ui b/starmath/uiconfig/smath/ui/printeroptions.ui new file mode 100644 index 000000000..544fd7b9c --- /dev/null +++ b/starmath/uiconfig/smath/ui/printeroptions.ui @@ -0,0 +1,239 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <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="GtkBox" id="box2"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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="draw-indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="title-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="printeroptions|extended_tip|title">Specifies whether you want the name of the document to be included in the printout.</property> + </object> + </child> + </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="draw-indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="formulatext-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="printeroptions|extended_tip|formulatext">Specifies whether to include the contents of the Commands window at the bottom of the printout.</property> + </object> + </child> + </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="draw-indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="borders-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="printeroptions|extended_tip|borders">Applies a thin border to the formula area in the printout.</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </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="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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="active">True</property> + <property name="draw-indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="originalsize-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="printeroptions|extended_tip|originalsize">Prints the formula without adjusting the current font size.</property> + </object> + </child> + </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="draw-indicator">True</property> + <property name="group">originalsize</property> + <child internal-child="accessible"> + <object class="AtkObject" id="fittopage-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="printeroptions|extended_tip|fittopage">Adjusts the formula to the page format used in the printout.</property> + </object> + </child> + </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="draw-indicator">True</property> + <property name="group">originalsize</property> + <child internal-child="accessible"> + <object class="AtkObject" id="scaling-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="printeroptions|extended_tip|scaling">Reduces or enlarges the size of the printed formula by a specified factor.</property> + </object> + </child> + </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="truncate-multiline">True</property> + <property name="adjustment">adjustment1</property> + <child internal-child="accessible"> + <object class="AtkObject" id="scalingspin-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="printeroptions|extended_tip|scalingspim">Enter the scale factor for scaling the formula.</property> + </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">2</property> + </packing> + </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..7a4f861cc --- /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.20"/> + <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/sidebarelements_math.ui b/starmath/uiconfig/smath/ui/sidebarelements_math.ui new file mode 100644 index 000000000..80bdfda40 --- /dev/null +++ b/starmath/uiconfig/smath/ui/sidebarelements_math.ui @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.1 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <object class="GtkTreeStore" id="liststore1"> + <columns> + <!-- column-name text --> + <column type="gchararray"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkTreeStore" id="liststore2"> + <columns> + <!-- column-name expander --> + <column type="GdkPixbuf"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkPaned" id="MathElementsPanel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="border-width">6</property> + <property name="wide-handle">True</property> + <child> + <object class="GtkScrolledWindow"> + <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="GtkTreeView" id="categorylist"> + <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="search-column">0</property> + <property name="show-expanders">False</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeview-selection2"/> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn0"> + <child> + <object class="GtkCellRendererText" id="cellrenderertext1"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkScrolledWindow"> + <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="GtkIconView" id="elements"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="margin">6</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">liststore2</property> + <property name="pixbuf-column">0</property> + <property name="activate-on-single-click">True</property> + </object> + </child> + </object> + <packing> + <property name="resize">True</property> + <property name="shrink">True</property> + </packing> + </child> + </object> +</interface> diff --git a/starmath/uiconfig/smath/ui/sidebarproperties_math.ui b/starmath/uiconfig/smath/ui/sidebarproperties_math.ui new file mode 100644 index 000000000..698e86687 --- /dev/null +++ b/starmath/uiconfig/smath/ui/sidebarproperties_math.ui @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.1 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <!-- n-columns=1 n-rows=1 --> + <object class="GtkGrid" id="MathPropertiesPanel"> + <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> + <child> + <!-- n-columns=1 n-rows=1 --> + <object class="GtkBox" id="container"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkButton" id="btnFormatFonts"> + <!-- No need to translate - set in code --> + <property name="label" translatable="no">Fonts</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="valign">start</property> + <property name="receives_default">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="btnFormatFontSize"> + <!-- No need to translate - set in code --> + <property name="label" translatable="no">Font Size</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="valign">start</property> + <property name="receives_default">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="btnFormatSpacing"> + <!-- No need to translate - set in code --> + <property name="label" translatable="no">Spacing</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="valign">start</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="btnFormatAlignment"> + <!-- No need to translate - set in code --> + <property name="label" translatable="no">Alignment</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="valign">start</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">0</property> + </packing> + </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..1c47c872f --- /dev/null +++ b/starmath/uiconfig/smath/ui/smathsettings.ui @@ -0,0 +1,382 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <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="GtkAdjustment" id="adjustment2"> + <property name="lower">10</property> + <property name="upper">1000</property> + <property name="step-increment">10</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="GtkBox" id="box2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="margin-start">12</property> + <property name="margin-top">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="draw_indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="title-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|title">Specifies whether you want the name of the document to be included in the printout.</property> + </object> + </child> + </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="draw_indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="text-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|text">Specifies whether to include the contents of the Commands window at the bottom of the printout.</property> + </object> + </child> + </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="draw_indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="frame-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|frame">Applies a thin border to the formula area in the printout.</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </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="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> + <property name="margin-start">12</property> + <property name="margin-top">6</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="active">True</property> + <property name="draw_indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="sizenormal-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|sizenormal">Prints the formula without adjusting the current font size.</property> + </object> + </child> + </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="draw_indicator">True</property> + <property name="group">sizenormal</property> + <child internal-child="accessible"> + <object class="AtkObject" id="sizescaled-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|sizescaled">Adjusts the formula to the page format used in the printout.</property> + </object> + </child> + </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="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> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="zoom-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|zoom">Reduces or enlarges the size of the printed formula by a specified enlargement factor.</property> + </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">2</property> + </packing> + </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="GtkBox" id="box4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="margin-start">12</property> + <property name="margin-top">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="draw_indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="norightspaces-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|norightspaces">Specifies that these space wildcards will be removed if they are at the end of a line.</property> + </object> + </child> + </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="draw_indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="saveonlyusedsymbols-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|saveonlyusedsymbols">Saves only those symbols with each formula that are used in that formula.</property> + </object> + </child> + </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="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box5"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="mnemonic-widget">smzoom</property> + <property name="use-underline">True</property> + <property name="label" translatable="yes" context="smathsettings|smzoom">Scaling code input window:</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="smzoom"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="activates-default">True</property> + <property name="truncate-multiline">True</property> + <property name="adjustment">adjustment2</property> + <property name="value">100</property> + <property name="tooltip-text" translatable="yes" context="extended_tip|smzoom">Reduces or enlarges the size of the formula code by a specified enlargement factor.</property> + <child internal-child="accessible"> + <object class="AtkObject" id="smzoom-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|smzoom">Reduces or enlarges the size of the formula code by a specified enlargement factor.</property> + </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">4</property> + </packing> + </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> + <child internal-child="accessible"> + <object class="AtkObject" id="SmathSettings-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|SmathSettings">Defines formula settings that will be valid for all documents.</property> + </object> + </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..c62ff2263 --- /dev/null +++ b/starmath/uiconfig/smath/ui/spacingdialog.ui @@ -0,0 +1,1782 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <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="draw-indicator">True</property> + <property name="popup">menu</property> + <property name="use-popover">False</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" translatable="yes" context="stock">_Cancel</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" translatable="yes" context="stock">_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-underline">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" translatable="yes" context="stock">_Help</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">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="GtkBox" id="box4"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=2 n-rows=5 --> + <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="truncate-multiline">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="truncate-multiline">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">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">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">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="truncate-multiline">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">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="no-show-all">True</property> + <property name="use-underline">True</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="truncate-multiline">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> + <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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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> + <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="GtkBox" id="box8"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=1 n-rows=3 --> + <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> + <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="GtkBox" id="box11"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=1 n-rows=2 --> + <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> + <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="GtkBox" id="box14"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=1 n-rows=2 --> + <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> + <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="GtkBox" id="box17"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=1 n-rows=2 --> + <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> + <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="GtkBox" id="box20"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=1 n-rows=2 --> + <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> + <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="GtkBox" id="box23"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=1 n-rows=3 --> + <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> + <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="GtkBox" id="box26"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=1 n-rows=2 --> + <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> + <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="GtkBox" id="box29"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=1 n-rows=2 --> + <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> + <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="GtkBox" id="box32"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=1 n-rows=2 --> + <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> + <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="GtkBox" id="box2"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">12</property> + <property name="margin-top">6</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> + <!-- n-columns=1 n-rows=4 --> + <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> + <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> + </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..46eeb5404 --- /dev/null +++ b/starmath/uiconfig/smath/ui/symdefinedialog.ui @@ -0,0 +1,660 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.36.0 --> +<interface domain="sm"> + <requires lib="gtk+" version="3.20"/> + <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 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" translatable="yes" context="stock">_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-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="cancel"> + <property name="label" translatable="yes" context="stock">_Cancel</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">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="help"> + <property name="label" translatable="yes" context="stock">_Help</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> + <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> + <!-- n-columns=1 n-rows=1 --> + <object class="GtkGrid" id="grid1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="column_spacing">18</property> + <child> + <!-- n-columns=1 n-rows=1 --> + <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="truncate-multiline">True</property> + <property name="activates_default">True</property> + </object> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="oldSymbolSets-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|oldSymbolSets">This list box contains the name of the current symbol set. If you want, you can also select a different symbol set.</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> + <!-- n-columns=1 n-rows=1 --> + <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="truncate-multiline">True</property> + <property name="activates_default">True</property> + </object> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="oldSymbols-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|oldSymbols">Select the name of the current symbol.</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> + <!-- n-columns=1 n-rows=1 --> + <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" translatable="yes" context="stock">_Add</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use-underline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="add-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|add">Click this button to add the symbol shown in the right preview window to the current symbol set.</property> + </object> + </child> + </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> + <child internal-child="accessible"> + <object class="AtkObject" id="modify-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|modify">Click this button to replace the name of the symbol shown in the left preview window (the old name is displayed in the Old symbol list box) with the new name you have entered in the Symbol list box.</property> + </object> + </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="delete"> + <property name="label" translatable="yes" context="stock">_Delete</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use-underline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="delete-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|delete">Click to remove the symbol shown in the left preview window from the current symbol set.</property> + </object> + </child> + </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> + <!-- n-columns=1 n-rows=1 --> + <object class="GtkGrid" id="grid2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="column_spacing">18</property> + <child> + <!-- n-columns=1 n-rows=1 --> + <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> + <child internal-child="accessible"> + <object class="AtkObject" id="fonts-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|fonts">Displays the name of the current font and enables you to select a different font.</property> + </object> + </child> + </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> + <child internal-child="accessible"> + <object class="AtkObject" id="fontsSubsetLB-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|fontsSubsetLB">If you selected a non-symbol font in the Font list box, you can select a Unicode subset in which to place your new or edited symbol. When a subset has been selected, all symbols belonging to this subset of the current symbol set are displayed in the symbols list above.</property> + </object> + </child> + </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="truncate-multiline">True</property> + <property name="activates_default">True</property> + </object> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="symbols-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|symbols">Lists the names for the symbols in the current symbol set. Select a name from the list or type a name for a newly added symbol.</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="truncate-multiline">True</property> + <property name="activates_default">True</property> + </object> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="symbolSets-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|symbolSets">The Symbol set list box contains the names of all existing symbol sets. You can modify a symbol set or create a new one.</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> + <child internal-child="accessible"> + <object class="AtkObject" id="styles-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|styles">The current typeface is displayed. You can change the typeface by selecting one from the list box.</property> + </object> + </child> + </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> + <!-- n-columns=1 n-rows=1 --> + <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> + <child type="titlebar"> + <placeholder/> + </child> + <child internal-child="accessible"> + <object class="AtkObject" id="EditSymbols-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="symdefinedialog|extended_tip|EditSymbols">Use this dialog to add symbols to a symbol set, to edit symbol sets, or to modify symbol notations.</property> + </object> + </child> + </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..2b84a17c4 --- /dev/null +++ b/starmath/util/sm.component @@ -0,0 +1,70 @@ +<?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.FormulaDocument" + constructor="Math_FormulaDocument_get_implementation"> + <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> + <implementation name="org.libreoffice.comp.Math.sidebar.SmPanelFactory" + constructor="org_libreoffice_comp_Math_sidebar_SmPanelFactory"> + <service name="com.sun.star.ui.UIElementFactory"/> + </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..7e3cc5d24 --- /dev/null +++ b/starmath/visual-editor-todo @@ -0,0 +1,42 @@ +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. SmGraphicWidget::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. 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. |