diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /toolkit | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit')
247 files changed, 76136 insertions, 0 deletions
diff --git a/toolkit/CppunitTest_toolkit.mk b/toolkit/CppunitTest_toolkit.mk new file mode 100644 index 0000000000..496ed0d7ba --- /dev/null +++ b/toolkit/CppunitTest_toolkit.mk @@ -0,0 +1,58 @@ +# -*- 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,toolkit)) + +$(eval $(call gb_CppunitTest_add_exception_objects,toolkit, \ + toolkit/qa/cppunit/Dialog \ + toolkit/qa/cppunit/EventContainer \ + toolkit/qa/cppunit/UnitConversion \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,toolkit, \ + basegfx \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + salhelper \ + test \ + tk \ + tl \ + unotest \ + vcl \ +)) + +$(eval $(call gb_CppunitTest_use_api,toolkit,\ + offapi \ + udkapi \ +)) + +$(eval $(call gb_CppunitTest_use_externals,toolkit,\ + boost_headers \ +)) + +$(eval $(call gb_CppunitTest_use_ure,toolkit)) + +$(eval $(call gb_CppunitTest_use_configuration,toolkit)) + +$(eval $(call gb_CppunitTest_use_vcl,toolkit)) + +$(eval $(call gb_CppunitTest_use_components,toolkit,\ + comphelper/util/comphelp \ + configmgr/source/configmgr \ + framework/util/fwk \ + i18npool/util/i18npool \ + sfx2/util/sfx \ + toolkit/util/tk \ + ucb/source/core/ucb1 \ + ucb/source/ucp/file/ucpfile1 \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/toolkit/CppunitTest_toolkit_a11y.mk b/toolkit/CppunitTest_toolkit_a11y.mk new file mode 100644 index 0000000000..4b8c60b0a6 --- /dev/null +++ b/toolkit/CppunitTest_toolkit_a11y.mk @@ -0,0 +1,54 @@ +# -*- 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,toolkit_a11y)) + +$(eval $(call gb_CppunitTest_add_exception_objects,toolkit_a11y, \ + toolkit/qa/cppunit/a11y/AccessibleStatusBarTest \ + toolkit/qa/cppunit/a11y/XAccessibleComponentTester \ + toolkit/qa/cppunit/a11y/XAccessibleContextTester \ + toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester \ + toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,toolkit_a11y, \ + basegfx \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + salhelper \ + subsequenttest \ + test \ + tk \ + tl \ + unotest \ + vcl \ +)) + +$(eval $(call gb_CppunitTest_use_api,toolkit_a11y,\ + offapi \ + udkapi \ +)) + +$(eval $(call gb_CppunitTest_use_externals,toolkit_a11y,\ + boost_headers \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,toolkit_a11y)) + +$(eval $(call gb_CppunitTest_use_ure,toolkit_a11y)) + +$(eval $(call gb_CppunitTest_use_configuration,toolkit_a11y)) + +$(eval $(call gb_CppunitTest_use_vcl,toolkit_a11y)) + +$(eval $(call gb_CppunitTest_use_rdb,toolkit_a11y,services)) + +# vim: set noet sw=4 ts=4: diff --git a/toolkit/IwyuFilter_toolkit.yaml b/toolkit/IwyuFilter_toolkit.yaml new file mode 100644 index 0000000000..6606014d29 --- /dev/null +++ b/toolkit/IwyuFilter_toolkit.yaml @@ -0,0 +1,103 @@ +--- +assumeFilename: toolkit/source/controls/unocontrol.cxx +excludelist: + toolkit/source/awt/vclxmenu.cxx: + # Needed for direct member access + - vcl/window.hxx + toolkit/source/helper/servicenames.cxx: + # Needed for extern array declarations + - toolkit/helper/servicenames.hxx + toolkit/inc/awt/animatedimagespeer.hxx: + # base class has to be a complete type + - com/sun/star/awt/XAnimation.hpp + - com/sun/star/container/XContainerListener.hpp + - com/sun/star/util/XModifyListener.hpp + - toolkit/awt/vclxwindow.hxx + toolkit/inc/awt/vclxpointer.hxx: + # base class has to be a complete type + - com/sun/star/awt/XPointer.hpp + - com/sun/star/lang/XServiceInfo.hpp + - com/sun/star/lang/XUnoTunnel.hpp + toolkit/inc/awt/vclxspinbutton.hxx: + # base class has to be a complete type + - com/sun/star/awt/XSpinValue.hpp + toolkit/inc/awt/vclxprinter.hxx: + # base class has to be a complete type + - com/sun/star/awt/XPrinterPropertySet.hpp + - com/sun/star/awt/XInfoPrinter.hpp + - com/sun/star/awt/XPrinterPropertySet.hpp + - com/sun/star/awt/XPrinterServer.hpp + - com/sun/star/lang/XServiceInfo.hpp + toolkit/inc/awt/vclxtabpagecontainer.hxx: + # base class has to be a complete type + - com/sun/star/awt/tab/XTabPageContainer.hpp + - com/sun/star/container/XContainerListener.hpp + - toolkit/awt/vclxcontainer.hxx + toolkit/inc/controls/accessiblecontrolcontext.hxx: + # base class has to be a complete type + - com/sun/star/lang/XEventListener.hpp + toolkit/inc/controls/animatedimages.hxx: + # base class has to be a complete type + - com/sun/star/awt/XAnimatedImages.hpp + - toolkit/controls/unocontrolmodel.hxx + toolkit/inc/controls/controlmodelcontainerbase.hxx: + # base class has to be a complete type + - com/sun/star/awt/tab/XTabPageModel.hpp + - com/sun/star/awt/XTabControllerModel.hpp + - com/sun/star/beans/XPropertyChangeListener.hpp + - com/sun/star/container/XContainer.hpp + - com/sun/star/container/XNameContainer.hpp + - com/sun/star/lang/XInitialization.hpp + - com/sun/star/lang/XMultiServiceFactory.hpp + - com/sun/star/util/XChangesListener.hpp + - com/sun/star/util/XChangesNotifier.hpp + - toolkit/controls/unocontrolcontainer.hxx + toolkit/inc/controls/dialogcontrol.hxx: + # base class has to be a complete type + - com/sun/star/awt/UnoControlDialog.hpp + toolkit/inc/controls/eventcontainer.hxx: + # base class has to be a complete type + - com/sun/star/container/XContainer.hpp + - com/sun/star/container/XNameContainer.hpp + toolkit/inc/controls/geometrycontrolmodel.hxx: + # base class has to be a complete type + - com/sun/star/script/XScriptEventsSupplier.hpp + - com/sun/star/util/XCloneable.hpp + # Needed for template classes + - controls/geometrycontrolmodel_impl.hxx + toolkit/inc/controls/roadmapcontrol.hxx: + # base class has to be a complete type + - com/sun/star/awt/XItemEventBroadcaster.hpp + - com/sun/star/awt/XItemListener.hpp + - com/sun/star/beans/XPropertyChangeListener.hpp + - com/sun/star/container/XContainerListener.hpp + toolkit/inc/controls/roadmapentry.hxx: + # base class has to be a complete type + - com/sun/star/lang/XServiceInfo.hpp + toolkit/inc/controls/tabpagecontainer.hxx: + # base class has to be a complete type + - com/sun/star/awt/tab/XTabPageContainer.hpp + - toolkit/controls/controlmodelcontainerbase.hxx + toolkit/inc/controls/tabpagemodel.hxx: + # base class has to be a complete type + - com/sun/star/awt/tab/XTabPage.hpp + - toolkit/controls/unocontrolcontainer.hxx + toolkit/inc/controls/unocontrolcontainer.hxx: + # base class has to be a complete type + - com/sun/star/awt/XControlContainer.hpp + - com/sun/star/awt/XUnoControlContainer.hpp + - com/sun/star/container/XContainer.hpp + - com/sun/star/container/XIdentifierContainer.hpp + - toolkit/controls/unocontrolbase.hxx + toolkit/source/controls/animatedimages.cxx: + # Actually used + - com/sun/star/beans/XPropertySetInfo.hpp + toolkit/source/awt/vclxwindow.cxx: + # Needed for rtl::math::round + - rtl/math.hxx + toolkit/source/hatchwindow/hatchwindow.cxx: + # Actually used + - com/sun/star/embed/XHatchWindowController.hpp + toolkit/source/helper/servicenames.cxx: + # Needed for extern + - helper/servicenames.hxx diff --git a/toolkit/JunitTest_toolkit_complex.mk b/toolkit/JunitTest_toolkit_complex.mk new file mode 100644 index 0000000000..4a06582a00 --- /dev/null +++ b/toolkit/JunitTest_toolkit_complex.mk @@ -0,0 +1,44 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +$(eval $(call gb_JunitTest_JunitTest,toolkit_complex)) + +$(eval $(call gb_JunitTest_use_jars,toolkit_complex,\ + OOoRunner \ + libreoffice \ + test \ +)) + +$(eval $(call gb_JunitTest_add_sourcefiles,toolkit_complex,\ + toolkit/qa/complex/toolkit/accessibility/_XAccessibleComponent \ + toolkit/qa/complex/toolkit/accessibility/_XAccessibleContext \ + toolkit/qa/complex/toolkit/accessibility/_XAccessibleEventBroadcaster \ + toolkit/qa/complex/toolkit/accessibility/_XAccessibleExtendedComponent \ + toolkit/qa/complex/toolkit/accessibility/_XAccessibleText \ + toolkit/qa/complex/toolkit/Assert \ + toolkit/qa/complex/toolkit/awtgrid/GridDataListener \ + toolkit/qa/complex/toolkit/awtgrid/TMutableGridDataModel \ + toolkit/qa/complex/toolkit/awtgrid/DummyColumn \ + toolkit/qa/complex/toolkit/GridControl \ +)) + +$(eval $(call gb_JunitTest_add_classes,toolkit_complex,\ + complex.toolkit.GridControl \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/toolkit/JunitTest_toolkit_unoapi_1.mk b/toolkit/JunitTest_toolkit_unoapi_1.mk new file mode 100644 index 0000000000..ddd916107f --- /dev/null +++ b/toolkit/JunitTest_toolkit_unoapi_1.mk @@ -0,0 +1,24 @@ +# -*- 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +$(eval $(call gb_JunitTest_JunitTest,toolkit_unoapi_1)) + +$(eval $(call gb_JunitTest_set_unoapi_test_defaults,toolkit_unoapi_1)) + +# vim: set noet sw=4 ts=4: diff --git a/toolkit/JunitTest_toolkit_unoapi_2.mk b/toolkit/JunitTest_toolkit_unoapi_2.mk new file mode 100644 index 0000000000..642829a23a --- /dev/null +++ b/toolkit/JunitTest_toolkit_unoapi_2.mk @@ -0,0 +1,24 @@ +# -*- 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +$(eval $(call gb_JunitTest_JunitTest,toolkit_unoapi_2)) + +$(eval $(call gb_JunitTest_set_unoapi_test_defaults,toolkit_unoapi_2)) + +# vim: set noet sw=4 ts=4: diff --git a/toolkit/JunitTest_toolkit_unoapi_3.mk b/toolkit/JunitTest_toolkit_unoapi_3.mk new file mode 100644 index 0000000000..71c69d0a2e --- /dev/null +++ b/toolkit/JunitTest_toolkit_unoapi_3.mk @@ -0,0 +1,24 @@ +# -*- 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +$(eval $(call gb_JunitTest_JunitTest,toolkit_unoapi_3)) + +$(eval $(call gb_JunitTest_set_unoapi_test_defaults,toolkit_unoapi_3)) + +# vim: set noet sw=4 ts=4: diff --git a/toolkit/JunitTest_toolkit_unoapi_4.mk b/toolkit/JunitTest_toolkit_unoapi_4.mk new file mode 100644 index 0000000000..e615716333 --- /dev/null +++ b/toolkit/JunitTest_toolkit_unoapi_4.mk @@ -0,0 +1,24 @@ +# -*- 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +$(eval $(call gb_JunitTest_JunitTest,toolkit_unoapi_4)) + +$(eval $(call gb_JunitTest_set_unoapi_test_defaults,toolkit_unoapi_4)) + +# vim: set noet sw=4 ts=4: diff --git a/toolkit/Library_tk.mk b/toolkit/Library_tk.mk new file mode 100644 index 0000000000..bcea9abb6d --- /dev/null +++ b/toolkit/Library_tk.mk @@ -0,0 +1,151 @@ +# -*- 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +$(eval $(call gb_Library_Library,tk)) + +$(eval $(call gb_Library_set_componentfile,tk,toolkit/util/tk,services)) + +$(eval $(call gb_Library_use_externals,tk,\ + boost_headers \ +)) + +$(eval $(call gb_Library_set_include,tk,\ + $$(INCLUDE) \ + -I$(SRCDIR)/toolkit/inc \ + -I$(SRCDIR)/toolkit/source \ +)) + +$(eval $(call gb_Library_add_defs,tk,\ + -DTOOLKIT_DLLIMPLEMENTATION \ + -DSVT_DLL_NAME=\"$(call gb_Library_get_runtime_filename,$(call gb_Library__get_name,svt))\" \ +)) + +$(eval $(call gb_Library_use_sdk_api,tk)) + +$(eval $(call gb_Library_use_libraries,tk,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + salhelper \ + i18nlangtag \ + svl \ + tl \ + utl \ + vcl \ +)) + +$(eval $(call gb_Library_add_exception_objects,tk,\ + toolkit/source/awt/asynccallback \ + toolkit/source/awt/stylesettings \ + toolkit/source/awt/scrollabledialog \ + toolkit/source/awt/vclxaccessiblecomponent \ + toolkit/source/awt/vclxbitmap \ + toolkit/source/awt/vclxcontainer \ + toolkit/source/awt/vclxdevice \ + toolkit/source/awt/vclxfont \ + toolkit/source/awt/vclxgraphics \ + toolkit/source/awt/vclxmenu \ + toolkit/source/awt/vclxpointer \ + toolkit/source/awt/vclxprinter \ + toolkit/source/awt/vclxregion \ + toolkit/source/awt/vclxspinbutton \ + toolkit/source/awt/vclxsystemdependentwindow \ + toolkit/source/awt/vclxtabpagecontainer \ + toolkit/source/awt/animatedimagespeer \ + toolkit/source/awt/vclxtoolkit \ + toolkit/source/awt/vclxtopwindow \ + toolkit/source/awt/vclxwindow \ + toolkit/source/awt/vclxwindow1 \ + toolkit/source/awt/vclxwindows \ + toolkit/source/controls/accessiblecontrolcontext \ + toolkit/source/controls/controlmodelcontainerbase \ + toolkit/source/controls/dialogcontrol \ + toolkit/source/controls/eventcontainer \ + toolkit/source/controls/filectrl \ + toolkit/source/controls/formattedcontrol \ + toolkit/source/controls/geometrycontrolmodel \ + toolkit/source/controls/grid/defaultgridcolumnmodel \ + toolkit/source/controls/grid/defaultgriddatamodel \ + toolkit/source/controls/grid/gridcolumn \ + toolkit/source/controls/grid/grideventforwarder \ + toolkit/source/controls/grid/sortablegriddatamodel \ + toolkit/source/controls/grid/gridcontrol \ + toolkit/source/controls/roadmapcontrol \ + toolkit/source/controls/roadmapentry \ + toolkit/source/controls/svtxgridcontrol \ + toolkit/source/controls/svmedit \ + toolkit/source/controls/tabpagecontainer \ + toolkit/source/controls/tabpagemodel \ + toolkit/source/controls/stdtabcontroller \ + toolkit/source/controls/stdtabcontrollermodel \ + toolkit/source/controls/tkscrollbar \ + toolkit/source/controls/tkspinbutton \ + toolkit/source/controls/animatedimages \ + toolkit/source/controls/spinningprogress \ + toolkit/source/controls/table/cellvalueconversion \ + toolkit/source/controls/table/defaultinputhandler \ + toolkit/source/controls/table/gridtablerenderer \ + toolkit/source/controls/table/mousefunction \ + toolkit/source/controls/table/tablecontrol \ + toolkit/source/controls/table/tablecontrol_impl \ + toolkit/source/controls/table/tabledatawindow \ + toolkit/source/controls/table/tablegeometry \ + toolkit/source/controls/tree/treecontrol \ + toolkit/source/controls/tree/treecontrolpeer \ + toolkit/source/controls/tree/treedatamodel \ + toolkit/source/controls/unocontrol \ + toolkit/source/controls/unocontrolbase \ + toolkit/source/controls/unocontrolcontainer \ + toolkit/source/controls/unocontrolcontainermodel \ + toolkit/source/controls/unocontrolmodel \ + toolkit/source/controls/unocontroltablemodel \ + toolkit/source/controls/unocontrols \ + toolkit/source/controls/unogridcolumnfacade \ + toolkit/source/hatchwindow/documentcloser \ + toolkit/source/hatchwindow/hatchwindow \ + toolkit/source/hatchwindow/hatchwindowfactory \ + toolkit/source/hatchwindow/ipwin \ + toolkit/source/helper/accessibilityclient \ + toolkit/source/helper/btndlg \ + toolkit/source/helper/imagealign \ + toolkit/source/helper/listenermultiplexer \ + toolkit/source/helper/property \ + toolkit/source/helper/servicenames \ + toolkit/source/helper/tkresmgr \ + toolkit/source/helper/unopropertyarrayhelper \ + toolkit/source/helper/unowrapper \ + toolkit/source/helper/vclunohelper \ +)) + +ifeq ($(OS),MACOSX) +$(eval $(call gb_Library_add_cxxflags,tk,\ + $(gb_OBJCXXFLAGS) \ +)) +$(eval $(call gb_Library_add_libs,tk,\ + -lobjc \ +)) +endif + +ifeq ($(OS),iOS) +$(eval $(call gb_Library_add_cxxflags,tk,\ + $(gb_OBJCXXFLAGS))) +endif + +# vim: set noet sw=4 ts=4: diff --git a/toolkit/Makefile b/toolkit/Makefile new file mode 100644 index 0000000000..ccb1c85a04 --- /dev/null +++ b/toolkit/Makefile @@ -0,0 +1,7 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- + +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/toolkit/Module_toolkit.mk b/toolkit/Module_toolkit.mk new file mode 100644 index 0000000000..d039559316 --- /dev/null +++ b/toolkit/Module_toolkit.mk @@ -0,0 +1,44 @@ +# -*- 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +$(eval $(call gb_Module_Module,toolkit)) + +$(eval $(call gb_Module_add_targets,toolkit,\ + Library_tk \ +)) + +# FIXME fails on some tinderboxes, needs investigation +ifneq ($(OS),WNT) +$(eval $(call gb_Module_add_check_targets,toolkit,\ + CppunitTest_toolkit \ + CppunitTest_toolkit_a11y \ +)) +endif + +ifneq ($(OOO_JUNIT_JAR),) +$(eval $(call gb_Module_add_subsequentcheck_targets,toolkit,\ + JunitTest_toolkit_complex \ + JunitTest_toolkit_unoapi_1 \ + JunitTest_toolkit_unoapi_2 \ + JunitTest_toolkit_unoapi_3 \ + JunitTest_toolkit_unoapi_4 \ +)) +endif + +# vim: set noet sw=4 ts=4: diff --git a/toolkit/README.md b/toolkit/README.md new file mode 100644 index 0000000000..f41fb3760c --- /dev/null +++ b/toolkit/README.md @@ -0,0 +1,15 @@ +# Abstract Windowing Toolkit + +"Abstract" windowing thing. UNO implementations of windowing stuff so that it +can be used from Basic or Java. But also stuff that has no connection to Basic +or Java. + +## Notes + +The "awt" here has no relation to the Java AWT, as far as I know. It +might be inspired by it API-wise, perhaps. (If you know differently, feel free +to improve this `README.md` file.) + +Also note that `toolkit/` is itself not really a toolkit, it is at root a +reasonably simple wrapper of `vcl/`. If you came here looking for a +toolkit, please look at `vcl/` instead. diff --git a/toolkit/inc/awt/animatedimagespeer.hxx b/toolkit/inc/awt/animatedimagespeer.hxx new file mode 100644 index 0000000000..839e249b3f --- /dev/null +++ b/toolkit/inc/awt/animatedimagespeer.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 <toolkit/awt/vclxwindow.hxx> +#include <com/sun/star/container/XContainerListener.hpp> + +#include <com/sun/star/awt/XAnimatedImages.hpp> +#include <com/sun/star/awt/XAnimation.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/util/XModifyListener.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace toolkit +{ + + + //= AnimatedImagesPeer + + struct AnimatedImagesPeer_Data; + typedef cppu::ImplInheritanceHelper< VCLXWindow, + css::awt::XAnimation, + css::container::XContainerListener, + css::util::XModifyListener + > AnimatedImagesPeer_Base; + + class AnimatedImagesPeer final : public AnimatedImagesPeer_Base + { + public: + AnimatedImagesPeer(); + + private: + virtual ~AnimatedImagesPeer() override; + + public: + // XAnimation + virtual void SAL_CALL startAnimation( ) override; + virtual void SAL_CALL stopAnimation( ) override; + virtual sal_Bool SAL_CALL isAnimationRunning( ) override; + + // VclWindowPeer + virtual void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + virtual css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& i_event ) override; + + // XModifyListener + virtual void SAL_CALL modified( const css::lang::EventObject& i_event ) override; + + // XComponent + void SAL_CALL dispose( ) override; + + struct CachedImage + { + OUString sImageURL; + mutable css::uno::Reference< css::graphic::XGraphic > xGraphic; + }; + + private: + void ProcessWindowEvent( const VclWindowEvent& i_windowEvent ) override; + + /** updates our images with the ones from the given XAnimatedImages component + */ + void impl_updateImages_nolck( const css::uno::Reference< css::uno::XInterface >& i_animatedImages ); + + AnimatedImagesPeer(const AnimatedImagesPeer&) = delete; + AnimatedImagesPeer& operator=(const AnimatedImagesPeer&) = delete; + + void updateImageList_nothrow(); + void updateImageList_nothrow( const css::uno::Reference< css::awt::XAnimatedImages >& i_images ); + + std::vector< std::vector< CachedImage > > maCachedImageSets; + }; + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxbitmap.hxx b/toolkit/inc/awt/vclxbitmap.hxx new file mode 100644 index 0000000000..9edf562c1d --- /dev/null +++ b/toolkit/inc/awt/vclxbitmap.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/XBitmap.hpp> +#include <com/sun/star/awt/XDisplayBitmap.hpp> +#include <com/sun/star/util/XAccounting.hpp> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <mutex> +#include <vcl/bitmapex.hxx> + + + + +class VCLXBitmap final : public cppu::WeakImplHelper< + css::awt::XBitmap, + css::awt::XDisplayBitmap, + css::util::XAccounting> +{ + std::mutex maMutex; + BitmapEx maBitmap; + + std::mutex& GetMutex() { return maMutex; } + + +public: + // inline constructors + VCLXBitmap() : maMutex(), maBitmap() {} + VCLXBitmap(const BitmapEx& rBitmapEx) : maMutex(), maBitmap(rBitmapEx) {} + + void SetBitmap( const BitmapEx& rBmp ) { maBitmap = rBmp; } + const BitmapEx& GetBitmap() const { return maBitmap; } + + // css::awt::XBitmap + css::awt::Size SAL_CALL getSize() override; + css::uno::Sequence< sal_Int8 > SAL_CALL getDIB() override; + css::uno::Sequence< sal_Int8 > SAL_CALL getMaskDIB() override; + + // XAccounting + sal_Int64 SAL_CALL estimateUsage() override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxcontainer.hxx b/toolkit/inc/awt/vclxcontainer.hxx new file mode 100644 index 0000000000..248f2e6505 --- /dev/null +++ b/toolkit/inc/awt/vclxcontainer.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/awt/XVclContainer.hpp> +#include <com/sun/star/awt/XVclContainerPeer.hpp> +#include <cppuhelper/implbase.hxx> + +#include <toolkit/awt/vclxwindow.hxx> + +class VCLXContainer : public cppu::ImplInheritanceHelper<VCLXWindow, + css::awt::XVclContainer, + css::awt::XVclContainerPeer> +{ +public: + VCLXContainer(); + virtual ~VCLXContainer() override; + + // css::awt::XVclContainer + void SAL_CALL addVclContainerListener( const css::uno::Reference< css::awt::XVclContainerListener >& l ) override; + void SAL_CALL removeVclContainerListener( const css::uno::Reference< css::awt::XVclContainerListener >& l ) override; + css::uno::Sequence< css::uno::Reference< css::awt::XWindow > > SAL_CALL getWindows( ) override; + + // css::awt::XVclContainerPeer + void SAL_CALL enableDialogControl( sal_Bool bEnable ) override; + void SAL_CALL setTabOrder( const css::uno::Sequence< css::uno::Reference< css::awt::XWindow > >& WindowOrder, const css::uno::Sequence< css::uno::Any >& Tabs, sal_Bool GroupControl ) override; + void SAL_CALL setGroup( const css::uno::Sequence< css::uno::Reference< css::awt::XWindow > >& Windows ) override; + + // css::awt::XVclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxgraphics.hxx b/toolkit/inc/awt/vclxgraphics.hxx new file mode 100644 index 0000000000..acf03dd178 --- /dev/null +++ b/toolkit/inc/awt/vclxgraphics.hxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <comphelper/servicehelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <tools/color.hxx> +#include <o3tl/typed_flags_set.hxx> +#include <vcl/font.hxx> +#include <vcl/rendercontext/RasterOp.hxx> +#include <vcl/vclptr.hxx> + +#include <com/sun/star/awt/XGraphics2.hpp> + +#include <memory> + +class OutputDevice; +namespace vcl { class Region; } +namespace com::sun::star::graphic { class XGraphic; } + + +enum class InitOutDevFlags +{ + NONE = 0x0000, + FONT = 0x0001, + COLORS = 0x0002, +}; +namespace o3tl +{ + template<> struct typed_flags<InitOutDevFlags> : is_typed_flags<InitOutDevFlags, 0x03> {}; +} + + + + +class VCLXGraphics final : public cppu::WeakImplHelper< + css::awt::XGraphics2> +{ +private: + // used to return same reference on each call to getDevice() + css::uno::Reference< css::awt::XDevice> mxDevice; + + VclPtr<OutputDevice> mpOutputDevice; + vcl::Font maFont; + Color maTextColor; + Color maTextFillColor; + Color maLineColor; + Color maFillColor; + RasterOp meRasterOp; + std::unique_ptr<vcl::Region> mpClipRegion; + + void initAttrs(); + +public: + VCLXGraphics(); + virtual ~VCLXGraphics() override; + + void Init( OutputDevice* pOutDev ); + void InitOutputDevice( InitOutDevFlags nFlags ); + + void SetOutputDevice( OutputDevice* pOutDev ); + OutputDevice* GetOutputDevice() const { return mpOutputDevice; } + + // css::awt::XGraphics Attributes + virtual css::uno::Reference< css::awt::XDevice > SAL_CALL getDevice() override; + virtual void SAL_CALL setTextColor( ::sal_Int32 _textcolor ) override; + virtual void SAL_CALL setTextFillColor( ::sal_Int32 _textfillcolor ) override; + virtual void SAL_CALL setLineColor( ::sal_Int32 _linecolor ) override; + virtual void SAL_CALL setFillColor( ::sal_Int32 _fillcolor ) override; + virtual void SAL_CALL setRasterOp( css::awt::RasterOperation _rasterop ) override; + virtual void SAL_CALL setFont( const css::uno::Reference< css::awt::XFont >& _font ) override; + virtual css::awt::SimpleFontMetric SAL_CALL getFontMetric() override; + + // css::awt::XGraphics Methods + virtual void SAL_CALL selectFont( const css::awt::FontDescriptor& aDescription ) override; + virtual void SAL_CALL setClipRegion( const css::uno::Reference< css::awt::XRegion >& Clipping ) override; + virtual void SAL_CALL intersectClipRegion( const css::uno::Reference< css::awt::XRegion >& xClipping ) override; + virtual void SAL_CALL push( ) override; + virtual void SAL_CALL pop( ) override; + virtual void SAL_CALL clear( const css::awt::Rectangle& aRect ) override; + virtual void SAL_CALL copy( const css::uno::Reference< css::awt::XDevice >& xSource, ::sal_Int32 nSourceX, ::sal_Int32 nSourceY, ::sal_Int32 nSourceWidth, ::sal_Int32 nSourceHeight, ::sal_Int32 nDestX, ::sal_Int32 nDestY, ::sal_Int32 nDestWidth, ::sal_Int32 nDestHeight ) override; + virtual void SAL_CALL draw( const css::uno::Reference< css::awt::XDisplayBitmap >& xBitmapHandle, ::sal_Int32 SourceX, ::sal_Int32 SourceY, ::sal_Int32 SourceWidth, ::sal_Int32 SourceHeight, ::sal_Int32 DestX, ::sal_Int32 DestY, ::sal_Int32 DestWidth, ::sal_Int32 DestHeight ) override; + virtual void SAL_CALL drawPixel( ::sal_Int32 X, ::sal_Int32 Y ) override; + virtual void SAL_CALL drawLine( ::sal_Int32 X1, ::sal_Int32 Y1, ::sal_Int32 X2, ::sal_Int32 Y2 ) override; + virtual void SAL_CALL drawRect( ::sal_Int32 X, ::sal_Int32 Y, ::sal_Int32 Width, ::sal_Int32 Height ) override; + virtual void SAL_CALL drawRoundedRect( ::sal_Int32 X, ::sal_Int32 Y, ::sal_Int32 Width, ::sal_Int32 Height, ::sal_Int32 nHorzRound, ::sal_Int32 nVertRound ) override; + virtual void SAL_CALL drawPolyLine( const css::uno::Sequence< ::sal_Int32 >& DataX, const css::uno::Sequence< ::sal_Int32 >& DataY ) override; + virtual void SAL_CALL drawPolygon( const css::uno::Sequence< ::sal_Int32 >& DataX, const css::uno::Sequence< ::sal_Int32 >& DataY ) override; + virtual void SAL_CALL drawPolyPolygon( const css::uno::Sequence< css::uno::Sequence< ::sal_Int32 > >& DataX, const css::uno::Sequence< css::uno::Sequence< ::sal_Int32 > >& DataY ) override; + virtual void SAL_CALL drawEllipse( ::sal_Int32 X, ::sal_Int32 Y, ::sal_Int32 Width, ::sal_Int32 Height ) override; + virtual void SAL_CALL drawArc( ::sal_Int32 X, ::sal_Int32 Y, ::sal_Int32 Width, ::sal_Int32 Height, ::sal_Int32 X1, ::sal_Int32 Y1, ::sal_Int32 X2, ::sal_Int32 Y2 ) override; + virtual void SAL_CALL drawPie( ::sal_Int32 X, ::sal_Int32 Y, ::sal_Int32 Width, ::sal_Int32 Height, ::sal_Int32 X1, ::sal_Int32 Y1, ::sal_Int32 X2, ::sal_Int32 Y2 ) override; + virtual void SAL_CALL drawChord( ::sal_Int32 nX, ::sal_Int32 nY, ::sal_Int32 nWidth, ::sal_Int32 nHeight, ::sal_Int32 nX1, ::sal_Int32 nY1, ::sal_Int32 nX2, ::sal_Int32 nY2 ) override; + virtual void SAL_CALL drawGradient( ::sal_Int32 nX, ::sal_Int32 nY, ::sal_Int32 nWidth, ::sal_Int32 Height, const css::awt::Gradient& aGradient ) override; + virtual void SAL_CALL drawText( ::sal_Int32 X, ::sal_Int32 Y, const OUString& Text ) override; + virtual void SAL_CALL drawTextArray( ::sal_Int32 X, ::sal_Int32 Y, const OUString& Text, const css::uno::Sequence< ::sal_Int32 >& Longs ) override; + virtual void SAL_CALL drawImage( ::sal_Int32 nX, ::sal_Int32 nY, ::sal_Int32 nWidth, ::sal_Int32 nHeight, ::sal_Int16 nStyle, const css::uno::Reference< css::graphic::XGraphic >& aGraphic ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxpointer.hxx b/toolkit/inc/awt/vclxpointer.hxx new file mode 100644 index 0000000000..1030319269 --- /dev/null +++ b/toolkit/inc/awt/vclxpointer.hxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + + +#include <com/sun/star/awt/XPointer.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <mutex> + +#include <vcl/ptrstyle.hxx> + + + + +class VCLXPointer final : public cppu::WeakImplHelper< + css::awt::XPointer, css::lang::XServiceInfo> +{ + std::mutex maMutex; + PointerStyle maPointer; + +public: + VCLXPointer(); + virtual ~VCLXPointer() override; + + PointerStyle GetPointer() const { return maPointer; } + + // css::awt::XPointer + void SAL_CALL setType( sal_Int32 nType ) override; + sal_Int32 SAL_CALL getType( ) override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxprinter.hxx b/toolkit/inc/awt/vclxprinter.hxx new file mode 100644 index 0000000000..e5440f38aa --- /dev/null +++ b/toolkit/inc/awt/vclxprinter.hxx @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/XPrinterPropertySet.hpp> +#include <com/sun/star/awt/XPrinterServer2.hpp> +#include <com/sun/star/awt/XInfoPrinter.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <comphelper/broadcasthelper.hxx> +#include <cppuhelper/propshlp.hxx> +#include <cppuhelper/implbase.hxx> +#include <comphelper/uno3.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/jobset.hxx> + +#include <memory> + +namespace com::sun::star::awt { class XPrinter; } +namespace vcl { class OldStylePrintAdaptor; } +class Printer; + +// relevant properties for the printer: +/* + sal_Bool Horizontal + sal_uInt16 CopyCount; + sal_Bool Collate; + String FormDescriptor; + sal_uInt16 Orientation; // PORTRAIT, LANDSCAPE +*/ + + + + +typedef ::cppu::WeakImplHelper < css::awt::XPrinterPropertySet + > VCLXPrinterPropertySet_Base; +class VCLXPrinterPropertySet :public VCLXPrinterPropertySet_Base + ,public comphelper::OMutexAndBroadcastHelper + ,public ::cppu::OPropertySetHelper +{ +protected: + VclPtr<Printer> mxPrinter; + css::uno::Reference< css::awt::XDevice > mxPrnDevice; + + sal_Int16 mnOrientation; + bool mbHorizontal; +public: + VCLXPrinterPropertySet( const OUString& rPrinterName ); + virtual ~VCLXPrinterPropertySet() override; + + Printer* GetPrinter() const { return mxPrinter.get(); } + css::uno::Reference< css::awt::XDevice > const & GetDevice(); + + // css::uno::XInterface + DECLARE_XINTERFACE(); + + // css::lang::XTypeProvider + DECLARE_XTYPEPROVIDER(); + + // css::beans::XPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + void SAL_CALL setPropertyValue( const OUString& rPropertyName, const css::uno::Any& aValue ) override { OPropertySetHelper::setPropertyValue( rPropertyName, aValue ); } + css::uno::Any SAL_CALL getPropertyValue( const OUString& rPropertyName ) override { return OPropertySetHelper::getPropertyValue( rPropertyName ); } + void SAL_CALL addPropertyChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& rxListener ) override { OPropertySetHelper::addPropertyChangeListener( rPropertyName, rxListener ); } + void SAL_CALL removePropertyChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& rxListener ) override { OPropertySetHelper::removePropertyChangeListener( rPropertyName, rxListener ); } + void SAL_CALL addVetoableChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& rxListener ) override { OPropertySetHelper::addVetoableChangeListener( rPropertyName, rxListener ); } + void SAL_CALL removeVetoableChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& rxListener ) override { OPropertySetHelper::removeVetoableChangeListener( rPropertyName, rxListener ); } + + // ::cppu::OPropertySetHelper + ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any & rConvertedValue, css::uno::Any & rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) override; + void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override; + using cppu::OPropertySetHelper::getFastPropertyValue; + void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override; + + // css::awt::XPrinterPropertySet + void SAL_CALL setHorizontal( sal_Bool bHorizontal ) override; + css::uno::Sequence< OUString > SAL_CALL getFormDescriptions( ) override; + void SAL_CALL selectForm( const OUString& aFormDescription ) override; + css::uno::Sequence< sal_Int8 > SAL_CALL getBinarySetup( ) override; + void SAL_CALL setBinarySetup( const css::uno::Sequence< sal_Int8 >& data ) override; +}; + + + + +typedef ::cppu::ImplInheritanceHelper < VCLXPrinterPropertySet + , css::awt::XPrinter + > VCLXPrinter_Base; +class VCLXPrinter final : public VCLXPrinter_Base +{ + std::shared_ptr<vcl::OldStylePrintAdaptor> mxListener; + JobSetup maInitJobSetup; +public: + VCLXPrinter( const OUString& rPrinterName ); + virtual ~VCLXPrinter() override; + + // css::beans::XPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override { return VCLXPrinterPropertySet::getPropertySetInfo(); } + void SAL_CALL setPropertyValue( const OUString& rPropertyName, const css::uno::Any& aValue ) override { VCLXPrinterPropertySet::setPropertyValue( rPropertyName, aValue ); } + css::uno::Any SAL_CALL getPropertyValue( const OUString& rPropertyName ) override { return VCLXPrinterPropertySet::getPropertyValue( rPropertyName ); } + void SAL_CALL addPropertyChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& rxListener ) override { VCLXPrinterPropertySet::addPropertyChangeListener( rPropertyName, rxListener ); } + void SAL_CALL removePropertyChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& rxListener ) override { VCLXPrinterPropertySet::removePropertyChangeListener( rPropertyName, rxListener ); } + void SAL_CALL addVetoableChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& rxListener ) override { VCLXPrinterPropertySet::addVetoableChangeListener( rPropertyName, rxListener ); } + void SAL_CALL removeVetoableChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& rxListener ) override { VCLXPrinterPropertySet::removeVetoableChangeListener( rPropertyName, rxListener ); } + + // css::awt::XPrinterPropertySet + void SAL_CALL setHorizontal( sal_Bool bHorizontal ) override { VCLXPrinterPropertySet::setHorizontal( bHorizontal ); } + css::uno::Sequence< OUString > SAL_CALL getFormDescriptions( ) override { return VCLXPrinterPropertySet::getFormDescriptions(); } + void SAL_CALL selectForm( const OUString& aFormDescription ) override { VCLXPrinterPropertySet::selectForm( aFormDescription ); } + css::uno::Sequence< sal_Int8 > SAL_CALL getBinarySetup( ) override { return VCLXPrinterPropertySet::getBinarySetup(); } + void SAL_CALL setBinarySetup( const css::uno::Sequence< sal_Int8 >& data ) override { VCLXPrinterPropertySet::setBinarySetup( data ); } + + // css::awt::XPrinter + sal_Bool SAL_CALL start( const OUString& nJobName, sal_Int16 nCopies, sal_Bool nCollate ) override; + void SAL_CALL end( ) override; + void SAL_CALL terminate( ) override; + css::uno::Reference< css::awt::XDevice > SAL_CALL startPage( ) override; + void SAL_CALL endPage( ) override; +}; + + + + +typedef ::cppu::ImplInheritanceHelper < VCLXPrinterPropertySet + , css::awt::XInfoPrinter + > VCLXInfoPrinter_Base; +class VCLXInfoPrinter final : public VCLXInfoPrinter_Base +{ +public: + VCLXInfoPrinter( const OUString& rPrinterName ); + virtual ~VCLXInfoPrinter() override; + + // css::beans::XPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override { return VCLXPrinterPropertySet::getPropertySetInfo(); } + void SAL_CALL setPropertyValue( const OUString& rPropertyName, const css::uno::Any& aValue ) override { VCLXPrinterPropertySet::setPropertyValue( rPropertyName, aValue ); } + css::uno::Any SAL_CALL getPropertyValue( const OUString& rPropertyName ) override { return VCLXPrinterPropertySet::getPropertyValue( rPropertyName ); } + void SAL_CALL addPropertyChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& rxListener ) override { VCLXPrinterPropertySet::addPropertyChangeListener( rPropertyName, rxListener ); } + void SAL_CALL removePropertyChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& rxListener ) override { VCLXPrinterPropertySet::removePropertyChangeListener( rPropertyName, rxListener ); } + void SAL_CALL addVetoableChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& rxListener ) override { VCLXPrinterPropertySet::addVetoableChangeListener( rPropertyName, rxListener ); } + void SAL_CALL removeVetoableChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& rxListener ) override { VCLXPrinterPropertySet::removeVetoableChangeListener( rPropertyName, rxListener ); } + + // css::awt::XPrinterPropertySet + void SAL_CALL setHorizontal( sal_Bool bHorizontal ) override { VCLXPrinterPropertySet::setHorizontal( bHorizontal ); } + css::uno::Sequence< OUString > SAL_CALL getFormDescriptions( ) override { return VCLXPrinterPropertySet::getFormDescriptions(); } + void SAL_CALL selectForm( const OUString& aFormDescription ) override { VCLXPrinterPropertySet::selectForm( aFormDescription ); } + css::uno::Sequence< sal_Int8 > SAL_CALL getBinarySetup( ) override { return VCLXPrinterPropertySet::getBinarySetup(); } + void SAL_CALL setBinarySetup( const css::uno::Sequence< sal_Int8 >& data ) override { VCLXPrinterPropertySet::setBinarySetup( data ); } + + // css::awt::XInfoPrinter + css::uno::Reference< css::awt::XDevice > SAL_CALL createDevice( ) override; +}; + + + + +typedef ::cppu::WeakImplHelper < css::awt::XPrinterServer2, + css::lang::XServiceInfo + > VCLXPrinterServer_Base; +class VCLXPrinterServer final : public VCLXPrinterServer_Base +{ +public: + // css::awt::XPrinterServer2 + css::uno::Sequence< OUString > SAL_CALL getPrinterNames( ) override; + OUString SAL_CALL getDefaultPrinterName() override; + css::uno::Reference< css::awt::XPrinter > SAL_CALL createPrinter( const OUString& printerName ) override; + css::uno::Reference< css::awt::XInfoPrinter > SAL_CALL createInfoPrinter( const OUString& printerName ) override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxregion.hxx b/toolkit/inc/awt/vclxregion.hxx new file mode 100644 index 0000000000..c0c64d37a6 --- /dev/null +++ b/toolkit/inc/awt/vclxregion.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/XRegion.hpp> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <mutex> + +#include <vcl/region.hxx> + + + + +class VCLXRegion final : public cppu::WeakImplHelper< + css::awt::XRegion> +{ + std::mutex maMutex; + vcl::Region maRegion; + +public: + VCLXRegion(); + virtual ~VCLXRegion() override; + + const vcl::Region& GetRegion() const { return maRegion; } + + // css::awt::XRegion + css::awt::Rectangle SAL_CALL getBounds() override; + void SAL_CALL clear() override; + void SAL_CALL move( sal_Int32 nHorzMove, sal_Int32 nVertMove ) override; + void SAL_CALL unionRectangle( const css::awt::Rectangle& rRect ) override; + void SAL_CALL intersectRectangle( const css::awt::Rectangle& rRect ) override; + void SAL_CALL excludeRectangle( const css::awt::Rectangle& rRect ) override; + void SAL_CALL xOrRectangle( const css::awt::Rectangle& rRect ) override; + void SAL_CALL unionRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) override; + void SAL_CALL intersectRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) override; + void SAL_CALL excludeRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) override; + void SAL_CALL xOrRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) override; + css::uno::Sequence< css::awt::Rectangle > SAL_CALL getRectangles() override; + +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxspinbutton.hxx b/toolkit/inc/awt/vclxspinbutton.hxx new file mode 100644 index 0000000000..acd7a68984 --- /dev/null +++ b/toolkit/inc/awt/vclxspinbutton.hxx @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> +#include <cppuhelper/implbase1.hxx> +#include <comphelper/uno3.hxx> +#include <com/sun/star/awt/XSpinValue.hpp> + + +namespace toolkit +{ + + + //= VCLXSpinButton + + typedef ::cppu::ImplHelper1 < css::awt::XSpinValue + > VCLXSpinButton_Base; + + class VCLXSpinButton final : public VCLXWindow + ,public VCLXSpinButton_Base + { + private: + AdjustmentListenerMultiplexer maAdjustmentListeners; + + public: + VCLXSpinButton(); + + private: + virtual ~VCLXSpinButton( ) override; + + // XInterface + DECLARE_XINTERFACE() + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + + // XComponent + void SAL_CALL dispose( ) override; + + // XSpinValue + virtual void SAL_CALL addAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener >& listener ) override; + virtual void SAL_CALL removeAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener >& listener ) override; + virtual void SAL_CALL setValue( sal_Int32 n ) override; + virtual void SAL_CALL setValues( sal_Int32 minValue, sal_Int32 maxValue, sal_Int32 currentValue ) override; + virtual sal_Int32 SAL_CALL getValue( ) override; + virtual void SAL_CALL setMinimum( sal_Int32 minValue ) override; + virtual void SAL_CALL setMaximum( sal_Int32 maxValue ) override; + virtual sal_Int32 SAL_CALL getMinimum( ) override; + virtual sal_Int32 SAL_CALL getMaximum( ) override; + virtual void SAL_CALL setSpinIncrement( sal_Int32 spinIncrement ) override; + virtual sal_Int32 SAL_CALL getSpinIncrement( ) override; + virtual void SAL_CALL setOrientation( sal_Int32 orientation ) override; + virtual sal_Int32 SAL_CALL getOrientation( ) override; + + // VclWindowPeer + virtual void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + virtual css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + // VCLXWindow + void ProcessWindowEvent( const VclWindowEvent& _rVclWindowEvent ) override; + + VCLXSpinButton( const VCLXSpinButton& ) = delete; + VCLXSpinButton& operator=( const VCLXSpinButton& ) = delete; + }; + + +} // namespacetoolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxsystemdependentwindow.hxx b/toolkit/inc/awt/vclxsystemdependentwindow.hxx new file mode 100644 index 0000000000..69eba013c6 --- /dev/null +++ b/toolkit/inc/awt/vclxsystemdependentwindow.hxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/XSystemDependentWindowPeer.hpp> +#include <cppuhelper/implbase.hxx> + +#include <toolkit/awt/vclxwindow.hxx> + +class VCLXSystemDependentWindow final : + public cppu::ImplInheritanceHelper<VCLXWindow, css::awt::XSystemDependentWindowPeer> +{ +public: + VCLXSystemDependentWindow(); + virtual ~VCLXSystemDependentWindow() override; + + // css::awt::XSystemDependentWindowPeer + css::uno::Any SAL_CALL getWindowHandle( const css::uno::Sequence< sal_Int8 >& ProcessId, sal_Int16 SystemType ) override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxtabpagecontainer.hxx b/toolkit/inc/awt/vclxtabpagecontainer.hxx new file mode 100644 index 0000000000..e300e03aff --- /dev/null +++ b/toolkit/inc/awt/vclxtabpagecontainer.hxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/container/XContainerListener.hpp> +#include <com/sun/star/beans/XPropertiesChangeListener.hpp> +#include <com/sun/star/awt/tab/XTabPageContainer.hpp> +#include <cppuhelper/implbase.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> +#include <awt/vclxcontainer.hxx> + + +typedef cppu::ImplInheritanceHelper< VCLXContainer, + css::awt::tab::XTabPageContainer, + css::beans::XPropertiesChangeListener, + css::container::XContainerListener + > VCLXTabPageContainer_Base; +class VCLXTabPageContainer final : public VCLXTabPageContainer_Base +{ +public: + VCLXTabPageContainer(); + virtual ~VCLXTabPageContainer() override; + + // css::awt::XView + void SAL_CALL draw( sal_Int32 nX, sal_Int32 nY ) override; + + // css::awt::grid::XTabPageContainer + virtual ::sal_Int16 SAL_CALL getActiveTabPageID() override; + virtual void SAL_CALL setActiveTabPageID( ::sal_Int16 _activetabpageid ) override; + virtual ::sal_Int16 SAL_CALL getTabPageCount( ) override; + virtual sal_Bool SAL_CALL isTabPageActive( ::sal_Int16 tabPageIndex ) override; + virtual css::uno::Reference< css::awt::tab::XTabPage > SAL_CALL getTabPage( ::sal_Int16 tabPageIndex ) override; + virtual css::uno::Reference< css::awt::tab::XTabPage > SAL_CALL getTabPageByID( ::sal_Int16 tabPageID ) override; + virtual void SAL_CALL addTabPageContainerListener( const css::uno::Reference< css::awt::tab::XTabPageContainerListener >& listener ) override; + virtual void SAL_CALL removeTabPageContainerListener( const css::uno::Reference< css::awt::tab::XTabPageContainerListener >& listener ) override; + + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override; + + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // css::beans::XPropertiesChangeListener + virtual void SAL_CALL propertiesChange( const ::css::uno::Sequence< ::css::beans::PropertyChangeEvent >& aEvent ) override; + + // css::awt::XVclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; +private: + virtual void ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) override; + + TabPageListenerMultiplexer m_aTabPageListeners; + ::std::vector< css::uno::Reference< css::awt::tab::XTabPage > > m_aTabPages; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxtopwindow.hxx b/toolkit/inc/awt/vclxtopwindow.hxx new file mode 100644 index 0000000000..1913c4594f --- /dev/null +++ b/toolkit/inc/awt/vclxtopwindow.hxx @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_AWT_VCLXTOPWINDOW_HXX +#define INCLUDED_TOOLKIT_AWT_VCLXTOPWINDOW_HXX + +#include <com/sun/star/awt/XSystemDependentWindowPeer.hpp> +#include <com/sun/star/awt/XTopWindow2.hpp> + +#include <cppuhelper/implbase.hxx> + +#include <awt/vclxcontainer.hxx> + +namespace com::sun::star::awt { class XMenuBar; } + + +class VCLXTopWindow: public cppu::ImplInheritanceHelper< + VCLXContainer, css::awt::XTopWindow2, css::awt::XSystemDependentWindowPeer > +{ +public: + VCLXTopWindow(); + virtual ~VCLXTopWindow() override; + + // css::awt::XSystemDependentWindowPeer + css::uno::Any SAL_CALL getWindowHandle( const css::uno::Sequence< sal_Int8 >& ProcessId, sal_Int16 SystemType ) override; + + // css::awt::XTopWindow + void SAL_CALL addTopWindowListener( const css::uno::Reference< css::awt::XTopWindowListener >& rxListener ) override; + void SAL_CALL removeTopWindowListener( const css::uno::Reference< css::awt::XTopWindowListener >& rxListener ) override; + void SAL_CALL toFront() override; + void SAL_CALL toBack() override; + void SAL_CALL setMenuBar( const css::uno::Reference< css::awt::XMenuBar >& xMenu ) override; + + // XTopWindow2 + virtual sal_Bool SAL_CALL getIsMaximized() override; + virtual void SAL_CALL setIsMaximized( sal_Bool _ismaximized ) override; + virtual sal_Bool SAL_CALL getIsMinimized() override; + virtual void SAL_CALL setIsMinimized( sal_Bool _isminimized ) override; + virtual ::sal_Int32 SAL_CALL getDisplay() override; + virtual void SAL_CALL setDisplay( ::sal_Int32 _display ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + + +#endif // INCLUDED_TOOLKIT_AWT_VCLXTOPWINDOW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/awt/vclxwindows.hxx b/toolkit/inc/awt/vclxwindows.hxx new file mode 100644 index 0000000000..22367ddcc0 --- /dev/null +++ b/toolkit/inc/awt/vclxwindows.hxx @@ -0,0 +1,605 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/XCurrencyField.hpp> +#include <com/sun/star/awt/XDateField.hpp> +#include <com/sun/star/awt/XDialog2.hpp> +#include <com/sun/star/awt/XMessageBox.hpp> +#include <com/sun/star/awt/XMetricField.hpp> +#include <com/sun/star/awt/XNumericField.hpp> +#include <com/sun/star/awt/XPatternField.hpp> +#include <com/sun/star/awt/XProgressBar.hpp> +#include <com/sun/star/awt/XSimpleTabController.hpp> +#include <com/sun/star/awt/XTimeField.hpp> +#include <com/sun/star/awt/grid/XGridControl.hpp> +#include <com/sun/star/awt/grid/XGridRowSelection.hpp> +#include <com/sun/star/awt/grid/XGridDataListener.hpp> +#include <com/sun/star/awt/grid/GridDataEvent.hpp> +#include <com/sun/star/awt/grid/XGridSelectionListener.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/Date.hpp> + +#include <cppuhelper/implbase.hxx> + +#include <awt/vclxtopwindow.hxx> +#include <toolkit/awt/vclxwindows.hxx> + +class FormatterBase; +class TabControl; +class TabPage; +class Edit; + +// class VCLXImageControl +class VCLXImageControl final : public VCLXGraphicControl +{ +public: + VCLXImageControl(); + virtual ~VCLXImageControl() override; + + // css::awt::XLayoutConstrains + css::awt::Size SAL_CALL getMinimumSize( ) override; + css::awt::Size SAL_CALL getPreferredSize( ) override; + css::awt::Size SAL_CALL calcAdjustedSize( const css::awt::Size& rNewSize ) override; + + // css::awt::VclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } + +private: + virtual void ImplSetNewImage() override; +}; + +// class VCLXMessageBox +class VCLXMessageBox final : + public cppu::ImplInheritanceHelper<VCLXTopWindow, css::awt::XMessageBox> +{ +public: + VCLXMessageBox(); + virtual ~VCLXMessageBox() override; + + + // css::awt::XMessageBox + void SAL_CALL setCaptionText( const OUString& aText ) override; + OUString SAL_CALL getCaptionText( ) override; + void SAL_CALL setMessageText( const OUString& aText ) override; + OUString SAL_CALL getMessageText( ) override; + sal_Int16 SAL_CALL execute( ) override; + + // css::awt::XLayoutConstrains + css::awt::Size SAL_CALL getMinimumSize() override; + + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override; +}; + +// class VCLXFrame +class VCLXFrame final : public VCLXContainer +{ + void ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) override; + +public: + VCLXFrame(); + virtual ~VCLXFrame() override; + + // css::awt::XView + void SAL_CALL draw( sal_Int32 nX, sal_Int32 nY ) override; + + // css::awt::XVclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + +// class VCLXDialog +class VCLXDialog final : public cppu::ImplInheritanceHelper<VCLXTopWindow, css::awt::XDialog2> +{ +public: + VCLXDialog(); + virtual ~VCLXDialog() override; + + // css::awt::XDialog2 + virtual void SAL_CALL endDialog( ::sal_Int32 Result ) override; + virtual void SAL_CALL setHelpId( const OUString& Id ) override; + + // css::awt::XDialog + void SAL_CALL setTitle( const OUString& Title ) override; + OUString SAL_CALL getTitle( ) override; + sal_Int16 SAL_CALL execute( ) override; + void SAL_CALL endExecute( ) override; + + // css::awt::XView + void SAL_CALL draw( sal_Int32 nX, sal_Int32 nY ) override; + + // css::awt::XDevice, + css::awt::DeviceInfo SAL_CALL getInfo() override; + + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override; + + // css::awt::XVclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + +}; + +// class VCLXTabPage +class VCLXTabPage final : public VCLXContainer +{ +public: + VCLXTabPage(); + virtual ~VCLXTabPage() override; + + // css::awt::XView + void SAL_CALL draw( sal_Int32 nX, sal_Int32 nY ) override; + + // css::awt::XVclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + + /// @throws css::uno::RuntimeException + TabPage* getTabPage() const; + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + +class VCLXMultiPage final : + public cppu::ImplInheritanceHelper<VCLXContainer, css::awt::XSimpleTabController> +{ + TabListenerMultiplexer maTabListeners; + sal_Int32 mTabId; + + void ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) override; +public: + VCLXMultiPage(); + virtual ~VCLXMultiPage() override; + + // css::lang::XComponent + void SAL_CALL dispose( ) override; + + // css::awt::XView + void SAL_CALL draw( sal_Int32 nX, sal_Int32 nY ) override; + + // css::awt::XVclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + // XSimpleTabController + virtual ::sal_Int32 SAL_CALL insertTab() override; + virtual void SAL_CALL removeTab( ::sal_Int32 ID ) override; + + virtual void SAL_CALL setTabProps( ::sal_Int32 ID, const css::uno::Sequence< css::beans::NamedValue >& Properties ) override; + virtual css::uno::Sequence< css::beans::NamedValue > SAL_CALL getTabProps( ::sal_Int32 ID ) override; + + virtual void SAL_CALL activateTab( ::sal_Int32 ID ) override; + virtual ::sal_Int32 SAL_CALL getActiveTabID() override; + + virtual void SAL_CALL addTabListener( const css::uno::Reference< css::awt::XTabListener >& Listener ) override; + virtual void SAL_CALL removeTabListener( const css::uno::Reference< css::awt::XTabListener >& Listener ) override; + // C++ + /// @throws css::uno::RuntimeException + TabControl* getTabControl() const; + sal_uInt16 insertTab( TabPage*, OUString const & sTitle ); + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + +// class VCLXProgressBar +class VCLXProgressBar final : public cppu::ImplInheritanceHelper<VCLXWindow, css::awt::XProgressBar> +{ +private: + sal_Int32 m_nValue; + sal_Int32 m_nValueMin; + sal_Int32 m_nValueMax; + + void ImplUpdateValue(); + +public: + VCLXProgressBar(); + virtual ~VCLXProgressBar() override; + + // css::awt::XProgressBar + void SAL_CALL setForegroundColor( sal_Int32 nColor ) override; + void SAL_CALL setBackgroundColor( sal_Int32 nColor ) override; + void SAL_CALL setValue( sal_Int32 nValue ) override; + void SAL_CALL setRange( sal_Int32 nMin, sal_Int32 nMax ) override; + sal_Int32 SAL_CALL getValue() override; + + // css::awt::VclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + + +// class VCLXFormattedSpinField +class VCLXFormattedSpinField : public VCLXSpinField +{ +private: + FormatterBase* mpFormatter; + +protected: + FormatterBase* GetFormatter() const { return GetWindow() ? mpFormatter : nullptr; } + +public: + VCLXFormattedSpinField(); + virtual ~VCLXFormattedSpinField() override; + + void SetFormatter( FormatterBase* pFormatter ) { mpFormatter = pFormatter; } + + void setStrictFormat( bool bStrict ); + bool isStrictFormat() const; + + // css::awt::VclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + + +// class VCLXDateField + +class VCLXDateField : + public cppu::ImplInheritanceHelper<VCLXFormattedSpinField, css::awt::XDateField> +{ +protected: + virtual css::uno::Reference< css::accessibility::XAccessibleContext > CreateAccessibleContext() override; +public: + VCLXDateField(); + virtual ~VCLXDateField() override; + + + // css::awt::XDateField + void SAL_CALL setDate( const css::util::Date& Date ) override; + css::util::Date SAL_CALL getDate( ) override; + void SAL_CALL setMin( const css::util::Date& Date ) override; + css::util::Date SAL_CALL getMin( ) override; + void SAL_CALL setMax( const css::util::Date& Date ) override; + css::util::Date SAL_CALL getMax( ) override; + void SAL_CALL setFirst( const css::util::Date& Date ) override; + css::util::Date SAL_CALL getFirst( ) override; + void SAL_CALL setLast( const css::util::Date& Date ) override; + css::util::Date SAL_CALL getLast( ) override; + void SAL_CALL setLongFormat( sal_Bool bLong ) override; + sal_Bool SAL_CALL isLongFormat( ) override; + void SAL_CALL setEmpty( ) override; + sal_Bool SAL_CALL isEmpty( ) override; + void SAL_CALL setStrictFormat( sal_Bool bStrict ) override; + sal_Bool SAL_CALL isStrictFormat( ) override; + + // css::awt::VclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + + +// class VCLXTimeField + +class VCLXTimeField final : + public cppu::ImplInheritanceHelper<VCLXFormattedSpinField, css::awt::XTimeField> +{ + virtual css::uno::Reference< css::accessibility::XAccessibleContext > CreateAccessibleContext() override; +public: + VCLXTimeField(); + virtual ~VCLXTimeField() override; + + // css::awt::XTimeField + void SAL_CALL setTime( const css::util::Time& Time ) override; + css::util::Time SAL_CALL getTime( ) override; + void SAL_CALL setMin( const css::util::Time& Time ) override; + css::util::Time SAL_CALL getMin( ) override; + void SAL_CALL setMax( const css::util::Time& Time ) override; + css::util::Time SAL_CALL getMax( ) override; + void SAL_CALL setFirst( const css::util::Time& Time ) override; + css::util::Time SAL_CALL getFirst( ) override; + void SAL_CALL setLast( const css::util::Time& Time ) override; + css::util::Time SAL_CALL getLast( ) override; + void SAL_CALL setEmpty( ) override; + sal_Bool SAL_CALL isEmpty( ) override; + void SAL_CALL setStrictFormat( sal_Bool bStrict ) override; + sal_Bool SAL_CALL isStrictFormat( ) override; + + // css::awt::VclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + + +// class VCLXNumericField + +class VCLXNumericField final : + public cppu::ImplInheritanceHelper<VCLXFormattedSpinField, css::awt::XNumericField> +{ +public: + VCLXNumericField(); + virtual ~VCLXNumericField() override; + + // css::awt::XNumericField + void SAL_CALL setValue( double Value ) override; + double SAL_CALL getValue( ) override; + void SAL_CALL setMin( double Value ) override; + double SAL_CALL getMin( ) override; + void SAL_CALL setMax( double Value ) override; + double SAL_CALL getMax( ) override; + void SAL_CALL setFirst( double Value ) override; + double SAL_CALL getFirst( ) override; + void SAL_CALL setLast( double Value ) override; + double SAL_CALL getLast( ) override; + void SAL_CALL setSpinSize( double Value ) override; + double SAL_CALL getSpinSize( ) override; + void SAL_CALL setDecimalDigits( sal_Int16 nDigits ) override; + sal_Int16 SAL_CALL getDecimalDigits( ) override; + void SAL_CALL setStrictFormat( sal_Bool bStrict ) override; + sal_Bool SAL_CALL isStrictFormat( ) override; + + // css::awt::VclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + + +// class VCLXMetricField + +class MetricFormatter; +class MetricField; +class VCLXMetricField final : + public cppu::ImplInheritanceHelper<VCLXFormattedSpinField, css::awt::XMetricField> +{ + /// @throws css::uno::RuntimeException + MetricFormatter *GetMetricFormatter(); + /// @throws css::uno::RuntimeException + MetricField *GetMetricField(); + void CallListeners(); +public: + VCLXMetricField(); + virtual ~VCLXMetricField() override; + + // css::awt::XMetricField + virtual void SAL_CALL setValue( ::sal_Int64 Value, ::sal_Int16 Unit ) override; + virtual void SAL_CALL setUserValue( ::sal_Int64 Value, ::sal_Int16 Unit ) override; + virtual ::sal_Int64 SAL_CALL getValue( ::sal_Int16 Unit ) override; + virtual ::sal_Int64 SAL_CALL getCorrectedValue( ::sal_Int16 Unit ) override; + virtual void SAL_CALL setMin( ::sal_Int64 Value, ::sal_Int16 Unit ) override; + virtual ::sal_Int64 SAL_CALL getMin( ::sal_Int16 Unit ) override; + virtual void SAL_CALL setMax( ::sal_Int64 Value, ::sal_Int16 Unit ) override; + virtual ::sal_Int64 SAL_CALL getMax( ::sal_Int16 Unit ) override; + virtual void SAL_CALL setFirst( ::sal_Int64 Value, ::sal_Int16 Unit ) override; + virtual ::sal_Int64 SAL_CALL getFirst( ::sal_Int16 Unit ) override; + virtual void SAL_CALL setLast( ::sal_Int64 Value, ::sal_Int16 Unit ) override; + virtual ::sal_Int64 SAL_CALL getLast( ::sal_Int16 Unit ) override; + virtual void SAL_CALL setSpinSize( ::sal_Int64 Value ) override; + virtual ::sal_Int64 SAL_CALL getSpinSize( ) override; + virtual void SAL_CALL setDecimalDigits( ::sal_Int16 nDigits ) override; + virtual ::sal_Int16 SAL_CALL getDecimalDigits( ) override; + virtual void SAL_CALL setStrictFormat( sal_Bool bStrict ) override; + virtual sal_Bool SAL_CALL isStrictFormat( ) override; + + // css::awt::VclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + +// class VCLXPatternField +class VCLXPatternField final : + public cppu::ImplInheritanceHelper<VCLXFormattedSpinField, css::awt::XPatternField> +{ +public: + VCLXPatternField(); + virtual ~VCLXPatternField() override; + + + // css::awt::XPatternField + void SAL_CALL setMasks( const OUString& EditMask, const OUString& LiteralMask ) override; + void SAL_CALL getMasks( OUString& EditMask, OUString& LiteralMask ) override; + void SAL_CALL setString( const OUString& Str ) override; + OUString SAL_CALL getString( ) override; + void SAL_CALL setStrictFormat( sal_Bool bStrict ) override; + sal_Bool SAL_CALL isStrictFormat( ) override; + + // css::awt::VclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + + +class VCLXFileControl final : public cppu::ImplInheritanceHelper<VCLXWindow, css::awt::XTextComponent, css::awt::XTextLayoutConstrains> +{ + DECL_LINK(ModifyHdl, Edit&, void); + void ModifyHdl(); + TextListenerMultiplexer maTextListeners; + +public: + VCLXFileControl(); + virtual ~VCLXFileControl() override; + + virtual void SetWindow( const VclPtr< vcl::Window > &pWindow ) override; + + // css::awt::XTextComponent + void SAL_CALL addTextListener( const css::uno::Reference< css::awt::XTextListener >& l ) override; + void SAL_CALL removeTextListener( const css::uno::Reference< css::awt::XTextListener >& l ) override; + void SAL_CALL setText( const OUString& aText ) override; + void SAL_CALL insertText( const css::awt::Selection& Sel, const OUString& Text ) override; + OUString SAL_CALL getText( ) override; + OUString SAL_CALL getSelectedText( ) override; + void SAL_CALL setSelection( const css::awt::Selection& aSelection ) override; + css::awt::Selection SAL_CALL getSelection( ) override; + sal_Bool SAL_CALL isEditable( ) override; + void SAL_CALL setEditable( sal_Bool bEditable ) override; + void SAL_CALL setMaxTextLen( sal_Int16 nLen ) override; + sal_Int16 SAL_CALL getMaxTextLen( ) override; + + // css::awt::XLayoutConstrains + css::awt::Size SAL_CALL getMinimumSize( ) override; + css::awt::Size SAL_CALL getPreferredSize( ) override; + css::awt::Size SAL_CALL calcAdjustedSize( const css::awt::Size& aNewSize ) override; + + // css::awt::XTextLayoutConstrains + css::awt::Size SAL_CALL getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) override; + void SAL_CALL getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) override; + + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + +class SVTXCurrencyField final : + public cppu::ImplInheritanceHelper<SVTXFormattedField, css::awt::XCurrencyField> +{ +public: + SVTXCurrencyField(); + virtual ~SVTXCurrencyField() override; + + // css::awt::XVclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + // css::awt::XCurrencyField + void SAL_CALL setValue( double Value ) override; + double SAL_CALL getValue( ) override; + void SAL_CALL setMin( double Value ) override; + double SAL_CALL getMin( ) override; + void SAL_CALL setMax( double Value ) override; + double SAL_CALL getMax( ) override; + void SAL_CALL setFirst( double Value ) override; + double SAL_CALL getFirst( ) override; + void SAL_CALL setLast( double Value ) override; + double SAL_CALL getLast( ) override; + void SAL_CALL setSpinSize( double Value ) override; + double SAL_CALL getSpinSize( ) override; + void SAL_CALL setDecimalDigits( sal_Int16 nDigits ) override; + sal_Int16 SAL_CALL getDecimalDigits( ) override; + void SAL_CALL setStrictFormat( sal_Bool bStrict ) override; + sal_Bool SAL_CALL isStrictFormat( ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + + +class SVTXDateField final : public VCLXDateField +{ +public: + SVTXDateField(); + virtual ~SVTXDateField() override; + + // css::awt::VclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } +}; + +namespace svt::table { + class TableControl; + class UnoControlTableModel; +} + +typedef ::cppu::ImplInheritanceHelper < VCLXWindow + , css::awt::grid::XGridControl + , css::awt::grid::XGridRowSelection + , css::awt::grid::XGridDataListener + , css::container::XContainerListener + > SVTXGridControl_Base; +class SVTXGridControl final : public SVTXGridControl_Base +{ +public: + SVTXGridControl(); + virtual ~SVTXGridControl() override; + + // XGridDataListener + virtual void SAL_CALL rowsInserted( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowsRemoved( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL dataChanged( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowHeadingChanged( const css::awt::grid::GridDataEvent& Event ) override; + + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XGridControl + virtual ::sal_Int32 SAL_CALL getRowAtPoint(::sal_Int32 x, ::sal_Int32 y) override; + virtual ::sal_Int32 SAL_CALL getColumnAtPoint(::sal_Int32 x, ::sal_Int32 y) override; + virtual ::sal_Int32 SAL_CALL getCurrentColumn( ) override; + virtual ::sal_Int32 SAL_CALL getCurrentRow( ) override; + virtual void SAL_CALL goToCell( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) override; + + // XGridRowSelection + virtual void SAL_CALL selectRow( ::sal_Int32 i_rowIndex ) override; + virtual void SAL_CALL selectAllRows() override; + virtual void SAL_CALL deselectRow( ::sal_Int32 i_rowIndex ) override; + virtual void SAL_CALL deselectAllRows() override; + virtual css::uno::Sequence< ::sal_Int32 > SAL_CALL getSelectedRows() override; + virtual sal_Bool SAL_CALL hasSelectedRows() override; + virtual sal_Bool SAL_CALL isRowSelected(::sal_Int32 index) override; + virtual void SAL_CALL addSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) override; + virtual void SAL_CALL removeSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) override; + + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + // css::lang::XComponent + void SAL_CALL dispose( ) override; + + // XWindow + void SAL_CALL setEnable( sal_Bool bEnable ) override; + +private: + // VCLXWindow + virtual void SetWindow( const VclPtr< vcl::Window > &pWindow ) override; + + void impl_updateColumnsFromModel_nothrow(); + void impl_checkTableModelInit(); + + void impl_checkColumnIndex_throw( ::svt::table::TableControl const & i_table, sal_Int32 const i_columnIndex ) const; + void impl_checkRowIndex_throw( ::svt::table::TableControl const & i_table, sal_Int32 const i_rowIndex ) const; + + virtual void ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) override; + void ImplCallItemListeners(); + + std::shared_ptr< ::svt::table::UnoControlTableModel > m_xTableModel; + bool m_bTableModelInitCompleted; + SelectionListenerMultiplexer m_aSelectionListeners; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/accessiblecontrolcontext.hxx b/toolkit/inc/controls/accessiblecontrolcontext.hxx new file mode 100644 index 0000000000..416984c334 --- /dev/null +++ b/toolkit/inc/controls/accessiblecontrolcontext.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 . + */ + +#pragma once + +#include <comphelper/accessiblecomponenthelper.hxx> +#include <com/sun/star/lang/XEventListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +namespace vcl { class Window; } +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::beans { class XPropertySet; } +namespace com::sun::star::beans { class XPropertySetInfo; } + +namespace toolkit +{ + + + //= OAccessibleControlContext + + + typedef ::comphelper::OAccessibleComponentHelper OAccessibleControlContext_Base; + + /** class implementing the AccessibleContext for a UNO control - to be used in design mode of the control. + <p><b>life time control<b/><br/> + This control should be held weak by the creator (a UNO control), it itself holds a hard reference to the + control model, and a weak reference to the control. The reference to the model is freed when the model + is being disposed.</p> + */ + class OAccessibleControlContext final + :public cppu::ImplInheritanceHelper< + OAccessibleControlContext_Base, css::lang::XEventListener> + { + public: + /** creates an accessible context for a uno control + @param _rxCreator + the uno control's XAccessible interface. This must be an XControl, from which an XControlModel + can be retrieved. + */ + static rtl::Reference<OAccessibleControlContext> create( + const css::uno::Reference< css::accessibility::XAccessible >& _rxCreator + ); + + private: + + // XAccessibleContext + virtual sal_Int64 SAL_CALL getAccessibleChildCount( ) override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 i ) override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override; + virtual sal_Int16 SAL_CALL getAccessibleRole( ) override; + virtual OUString SAL_CALL getAccessibleDescription( ) override; + virtual OUString SAL_CALL getAccessibleName( ) override; + virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override; + virtual sal_Int64 SAL_CALL getAccessibleStateSet( ) override; + + // XAccessibleComponent + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override; + virtual void SAL_CALL grabFocus( ) override; + virtual sal_Int32 SAL_CALL getForeground( ) override; + virtual sal_Int32 SAL_CALL getBackground( ) override; + + // XEventListener + using comphelper::OCommonAccessibleComponent::disposing; + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // retrieves the value of a string property from the model, if the property is present + OUString getModelStringProperty( const char* _pPropertyName ); + + // starts listening at the control model (currently for disposal only) + void startModelListening( ); + // stops listening at the control model + void stopModelListening( ); + + vcl::Window* implGetWindow( css::uno::Reference< css::awt::XWindow >* _pxUNOWindow = nullptr ) const; + + /// ctor. @see Init + OAccessibleControlContext(); + virtual ~OAccessibleControlContext() override; + + /** late ctor + */ + void Init( + const css::uno::Reference< css::accessibility::XAccessible >& _rxCreator + ); + + // OCommonAccessibleComponent overridables + virtual css::awt::Rectangle implGetBounds( ) override; + + css::uno::Reference< css::beans::XPropertySet > + m_xControlModel; // the model of the control which's context we implement + css::uno::Reference< css::beans::XPropertySetInfo > + m_xModelPropsInfo; // the cached property set info of the model + }; + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/animatedimages.hxx b/toolkit/inc/controls/animatedimages.hxx new file mode 100644 index 0000000000..fca49bfa39 --- /dev/null +++ b/toolkit/inc/controls/animatedimages.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <toolkit/controls/unocontrolmodel.hxx> +#include <com/sun/star/awt/XAnimatedImages.hpp> +#include <cppuhelper/implbase1.hxx> + +namespace com::sun::star::container { class XContainerListener; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace toolkit +{ + + + typedef ::cppu::AggImplInheritanceHelper1 < UnoControlModel + , css::awt::XAnimatedImages + > AnimatedImagesControlModel_Base; + class AnimatedImagesControlModel : public AnimatedImagesControlModel_Base + { + public: + AnimatedImagesControlModel( css::uno::Reference< css::uno::XComponentContext > const & i_factory ); + AnimatedImagesControlModel( const AnimatedImagesControlModel& i_copySource ); + + virtual rtl::Reference<UnoControlModel> Clone() const override; + + // XPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XPersistObject + OUString SAL_CALL getServiceName() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XAnimatedImages + virtual ::sal_Int32 SAL_CALL getStepTime() override; + virtual void SAL_CALL setStepTime( ::sal_Int32 _steptime ) override; + virtual sal_Bool SAL_CALL getAutoRepeat() override; + virtual void SAL_CALL setAutoRepeat( sal_Bool _autorepeat ) override; + virtual ::sal_Int16 SAL_CALL getScaleMode() override; + virtual void SAL_CALL setScaleMode( ::sal_Int16 _scalemode ) override; + virtual ::sal_Int32 SAL_CALL getImageSetCount( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getImageSet( ::sal_Int32 i_index ) override; + virtual void SAL_CALL insertImageSet( ::sal_Int32 i_index, const css::uno::Sequence< OUString >& i_imageURLs ) override; + virtual void SAL_CALL replaceImageSet( ::sal_Int32 i_index, const css::uno::Sequence< OUString >& i_imageURLs ) override; + virtual void SAL_CALL removeImageSet( ::sal_Int32 i_index ) override; + + // XAnimatedImages::XContainer + virtual void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& i_listener ) override; + virtual void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& i_listener ) override; + + protected: + virtual ~AnimatedImagesControlModel() override; + + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + void setFastPropertyValue_NoBroadcast( + std::unique_lock<std::mutex>& rGuard, + sal_Int32 nHandle, const css::uno::Any& rValue ) override; + + private: + comphelper::OInterfaceContainerHelper4<css::container::XContainerListener> maContainerListeners; + std::vector< css::uno::Sequence< OUString > > maImageSets; + }; + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/controlmodelcontainerbase.hxx b/toolkit/inc/controls/controlmodelcontainerbase.hxx new file mode 100644 index 0000000000..ae414346c7 --- /dev/null +++ b/toolkit/inc/controls/controlmodelcontainerbase.hxx @@ -0,0 +1,270 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/container/XNameContainer.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/awt/XTabControllerModel.hpp> +#include <com/sun/star/util/XChangesNotifier.hpp> +#include <com/sun/star/util/XChangesListener.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <cppuhelper/implbase8.hxx> +#include <cppuhelper/implbase3.hxx> +#include <cppuhelper/weak.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <controls/unocontrolcontainer.hxx> +#include <cppuhelper/propshlp.hxx> +#include <com/sun/star/awt/tab/XTabPageModel.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/interfacecontainer4.hxx> +#include <mutex> +#include <vector> + +namespace com::sun::star::resource { class XStringResourceResolver; } +namespace com::sun::star::uno { class XComponentContext; } + + +typedef UnoControlModel ControlModel_Base; +typedef ::cppu::AggImplInheritanceHelper8 < ControlModel_Base + , css::lang::XMultiServiceFactory + , css::container::XContainer + , css::container::XNameContainer + , css::awt::XTabControllerModel + , css::util::XChangesNotifier + , css::beans::XPropertyChangeListener + , css::awt::tab::XTabPageModel + , css::lang::XInitialization + > ControlModelContainer_IBase; + +class ControlModelContainerBase : public ControlModelContainer_IBase +{ +public: + enum ChildOperation { Insert = 0, Remove }; + // would like to make this typedef private, too, but the Forte 7 compiler does have + // problems with this... + typedef ::std::pair< css::uno::Reference< css::awt::XControlModel >, OUString > + UnoControlModelHolder; +private: + typedef ::std::vector< UnoControlModelHolder > UnoControlModelHolderVector; + +public: + // for grouping control models (XTabControllerModel::getGroupXXX) + typedef ::std::vector< css::uno::Reference< css::awt::XControlModel > > + ModelGroup; + typedef ::std::vector< ModelGroup > AllGroups; + + friend struct CloneControlModel; + friend struct CompareControlModel; + +protected: + ContainerListenerMultiplexer maContainerListeners; + ::comphelper::OInterfaceContainerHelper4<css::util::XChangesListener> maChangeListeners; + UnoControlModelHolderVector maModels; + + AllGroups maGroups; + bool mbGroupsUpToDate; + + OUString m_sImageURL; + OUString m_sTooltip; + sal_Int16 m_nTabPageId; + + void Clone_Impl(ControlModelContainerBase& _rClone) const; +protected: + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + + UnoControlModelHolderVector::iterator ImplFindElement( std::u16string_view rName ); + + /// @throws css::lang::IllegalArgumentException + /// @throws css::container::ElementExistException + /// @throws css::lang::WrappedTargetException + /// @throws css::uno::RuntimeException + void updateUserFormChildren( const css::uno::Reference< css::container::XNameContainer >& xAllChildren, const OUString& aName, ChildOperation Operation, const css::uno::Reference< css::awt::XControlModel >& xTarget ); +public: + ControlModelContainerBase( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + ControlModelContainerBase( const ControlModelContainerBase& rModel ); + virtual ~ControlModelContainerBase() override; + + rtl::Reference<UnoControlModel> Clone() const override; + + // css::container::XContainer + void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + + // css::container::XElementAccess + css::uno::Type SAL_CALL getElementType( ) override; + sal_Bool SAL_CALL hasElements( ) override; + + // css::container::XNameContainer, XNameReplace, XNameAccess + void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override; + css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override; + void SAL_CALL removeByName( const OUString& Name ) override; + + // css::beans::XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // css::lang::XMultiServiceFactory + css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( const OUString& aServiceSpecifier ) override; + css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const OUString& ServiceSpecifier, const css::uno::Sequence< css::uno::Any >& Arguments ) override; + css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames( ) override; + + // XComponent + void SAL_CALL dispose( ) override; + + // XTabControllerModel + virtual sal_Bool SAL_CALL getGroupControl( ) override; + virtual void SAL_CALL setGroupControl( sal_Bool GroupControl ) override; + virtual void SAL_CALL setControlModels( const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Controls ) override; + virtual css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > SAL_CALL getControlModels( ) override; + virtual void SAL_CALL setGroup( const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Group, const OUString& GroupName ) override; + virtual sal_Int32 SAL_CALL getGroupCount( ) override; + virtual void SAL_CALL getGroup( sal_Int32 nGroup, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Group, OUString& Name ) override; + virtual void SAL_CALL getGroupByName( const OUString& Name, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Group ) override; + + // XChangesNotifier + virtual void SAL_CALL addChangesListener( const css::uno::Reference< css::util::XChangesListener >& aListener ) override; + virtual void SAL_CALL removeChangesListener( const css::uno::Reference< css::util::XChangesListener >& aListener ) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override; + + // XEventListener + using comphelper::OPropertySetHelper::disposing; + virtual void SAL_CALL disposing( const css::lang::EventObject& evt ) override; + + // XServiceInfo + DECLIMPL_SERVICEINFO_DERIVED(ControlModelContainerBase, ControlModel_Base, "toolkit.ControlModelContainerBase" ) + + // XInitialization + virtual void SAL_CALL initialize (const css::uno::Sequence<css::uno::Any>& rArguments) override; + + // css::awt::tab::XTabPageModel + virtual ::sal_Int16 SAL_CALL getTabPageID() override; + virtual sal_Bool SAL_CALL getEnabled() override; + virtual void SAL_CALL setEnabled( sal_Bool _enabled ) override; + virtual OUString SAL_CALL getTitle() override; + virtual void SAL_CALL setTitle( const OUString& _title ) override; + virtual OUString SAL_CALL getImageURL() override; + virtual void SAL_CALL setImageURL( const OUString& _imageurl ) override; + virtual OUString SAL_CALL getToolTip() override; + virtual void SAL_CALL setToolTip( const OUString& _tooltip ) override; + +protected: + void startControlListening( const css::uno::Reference< css::awt::XControlModel >& _rxChildModel ); + void stopControlListening( const css::uno::Reference< css::awt::XControlModel >& _rxChildModel ); + + void implNotifyTabModelChange( const OUString& _rAccessor ); + + void implUpdateGroupStructure(); +}; + +class ResourceListener final : public css::util::XModifyListener, + public ::cppu::OWeakObject +{ + public: + ResourceListener( const css::uno::Reference< css::util::XModifyListener >& xListener ); + virtual ~ResourceListener() override; + + void startListening( const css::uno::Reference< css::resource::XStringResourceResolver >& rResource ); + void stopListening(); + + // 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; + + // XModifyListener + virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + std::mutex m_aMutex; + css::uno::Reference< css::resource::XStringResourceResolver > m_xResource; + css::uno::Reference< css::util::XModifyListener > m_xListener; + bool m_bListening; +}; + +typedef ::cppu::AggImplInheritanceHelper3 < UnoControlContainer + , css::container::XContainerListener + , css::util::XChangesListener + , css::util::XModifyListener + > ControlContainer_IBase; + +class ControlContainerBase : public ControlContainer_IBase +{ +protected: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + bool mbSizeModified; + bool mbPosModified; + css::uno::Reference< css::awt::XTabController > mxTabController; + css::uno::Reference< css::util::XModifyListener > mxListener; + + void ImplInsertControl( css::uno::Reference< css::awt::XControlModel > const & rxModel, const OUString& rName ); + void ImplRemoveControl( css::uno::Reference< css::awt::XControlModel > const & rxModel ); + virtual void ImplSetPosSize( css::uno::Reference< css::awt::XControl >& rxCtrl ); + void ImplUpdateResourceResolver(); + void ImplStartListingForResourceEvents(); + +#ifdef _MSC_VER + // just implemented to let the various FooImplInheritanceHelper compile + ControlContainerBase(); +#endif + +public: + ControlContainerBase( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~ControlContainerBase() override; + + DECLIMPL_SERVICEINFO_DERIVED( ControlContainerBase, UnoControlBase, "toolkit.ControlContainerBase" ) + + void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + void SAL_CALL dispose() override; + + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + + // css::container::XContainerListener + void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // XChangesListener + virtual void SAL_CALL changesOccurred( const css::util::ChangesEvent& Event ) override; + + // css::awt::XControl + sal_Bool SAL_CALL setModel( const css::uno::Reference< css::awt::XControlModel >& Model ) override; + void SAL_CALL setDesignMode( sal_Bool bOn ) override; + // XModifyListener + // Using a dummy/no-op implementation here, not sure if every container control needs + // to implement this, certainly Dialog does, lets see about others + virtual void SAL_CALL modified( const css::lang::EventObject& ) override {} +protected: + virtual void ImplModelPropertiesChanged( const css::uno::Sequence< css::beans::PropertyChangeEvent >& rEvents ) override; + virtual void removingControl( const css::uno::Reference< css::awt::XControl >& _rxControl ) override; + virtual void addingControl( const css::uno::Reference< css::awt::XControl >& _rxControl ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/dialogcontrol.hxx b/toolkit/inc/controls/dialogcontrol.hxx new file mode 100644 index 0000000000..3aaf3e50b1 --- /dev/null +++ b/toolkit/inc/controls/dialogcontrol.hxx @@ -0,0 +1,299 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/controlmodelcontainerbase.hxx> +#include <com/sun/star/awt/UnoControlDialog.hpp> +#include <com/sun/star/awt/XSimpleTabController.hpp> +#include <toolkit/helper/macros.hxx> +#include <cppuhelper/implbase2.hxx> + +namespace com::sun::star::awt { class XTopWindowListener; } + +typedef ::cppu::AggImplInheritanceHelper2 < ControlContainerBase + , css::awt::XUnoControlDialog + , css::awt::XWindowListener + > UnoDialogControl_Base; +class UnoDialogControl final : public UnoDialogControl_Base +{ +private: + css::uno::Reference< css::awt::XMenuBar > mxMenuBar; + TopWindowListenerMultiplexer maTopWindowListeners; + bool mbWindowListener; + +public: + + UnoDialogControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~UnoDialogControl() override; + OUString GetComponentServiceName() const override; + + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + void SAL_CALL dispose() override; + + // css::awt::XTopWindow + void SAL_CALL addTopWindowListener( const css::uno::Reference< css::awt::XTopWindowListener >& xListener ) override; + void SAL_CALL removeTopWindowListener( const css::uno::Reference< css::awt::XTopWindowListener >& xListener ) override; + void SAL_CALL toFront( ) override; + void SAL_CALL toBack( ) override; + void SAL_CALL setMenuBar( const css::uno::Reference< css::awt::XMenuBar >& xMenu ) override; + + // css::awt::XWindowListener + virtual void SAL_CALL windowResized( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowShown( const css::lang::EventObject& e ) override; + virtual void SAL_CALL windowHidden( const css::lang::EventObject& e ) override; + + // css::awt::XDialog2 + virtual void SAL_CALL endDialog( ::sal_Int32 Result ) override; + virtual void SAL_CALL setHelpId( const OUString& Id ) override; + + // css::awt::XDialog + void SAL_CALL setTitle( const OUString& Title ) override; + OUString SAL_CALL getTitle() override; + sal_Int16 SAL_CALL execute() override; + void SAL_CALL endExecute() override; + + // css::awt::XControl + sal_Bool SAL_CALL setModel( const css::uno::Reference< css::awt::XControlModel >& Model ) override; + + // XModifyListener + virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override; + + // resolve some ambiguous methods + virtual css::uno::Reference<css::awt::XWindowPeer> SAL_CALL getPeer() override + { return UnoDialogControl_Base::ControlContainerBase::getPeer(); } + virtual void SAL_CALL addWindowListener(const css::uno::Reference<css::awt::XWindowListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::addWindowListener(p1); } + virtual css::uno::Reference<css::awt::XControlModel> SAL_CALL getModel() override + { return UnoDialogControl_Base::ControlContainerBase::getModel(); } + virtual void SAL_CALL addEventListener(const css::uno::Reference<css::lang::XEventListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::addEventListener(p1); } + virtual void SAL_CALL removeEventListener(const css::uno::Reference<css::lang::XEventListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::removeEventListener(p1); } + virtual void SAL_CALL setContext(const css::uno::Reference<css::uno::XInterface>& p1) override + { UnoDialogControl_Base::ControlContainerBase::setContext(p1); } + virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getContext() override + { return UnoDialogControl_Base::ControlContainerBase::getContext(); } + virtual css::uno::Reference<css::awt::XView> SAL_CALL getView() override + { return UnoDialogControl_Base::ControlContainerBase::getView(); } + virtual void SAL_CALL setDesignMode(sal_Bool p1) override + { UnoDialogControl_Base::ControlContainerBase::setDesignMode(p1); } + virtual sal_Bool SAL_CALL isDesignMode() override + { return UnoDialogControl_Base::ControlContainerBase::isDesignMode(); } + virtual sal_Bool SAL_CALL isTransparent() override + { return UnoDialogControl_Base::ControlContainerBase::isTransparent(); } + virtual void SAL_CALL setPosSize(sal_Int32 p1, sal_Int32 p2, sal_Int32 p3, sal_Int32 p4, sal_Int16 p5) override + { UnoDialogControl_Base::ControlContainerBase::setPosSize(p1, p2, p3, p4, p5); } + virtual css::awt::Rectangle SAL_CALL getPosSize() override + { return UnoDialogControl_Base::ControlContainerBase::getPosSize(); } + virtual void SAL_CALL setVisible(sal_Bool p1) override + { UnoDialogControl_Base::ControlContainerBase::setVisible(p1); } + virtual void SAL_CALL setEnable(sal_Bool p1) override + { UnoDialogControl_Base::ControlContainerBase::setEnable(p1); } + virtual void SAL_CALL setFocus() override + { UnoDialogControl_Base::ControlContainerBase::setFocus(); } + virtual void SAL_CALL removeWindowListener(const css::uno::Reference<css::awt::XWindowListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::removeWindowListener(p1); } + virtual void SAL_CALL addFocusListener(const css::uno::Reference<css::awt::XFocusListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::addFocusListener(p1); } + virtual void SAL_CALL removeFocusListener(const css::uno::Reference<css::awt::XFocusListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::removeFocusListener(p1); } + virtual void SAL_CALL addKeyListener(const css::uno::Reference<css::awt::XKeyListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::addKeyListener(p1); } + virtual void SAL_CALL removeKeyListener(const css::uno::Reference<css::awt::XKeyListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::removeKeyListener(p1); } + virtual void SAL_CALL addMouseListener(const css::uno::Reference<css::awt::XMouseListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::addMouseListener(p1); } + virtual void SAL_CALL removeMouseListener(const css::uno::Reference<css::awt::XMouseListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::removeMouseListener(p1); } + virtual void SAL_CALL addMouseMotionListener(const css::uno::Reference<css::awt::XMouseMotionListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::addMouseMotionListener(p1); } + virtual void SAL_CALL removeMouseMotionListener(const css::uno::Reference<css::awt::XMouseMotionListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::removeMouseMotionListener(p1); } + virtual void SAL_CALL addPaintListener(const css::uno::Reference<css::awt::XPaintListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::addPaintListener(p1); } + virtual void SAL_CALL removePaintListener(const css::uno::Reference<css::awt::XPaintListener>& p1) override + { UnoDialogControl_Base::ControlContainerBase::removePaintListener(p1); } + virtual void SAL_CALL setStatusText(const OUString& p1) override + { UnoDialogControl_Base::ControlContainerBase::setStatusText(p1); } + virtual css::uno::Sequence<css::uno::Reference<css::awt::XControl> > SAL_CALL getControls() override + { return UnoDialogControl_Base::ControlContainerBase::getControls(); } + virtual css::uno::Reference<css::awt::XControl> SAL_CALL getControl(const OUString& p1) override + { return UnoDialogControl_Base::ControlContainerBase::getControl(p1); } + virtual void SAL_CALL addControl(const OUString& p1, const css::uno::Reference<css::awt::XControl>& p2) override + { UnoDialogControl_Base::ControlContainerBase::addControl(p1, p2); } + virtual void SAL_CALL removeControl(const css::uno::Reference<css::awt::XControl>& p1) override + { UnoDialogControl_Base::ControlContainerBase::removeControl(p1); } + + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + virtual void PrepareWindowDescriptor( css::awt::WindowDescriptor& rDesc ) override; + virtual void ImplModelPropertiesChanged( const css::uno::Sequence< css::beans::PropertyChangeEvent >& rEvents ) override; +}; + +class UnoMultiPageModel final : public ControlModelContainerBase +{ +public: + UnoMultiPageModel( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + UnoMultiPageModel(const UnoMultiPageModel& rOther) : ControlModelContainerBase(rOther) {} + virtual ~UnoMultiPageModel() override; + + rtl::Reference<UnoControlModel> Clone() const override; + + DECLIMPL_SERVICEINFO_DERIVED( UnoMultiPageModel, ControlModelContainerBase, "com.sun.star.awt.UnoMultiPageModel" ) + + virtual OUString SAL_CALL getServiceName() override; + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // XNamedContainer + void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override; + + // Override the method of parent class + virtual sal_Bool SAL_CALL getGroupControl( ) override; +private: + virtual css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + +}; + +class UnoMultiPageControl final : public ControlContainerBase + ,public css::awt::XSimpleTabController + ,public css::awt::XTabListener +{ + TabListenerMultiplexer maTabListeners; + void bindPage( const css::uno::Reference< css::awt::XControl >& _rxControl ); +public: + UnoMultiPageControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~UnoMultiPageControl() override; + OUString GetComponentServiceName() const override; + + // css::lang::XServiceInfo + DECLIMPL_SERVICEINFO_DERIVED( UnoMultiPageControl, ControlContainerBase, "com.sun.star.awt.UnoControlMultiPage" ) + css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override { return ControlContainerBase::queryInterface(rType); } + css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override; + void SAL_CALL acquire() noexcept override { OWeakAggObject::acquire(); } + void SAL_CALL release() noexcept override { OWeakAggObject::release(); } + // css::lang::XTypeProvider + css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + // css::awt::XSimpleTabController + virtual ::sal_Int32 SAL_CALL insertTab() override; + virtual void SAL_CALL removeTab( ::sal_Int32 ID ) override; + + virtual void SAL_CALL setTabProps( ::sal_Int32 ID, const css::uno::Sequence< css::beans::NamedValue >& Properties ) override; + virtual css::uno::Sequence< css::beans::NamedValue > SAL_CALL getTabProps( ::sal_Int32 ID ) override; + + virtual void SAL_CALL activateTab( ::sal_Int32 ID ) override; + virtual ::sal_Int32 SAL_CALL getActiveTabID() override; + + virtual void SAL_CALL addTabListener( const css::uno::Reference< css::awt::XTabListener >& Listener ) override; + virtual void SAL_CALL removeTabListener( const css::uno::Reference< css::awt::XTabListener >& Listener ) override; + // XTabListener + virtual void SAL_CALL inserted( ::sal_Int32 ID ) override; + virtual void SAL_CALL removed( ::sal_Int32 ID ) override; + virtual void SAL_CALL changed( ::sal_Int32 ID, const css::uno::Sequence< css::beans::NamedValue >& Properties ) override; + virtual void SAL_CALL activated( ::sal_Int32 ID ) override; + virtual void SAL_CALL deactivated( ::sal_Int32 ID ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& evt ) override; + // XComponent + void SAL_CALL dispose( ) override; + +private: + virtual void impl_createControlPeerIfNecessary( + const css::uno::Reference< css::awt::XControl >& _rxControl + ) override; + +}; + + +class UnoPageModel final : public ControlModelContainerBase +{ +public: + UnoPageModel( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + UnoPageModel(const UnoPageModel& rOther) : ControlModelContainerBase(rOther) {} + virtual ~UnoPageModel() override; + + rtl::Reference<UnoControlModel> Clone() const override; + + DECLIMPL_SERVICEINFO_DERIVED( UnoPageModel, ControlModelContainerBase, "com.sun.star.awt.UnoPageModel" ) + + virtual OUString SAL_CALL getServiceName() override; + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // Override the method of parent class + virtual sal_Bool SAL_CALL getGroupControl( ) override; +private: + virtual css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + +}; + +class UnoPageControl final : public ControlContainerBase +{ +public: + UnoPageControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~UnoPageControl() override; + OUString GetComponentServiceName() const override; + + + // css::lang::XServiceInfo + DECLIMPL_SERVICEINFO_DERIVED( UnoPageControl, ControlContainerBase, "com.sun.star.awt.UnoControlPage" ) +}; + +class UnoFrameModel final : public ControlModelContainerBase +{ +public: + UnoFrameModel( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + UnoFrameModel(const UnoFrameModel& rOther) : ControlModelContainerBase(rOther) {} + virtual ~UnoFrameModel() override; + + rtl::Reference<UnoControlModel> Clone() const override; + + DECLIMPL_SERVICEINFO_DERIVED( UnoFrameModel, ControlModelContainerBase, "com.sun.star.awt.UnoFrameModel" ) + + virtual OUString SAL_CALL getServiceName() override; + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + +private: + virtual css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; +}; + +class UnoFrameControl final : public ControlContainerBase +{ + virtual void ImplSetPosSize( css::uno::Reference< css::awt::XControl >& rxCtrl ) override; +public: + UnoFrameControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~UnoFrameControl() override; + OUString GetComponentServiceName() const override; + +// css::lang::XServiceInfo +DECLIMPL_SERVICEINFO_DERIVED( UnoFrameControl, ControlContainerBase, "com.sun.star.awt.UnoControlFrame" ) +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/eventcontainer.hxx b/toolkit/inc/controls/eventcontainer.hxx new file mode 100644 index 0000000000..79a434a398 --- /dev/null +++ b/toolkit/inc/controls/eventcontainer.hxx @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XContainer.hpp> + +#include <toolkit/helper/listenermultiplexer.hxx> + +#include <cppuhelper/implbase.hxx> +#include <unordered_map> + +namespace toolkit +{ + +// Hashtable to optimize +typedef std::unordered_map +< + OUString, + sal_Int32, + OUStringHash +> +NameContainerNameMap; + +class ScriptEventContainer final : public ::cppu::WeakImplHelper< + css::container::XNameContainer, + css::container::XContainer > +{ + // The map needs to keep the insertion order, otherwise Macro signatures would get broken + // if the order changes here (Dialog xml files are digitally signed too). + // Thus a std::map or std::unordered_map can't be used. + NameContainerNameMap mHashMap; + css::uno::Sequence< OUString > mNames; + std::vector< css::uno::Any > mValues; + css::uno::Type mType; + + ContainerListenerMultiplexer maContainerListeners; + +public: + ScriptEventContainer(); + + // Methods XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + // Methods XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // Methods XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override; + + // Methods XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; + + // Methods XContainer + void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; +}; + +} // namespace toolkit_namecontainer + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/filectrl.hxx b/toolkit/inc/controls/filectrl.hxx new file mode 100644 index 0000000000..1330d0ce7c --- /dev/null +++ b/toolkit/inc/controls/filectrl.hxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <vcl/window.hxx> +#include <vcl/toolkit/button.hxx> + +class Edit; + +// Flags for internal use of FileControl +enum class FileControlMode_Internal +{ + INRESIZE = 0x0001, + ORIGINALBUTTONTEXT = 0x0002, +}; + +namespace o3tl +{ + template<> struct typed_flags<FileControlMode_Internal> : is_typed_flags<FileControlMode_Internal, 0x03> {}; +} + + +class FileControl final : public vcl::Window +{ + VclPtr<Edit> maEdit; + VclPtr<PushButton> maButton; + OUString maButtonText; + FileControlMode_Internal mnInternalFlags; + + void Resize() override; + void GetFocus() override; + void StateChanged( StateChangedType nType ) override; + WinBits ImplInitStyle( WinBits nStyle ); + DECL_LINK( ButtonHdl, Button*, void ); + +public: + FileControl( vcl::Window* pParent, WinBits nStyle ); + virtual ~FileControl() override; + virtual void dispose() override; + + Edit& GetEdit() { return *maEdit; } + PushButton& GetButton() { return *maButton; } + + void Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags nFlags ) override; + + void SetText( const OUString& rStr ) override; + OUString GetText() const override; + + void SetEditModifyHdl( const Link<Edit&,void>& rLink ); +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/formattedcontrol.hxx b/toolkit/inc/controls/formattedcontrol.hxx new file mode 100644 index 0000000000..c614b42c74 --- /dev/null +++ b/toolkit/inc/controls/formattedcontrol.hxx @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <toolkit/controls/unocontrols.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> + +namespace com::sun::star::util { class XNumberFormatter; } + + +namespace toolkit +{ + + + // = UnoControlFormattedFieldModel + + class UnoControlFormattedFieldModel final : public UnoControlModel + { + public: + UnoControlFormattedFieldModel( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + UnoControlFormattedFieldModel( const UnoControlFormattedFieldModel& rModel ) + : UnoControlModel(rModel) + , m_bRevokedAsClient(false) + , m_bSettingValueAndText(false) + { + } + + rtl::Reference<UnoControlModel> Clone() const override { return new UnoControlFormattedFieldModel( *this ); } + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // css::beans::XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + private: + virtual ~UnoControlFormattedFieldModel() override; + + // XComponent + void SAL_CALL dispose() override; + + // XPropertySet + void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& PropertyNames, const css::uno::Sequence< css::uno::Any >& Values ) override; + + // UnoControlModel + virtual void ImplNormalizePropertySequence( + const sal_Int32 _nCount, /// the number of entries in the arrays + sal_Int32* _pHandles, /// the handles of the properties to set + css::uno::Any* _pValues, /// the values of the properties to set + sal_Int32* _pValidHandles /// pointer to the valid handles, allowed to be adjusted + ) const override; + void impl_updateTextFromValue_nothrow(std::unique_lock<std::mutex>& rGuard); + void impl_updateCachedFormatter_nothrow(std::unique_lock<std::mutex>& rGuard); + void impl_updateCachedFormatKey_nothrow(std::unique_lock<std::mutex>& rGuard); + + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + bool convertFastPropertyValue( + std::unique_lock<std::mutex>& rGuard, + css::uno::Any& rConvertedValue, + css::uno::Any& rOldValue, + sal_Int32 nPropId, + const css::uno::Any& rValue + ) override; + + void setFastPropertyValue_NoBroadcast( + std::unique_lock<std::mutex>& rGuard, + sal_Int32 nHandle, + const css::uno::Any& rValue + ) override; + + css::uno::Any m_aCachedFormat; + bool m_bRevokedAsClient; + bool m_bSettingValueAndText; + css::uno::Reference< css::util::XNumberFormatter > + m_xCachedFormatter; + }; + + + // = UnoFormattedFieldControl + + class UnoFormattedFieldControl final : public UnoSpinFieldControl + { + public: + UnoFormattedFieldControl(); + OUString GetComponentServiceName() const override; + + // css::awt::XTextListener + void SAL_CALL textChanged( const css::awt::TextEvent& rEvent ) override; + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + }; + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/geometrycontrolmodel.hxx b/toolkit/inc/controls/geometrycontrolmodel.hxx new file mode 100644 index 0000000000..c2dc13b49f --- /dev/null +++ b/toolkit/inc/controls/geometrycontrolmodel.hxx @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <comphelper/broadcasthelper.hxx> +#include <comphelper/propagg.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertycontainer.hxx> +#include <cppuhelper/compbase2.hxx> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/script/XScriptEventsSupplier.hpp> +#include <comphelper/IdPropArrayHelper.hxx> +#include <rtl/ref.hxx> + +namespace com::sun::star::resource { class XStringResourceResolver; } + +namespace com::sun::star { + namespace uno { + class XComponentContext; + } +} + + +// namespace toolkit +// { + + + //= OGeometryControlModel_Base + + typedef ::cppu::WeakAggComponentImplHelper2 < css::util::XCloneable + , css::script::XScriptEventsSupplier + > OGCM_Base; + class OGeometryControlModel_Base + :public ::comphelper::OMutexAndBroadcastHelper + ,public ::comphelper::OPropertySetAggregationHelper + ,public ::comphelper::OPropertyContainer + ,public OGCM_Base + { + protected: + css::uno::Reference< css::uno::XAggregation > + m_xAggregate; + css::uno::Reference< css::container::XNameContainer > + mxEventContainer; + + // <properties> + sal_Int32 m_nPosX; + sal_Int32 m_nPosY; + sal_Int32 m_nWidth; + sal_Int32 m_nHeight; + OUString m_aName; + sal_Int16 m_nTabIndex; + sal_Int32 m_nStep; + OUString m_aTag; + css::uno::Reference< css::resource::XStringResourceResolver > m_xStrResolver; + // </properties> + + bool m_bCloneable; + + protected: + static css::uno::Any ImplGetDefaultValueByHandle(sal_Int32 nHandle); + css::uno::Any ImplGetPropertyValueByHandle(sal_Int32 nHandle) const; + void ImplSetPropertyValueByHandle(sal_Int32 nHandle, const css::uno::Any& aValue); + + protected: + /** + @param _pAggregateInstance + the object to be aggregated. The refcount of the instance given MUST be 0! + */ + OGeometryControlModel_Base(css::uno::XAggregation* _pAggregateInstance); + + /** + @param _rxAggregateInstance + is the object to be aggregated. Must be acquired exactly once (by the reference object given).<br/> + Will be reset to NULL upon leaving + */ + OGeometryControlModel_Base(css::uno::Reference< css::util::XCloneable >& _rxAggregateInstance); + + /** releases the aggregation + <p>Can be used if in a derived class, an exception has to be thrown after this base class here already + did the aggregation</p> + */ + void releaseAggregation(); + + protected: + virtual ~OGeometryControlModel_Base() override; + + // XAggregation + css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _aType ) override; + + public: + // 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; + + protected: + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // OPropertySetHelper overridables + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, + sal_Int32 _nHandle, const css::uno::Any& _rValue ) override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 _nHandle, const css::uno::Any& _rValue) override; + + using comphelper::OPropertySetAggregationHelper::getFastPropertyValue; + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& _rValue, sal_Int32 _nHandle) const override; + + // OPropertyStateHelper overridables + virtual css::beans::PropertyState getPropertyStateByHandle(sal_Int32 nHandle) override; + virtual void setPropertyToDefaultByHandle(sal_Int32 nHandle) override; + virtual css::uno::Any getPropertyDefaultByHandle(sal_Int32 nHandle) const override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + + // OPropertySetAggregationHelper overridables + using OPropertySetAggregationHelper::getInfoHelper; + + // XCloneable + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + + //XScriptEventsSupplier + virtual css::uno::Reference< css::container::XNameContainer > + SAL_CALL getEvents( ) override; + + // XCloneable implementation - to be overwritten + virtual rtl::Reference<OGeometryControlModel_Base> createClone_Impl( + css::uno::Reference< css::util::XCloneable >& _rxAggregateInstance) = 0; + + // XComponent + using comphelper::OPropertySetAggregationHelper::disposing; + virtual void SAL_CALL disposing() override; + + private: + void registerProperties(); + }; + + + //= OTemplateInstanceDisambiguation + + template <class CONTROLMODEL> + class OTemplateInstanceDisambiguation + { + }; + + + //= OGeometryControlModel + + /* example for usage: + Reference< XAggregation > xIFace = new ::toolkit::OGeometryControlModel< UnoControlButtonModel > (); + */ + template <class CONTROLMODEL> + class OGeometryControlModel final + :public OGeometryControlModel_Base + ,public ::comphelper::OAggregationArrayUsageHelper< OTemplateInstanceDisambiguation< CONTROLMODEL > > + { + public: + OGeometryControlModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ); + + private: + OGeometryControlModel(css::uno::Reference< css::util::XCloneable >& _rxAggregateInstance); + + // OAggregationArrayUsageHelper overridables + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& _rProps, + css::uno::Sequence< css::beans::Property >& _rAggregateProps + ) const override; + + // OPropertySetAggregationHelper overridables + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // OGeometryControlModel_Base + virtual rtl::Reference<OGeometryControlModel_Base> createClone_Impl( + css::uno::Reference< css::util::XCloneable >& _rxAggregateInstance) override; + + // XTypeProvider + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + }; + + + //= OCommonGeometryControlModel + + /** allows to extend an arbitrary com.sun.star.awt::UnoControlModel with geometry + information. + */ + class OCommonGeometryControlModel final + :public OGeometryControlModel_Base + ,public ::comphelper::OIdPropertyArrayUsageHelper< OCommonGeometryControlModel > + { + private: + OUString m_sServiceSpecifier; // the service specifier of our aggregate + sal_Int32 m_nPropertyMapId; // our unique property info id, used to look up in s_aAggregateProperties + + public: + /** instantiate the model + + @param _rxAgg + the instance to aggregate. Must support the com.sun.star.awt::UnoControlModel + (this is not checked here) + */ + OCommonGeometryControlModel( + css::uno::Reference< css::util::XCloneable >& _rxAgg, + OUString _aServiceSpecifier + ); + + // OIdPropertyArrayUsageHelper overridables + virtual ::cppu::IPropertyArrayHelper* createArrayHelper(sal_Int32 nId) const override; + + // OPropertySetAggregationHelper overridables + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // OGeometryControlModel_Base + virtual rtl::Reference<OGeometryControlModel_Base> createClone_Impl( + css::uno::Reference< css::util::XCloneable >& _rxAggregateInstance) override; + + // XTypeProvider + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + private: + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 _nHandle, const css::uno::Any& _rValue) override; + }; + +#include <controls/geometrycontrolmodel_impl.hxx> + + +// } // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/geometrycontrolmodel_impl.hxx b/toolkit/inc/controls/geometrycontrolmodel_impl.hxx new file mode 100644 index 0000000000..2ec8595ddd --- /dev/null +++ b/toolkit/inc/controls/geometrycontrolmodel_impl.hxx @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +// no include protection. This is included from within geometrycontrolmodel.hxx only + +//= OGeometryControlModel + +template <class CONTROLMODEL> +OGeometryControlModel<CONTROLMODEL>::OGeometryControlModel( + const css::uno::Reference<css::uno::XComponentContext>& i_factory) + : OGeometryControlModel_Base(new CONTROLMODEL(i_factory)) +{ +} + +template <class CONTROLMODEL> +OGeometryControlModel<CONTROLMODEL>::OGeometryControlModel( + css::uno::Reference<css::util::XCloneable>& _rxAggregateInstance) + : OGeometryControlModel_Base(_rxAggregateInstance) +{ +} + +template <class CONTROLMODEL> +::cppu::IPropertyArrayHelper& SAL_CALL OGeometryControlModel<CONTROLMODEL>::getInfoHelper() +{ + return *this->getArrayHelper(); +} + +template <class CONTROLMODEL> +void OGeometryControlModel<CONTROLMODEL>::fillProperties( + css::uno::Sequence<css::beans::Property>& _rProps, + css::uno::Sequence<css::beans::Property>& _rAggregateProps) const +{ + // our own properties + OPropertyContainer::describeProperties(_rProps); + // the aggregate properties + if (m_xAggregateSet.is()) + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); +} + +template <class CONTROLMODEL> +css::uno::Sequence<sal_Int8> SAL_CALL OGeometryControlModel<CONTROLMODEL>::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +template <class CONTROLMODEL> +rtl::Reference<OGeometryControlModel_Base> OGeometryControlModel<CONTROLMODEL>::createClone_Impl( + css::uno::Reference<css::util::XCloneable>& _rxAggregateInstance) +{ + return new OGeometryControlModel<CONTROLMODEL>(_rxAggregateInstance); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/roadmapcontrol.hxx b/toolkit/inc/controls/roadmapcontrol.hxx new file mode 100644 index 0000000000..cb6e2cec45 --- /dev/null +++ b/toolkit/inc/controls/roadmapcontrol.hxx @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + + +#include <toolkit/controls/unocontrols.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/awt/XItemListener.hpp> +#include <com/sun/star/awt/XItemEventBroadcaster.hpp> +#include <cppuhelper/implbase2.hxx> +#include <cppuhelper/implbase3.hxx> +#include <cppuhelper/implbase4.hxx> + + +#include <comphelper/uno3.hxx> + + +namespace toolkit +{ + + + typedef GraphicControlModel UnoControlRoadmapModel_Base; + + + typedef ::cppu::ImplHelper3 < css::lang::XSingleServiceFactory + , css::container::XContainer + , css::container::XIndexContainer + > UnoControlRoadmapModel_IBase; + + + typedef UnoControlBase UnoControlRoadmap_Base; + typedef ::cppu::ImplHelper4 < css::awt::XItemEventBroadcaster + , css::container::XContainerListener + , css::awt::XItemListener + , css::beans::XPropertyChangeListener + > UnoControlRoadmap_IBase; + + + typedef ::cppu::ImplHelper2< css::container::XContainerListener, + css::awt::XItemEventBroadcaster> SVTXRoadmap_Base; + + + // = UnoControlRoadmapModel + + class UnoControlRoadmapModel final : public UnoControlRoadmapModel_Base, + public UnoControlRoadmapModel_IBase + + { + private: +// PropertyChangeListenerMultiplexer maPropertyListeners; + + typedef ::std::vector< css::uno::Reference< XInterface > > RoadmapItemHolderList; + + ContainerListenerMultiplexer maContainerListeners; + RoadmapItemHolderList maRoadmapItems; + + void MakeRMItemValidation( sal_Int32 Index, const css::uno::Reference< XInterface >& xRoadmapItem ); + css::container::ContainerEvent GetContainerEvent(sal_Int32 Index, const css::uno::Reference< XInterface >& ); + void SetRMItemDefaultProperties( const css::uno::Reference< XInterface >& ); + static sal_Int16 GetCurrentItemID( const css::uno::Reference< css::beans::XPropertySet >& xPropertySet ); + sal_Int32 GetUniqueID(); + + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + + public: + UnoControlRoadmapModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ); + UnoControlRoadmapModel( const UnoControlRoadmapModel& rModel ) : + UnoControlRoadmapModel_Base( rModel ), + UnoControlRoadmapModel_IBase( rModel ), + maContainerListeners( *this ) {} + rtl::Reference<UnoControlModel> Clone() const override { return new UnoControlRoadmapModel( *this ); } + + + // XTypeProvider + DECLARE_XTYPEPROVIDER( ) + + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + sal_Int32 SAL_CALL getCount() override; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const css::uno::Any & Element) override; + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override; + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const css::uno::Any & Element) override; + + virtual void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + virtual void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + + css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override { return UnoControlRoadmapModel_Base::queryInterface(rType); } + css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override; + void SAL_CALL acquire() noexcept override { UnoControlRoadmapModel_Base::acquire(); } + void SAL_CALL release() noexcept override { UnoControlRoadmapModel_Base::release(); } + + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + + virtual css::uno::Reference< XInterface > SAL_CALL createInstance( ) override; + virtual css::uno::Reference< XInterface > SAL_CALL createInstanceWithArguments( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + }; + + + // = UnoRoadmapControl + + class UnoRoadmapControl final : public UnoControlRoadmap_Base, + public UnoControlRoadmap_IBase + { + private: + ItemListenerMultiplexer maItemListeners; + public: + UnoRoadmapControl(); + OUString GetComponentServiceName() const override; + + void SAL_CALL disposing( const css::lang::EventObject& Source ) override { UnoControlBase::disposing( Source ); } + + void SAL_CALL dispose( ) override; + + + sal_Bool SAL_CALL setModel(const css::uno::Reference< css::awt::XControlModel >& Model) override; + + void SAL_CALL elementInserted( const css::container::ContainerEvent& rEvent ) override; + void SAL_CALL elementRemoved( const css::container::ContainerEvent& rEvent ) override; + void SAL_CALL elementReplaced( const css::container::ContainerEvent& rEvent ) override; + + virtual void SAL_CALL addItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) override; + virtual void SAL_CALL removeItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) override; + + + virtual void SAL_CALL itemStateChanged( const css::awt::ItemEvent& rEvent ) override; + + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override; + + // XTypeProvider + DECLARE_XTYPEPROVIDER( ) + DECLARE_UNO3_AGG_DEFAULTS(UnoRoadmapControl, UnoControlRoadmap_Base) + + css::uno::Any SAL_CALL queryAggregation(css::uno::Type const & aType) override; + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + }; + + +} // toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/roadmapentry.hxx b/toolkit/inc/controls/roadmapentry.hxx new file mode 100644 index 0000000000..b8490439c9 --- /dev/null +++ b/toolkit/inc/controls/roadmapentry.hxx @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <comphelper/uno3.hxx> +#include <comphelper/broadcasthelper.hxx> +#include <comphelper/propertycontainer.hxx> +#include <comphelper/proparrhlp.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> + + +#define RM_PROPERTY_ID_LABEL 1 +#define RM_PROPERTY_ID_ID 2 +#define RM_PROPERTY_ID_ENABLED 4 +#define RM_PROPERTY_ID_INTERACTIVE 5 + +typedef cppu::WeakImplHelper< css::lang::XServiceInfo > ORoadmapEntry_Base; + +class ORoadmapEntry final : public ORoadmapEntry_Base + ,public ::comphelper::OMutexAndBroadcastHelper + ,public ::comphelper::OPropertyContainer + ,public ::comphelper::OPropertyArrayUsageHelper< ORoadmapEntry > +{ + +public: + ORoadmapEntry(); + + DECLARE_XINTERFACE() // merge XInterface implementations + DECLARE_XTYPEPROVIDER() // merge XTypeProvider implementations + +private: + /// @see css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > + SAL_CALL getPropertySetInfo() override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + + 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; + + OUString m_sLabel; + sal_Int32 m_nID; + bool m_bEnabled; + bool m_bInteractive; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/stdtabcontroller.hxx b/toolkit/inc/controls/stdtabcontroller.hxx new file mode 100644 index 0000000000..37de42d7da --- /dev/null +++ b/toolkit/inc/controls/stdtabcontroller.hxx @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/XTabController.hpp> +#include <com/sun/star/lang/XTypeProvider.hpp> +#include <cppuhelper/weakagg.hxx> +#include <osl/mutex.hxx> + + +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::awt { class XControl; } +namespace com::sun::star::awt { class XControlContainer; } + +class StdTabController final : public css::awt::XTabController, + public css::lang::XServiceInfo, + public css::lang::XTypeProvider, + public ::cppu::OWeakAggObject +{ +private: + ::osl::Mutex maMutex; + css::uno::Reference< css::awt::XTabControllerModel > mxModel; + css::uno::Reference< css::awt::XControlContainer > mxControlContainer; + + ::osl::Mutex& GetMutex() { return maMutex; } + static bool ImplCreateComponentSequence( css::uno::Sequence< css::uno::Reference< css::awt::XControl > >& rControls, const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& rModels, css::uno::Sequence< css::uno::Reference< css::awt::XWindow > >& rComponents, css::uno::Sequence< css::uno::Any>* pTabStops, bool bPeerComponent ); + // if sequence length of rModels is less than rControls, return only the matching elements in rModels sequence and remove corresponding elements from rControls + void ImplActivateControl( bool bFirst ) const; + +public: + StdTabController(); + virtual ~StdTabController() override; + + static css::uno::Reference< css::awt::XControl > FindControl( css::uno::Sequence< css::uno::Reference< css::awt::XControl > >& rCtrls, const css::uno::Reference< css::awt::XControlModel > & rxCtrlModel ); + + // css::uno::XInterface + css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override { return OWeakAggObject::queryInterface(rType); } + void SAL_CALL acquire() noexcept override { OWeakAggObject::acquire(); } + void SAL_CALL release() noexcept override { OWeakAggObject::release(); } + + css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override; + + // css::lang::XTypeProvider + css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XTabController + void SAL_CALL setModel( const css::uno::Reference< css::awt::XTabControllerModel >& Model ) override; + css::uno::Reference< css::awt::XTabControllerModel > SAL_CALL getModel( ) override; + void SAL_CALL setContainer( const css::uno::Reference< css::awt::XControlContainer >& Container ) override; + css::uno::Reference< css::awt::XControlContainer > SAL_CALL getContainer( ) override; + css::uno::Sequence< css::uno::Reference< css::awt::XControl > > SAL_CALL getControls( ) override; + void SAL_CALL autoTabOrder( ) override; + void SAL_CALL activateTabOrder( ) override; + void SAL_CALL activateFirst( ) override; + void SAL_CALL activateLast( ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/stdtabcontrollermodel.hxx b/toolkit/inc/controls/stdtabcontrollermodel.hxx new file mode 100644 index 0000000000..4fa12d252e --- /dev/null +++ b/toolkit/inc/controls/stdtabcontrollermodel.hxx @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/io/XPersistObject.hpp> +#include <com/sun/star/awt/XTabControllerModel.hpp> +#include <com/sun/star/lang/XTypeProvider.hpp> +#include <cppuhelper/weakagg.hxx> +#include <osl/mutex.hxx> + +#include <vector> + +struct UnoControlModelEntry; +typedef ::std::vector< UnoControlModelEntry* > UnoControlModelEntryListBase; + +class UnoControlModelEntryList +{ +private: + UnoControlModelEntryListBase maList; + OUString maGroupName; + +public: + UnoControlModelEntryList(); + ~UnoControlModelEntryList(); + + const OUString& GetName() const { return maGroupName; } + void SetName( const OUString& rName ) { maGroupName = rName; } + + void Reset(); + void DestroyEntry( size_t nEntry ); + size_t size() const; + UnoControlModelEntry* operator[]( size_t i ) const; + void push_back( UnoControlModelEntry* item ); + void insert( size_t i, UnoControlModelEntry* item ); +}; + +struct UnoControlModelEntry +{ + bool bGroup; + union + { + css::uno::Reference< css::awt::XControlModel >* pxControl; + UnoControlModelEntryList* pGroup; + }; +}; + +#define CONTROLPOS_NOTFOUND 0xFFFFFFFF + +class StdTabControllerModel final : public css::awt::XTabControllerModel, + public css::lang::XServiceInfo, + public css::io::XPersistObject, + public css::lang::XTypeProvider, + public ::cppu::OWeakAggObject +{ + ::osl::Mutex maMutex; + UnoControlModelEntryList maControls; + bool mbGroupControl; + + ::osl::Mutex& GetMutex() { return maMutex; } + sal_uInt32 ImplGetControlCount( const UnoControlModelEntryList& rList ) const; + void ImplGetControlModels( css::uno::Reference< css::awt::XControlModel > ** pRefs, const UnoControlModelEntryList& rList ) const; + static void ImplSetControlModels( UnoControlModelEntryList& rList, const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Controls ); + static sal_uInt32 ImplGetControlPos( const css::uno::Reference< css::awt::XControlModel >& rCtrl, const UnoControlModelEntryList& rList ); + +public: + StdTabControllerModel(); + virtual ~StdTabControllerModel() override; + + // css::uno::XInterface + css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override { return OWeakAggObject::queryInterface(rType); } + void SAL_CALL acquire() noexcept override { OWeakAggObject::acquire(); } + void SAL_CALL release() noexcept override { OWeakAggObject::release(); } + + css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override; + + // css::lang::XTypeProvider + css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // css::awt::XTabControllerModel + sal_Bool SAL_CALL getGroupControl( ) override; + void SAL_CALL setGroupControl( sal_Bool GroupControl ) override; + void SAL_CALL setControlModels( const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Controls ) override; + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > SAL_CALL getControlModels( ) override; + void SAL_CALL setGroup( const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Group, const OUString& GroupName ) override; + sal_Int32 SAL_CALL getGroupCount( ) override; + void SAL_CALL getGroup( sal_Int32 nGroup, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Group, OUString& Name ) override; + void SAL_CALL getGroupByName( const OUString& Name, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Group ) override; + + // css::io::XPersistObject + OUString SAL_CALL getServiceName( ) override; + void SAL_CALL write( const css::uno::Reference< css::io::XObjectOutputStream >& OutStream ) override; + void SAL_CALL read( const css::uno::Reference< css::io::XObjectInputStream >& InStream ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/svmedit.hxx b/toolkit/inc/controls/svmedit.hxx new file mode 100644 index 0000000000..61befc9815 --- /dev/null +++ b/toolkit/inc/controls/svmedit.hxx @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <vcl/toolkit/vclmedit.hxx> + +class MultiLineEdit final : public VclMultiLineEdit +{ +public: + MultiLineEdit( vcl::Window* pParent, WinBits nWinStyle ); + + virtual css::uno::Reference< css::awt::XVclWindowPeer > GetComponentInterface(bool bCreate = true) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/table/defaultinputhandler.hxx b/toolkit/inc/controls/table/defaultinputhandler.hxx new file mode 100644 index 0000000000..3f3d425a69 --- /dev/null +++ b/toolkit/inc/controls/table/defaultinputhandler.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/table/mousefunction.hxx> +#include <controls/table/tableinputhandler.hxx> +#include <rtl/ref.hxx> + +#include <vector> + +namespace svt::table +{ + + class DefaultInputHandler final : public ITableInputHandler + { + public: + DefaultInputHandler(); + virtual ~DefaultInputHandler() override; + + virtual bool MouseMove ( ITableControl& _rControl, const MouseEvent& rMEvt ) override; + virtual bool MouseButtonDown ( ITableControl& _rControl, const MouseEvent& rMEvt ) override; + virtual bool MouseButtonUp ( ITableControl& _rControl, const MouseEvent& rMEvt ) override; + virtual bool KeyInput ( ITableControl& _rControl, const KeyEvent& rKEvt ) override; + virtual bool GetFocus ( ITableControl& _rControl ) override; + virtual bool LoseFocus ( ITableControl& _rControl ) override; + + private: + bool delegateMouseEvent( ITableControl& i_control, const MouseEvent& i_event, + FunctionResult ( MouseFunction::*i_handlerMethod )( ITableControl&, const MouseEvent& ) ); + + rtl::Reference< MouseFunction > pActiveFunction; + std::vector< rtl::Reference< MouseFunction > > aMouseFunctions; + }; + + +} // namespace svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/table/gridtablerenderer.hxx b/toolkit/inc/controls/table/gridtablerenderer.hxx new file mode 100644 index 0000000000..e2634b2d26 --- /dev/null +++ b/toolkit/inc/controls/table/gridtablerenderer.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 . + */ + +#pragma once + +#include <controls/table/tablemodel.hxx> +#include <vcl/image.hxx> + +#include <memory> + + +namespace svt::table +{ + + + struct GridTableRenderer_Impl; + + + //= GridTableRenderer + + /** a default implementation for the ->ITableRenderer interface + + This class is able to paint a table grid, table headers, and cell + backgrounds according to the selected/active state of cells. + */ + class GridTableRenderer final : public ITableRenderer + { + private: + ::std::unique_ptr< GridTableRenderer_Impl > m_pImpl; + + public: + /** creates a table renderer associated with the given model + + @param _rModel + the model which should be rendered. The caller is responsible + for lifetime control, that is, the model instance must live + at least as long as the renderer instance lives + */ + GridTableRenderer( ITableModel& _rModel ); + virtual ~GridTableRenderer() override; + + /** determines whether or not to paint grid lines + */ + bool useGridLines() const; + + /** controls whether or not to paint grid lines + */ + void useGridLines( bool const i_use ); + + public: + // ITableRenderer overridables + virtual void PaintHeaderArea( + vcl::RenderContext& _rDevice, const tools::Rectangle& _rArea, + bool _bIsColHeaderArea, bool _bIsRowHeaderArea, + const StyleSettings& _rStyle ) override; + virtual void PaintColumnHeader( ColPos _nCol, + vcl::RenderContext& _rDevice, const tools::Rectangle& _rArea, + const StyleSettings& _rStyle ) override; + virtual void PrepareRow( RowPos _nRow, bool i_hasControlFocus, bool _bSelected, + vcl::RenderContext& _rDevice, const tools::Rectangle& _rRowArea, + const StyleSettings& _rStyle ) override; + virtual void PaintRowHeader( + vcl::RenderContext& _rDevice, const tools::Rectangle& _rArea, + const StyleSettings& _rStyle ) override; + virtual void PaintCell( ColPos const i_col, + bool i_hasControlFocus, bool _bSelected, + vcl::RenderContext& _rDevice, const tools::Rectangle& _rArea, + const StyleSettings& _rStyle ) override; + virtual void ShowCellCursor( vcl::Window& _rView, const tools::Rectangle& _rCursorRect) override; + virtual void HideCellCursor( vcl::Window& _rView ) override; + virtual bool FitsIntoCell( + css::uno::Any const & i_cellContent, + OutputDevice& i_targetDevice, tools::Rectangle const & i_targetArea + ) const override; + virtual bool GetFormattedCellString( + css::uno::Any const & i_cellValue, + OUString & o_cellString + ) const override; + + private: + struct CellRenderContext; + + void impl_paintCellContent( + CellRenderContext const & i_context + ); + void impl_paintCellImage( + CellRenderContext const & i_context, + Image const & i_image + ); + void impl_paintCellText( + CellRenderContext const & i_context, + OUString const & i_text + ); + }; + +} // namespace svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/table/mousefunction.hxx b/toolkit/inc/controls/table/mousefunction.hxx new file mode 100644 index 0000000000..ef68dc89ce --- /dev/null +++ b/toolkit/inc/controls/table/mousefunction.hxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "tabletypes.hxx" + +#include <salhelper/simplereferenceobject.hxx> + +class MouseEvent; + +namespace svt::table +{ +class ITableControl; + +//= FunctionResult + +enum FunctionResult +{ + ActivateFunction, + ContinueFunction, + DeactivateFunction, + + SkipFunction +}; + +//= MouseFunction + +class MouseFunction : public ::salhelper::SimpleReferenceObject +{ +public: + MouseFunction() {} + MouseFunction(const MouseFunction&) = delete; + MouseFunction& operator=(const MouseFunction&) = delete; + virtual FunctionResult handleMouseMove(ITableControl& i_tableControl, MouseEvent const& i_event) + = 0; + virtual FunctionResult handleMouseDown(ITableControl& i_tableControl, MouseEvent const& i_event) + = 0; + virtual FunctionResult handleMouseUp(ITableControl& i_tableControl, MouseEvent const& i_event) + = 0; + +protected: + virtual ~MouseFunction() override {} +}; + +//= ColumnResize + +class ColumnResize final : public MouseFunction +{ +public: + ColumnResize() + : m_nResizingColumn(COL_INVALID) + { + } + +public: + // MouseFunction + virtual FunctionResult handleMouseMove(ITableControl& i_tableControl, + MouseEvent const& i_event) override; + virtual FunctionResult handleMouseDown(ITableControl& i_tableControl, + MouseEvent const& i_event) override; + virtual FunctionResult handleMouseUp(ITableControl& i_tableControl, + MouseEvent const& i_event) override; + +private: + ColPos m_nResizingColumn; +}; + +//= RowSelection + +class RowSelection final : public MouseFunction +{ +public: + RowSelection() + : m_bActive(false) + { + } + +public: + // MouseFunction + virtual FunctionResult handleMouseMove(ITableControl& i_tableControl, + MouseEvent const& i_event) override; + virtual FunctionResult handleMouseDown(ITableControl& i_tableControl, + MouseEvent const& i_event) override; + virtual FunctionResult handleMouseUp(ITableControl& i_tableControl, + MouseEvent const& i_event) override; + +private: + bool m_bActive; +}; + +//= ColumnSortHandler + +class ColumnSortHandler final : public MouseFunction +{ +public: + ColumnSortHandler() + : m_nActiveColumn(COL_INVALID) + { + } + +public: + // MouseFunction + virtual FunctionResult handleMouseMove(ITableControl& i_tableControl, + MouseEvent const& i_event) override; + virtual FunctionResult handleMouseDown(ITableControl& i_tableControl, + MouseEvent const& i_event) override; + virtual FunctionResult handleMouseUp(ITableControl& i_tableControl, + MouseEvent const& i_event) override; + +private: + ColPos m_nActiveColumn; +}; + +} // namespace svt::table + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/table/tablecontrol.hxx b/toolkit/inc/controls/table/tablecontrol.hxx new file mode 100644 index 0000000000..9d9f98ff11 --- /dev/null +++ b/toolkit/inc/controls/table/tablecontrol.hxx @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/table/tablemodel.hxx> +#include <vcl/accessibletable.hxx> + +#include <vcl/ctrl.hxx> +#include <vcl/seleng.hxx> + +#include <memory> + +namespace svt::table +{ + class TableControl_Impl; + + + //= TableControl + + /** a basic control which manages table-like data, i.e. a number of cells + organized in <code>m</code> rows and <code>n</code> columns. + + The control itself does not do any assumptions about the concrete data + it displays, this is encapsulated in an instance supporting the + ->ITableModel interface. + + Also, the control does not do any assumptions about how the model's + content is rendered. This is the responsibility of a component + supporting the ->ITableRenderer interface (the renderer is obtained from + the model). + + The control supports the concept of a <em>current</em> (or <em>active</em> + cell). + The control supports accessibility, this is encapsulated in IAccessibleTable + */ + class TableControl final : public Control, public vcl::table::IAccessibleTable + { + private: + std::shared_ptr<TableControl_Impl> m_pImpl; + + + public: + TableControl( vcl::Window* _pParent, WinBits _nStyle ); + virtual ~TableControl() override; + virtual void dispose() override; + + /// sets a new table model + void SetModel( const PTableModel& _pModel ); + /// retrieves the current table model + PTableModel GetModel() const; + + /** retrieves the current row + + The current row is the one which contains the active cell. + + @return + the row index of the active cell, or ->ROW_INVALID + if there is no active cell, e.g. because the table does + not contain any rows or columns. + */ + sal_Int32 GetCurrentRow() const override; + + ITableControl& + getTableControlInterface(); + + /** retrieves the current column + + The current col is the one which contains the active cell. + + @return + the column index of the active cell, or ->COL_INVALID + if there is no active cell, e.g. because the table does + not contain any rows or columns. + */ + sal_Int32 GetCurrentColumn() const override; + + /** activates the cell at the given position + */ + void GoTo( ColPos _nColumnPos, RowPos _nRow); + + virtual void Resize() override; + void Select(); + + /**after removing a row, updates the vector which contains the selected rows + if the row, which should be removed, is selected, it will be erased from the vector + */ + SelectionEngine* getSelEngine(); + vcl::Window& getDataWindow(); + + // Window overridables + virtual void GetFocus() override; + virtual void LoseFocus() override; + virtual void KeyInput( const KeyEvent& rKEvt ) override; + virtual void StateChanged( StateChangedType i_nStateChange ) override; + + /** Creates and returns the accessible object of the whole GridControl. */ + virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override; + virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessibleControl( sal_Int32 _nIndex ) override; + virtual OUString GetAccessibleObjectName(vcl::table::AccessibleTableControlObjType eObjType, sal_Int32 _nRow, sal_Int32 _nCol) const override; + virtual void GoToCell( sal_Int32 _nColumnPos, sal_Int32 _nRow ) override; + virtual OUString GetAccessibleObjectDescription(vcl::table::AccessibleTableControlObjType eObjType) const override; + virtual void FillAccessibleStateSet( sal_Int64& rStateSet, vcl::table::AccessibleTableControlObjType eObjType ) const override; + + // temporary methods + // Those do not really belong into the public API - they're intended for firing A11Y-related events. However, + // firing those events should be an implementation internal to the TableControl resp. TableControl_Impl, + // instead of something triggered externally. + void commitCellEventIfAccessibleAlive( sal_Int16 const i_eventID, const css::uno::Any& i_newValue, const css::uno::Any& i_oldValue ); + void commitTableEventIfAccessibleAlive( sal_Int16 const i_eventID, const css::uno::Any& i_newValue, const css::uno::Any& i_oldValue ); + + + // IAccessibleTable + virtual AbsoluteScreenPixelRectangle GetWindowExtentsAbsolute() const override; + virtual tools::Rectangle GetWindowExtentsRelative(const vcl::Window& rRelativeWindow) const override; + virtual void GrabFocus() override; + virtual css::uno::Reference< css::accessibility::XAccessible > GetAccessible() override; + virtual vcl::Window* GetAccessibleParentWindow() const override; + virtual vcl::Window* GetWindowInstance() override; + virtual sal_Int32 GetAccessibleControlCount() const override; + virtual bool ConvertPointToControlIndex( sal_Int32& _rnIndex, const Point& _rPoint ) override; + virtual sal_Int32 GetRowCount() const override; + virtual sal_Int32 GetColumnCount() const override; + virtual bool ConvertPointToCellAddress( sal_Int32& _rnRow, sal_Int32& _rnColPos, const Point& _rPoint ) override; + virtual tools::Rectangle calcHeaderRect( bool _bIsColumnBar ) override; + virtual tools::Rectangle calcHeaderCellRect( bool _bIsColumnBar, sal_Int32 nPos) override; + virtual tools::Rectangle calcTableRect() override; + virtual tools::Rectangle calcCellRect( sal_Int32 _nRowPos, sal_Int32 _nColPos ) override; + virtual tools::Rectangle GetFieldCharacterBounds(sal_Int32 _nRow,sal_Int32 _nColumnPos,sal_Int32 nIndex) override; + virtual sal_Int32 GetFieldIndexAtPoint(sal_Int32 _nRow,sal_Int32 _nColumnPos,const Point& _rPoint) override; + virtual void FillAccessibleStateSetForCell( sal_Int64& _rStateSet, sal_Int32 _nRow, sal_uInt16 _nColumnPos ) const override; + virtual OUString GetRowName(sal_Int32 _nIndex) const override; + virtual OUString GetColumnName( sal_Int32 _nIndex ) const override; + virtual bool HasRowHeader() override; + virtual bool HasColHeader() override; + virtual OUString GetAccessibleCellText(sal_Int32 _nRowPos, sal_Int32 _nColPos) const override; + + virtual sal_Int32 GetSelectedRowCount() const override; + virtual sal_Int32 GetSelectedRowIndex( sal_Int32 const i_selectionIndex ) const override; + virtual bool IsRowSelected( sal_Int32 const i_rowIndex ) const override; + virtual void SelectRow( sal_Int32 const i_rowIndex, bool const i_select ) override; + virtual void SelectAllRows( bool const i_select ) override; + + + private: + DECL_LINK( ImplSelectHdl, LinkParamNone*, void ); + + private: + TableControl( const TableControl& ) = delete; + TableControl& operator=( const TableControl& ) = delete; + }; + + +} // namespace svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/table/tablecontrolinterface.hxx b/toolkit/inc/controls/table/tablecontrolinterface.hxx new file mode 100644 index 0000000000..8f5dca6763 --- /dev/null +++ b/toolkit/inc/controls/table/tablecontrolinterface.hxx @@ -0,0 +1,234 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <vcl/seleng.hxx> +#include <vcl/window.hxx> + +#include <controls/table/tabletypes.hxx> +#include <controls/table/tablemodel.hxx> + +namespace svt::table +{ + //= TableControlAction + enum TableControlAction + { + /// moves the cursor in the table control one row up, if possible, by keeping the current column + cursorUp, + /// moves the cursor in the table control one row down, if possible, by keeping the current column + cursorDown, + /// moves the cursor in the table control one column to the left, if possible, by keeping the current row + cursorLeft, + /// moves the cursor in the table control one column to the right, if possible, by keeping the current row + cursorRight, + /// moves the cursor to the beginning of the current line + cursorToLineStart, + /// moves the cursor to the end of the current line + cursorToLineEnd, + /// moves the cursor to the first row, keeping the current column + cursorToFirstLine, + /// moves the cursor to the last row, keeping the current column + cursorToLastLine, + /// moves the cursor one page up, keeping the current column + cursorPageUp, + /// moves the cursor one page down, keeping the current column + cursorPageDown, + /// moves the cursor to the top-most, left-most cell + cursorTopLeft, + /// moves the cursor to the bottom-most, right-most cell + cursorBottomRight, + /// selects the row, where the actual cursor is + cursorSelectRow, + /// selects the rows, above the actual cursor is + cursorSelectRowUp, + /// selects the row, beneath the actual cursor is + cursorSelectRowDown, + /// selects the row, from the actual cursor till top + cursorSelectRowAreaTop, + /// selects the row, from the actual cursor till bottom + cursorSelectRowAreaBottom + }; + + + //= TableCellArea + + enum TableCellArea + { + CellContent, + ColumnDivider + }; + + + //= TableCell + + struct TableCell + { + ColPos nColumn; + RowPos nRow; + TableCellArea eArea; + + TableCell( ColPos const i_column, RowPos const i_row ) + :nColumn( i_column ) + ,nRow( i_row ) + ,eArea( CellContent ) + { + } + }; + + + //= ColumnMetrics + + struct ColumnMetrics + { + /** the start of the column, in pixels. Might be negative, in case the column is scrolled out of the visible + area. Note: see below. + */ + tools::Long nStartPixel; + + /** the end of the column, in pixels, plus 1. Effectively, this is the accumulated width of all columns + up to the current one. + + Huh? Earlier you said that the nStartPixel of columns + scrolled out (to the left) of the visible area is + negative. Also, where is the promise that there is no gap + between columns? The above claim would be true only if the + first column always started at zero, and there is never a + gap. So these doc comments are inconsistent. How + surprising. + */ + tools::Long nEndPixel; + + ColumnMetrics() + :nStartPixel(0) + ,nEndPixel(0) + { + } + + ColumnMetrics( tools::Long const i_start, tools::Long const i_end ) + :nStartPixel( i_start ) + ,nEndPixel( i_end ) + { + } + }; + + + //= TableArea + + enum class TableArea + { + ColumnHeaders, + RowHeaders, + All + }; + + + //= ITableControl + + /** defines a callback interface to be implemented by a concrete table control + */ + class SAL_NO_VTABLE ITableControl + { + public: + /** hides the cell cursor + + The method cares for successive calls, that is, for every call to + ->hideCursor(), you need one call to ->showCursor. Only if the number + of both calls matches, the cursor is really shown. + + @see showCursor + */ + virtual void hideCursor() = 0; + + /** shows the cell cursor + + @see hideCursor + */ + virtual void showCursor() = 0; + + /** dispatches an action to the table control + + @return + <TRUE/> if the action could be dispatched successfully, <FALSE/> otherwise. Usual + failure conditions include some other instance vetoing the action, or impossibility + to execute the action at all (for instance moving up one row when already positioned + on the very first row). + + @see TableControlAction + */ + virtual bool dispatchAction( TableControlAction _eAction ) = 0; + + /** returns selection engine*/ + virtual SelectionEngine* getSelEngine() = 0; + + /** returns the table model + + The returned model is guaranteed to not be <NULL/>. + */ + virtual PTableModel getModel() const = 0; + + /// returns the index of the currently active column + virtual ColPos getCurrentColumn() const = 0; + + /// returns the index of the currently active row + virtual RowPos getCurrentRow() const = 0; + + /// activates the given cell + virtual void activateCell( ColPos const i_col, RowPos const i_row ) = 0; + + /// retrieves the size of the table window, in pixels + virtual ::Size getTableSizePixel() const = 0; + + /// sets a new mouse pointer for the table window + virtual void setPointer( PointerStyle i_pointer ) = 0; + + /// captures the mouse to the table window + virtual void captureMouse() = 0; + + /// releases the mouse, after it had previously been captured + virtual void releaseMouse() = 0; + + /// invalidates the table window + virtual void invalidate( TableArea const i_what ) = 0; + + /// calculates a width, given in pixels, into an AppFont-based width + virtual tools::Long pixelWidthToAppFont( tools::Long const i_pixels ) const = 0; + + /// shows a tracking rectangle + virtual void showTracking( tools::Rectangle const & i_location, ShowTrackFlags const i_flags ) = 0; + + /// hides a previously shown tracking rectangle + virtual void hideTracking() = 0; + + /// does a hit test for the given pixel coordinates + virtual TableCell hitTest( const Point& rPoint ) const = 0; + + /// retrieves the metrics for a given column + virtual ColumnMetrics getColumnMetrics( ColPos const i_column ) const = 0; + + /// determines whether a given row is selected + virtual bool isRowSelected( RowPos _nRow ) const = 0; + + virtual ~ITableControl() {}; + }; + +} // namespace svt::table + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/table/tableinputhandler.hxx b/toolkit/inc/controls/table/tableinputhandler.hxx new file mode 100644 index 0000000000..9d11df38db --- /dev/null +++ b/toolkit/inc/controls/table/tableinputhandler.hxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <memory> + +class MouseEvent; +class KeyEvent; + + +namespace svt::table +{ + + + class ITableControl; + + + //= ITableInputHandler + + /** interface for components handling input in a ->TableControl + */ + class ITableInputHandler + { + public: + // all those methods have the same semantics as the equal-named methods of ->Window, + // with the additional option to return a boolean value indicating whether + // the event should be further processed by the ->Window implementations (<FALSE/>), + // or whether it has been sufficiently handled by the ->ITableInputHandler instance + // (<FALSE/>). + + virtual bool MouseMove ( ITableControl& _rControl, const MouseEvent& rMEvt ) = 0; + virtual bool MouseButtonDown ( ITableControl& _rControl, const MouseEvent& rMEvt ) = 0; + virtual bool MouseButtonUp ( ITableControl& _rControl, const MouseEvent& rMEvt ) = 0; + virtual bool KeyInput ( ITableControl& _rControl, const KeyEvent& rKEvt ) = 0; + virtual bool GetFocus ( ITableControl& _rControl ) = 0; + virtual bool LoseFocus ( ITableControl& _rControl ) = 0; + + virtual ~ITableInputHandler() { } + }; + typedef std::shared_ptr< ITableInputHandler > PTableInputHandler; + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/table/tablemodel.hxx b/toolkit/inc/controls/table/tablemodel.hxx new file mode 100644 index 0000000000..7add49629a --- /dev/null +++ b/toolkit/inc/controls/table/tablemodel.hxx @@ -0,0 +1,454 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <toolkit/dllapi.h> +#include <controls/table/tabletypes.hxx> +#include <controls/table/tablerenderer.hxx> +#include <controls/table/tableinputhandler.hxx> + +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <com/sun/star/style/HorizontalAlignment.hpp> + +#include <sal/types.h> + +#include <optional> +#include <memory> +#include <vector> +#include <o3tl/typed_flags_set.hxx> + +namespace svt::table { class ITableDataSort; } + +class Color; + +enum class ColumnAttributeGroup +{ + NONE = 0x00, + /// denotes column attributes related to the width of the column + WIDTH = 0x01, + /// denotes column attributes related to the appearance of the column, i.e. those relevant for rendering + APPEARANCE = 0x02, + /// denotes the entirety of column attributes + ALL = 0x03, +}; +namespace o3tl +{ + template<> struct typed_flags<ColumnAttributeGroup> : is_typed_flags<ColumnAttributeGroup, 0x03> {}; +} + + +namespace svt::table +{ + //= ScrollbarVisibility + enum ScrollbarVisibility + { + /** enumeration value denoting that a scrollbar should never be visible, even + if needed normally + */ + ScrollbarShowNever, + /** enumeration value denoting that a scrollbar should be visible when needed only + */ + ScrollbarShowSmart, + /** enumeration value denoting that a scrollbar should always be visible, even + if not needed normally + */ + ScrollbarShowAlways + }; + + + //= ITableModelListener + + /** declares an interface to be implemented by components interested in + changes in an ->ITableModel + */ + class SAL_NO_VTABLE ITableModelListener : public std::enable_shared_from_this< ITableModelListener > + { + public: + /** notifies the listener that one or more rows have been inserted into + the table + + @param first + the index of the first newly inserted row + @param last + the index of the last newly inserted row. Must not be smaller + than ->first + */ + virtual void rowsInserted( RowPos first, RowPos last ) = 0; + + /** notifies the listener that one or more rows have been removed from + the table + + @param first + the old index of the first removed row. If this is <code>-1</code>, then all + rows have been removed from the model. + @param last + the old index of the last removed row. Must not be smaller + than ->first + */ + virtual void rowsRemoved( RowPos first, RowPos last ) = 0; + + /** notifies the listener that one or more columns have been inserted into + the table + */ + virtual void columnInserted() = 0; + + /** notifies the listener that one or more columns have been removed from + the table + */ + virtual void columnRemoved() = 0; + + /** notifies the listener that all columns have been removed from the model + */ + virtual void allColumnsRemoved() = 0; + + /** notifies the listener that a rectangular cell range in the table + has been updated + + Listeners are required to discard any possibly cached information + they have about the cells in question, in particular any possibly + cached cell values. + */ + virtual void cellsUpdated( RowPos const i_firstRow, RowPos const i_lastRow ) = 0; + + /** notifies the listener that attributes of a given column changed + + @param i_column + the position of the column whose attributes changed + @param i_attributeGroup + a combination of one or more <code>COL_ATTRS_*</code> flags, denoting the attribute group(s) + in which changes occurred. + */ + virtual void columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup ) = 0; + + /** notifies the listener that the metrics of the table changed. + + Metrics here include the column header height, the row header width, the row height, and the presence + of both the row and column header. + */ + virtual void tableMetricsChanged() = 0; + + /// deletes the listener instance + virtual ~ITableModelListener(){}; + }; + typedef std::shared_ptr< ITableModelListener > PTableModelListener; + + + //= IColumnModel + + /** interface to be implemented by table column models + */ + class SAL_NO_VTABLE IColumnModel + { + public: + /** returns the name of the column + + Column names should be human-readable, but not necessarily unique + within a given table. + + @see setName + */ + virtual OUString getName() const = 0; + + /** retrieves the help text to be displayed for the column. + */ + virtual OUString getHelpText() const = 0; + + /** determines whether the column can be interactively resized + + @see getMinWidth + @see getMaxWidth + @see getWidth + */ + virtual bool isResizable() const = 0; + + /** denotes the relative flexibility of the column + + This flexibility is taken into account when a table control auto-resizes its columns, because the available + space changed. In this case, the columns grow or shrink according to their flexibility. + + A value of 0 means the column is not auto-resized at all. + */ + virtual sal_Int32 getFlexibility() const = 0; + + /** returns the width of the column, in app-font units + + The returned value must be a positive ->TableMetrics value. + + @see setWidth + @see getMinWidth + @see getMaxWidth + */ + virtual TableMetrics getWidth() const = 0; + + /** sets a new width for the column + + @param _nWidth + the new width, app-font units + + @see getWidth + */ + virtual void setWidth( TableMetrics _nWidth ) = 0; + + /** returns the minimum width of the column, in app-font units, or 0 if the column + does not have a minimal width + + @see setMinWidth + @see getMaxWidth + @see getWidth + */ + virtual TableMetrics getMinWidth() const = 0; + + /** returns the maximum width of the column, in app-font units, or 0 if the column + does not have a minimal width + + @see setMaxWidth + @see getMinWidth + @see getWidth + */ + virtual TableMetrics getMaxWidth() const = 0; + + /** retrieves the horizontal alignment to be used for content in this cell + */ + virtual css::style::HorizontalAlignment getHorizontalAlign() = 0; + + /// deletes the column model instance + virtual ~IColumnModel() { } + }; + typedef std::shared_ptr< IColumnModel > PColumnModel; + + + //= ITableModel + + /** declares the interface to implement by an abstract table model + */ + class SAL_NO_VTABLE TOOLKIT_DLLPUBLIC ITableModel + { + public: + /** returns the number of columns in the table + */ + virtual TableSize getColumnCount() const = 0; + + /** returns the number of rows in the table + */ + virtual TableSize getRowCount() const = 0; + + /** determines whether the table has column headers + + If this method returns <TRUE/>, the renderer returned by + ->getRenderer must be able to render column headers. + + @see IColumnRenderer + */ + virtual bool hasColumnHeaders() const = 0; + + /** determines whether the table has row headers + + If this method returns <TRUE/>, the renderer returned by + ->getRenderer must be able to render row headers. + + @see IColumnRenderer + */ + virtual bool hasRowHeaders() const = 0; + + /** returns a model for a certain column + + @param column + the index of the column in question. Must be greater than or + equal 0, and smaller than the return value of ->getColumnCount() + + @return + the model of the column in question. Must not be <NULL/> + */ + virtual PColumnModel getColumnModel( ColPos column ) = 0; + + /** returns a renderer which is able to paint the table represented + by this table model + + @return the renderer to use. Must not be <NULL/> + */ + virtual PTableRenderer getRenderer() const = 0; + + /** returns the component handling input in a view associated with the model + */ + virtual PTableInputHandler getInputHandler() const = 0; + + /** determines the height of rows in the table. + + @return + the logical height of rows in the table, in app-font units. The height must be + greater 0. + */ + virtual TableMetrics getRowHeight() const = 0; + + /** determines the height of the column header row + + This method is not to be called if ->hasColumnHeaders() + returned <FALSE/>. + + @return + the logical height of the column header row, in app-font units. + Must be greater than 0. + */ + virtual TableMetrics getColumnHeaderHeight() const = 0; + + /** determines the width of the row header column + + This method is not to be called if ->hasRowHeaders() + returned <FALSE/>. + + @return + the logical width of the row header column, in app-font units. + Must be greater than 0. + */ + virtual TableMetrics getRowHeaderWidth() const = 0; + + /** returns the visibility mode of the vertical scrollbar + */ + virtual ScrollbarVisibility getVerticalScrollbarVisibility() const = 0; + + /** returns the visibility mode of the horizontal scrollbar + */ + virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const = 0; + + /** adds a listener to be notified of changes in the table model + */ + virtual void addTableModelListener( const PTableModelListener& i_listener ) = 0; + + /** remove a listener to be notified of changes in the table model + */ + virtual void removeTableModelListener( const PTableModelListener& i_listener ) = 0; + + /** retrieves the content of the given cell + */ + virtual void getCellContent( ColPos const i_col, RowPos const i_row, css::uno::Any& o_cellContent ) = 0; + + /** returns an object which should be displayed as tooltip for the given cell + + At the moment, only string-convertible values are supported here. In the future, one might imagine displaying + scaled-down versions of a graphic in a cell, and a larger version of that very graphic as tooltip. + + If no tooltip object is provided, then the cell content is used, and displayed as tooltip for the cell + if and only if it doesn't fit into the cell's space itself. + + @param i_col + The column index of the cell in question. COL_ROW_HEADERS is a valid argument here. + @param i_row + The row index of the cell in question. + @param o_cellToolTip + takes the tooltip object upon return. + */ + virtual void getCellToolTip( ColPos const i_col, RowPos const i_row, css::uno::Any & o_cellToolTip ) = 0; + + /** retrieves title of a given row + */ + virtual css::uno::Any getRowHeading( RowPos const i_rowPos ) const = 0; + + /** returns the color to be used for rendering the grid lines. + + If this value is not set, a default color from the style settings will be used. + */ + virtual ::std::optional< ::Color > getLineColor() const = 0; + + /** returns the color to be used for rendering the header background. + + If this value is not set, a default color from the style settings will be used. + */ + virtual ::std::optional< ::Color > getHeaderBackgroundColor() const = 0; + + /** returns the color to be used for rendering the header text. + + If this value is not set, a default color from the style settings will be used. + */ + virtual ::std::optional< ::Color > getHeaderTextColor() const = 0; + + /** returns the color to be used for the background of selected cells, when the control has the focus + + If this value is not set, a default color from the style settings will be used. + */ + virtual ::std::optional< ::Color > getActiveSelectionBackColor() const = 0; + + /** returns the color to be used for the background of selected cells, when the control does not have the focus + + If this value is not set, a default color from the style settings will be used. + */ + virtual ::std::optional< ::Color > getInactiveSelectionBackColor() const = 0; + + /** returns the color to be used for the text of selected cells, when the control has the focus + + If this value is not set, a default color from the style settings will be used. + */ + virtual ::std::optional< ::Color > getActiveSelectionTextColor() const = 0; + + /** returns the color to be used for the text of selected cells, when the control does not have the focus + + If this value is not set, a default color from the style settings will be used. + */ + virtual ::std::optional< ::Color > getInactiveSelectionTextColor() const = 0; + + /** returns the color to be used for rendering cell texts. + + If this value is not set, a default color from the style settings will be used. + */ + virtual ::std::optional< ::Color > getTextColor() const = 0; + + /** returns the color to be used for text lines (underline, strikethrough) when rendering cell text. + + If this value is not set, a default color from the style settings will be used. + */ + virtual ::std::optional< ::Color > getTextLineColor() const = 0; + + /** returns the colors to be used for the row backgrounds. + + If this value is not set, every second row will have a background color derived from the style settings's + selection color, the other rows will not have a special background at all. + + If this value is an empty sequence, the rows will not have a special background at all, instead the + normal background of the complete control will be used. + + If value is a non-empty sequence, then rows will have the background colors as specified in the sequence, + in alternating order. + */ + virtual ::std::optional< ::std::vector< ::Color > > + getRowBackgroundColors() const = 0; + + /** determines the vertical alignment of content within a cell + */ + virtual css::style::VerticalAlignment getVerticalAlign() const = 0; + + /** returns an adapter to the sorting functionality of the model + + It is legitimate to return <NULL/> here, in this case, the table model does not support sorting. + */ + virtual ITableDataSort* getSortAdapter() = 0; + + /** returns enabled state. + */ + virtual bool isEnabled() const = 0; + + /// destroys the table model instance + virtual ~ITableModel() { } + }; + typedef std::shared_ptr< ITableModel > PTableModel; + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/table/tablerenderer.hxx b/toolkit/inc/controls/table/tablerenderer.hxx new file mode 100644 index 0000000000..a4d7a48a62 --- /dev/null +++ b/toolkit/inc/controls/table/tablerenderer.hxx @@ -0,0 +1,249 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/table/tabletypes.hxx> + +#include <rtl/ustring.hxx> + +#include <memory> + +namespace com :: sun :: star :: uno { class Any; } +namespace tools { class Rectangle; } +namespace vcl { class Window; } + +class OutputDevice; +class StyleSettings; +namespace vcl { + typedef OutputDevice RenderContext; +}; + + +namespace svt::table +{ + + + //= ITableRenderer + + /** interface to implement by components rendering a ->TableControl + */ + class SAL_NO_VTABLE ITableRenderer + { + public: + + /** paints a (part of) header area + + There are two header areas in a table control: + <ul><li>The row containing all column headers, i.e. <em>above</em> all rows containing the data</li> + <li>The column containing all row headers. i.e. <em>left of</em> all columns containing the data</li> + </ul> + + A header area is more than the union of the single column/row headers. + + First, there might be less columns than fit into the view - in this case, right + beside the right-most column, there's still room which belongs to the column header + area, but is not occupied by any particular column header.<br/> + An equivalent statement holds for the row header area, if there are fewer rows than + fit into the view. + + Second, if the table control has both a row header and a column header, + the intersection between those both belongs to both the column header area and the + row header area, but not to any particular column or row header. + + There are two flags specifying whether the to-be-painted area is part of the column + and/or row header area. + <ul><li>If both are <TRUE/>, the intersection of both areas is to be painted.</li> + <li>If ->_bIsColHeaderArea is <TRUE/> and ->_bIsRowHeaderArea is <FALSE/>, + then ->_rArea denotes the column header area <em>excluding</em> the + intersection between row and column header area.</li> + <li>Equivalently for ->_bIsColHeaderArea being <FALSE/> and ->_bIsRowHeaderArea + being <TRUE/></li> + </ul> + Note that it's not possible for both ->_bIsColHeaderArea and ->_bIsRowHeaderArea + to be <FALSE/> at the same time. + + @param _rDevice + the device to paint onto + @param _rArea + the area to paint into + @param _bIsColHeaderArea + <TRUE/> if and only if ->_rArea is part of the column header area. + @param _bIsRowHeaderArea + <TRUE/> if and only if ->_rArea is part of the row header area. + @param _rStyle + the style to be used for drawing + */ + virtual void PaintHeaderArea( + vcl::RenderContext& _rDevice, const tools::Rectangle& _rArea, + bool _bIsColHeaderArea, bool _bIsRowHeaderArea, + const StyleSettings& _rStyle ) = 0; + + /** paints the header for a given column + + @param _nCol + the index of the column to paint + @param _rDevice + denotes the device to paint onto + @param _rArea + the are into which the column header should be painted + @param _rStyle + the style to be used for drawing + */ + virtual void PaintColumnHeader( ColPos _nCol, + vcl::RenderContext& _rDevice, const tools::Rectangle& _rArea, + const StyleSettings& _rStyle ) = 0; + + /** prepares a row for painting + + Painting a table means painting rows as necessary, in an increasing + order. The assumption is that retrieving data for two different rows + is (potentially) more expensive than retrieving data for two different + columns. Thus, the renderer will get the chance to "seek" to a certain + row, and then has to render all cells in this row, before another + row is going to be painted. + + @param _nRow + the row which is going to be painted. The renderer should + at least remember this row, since subsequent calls to + ->PaintRowHeader(), ->PaintCell(), and ->FinishRow() will + not pass this parameter again. + + However, the renderer is also allowed to render any + cell-independent content of this row. + + @param i_hasControlFocus + <TRUE/> if and only if the table control currently has the focus + @param _bSelected + <TRUE/> if and only if the row to be prepared is + selected currently. + @param _rDevice + denotes the device to paint onto + @param _rRowArea + the are into which the row should be painted. This excludes + the row header area, if applicable. + @param _rStyle + the style to be used for drawing + */ + virtual void PrepareRow( RowPos _nRow, bool i_hasControlFocus, bool _bSelected, + vcl::RenderContext& _rDevice, const tools::Rectangle& _rRowArea, + const StyleSettings& _rStyle ) = 0; + + /** paints the header of a row + + The row to be painted is denoted by the most recent call to + ->PrepareRow. + + @param _rDevice + denotes the device to paint onto + @param _rArea + the are into which the row header should be painted + @param _rStyle + the style to be used for drawing + */ + virtual void PaintRowHeader( + vcl::RenderContext& _rDevice, tools::Rectangle const & _rArea, + StyleSettings const & _rStyle ) = 0; + + /** paints a certain cell + + The row to be painted is denoted by the most recent call to + ->PrepareRow. + + @param _bSelected + <TRUE/> if and only if the cell to be painted is + selected currently. This is the case if either + the row or the column of the cell is currently selected. + <br/> + Note that this flag is equal to the respective flag in the + previous ->PrepareRow call, it's passed here for convenience + only. + @param i_hasControlFocus + <TRUE/> if and only if the table control currently has the focus + <br/> + Note that this flag is equal to the respective flag in the + previous ->PrepareRow call, it's passed here for convenience + only. + @param _rDevice + denotes the device to paint onto + @param _rArea + the are into which the cell should be painted + @param _rStyle + the style to be used for drawing + */ + virtual void PaintCell( ColPos const i_col, + bool i_hasControlFocus, bool _bSelected, + vcl::RenderContext& _rDevice, const tools::Rectangle& _rArea, + const StyleSettings& _rStyle ) = 0; + + /** draws a cell cursor in the given rectangle + + The cell cursor is used to indicate the active/current cell + of a table control. + */ + virtual void ShowCellCursor( vcl::Window& _rView, const tools::Rectangle& _rCursorRect) = 0; + + /** hides the cell cursor previously drawn into the given rectangle + + The cell cursor is used to indicate the active/current cell + of a table control. + */ + virtual void HideCellCursor( vcl::Window& _rView ) = 0; + + /** checks whether a given cell content fits into a given target area on a given device. + + @param i_targetDevice + denotes the target device for the assumed rendering operation + + @param i_targetArea + denotes the area within the target device for the assumed rendering operation. + + @return + <TRUE/> if and only if the given cell content could be rendered into the given device and the + given area. + */ + virtual bool FitsIntoCell( + css::uno::Any const & i_cellContent, + OutputDevice& i_targetDevice, tools::Rectangle const & i_targetArea + ) const = 0; + + /** attempts to format the content of the given cell as string + + @param i_cellValue + the value for which an attempt for a string conversion should be made + @param o_cellString + the cell content, formatted as string + @return + <TRUE/> if and only if the content could be formatted as string + */ + virtual bool GetFormattedCellString( + css::uno::Any const & i_cellValue, + OUString & o_cellString + ) const = 0; + + /// deletes the renderer instance + virtual ~ITableRenderer() { } + }; + typedef std::shared_ptr< ITableRenderer > PTableRenderer; + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/table/tablesort.hxx b/toolkit/inc/controls/table/tablesort.hxx new file mode 100644 index 0000000000..691beea4ed --- /dev/null +++ b/toolkit/inc/controls/table/tablesort.hxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <controls/table/tabletypes.hxx> + +namespace svt::table +{ + + + //= ColumnSortDirection + + enum ColumnSortDirection + { + ColumnSortAscending, + ColumnSortDescending + }; + + + //= ColumnSort + + struct ColumnSort + { + ColPos nColumnPos; + ColumnSortDirection eSortDirection; + + ColumnSort() + :nColumnPos( COL_INVALID ) + ,eSortDirection( ColumnSortAscending ) + { + } + + }; + + + //= ITableDataSort + + /** provides sorting functionality for the data underlying an ITableModel + */ + class SAL_NO_VTABLE ITableDataSort + { + public: + /** sorts the rows in the model by the given column's data, in the given direction. + */ + virtual void sortByColumn( ColPos const i_column, ColumnSortDirection const i_sortDirection ) = 0; + + /** retrieves the current sort order of the data + + If the <code>nColumnIndex</code> member of the returned structure is <code>COL_INVALID</code>, then + the data is currently not sorted. + */ + virtual ColumnSort getCurrentSortOrder() const = 0; + + protected: + ~ITableDataSort() {} + }; + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/table/tabletypes.hxx b/toolkit/inc/controls/table/tabletypes.hxx new file mode 100644 index 0000000000..8fdb18a830 --- /dev/null +++ b/toolkit/inc/controls/table/tabletypes.hxx @@ -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 . + */ + +#pragma once + +#include <sal/types.h> + + +namespace svt::table +{ + + /// a value denoting the size of a table + typedef sal_Int32 TableSize; + + /// a value denoting a column position within a table + typedef sal_Int32 ColPos; + /// a value denoting a row position within a table + typedef sal_Int32 RowPos; + + typedef sal_Int32 TableMetrics; + +/// denotes the column containing the row headers +#define COL_ROW_HEADERS (::svt::table::ColPos(-1)) +/// denotes the row containing the column headers +#define ROW_COL_HEADERS (::svt::table::RowPos(-1)) + +/// denotes an invalid column index +#define COL_INVALID (::svt::table::ColPos(-2)) +/// denotes an invalid row index +#define ROW_INVALID (::svt::table::RowPos(-2)) + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/tabpagecontainer.hxx b/toolkit/inc/controls/tabpagecontainer.hxx new file mode 100644 index 0000000000..f9fe2df02d --- /dev/null +++ b/toolkit/inc/controls/tabpagecontainer.hxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/tab/XTabPageContainer.hpp> +#include <com/sun/star/awt/tab/XTabPageContainerModel.hpp> +#include <toolkit/controls/unocontrolbase.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <cppuhelper/implbase1.hxx> +#include <controls/controlmodelcontainerbase.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> + + +namespace com::sun::star::awt::tab { class XTabPage; } +namespace com::sun::star::awt::tab { class XTabPageContainerListener; } +namespace com::sun::star::awt::tab { class XTabPageModel; } + + +typedef ::cppu::AggImplInheritanceHelper1 < UnoControlModel + , css::awt::tab::XTabPageContainerModel + > UnoControlTabPageContainerModel_Base; +class UnoControlTabPageContainerModel final : public UnoControlTabPageContainerModel_Base +{ +private: + std::vector< css::uno::Reference< css::awt::tab::XTabPageModel > > m_aTabPageVector; + ContainerListenerMultiplexer maContainerListeners; + + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + // css::beans::XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + +public: + UnoControlTabPageContainerModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ); + UnoControlTabPageContainerModel( const UnoControlTabPageContainerModel& rModel ) : UnoControlTabPageContainerModel_Base( rModel ),maContainerListeners( *this ) {} + + rtl::Reference<UnoControlModel> Clone() const override { return new UnoControlTabPageContainerModel( *this ); } + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // css::lang::XServiceInfo + DECLIMPL_SERVICEINFO_DERIVED( UnoControlTabPageContainerModel, UnoControlModel, "com.sun.star.awt.tab.UnoControlTabPageContainerModel" ) + + // XTabPageContainerModel + virtual css::uno::Reference< css::awt::tab::XTabPageModel > SAL_CALL createTabPage( ::sal_Int16 TabPageID ) override; + virtual css::uno::Reference< css::awt::tab::XTabPageModel > SAL_CALL loadTabPage( ::sal_Int16 TabPageID, const OUString& ResourceURL ) override; + + // XIndexContainer + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const css::uno::Any& Element ) override; + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override; + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const css::uno::Any& Element ) override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override; + + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // css::container::XContainer + void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; +}; + +// = UnoControlTabPageContainer + +typedef ::cppu::AggImplInheritanceHelper1 < ControlContainerBase + , css::awt::tab::XTabPageContainer + > UnoControlTabPageContainer_Base; +class UnoControlTabPageContainer final : public UnoControlTabPageContainer_Base +{ +public: + UnoControlTabPageContainer( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + OUString GetComponentServiceName() const override; + + // css::lang::XComponent + void SAL_CALL dispose( ) override; + + // css::awt::XControl + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + + // css::awt::tab::XTabPageContainer + virtual ::sal_Int16 SAL_CALL getActiveTabPageID() override; + virtual void SAL_CALL setActiveTabPageID( ::sal_Int16 _activetabpageid ) override; + virtual ::sal_Int16 SAL_CALL getTabPageCount( ) override; + virtual sal_Bool SAL_CALL isTabPageActive( ::sal_Int16 tabPageIndex ) override; + virtual css::uno::Reference< css::awt::tab::XTabPage > SAL_CALL getTabPage( ::sal_Int16 tabPageIndex ) override; + virtual css::uno::Reference< css::awt::tab::XTabPage > SAL_CALL getTabPageByID( ::sal_Int16 tabPageID ) override; + virtual void SAL_CALL addTabPageContainerListener( const css::uno::Reference< css::awt::tab::XTabPageContainerListener >& listener ) override; + virtual void SAL_CALL removeTabPageContainerListener( const css::uno::Reference< css::awt::tab::XTabPageContainerListener >& listener ) override; + + // css::beans::XPropertiesChangeListener + virtual void SAL_CALL propertiesChange( const ::css::uno::Sequence< ::css::beans::PropertyChangeEvent >& aEvent ) override; + + virtual void SAL_CALL addControl( const OUString& Name, const css::uno::Reference< css::awt::XControl >& Control ) override; + // css::lang::XServiceInfo + DECLIMPL_SERVICEINFO_DERIVED( UnoControlTabPageContainer, UnoControlBase, "com.sun.star.awt.tab.UnoControlTabPageContainer" ) + +// using UnoControl::getPeer; +private: + virtual void updateFromModel() override; + TabPageListenerMultiplexer m_aTabPageListeners; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/tabpagemodel.hxx b/toolkit/inc/controls/tabpagemodel.hxx new file mode 100644 index 0000000000..a1668e2d5d --- /dev/null +++ b/toolkit/inc/controls/tabpagemodel.hxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <controls/controlmodelcontainerbase.hxx> +#include <com/sun/star/awt/tab/XTabPage.hpp> +#include <cppuhelper/implbase2.hxx> + +class UnoControlTabPageModel final : public ControlModelContainerBase +{ + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; +public: + UnoControlTabPageModel( css::uno::Reference< css::uno::XComponentContext > const & i_factory); + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // css::beans::XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // XInitialization + virtual void SAL_CALL initialize (const css::uno::Sequence<css::uno::Any>& rArguments) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + + + +typedef ::cppu::AggImplInheritanceHelper2 < ControlContainerBase + , css::awt::tab::XTabPage + , css::awt::XWindowListener + > UnoControlTabPage_Base; +class UnoControlTabPage final : public UnoControlTabPage_Base +{ +private: + bool m_bWindowListener; +public: + + UnoControlTabPage( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~UnoControlTabPage() override; + OUString GetComponentServiceName() const override; + + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // css::awt::XWindowListener + virtual void SAL_CALL windowResized( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowShown( const css::lang::EventObject& e ) override; + virtual void SAL_CALL windowHidden( const css::lang::EventObject& e ) override; + // css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/tkscrollbar.hxx b/toolkit/inc/controls/tkscrollbar.hxx new file mode 100644 index 0000000000..c7948de072 --- /dev/null +++ b/toolkit/inc/controls/tkscrollbar.hxx @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <toolkit/controls/unocontrolmodel.hxx> +#include <toolkit/controls/unocontrolbase.hxx> +#include <com/sun/star/awt/XScrollBar.hpp> +#include <com/sun/star/awt/XAdjustmentListener.hpp> + + +namespace toolkit +{ + + + //= UnoControlScrollBarModel + + class UnoControlScrollBarModel final : public UnoControlModel + { + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + + public: + UnoControlScrollBarModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ); + UnoControlScrollBarModel( const UnoControlScrollBarModel& rModel ) : UnoControlModel( rModel ) {} + + rtl::Reference<UnoControlModel> Clone() const override { return new UnoControlScrollBarModel( *this ); } + + // css::beans::XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + }; + + + //= UnoControlScrollBarModel + + class UnoScrollBarControl final : public UnoControlBase, + public css::awt::XAdjustmentListener, + public css::awt::XScrollBar + { + private: + AdjustmentListenerMultiplexer maAdjustmentListeners; + + public: + UnoScrollBarControl(); + OUString GetComponentServiceName() const override; + + css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override { return UnoControlBase::queryInterface(rType); } + css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override; + void SAL_CALL acquire() noexcept override { OWeakAggObject::acquire(); } + void SAL_CALL release() noexcept override { OWeakAggObject::release(); } + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + void SAL_CALL disposing( const css::lang::EventObject& Source ) override { UnoControlBase::disposing( Source ); } + void SAL_CALL dispose( ) override; + + // css::lang::XTypeProvider + css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // css::awt::XAdjustmentListener + void SAL_CALL adjustmentValueChanged( const css::awt::AdjustmentEvent& rEvent ) override; + + // css::awt::XScrollBar + void SAL_CALL addAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener >& l ) override; + void SAL_CALL removeAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener >& l ) override; + void SAL_CALL setValue( sal_Int32 n ) override; + void SAL_CALL setValues( sal_Int32 nValue, sal_Int32 nVisible, sal_Int32 nMax ) override; + sal_Int32 SAL_CALL getValue( ) override; + void SAL_CALL setMaximum( sal_Int32 n ) override; + sal_Int32 SAL_CALL getMaximum( ) override; + void SAL_CALL setLineIncrement( sal_Int32 n ) override; + sal_Int32 SAL_CALL getLineIncrement( ) override; + void SAL_CALL setBlockIncrement( sal_Int32 n ) override; + sal_Int32 SAL_CALL getBlockIncrement( ) override; + void SAL_CALL setVisibleSize( sal_Int32 n ) override; + sal_Int32 SAL_CALL getVisibleSize( ) override; + void SAL_CALL setOrientation( sal_Int32 n ) override; + sal_Int32 SAL_CALL getOrientation( ) override; + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + }; + + +} // namespacetoolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/treecontrolpeer.hxx b/toolkit/inc/controls/treecontrolpeer.hxx new file mode 100644 index 0000000000..d584516ae3 --- /dev/null +++ b/toolkit/inc/controls/treecontrolpeer.hxx @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/tree/XTreeControl.hpp> +#include <com/sun/star/awt/tree/XTreeDataModel.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> + +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> + +#include <vcl/image.hxx> + +#include <cppuhelper/implbase.hxx> + +#include <map> + +namespace com::sun::star::awt::tree { class XTreeNode; } + +class UnoTreeListEntry; +class TreeControlPeer; +class UnoTreeListBoxImpl; + +class TreeControlPeer final : public ::cppu::ImplInheritanceHelper< VCLXWindow, css::awt::tree::XTreeControl, css::awt::tree::XTreeDataModelListener > +{ + typedef std::map<css::uno::Reference<css::awt::tree::XTreeNode>, UnoTreeListEntry*> TreeNodeMap; + + friend class UnoTreeListBoxImpl; + friend class UnoTreeListEntry; +public: + TreeControlPeer(); + virtual ~TreeControlPeer() override; + + vcl::Window* createVclControl( vcl::Window* pParent, sal_Int64 nWinStyle ); + + // css::view::XSelectionSupplier + virtual sal_Bool SAL_CALL select( const css::uno::Any& xSelection ) override; + virtual css::uno::Any SAL_CALL getSelection( ) override; + virtual void SAL_CALL addSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + virtual void SAL_CALL removeSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + + // css::view::XMultiSelectionSupplier + virtual sal_Bool SAL_CALL addSelection( const css::uno::Any& Selection ) override; + virtual void SAL_CALL removeSelection( const css::uno::Any& Selection ) override; + virtual void SAL_CALL clearSelection( ) override; + virtual ::sal_Int32 SAL_CALL getSelectionCount( ) override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSelectionEnumeration( ) override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createReverseSelectionEnumeration( ) override; + + // css::awt::XTreeControl + virtual OUString SAL_CALL getDefaultExpandedGraphicURL() override; + virtual void SAL_CALL setDefaultExpandedGraphicURL( const OUString& _defaultexpandedgraphicurl ) override; + virtual OUString SAL_CALL getDefaultCollapsedGraphicURL() override; + virtual void SAL_CALL setDefaultCollapsedGraphicURL( const OUString& _defaultcollapsedgraphicurl ) override; + virtual sal_Bool SAL_CALL isNodeExpanded( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL isNodeCollapsed( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL makeNodeVisible( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL isNodeVisible( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL expandNode( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL collapseNode( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL addTreeExpansionListener( const css::uno::Reference< css::awt::tree::XTreeExpansionListener >& Listener ) override; + virtual void SAL_CALL removeTreeExpansionListener( const css::uno::Reference< css::awt::tree::XTreeExpansionListener >& Listener ) override; + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getNodeForLocation( ::sal_Int32 x, ::sal_Int32 y ) override; + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getClosestNodeForLocation( ::sal_Int32 x, ::sal_Int32 y ) override; + virtual css::awt::Rectangle SAL_CALL getNodeRect( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL isEditing( ) override; + virtual sal_Bool SAL_CALL stopEditing( ) override; + virtual void SAL_CALL cancelEditing( ) override; + virtual void SAL_CALL startEditingAtNode( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL addTreeEditListener( const css::uno::Reference< css::awt::tree::XTreeEditListener >& Listener ) override; + virtual void SAL_CALL removeTreeEditListener( const css::uno::Reference< css::awt::tree::XTreeEditListener >& Listener ) override; + + // css::awt::tree::TreeDataModelListener + virtual void SAL_CALL treeNodesChanged( const css::awt::tree::TreeDataModelEvent& aEvent ) override; + virtual void SAL_CALL treeNodesInserted( const css::awt::tree::TreeDataModelEvent& aEvent ) override; + virtual void SAL_CALL treeNodesRemoved( const css::awt::tree::TreeDataModelEvent& aEvent ) override; + virtual void SAL_CALL treeStructureChanged( const css::awt::tree::TreeDataModelEvent& aEvent ) override; + + // XEventListener + void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // css::awt::XLayoutConstrains + css::awt::Size SAL_CALL getMinimumSize() override; + css::awt::Size SAL_CALL getPreferredSize() override; + css::awt::Size SAL_CALL calcAdjustedSize( const css::awt::Size& aNewSize ) override; + + // css::awt::XVclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + +private: + /// @throws css::lang::IllegalArgumentException + UnoTreeListEntry* getEntry( const css::uno::Reference< css::awt::tree::XTreeNode >& xNode, bool bThrow = true ); + + void disposeControl(); + + bool onEditingEntry( UnoTreeListEntry const * pEntry ); + bool onEditedEntry( UnoTreeListEntry const * pEntry, const OUString& rNewText ); + + void fillTree( UnoTreeListBoxImpl& rTree, const css::uno::Reference< css::awt::tree::XTreeDataModel >& xDataModel ); + void addNode( UnoTreeListBoxImpl& rTree, const css::uno::Reference< css::awt::tree::XTreeNode >& xNode, UnoTreeListEntry* pParentEntry ); + + UnoTreeListEntry* createEntry( const css::uno::Reference< css::awt::tree::XTreeNode >& xNode, UnoTreeListEntry* pParent, sal_uLong nPos ); + void updateEntry( UnoTreeListEntry* pEntry ); + + void updateTree( const css::awt::tree::TreeDataModelEvent& rEvent ); + void updateNode( UnoTreeListBoxImpl const & rTree, const css::uno::Reference< css::awt::tree::XTreeNode >& xNode ); + void updateChildNodes( UnoTreeListBoxImpl const & rTree, const css::uno::Reference< css::awt::tree::XTreeNode >& xParentNode, UnoTreeListEntry* pParentEntry ); + + static OUString getEntryString( const css::uno::Any& rValue ); + + /// @throws css::uno::RuntimeException + UnoTreeListBoxImpl& getTreeListBoxOrThrow() const; + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalArgumentException + void ChangeNodesSelection( const css::uno::Any& rSelection, bool bSelect, bool bSetSelection ); + + void onChangeDataModel( UnoTreeListBoxImpl& rTree, const css::uno::Reference< css::awt::tree::XTreeDataModel >& xDataModel ); + + void onSelectionChanged(); + void onRequestChildNodes( const css::uno::Reference< css::awt::tree::XTreeNode >& xNode ); + bool onExpanding( const css::uno::Reference< css::awt::tree::XTreeNode >& xNode, bool bExpanding ); + void onExpanded( const css::uno::Reference< css::awt::tree::XTreeNode >& xNode, bool bExpanding ); + + void onChangeRootDisplayed( bool bIsRootDisplayed ); + + void addEntry( UnoTreeListEntry* pEntry ); + void removeEntry( UnoTreeListEntry const * pEntry ); + + bool loadImage( const OUString& rURL, Image& rImage ); + +private: + css::uno::Reference< css::awt::tree::XTreeDataModel >mxDataModel; + TreeSelectionListenerMultiplexer maSelectionListeners; + TreeExpansionListenerMultiplexer maTreeExpansionListeners; + TreeEditListenerMultiplexer maTreeEditListeners; + bool mbIsRootDisplayed; + VclPtr<UnoTreeListBoxImpl> mpTreeImpl; + sal_Int32 mnEditLock; + OUString msDefaultCollapsedGraphicURL; + OUString msDefaultExpandedGraphicURL; + Image maDefaultExpandedImage; + Image maDefaultCollapsedImage; + std::unique_ptr<TreeNodeMap> mpTreeNodeMap; + css::uno::Reference< css::graphic::XGraphicProvider > mxGraphicProvider; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/unocontrolcontainer.hxx b/toolkit/inc/controls/unocontrolcontainer.hxx new file mode 100644 index 0000000000..ac4e018160 --- /dev/null +++ b/toolkit/inc/controls/unocontrolcontainer.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 <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/awt/XUnoControlContainer.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/container/XIdentifierContainer.hpp> + +#include <toolkit/controls/unocontrolbase.hxx> + +#include <cppuhelper/implbase4.hxx> +#include <memory> + +class UnoControlHolderList; + + + +typedef ::cppu::AggImplInheritanceHelper4 < UnoControlBase + , css::awt::XUnoControlContainer + , css::awt::XControlContainer + , css::container::XContainer + , css::container::XIdentifierContainer + > UnoControlContainer_Base; + +class UnoControlContainer : public UnoControlContainer_Base +{ +private: + std::unique_ptr<UnoControlHolderList> mpControls; + css::uno::Sequence< css::uno::Reference< css::awt::XTabController > > maTabControllers; + ContainerListenerMultiplexer maCListeners; + +protected: + void ImplActivateTabControllers(); + +public: + UnoControlContainer(); + UnoControlContainer( const css::uno::Reference< css::awt::XVclWindowPeer >& xPeer ); + virtual ~UnoControlContainer() override; + + + // css::lang::XComponent + void SAL_CALL dispose() override; + + // css::lang::XEventListener + void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // css::container::XContainer + void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + + // css::container::XIdentifierContainer + virtual ::sal_Int32 SAL_CALL insert( const css::uno::Any& aElement ) override; + + // css::container::XIdentifierReplace + virtual void SAL_CALL removeByIdentifier( ::sal_Int32 Identifier ) override; + virtual void SAL_CALL replaceByIdentifer( ::sal_Int32 Identifier, const css::uno::Any& aElement ) override; + + // css::container::XIdentifierAccess + virtual css::uno::Any SAL_CALL getByIdentifier( ::sal_Int32 Identifierr ) override; + virtual css::uno::Sequence< ::sal_Int32 > SAL_CALL getIdentifiers( ) override; + + // css::container::XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + // css::awt::XControlContainer + void SAL_CALL setStatusText( const OUString& StatusText ) override; + css::uno::Sequence< css::uno::Reference< css::awt::XControl > > SAL_CALL getControls( ) override; + css::uno::Reference< css::awt::XControl > SAL_CALL getControl( const OUString& aName ) override; + void SAL_CALL addControl( const OUString& Name, const css::uno::Reference< css::awt::XControl >& Control ) override; + void SAL_CALL removeControl( const css::uno::Reference< css::awt::XControl >& Control ) override; + + // css::awt::XUnoControlContainer + void SAL_CALL setTabControllers( const css::uno::Sequence< css::uno::Reference< css::awt::XTabController > >& TabControllers ) override; + css::uno::Sequence< css::uno::Reference< css::awt::XTabController > > SAL_CALL getTabControllers( ) override; + void SAL_CALL addTabController( const css::uno::Reference< css::awt::XTabController >& TabController ) override; + void SAL_CALL removeTabController( const css::uno::Reference< css::awt::XTabController >& TabController ) override; + + // css::awt::XControl + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + + // css::awt::XWindow + void SAL_CALL setVisible( sal_Bool Visible ) override; + + OUString SAL_CALL getImplementationName() override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +protected: + virtual void PrepareWindowDescriptor( css::awt::WindowDescriptor& rDesc ) override; + virtual void removingControl( const css::uno::Reference< css::awt::XControl >& _rxControl ); + virtual void addingControl( const css::uno::Reference< css::awt::XControl >& _rxControl ); + + /** ensures that the given control has a peer, if necessary and possible + @param _rxControl + an ->XControl which has just been inserted into the container. Must not be <NULL/>. + @precond + our mutex is locked + */ + virtual void impl_createControlPeerIfNecessary( + const css::uno::Reference< css::awt::XControl >& _rxControl + ); +private: + /** adds the control to the container, does necessary notifications, and the like + @param _rxControl + the control to add. Must not be <NULL/> + @param _pName + Pointer to a name for the control. Might be <NULL/>, in this case an automatic name is generated + @return + the ID of the newly added control + */ + sal_Int32 impl_addControl( + const css::uno::Reference< css::awt::XControl >& _rxControl, + const OUString* _pName = nullptr + ); + + /** removes the given control from the container, including necessary notifications and the like + @param _nId + the ID of the control to remove + @param _rxControl + the control itself. Must be the one which is stored under the given ID. This parameter could also be + obtained inside the method, but callers usually have obtained it, anyway. + */ + void impl_removeControl( + sal_Int32 _nId, + const css::uno::Reference< css::awt::XControl >& _rxControl + ); + +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/controls/unocontrolcontainermodel.hxx b/toolkit/inc/controls/unocontrolcontainermodel.hxx new file mode 100644 index 0000000000..83db8af31b --- /dev/null +++ b/toolkit/inc/controls/unocontrolcontainermodel.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + + +#include <toolkit/controls/unocontrolmodel.hxx> + + + +class UnoControlContainerModel final : public UnoControlModel +{ + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + +public: + UnoControlContainerModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ); + UnoControlContainerModel( const UnoControlContainerModel& rModel ) : UnoControlModel( rModel ) {} + + rtl::Reference<UnoControlModel> Clone() const override { return new UnoControlContainerModel( *this ); } + + // css::beans::XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/helper/accessibilityclient.hxx b/toolkit/inc/helper/accessibilityclient.hxx new file mode 100644 index 0000000000..f90414671d --- /dev/null +++ b/toolkit/inc/helper/accessibilityclient.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_INC_HELPER_ACCESSIBILITYCLIENT_HXX +#define INCLUDED_TOOLKIT_INC_HELPER_ACCESSIBILITYCLIENT_HXX + +#include <toolkit/helper/accessiblefactory.hxx> + +namespace toolkit +{ + /** a client for the accessibility implementations which have been + outsourced from the main toolkit library + + All instances of this class share a reference to a common IAccessibleFactory + instance, which is used for creating all kind of Accessibility related + components. + + When the AccessibilityClient goes away, also this factory goes away, and the respective + library is unloaded. + + This class is not thread-safe. + */ + class AccessibilityClient + { + private: + bool m_bInitialized; + + public: + AccessibilityClient(); + + IAccessibleFactory& getFactory(); + + private: + void ensureInitialized(); + }; + +} // namespace toolkit + +#endif // INCLUDED_TOOLKIT_INC_HELPER_ACCESSIBILITYCLIENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/helper/btndlg.hxx b/toolkit/inc/helper/btndlg.hxx new file mode 100644 index 0000000000..8894b8dec6 --- /dev/null +++ b/toolkit/inc/helper/btndlg.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_VCL_BTNDLG_HXX +#define INCLUDED_VCL_BTNDLG_HXX + +#include <vcl/toolkit/dialog.hxx> +#include <o3tl/typed_flags_set.hxx> + +#include <vector> +#include <memory> + +struct ImplBtnDlgItem; +class PushButton; +class Button; + +#define BUTTONDIALOG_BUTTON_NOTFOUND (sal_uInt16(0xFFFF)) + +enum class ButtonDialogFlags +{ + NONE = 0x0000, + Default = 0x0001, + OK = 0x0002, + Cancel = 0x0004, + Help = 0x0008, + Focus = 0x0010, +}; +namespace o3tl +{ + template<> struct typed_flags<ButtonDialogFlags> : is_typed_flags<ButtonDialogFlags, 0x001f> {}; +} + +class ButtonDialog : public Dialog +{ +public: + virtual ~ButtonDialog() override; + virtual void dispose() override; + + virtual void Resize() override; + virtual void StateChanged( StateChangedType nStateChange ) override; + + void SetPageSizePixel( const Size& rSize ) { maPageSize = rSize; } + + void AddButton( StandardButtonType eType, sal_uInt16 nId, ButtonDialogFlags nBtnFlags, tools::Long nSepPixel = 0 ); + void RemoveButton( sal_uInt16 nId ); + +protected: + ButtonDialog( WindowType nType ); + tools::Long ImplGetButtonSize(); + +private: + ButtonDialog( const ButtonDialog & ) = delete; + ButtonDialog& operator=( const ButtonDialog& ) = delete; + +private: + std::vector<std::unique_ptr<ImplBtnDlgItem>> m_ItemList; + Size maPageSize; + Size maCtrlSize; + tools::Long mnButtonSize; + sal_uInt16 mnCurButtonId; + sal_uInt16 mnFocusButtonId; + bool mbFormat; + + void ImplInitButtonDialogData(); + VclPtr<PushButton> ImplCreatePushButton( ButtonDialogFlags nBtnFlags ); + DECL_LINK( ImplClickHdl, Button* pBtn, void ); + void ImplPosControls(); + +}; + +#endif // INCLUDED_VCL_BTNDLG_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/helper/imagealign.hxx b/toolkit/inc/helper/imagealign.hxx new file mode 100644 index 0000000000..b3c68cd210 --- /dev/null +++ b/toolkit/inc/helper/imagealign.hxx @@ -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 . + */ + +#ifndef INCLUDED_TOOLKIT_INC_HELPER_IMAGEALIGN_HXX +#define INCLUDED_TOOLKIT_INC_HELPER_IMAGEALIGN_HXX + +#include <sal/types.h> +#include <vcl/wintypes.hxx> + +namespace toolkit +{ + + + /** translates a VCL ImageAlign value into a css.awt.ImagePosition value + */ + sal_Int16 translateImagePosition( ImageAlign _eVCLAlign ); + + /** translates a css.awt.ImagePosition value into a VCL ImageAlign + */ + ImageAlign translateImagePosition( sal_Int16 _nImagePosition ); + + /** translates a VCL ImageAlign value into a compatible css.awt.ImageAlign value + */ + sal_Int16 getCompatibleImageAlign( ImageAlign _eAlign ); + + /** translates a css.awt.ImageAlign value into a css.awt.ImagePosition value + */ + sal_Int16 getExtendedImagePosition( sal_Int16 _nImageAlign ); + + +} // namespace toolkit + + +#endif // INCLUDED_TOOLKIT_INC_HELPER_IMAGEALIGN_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/helper/msgbox.hxx b/toolkit/inc/helper/msgbox.hxx new file mode 100644 index 0000000000..7c2b26ee1e --- /dev/null +++ b/toolkit/inc/helper/msgbox.hxx @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <o3tl/typed_flags_set.hxx> +#include <helper/btndlg.hxx> +#include <vcl/toolkit/fixed.hxx> +#include <vcl/toolkit/vclmedit.hxx> + +// Window-Bits for MessageBoxen +enum class MessBoxStyle +{ + NONE = 0x0000, + Ok = 0x0001, + OkCancel = 0x0002, + YesNo = 0x0004, + YesNoCancel = 0x0008, + RetryCancel = 0x0010, + DefaultOk = 0x0020, + DefaultCancel = 0x0040, + DefaultRetry = 0x0080, + DefaultYes = 0x0100, + DefaultNo = 0x0200, + AbortRetryIgnore = 0x1000, + DefaultIgnore = 0x2000, +}; +namespace o3tl +{ +template <> struct typed_flags<MessBoxStyle> : is_typed_flags<MessBoxStyle, 0x33ff> +{ +}; +} + +class MessBox : public ButtonDialog +{ + VclPtr<VclMultiLineEdit> mpVCLMultiLineEdit; + VclPtr<FixedImage> mpFixedImage; + Image maImage; + bool mbHelpBtn; + MessBoxStyle mnMessBoxStyle; + OUString maMessText; + +protected: + void ImplInitButtons(); + void ImplPosControls(); + +public: + MessBox(vcl::Window* pParent, MessBoxStyle nMessBoxStyle, WinBits n, const OUString& rTitle, + OUString rMessage); + virtual ~MessBox() override; + virtual void dispose() override; + + virtual void StateChanged(StateChangedType nStateChange) override; + + void SetMessText(const OUString& rText) { maMessText = rText; } + const OUString& GetMessText() const { return maMessText; } + + void SetImage(const Image& rImage) { maImage = rImage; } + + virtual Size GetOptimalSize() const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/helper/property.hxx b/toolkit/inc/helper/property.hxx new file mode 100644 index 0000000000..013f734964 --- /dev/null +++ b/toolkit/inc/helper/property.hxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_HELPER_PROPERTY_HXX +#define INCLUDED_TOOLKIT_HELPER_PROPERTY_HXX + +#include <sal/types.h> +#include <rtl/ustring.hxx> + +namespace com::sun::star::uno { + class Type; + class Any; +} + + +#define BASEPROPERTY_NOTFOUND 0 + +#define BASEPROPERTY_TEXT 1 // OUString +#define BASEPROPERTY_BACKGROUNDCOLOR 2 // sal_Int32 +#define BASEPROPERTY_FILLCOLOR 3 // sal_Int32 +#define BASEPROPERTY_TEXTCOLOR 4 // sal_Int32 +#define BASEPROPERTY_LINECOLOR 5 // sal_Int32 +#define BASEPROPERTY_BORDER 6 // sal_Int16 +#define BASEPROPERTY_ALIGN 7 // sal_Int16 +#define BASEPROPERTY_FONTDESCRIPTOR 8 // css::awt::FontDescriptor +#define BASEPROPERTY_DROPDOWN 9 // sal_Bool +#define BASEPROPERTY_MULTILINE 10 // sal_Bool +#define BASEPROPERTY_STRINGITEMLIST 11 // UStringSequence +#define BASEPROPERTY_HSCROLL 12 // sal_Bool +#define BASEPROPERTY_VSCROLL 13 // sal_Bool +#define BASEPROPERTY_TABSTOP 14 // sal_Bool +#define BASEPROPERTY_STATE 15 // sal_Int16 +#define BASEPROPERTY_FONT_TYPE 16 // OLD: Font_Type +#define BASEPROPERTY_FONT_SIZE 17 // OLD: Font_Size +#define BASEPROPERTY_FONT_ATTRIBS 18 // OLD: Font_Attribs +#define BASEPROPERTY_DEFAULTCONTROL 19 // OUString (ServiceName) +#define BASEPROPERTY_LABEL 20 // OUString +#define BASEPROPERTY_LINECOUNT 21 // sal_Int16 +#define BASEPROPERTY_EXTDATEFORMAT 22 // sal_Int16 +#define BASEPROPERTY_DATESHOWCENTURY 23 // sal_Bool +#define BASEPROPERTY_EXTTIMEFORMAT 24 // sal_Int16 +#define BASEPROPERTY_NUMSHOWTHOUSANDSEP 25 // sal_Bool +#define BASEPROPERTY_CURRENCYSYMBOL 26 // OUString +#define BASEPROPERTY_SPIN 27 // sal_Bool +#define BASEPROPERTY_STRICTFORMAT 28 // sal_Bool +#define BASEPROPERTY_DECIMALACCURACY 29 // sal_Int16 +#define BASEPROPERTY_DATE 30 // css::util::Date +#define BASEPROPERTY_DATEMIN 31 // css::util::Date +#define BASEPROPERTY_DATEMAX 32 // css::util::Date +#define BASEPROPERTY_TIME 33 // css::util::Time +#define BASEPROPERTY_TIMEMIN 34 // css::util::Time +#define BASEPROPERTY_TIMEMAX 35 // css::util::Time +#define BASEPROPERTY_VALUE_INT32 36 // sal_Int32 +#define BASEPROPERTY_VALUEMIN_INT32 37 // sal_Int32 +#define BASEPROPERTY_VALUEMAX_INT32 38 // sal_Int32 +#define BASEPROPERTY_VALUESTEP_INT32 39 // sal_Int32 +#define BASEPROPERTY_EDITMASK 40 // OUString +#define BASEPROPERTY_LITERALMASK 41 // OUString +#define BASEPROPERTY_IMAGEURL 42 // OUString +#define BASEPROPERTY_READONLY 43 // sal_Bool +#define BASEPROPERTY_ENABLED 44 // sal_Bool +#define BASEPROPERTY_PRINTABLE 45 // sal_Bool +#define BASEPROPERTY_ECHOCHAR 46 // sal_Int16 +#define BASEPROPERTY_MAXTEXTLEN 47 // sal_Int16 +#define BASEPROPERTY_HARDLINEBREAKS 48 // sal_Int16 +#define BASEPROPERTY_AUTOCOMPLETE 49 // sal_Bool +#define BASEPROPERTY_MULTISELECTION 50 // sal_Bool +#define BASEPROPERTY_SELECTEDITEMS 51 // INT16Sequence +#define BASEPROPERTY_VALUE_DOUBLE 52 // DOUBLE +#define BASEPROPERTY_VALUEMIN_DOUBLE 53 // DOUBLE +#define BASEPROPERTY_VALUEMAX_DOUBLE 54 // DOUBLE +#define BASEPROPERTY_VALUESTEP_DOUBLE 55 // DOUBLE +#define BASEPROPERTY_TRISTATE 56 // sal_Bool +#define BASEPROPERTY_DEFAULTBUTTON 57 // sal_Bool +#define BASEPROPERTY_HELPURL 58 // OUString +#define BASEPROPERTY_AUTOTOGGLE 59 // sal_Bool +//#define BASEPROPERTY_FOCUSSELECTIONHIDE 60 // sal_Bool +#define BASEPROPERTY_FORMATKEY 61 // sal_Bool +#define BASEPROPERTY_FORMATSSUPPLIER 62 // css::util::XNumberFormatsSupplier +#define BASEPROPERTY_EFFECTIVE_VALUE 63 // Any (double or string) +#define BASEPROPERTY_TREATASNUMBER 64 // sal_Bool +#define BASEPROPERTY_EFFECTIVE_DEFAULT 65 // Any (double or string) +#define BASEPROPERTY_EFFECTIVE_MIN 66 // Double +#define BASEPROPERTY_EFFECTIVE_MAX 67 // Double +#define BASEPROPERTY_CURSYM_POSITION 68 // sal_Bool +#define BASEPROPERTY_TITLE 69 // OUString +#define BASEPROPERTY_MOVEABLE 70 // sal_Bool +#define BASEPROPERTY_CLOSEABLE 71 // sal_Bool +#define BASEPROPERTY_SIZEABLE 72 // sal_Bool +#define BASEPROPERTY_HELPTEXT 73 // OUString +#define BASEPROPERTY_PROGRESSVALUE 74 // sal_Int32 +#define BASEPROPERTY_PROGRESSVALUE_MIN 75 // sal_Int32 +#define BASEPROPERTY_PROGRESSVALUE_MAX 76 // sal_Int32 +#define BASEPROPERTY_SCROLLVALUE 77 // sal_Int32 +#define BASEPROPERTY_SCROLLVALUE_MAX 78 // sal_Int32 +#define BASEPROPERTY_LINEINCREMENT 79 // sal_Int32 +#define BASEPROPERTY_BLOCKINCREMENT 80 // sal_Int32 +#define BASEPROPERTY_VISIBLESIZE 81 // sal_Int32 +#define BASEPROPERTY_ORIENTATION 82 // sal_Int32 +#define BASEPROPERTY_FONTRELIEF 83 // sal_Int16 +#define BASEPROPERTY_FONTEMPHASISMARK 84 // sal_Int16 +#define BASEPROPERTY_TEXTLINECOLOR 85 // sal_Int32 +#define BASEPROPERTY_IMAGEALIGN 86 // sal_Int16 +#define BASEPROPERTY_SCALEIMAGE 87 // sal_Bool +#define BASEPROPERTY_PUSHBUTTONTYPE 88 // sal_Int16 +#define BASEPROPERTY_DISPLAYBACKGROUNDCOLOR 89 // sal_Int32 +#define BASEPROPERTY_AUTOMNEMONICS 90 // sal_Bool +#define BASEPROPERTY_MOUSETRANSPARENT 91 // sal_Bool +#define BASEPROPERTY_ACCESSIBLENAME 92 // OUString +#define BASEPROPERTY_PLUGINPARENT 93 // sal_Int64 +#define BASEPROPERTY_SCROLLVALUE_MIN 94 // sal_Int32 +#define BASEPROPERTY_REPEAT_DELAY 95 // sal_Int32 +#define BASEPROPERTY_SYMBOL_COLOR 96 // sal_Int32 +#define BASEPROPERTY_SPINVALUE 97 // sal_Int32 +#define BASEPROPERTY_SPINVALUE_MIN 98 // sal_Int32 +#define BASEPROPERTY_SPINVALUE_MAX 99 // sal_Int32 +#define BASEPROPERTY_SPININCREMENT 100 // sal_Int32 +#define BASEPROPERTY_REPEAT 101 // sal_Bool +#define BASEPROPERTY_ENFORCE_FORMAT 102 // sal_Bool +#define BASEPROPERTY_LIVE_SCROLL 103 // sal_Bool +#define BASEPROPERTY_LINE_END_FORMAT 104 // sal_Int16 +#define BASEPROPERTY_ACTIVATED 105 // sal Bool +#define BASEPROPERTY_COMPLETE 106 // sal_Bool +#define BASEPROPERTY_CURRENTITEMID 107 // sal_Int16 +#define BASEPROPERTY_TOGGLE 108 // sal_Bool +#define BASEPROPERTY_FOCUSONCLICK 109 // sal_Bool +#define BASEPROPERTY_HIDEINACTIVESELECTION 110 // sal_Bool +#define BASEPROPERTY_VISUALEFFECT 111 // sal_Int16 +#define BASEPROPERTY_BORDERCOLOR 112 // sal_Int32 +#define BASEPROPERTY_IMAGEPOSITION 113 // sal_Int16 +#define BASEPROPERTY_NATIVE_WIDGET_LOOK 114 // sal_Bool +#define BASEPROPERTY_VERTICALALIGN 115 // VerticalAlignment +#define BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR 116 // sal_Int16 +#define BASEPROPERTY_GRAPHIC 117 // css.graphic.XGraphic +#define BASEPROPERTY_STEP_TIME 118 // sal_Int32 +#define BASEPROPERTY_DECORATION 119 // sal_Bool +#define BASEPROPERTY_PAINTTRANSPARENT 120 // sal_Bool +#define BASEPROPERTY_AUTOHSCROLL 121 // sal_Bool +#define BASEPROPERTY_AUTOVSCROLL 122 // sal_Bool +#define BASEPROPERTY_DESKTOP_AS_PARENT 123 // sal_Bool +#define BASEPROPERTY_TREE_START 124 +#define BASEPROPERTY_TREE_SELECTIONTYPE 124 +#define BASEPROPERTY_TREE_EDITABLE 125 +#define BASEPROPERTY_TREE_DATAMODEL 126 +#define BASEPROPERTY_TREE_ROOTDISPLAYED 127 +#define BASEPROPERTY_TREE_SHOWSHANDLES 128 +#define BASEPROPERTY_TREE_SHOWSROOTHANDLES 129 +#define BASEPROPERTY_ROW_HEIGHT 130 +#define BASEPROPERTY_TREE_INVOKESSTOPNODEEDITING 131 +#define BASEPROPERTY_TREE_END 131 +#define BASEPROPERTY_DIALOGSOURCEURL 132 +#define BASEPROPERTY_NOLABEL 133 // OUString added for issue79712 +#define BASEPROPERTY_URL 134 // OUString +#define BASEPROPERTY_UNIT 135 // ::awt::FieldUnit +#define BASEPROPERTY_CUSTOMUNITTEXT 136 // OUString +#define BASEPROPERTY_IMAGE_SCALE_MODE 137 +#define BASEPROPERTY_WRITING_MODE 138 +#define BASEPROPERTY_CONTEXT_WRITING_MODE 139 +#define BASEPROPERTY_GRID_SHOWROWHEADER 140 +#define BASEPROPERTY_GRID_SHOWCOLUMNHEADER 141 +#define BASEPROPERTY_GRID_DATAMODEL 142 +#define BASEPROPERTY_GRID_COLUMNMODEL 143 +#define BASEPROPERTY_GRID_SELECTIONMODE 144 +#define BASEPROPERTY_ENABLEVISIBLE 145 // sal_Bool +#define BASEPROPERTY_REFERENCE_DEVICE 146 + +#define BASEPROPERTY_HIGHCONTRASTMODE 147 +#define BASEPROPERTY_GRID_HEADER_BACKGROUND 148 +#define BASEPROPERTY_GRID_HEADER_TEXT_COLOR 149 +#define BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS 150 +#define BASEPROPERTY_GRID_LINE_COLOR 151 +#define BASEPROPERTY_MULTISELECTION_SIMPLEMODE 152 +#define BASEPROPERTY_ITEM_SEPARATOR_POS 153 +#define BASEPROPERTY_GROUPNAME 154 // OUString +#define BASEPROPERTY_MULTIPAGEVALUE 155 // sal_Int32 +#define BASEPROPERTY_USERFORMCONTAINEES 156 // css::container::XNameContainer +#define BASEPROPERTY_AUTO_REPEAT 157 +#define BASEPROPERTY_ROW_HEADER_WIDTH 158 +#define BASEPROPERTY_COLUMN_HEADER_HEIGHT 159 +#define BASEPROPERTY_USE_GRID_LINES 160 +#define BASEPROPERTY_SCROLLWIDTH 161 +#define BASEPROPERTY_SCROLLHEIGHT 162 +#define BASEPROPERTY_SCROLLTOP 163 +#define BASEPROPERTY_SCROLLLEFT 164 +#define BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR 165 +#define BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR 166 +#define BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR 167 +#define BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR 168 +#define BASEPROPERTY_HIGHLIGHT_COLOR 169 +#define BASEPROPERTY_HIGHLIGHT_TEXT_COLOR 170 +#define BASEPROPERTY_TYPEDITEMLIST 171 // AnySequence + + +// These properties are not bound, they are always extracted from the BASEPROPERTY_FONTDESCRIPTOR property +#define BASEPROPERTY_FONTDESCRIPTORPART_START 1000 +#define BASEPROPERTY_FONTDESCRIPTORPART_NAME 1000 // OUString, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_STYLENAME 1001 // OUString, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_FAMILY 1002 // sal_Int16, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_CHARSET 1003 // sal_Int16, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_HEIGHT 1004 // sal_Int16, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_WEIGHT 1005 // Float, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_SLANT 1006 // sal_Int16, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_UNDERLINE 1007 // sal_Int16, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_STRIKEOUT 1008 // sal_Int16, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_WIDTH 1009 // sal_Int16, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_PITCH 1010 // sal_Int16, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_CHARWIDTH 1011 // Float, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_ORIENTATION 1012 // Float, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_KERNING 1013 // sal_Bool, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_WORDLINEMODE 1014 // sal_Bool, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_TYPE 1015 // sal_Int16, not bound +#define BASEPROPERTY_FONTDESCRIPTORPART_END 1015 + +#define PROPERTY_ALIGN_LEFT 0 +#define PROPERTY_ALIGN_CENTER 1 +#define PROPERTY_ALIGN_RIGHT 2 + + +sal_uInt16 GetPropertyId( const OUString& rPropertyName ); +const css::uno::Type* GetPropertyType( sal_uInt16 nPropertyId ); +const OUString& GetPropertyName( sal_uInt16 nPropertyId ); +sal_Int16 GetPropertyAttribs( sal_uInt16 nPropertyId ); +bool DoesDependOnOthers( sal_uInt16 nPropertyId ); +bool CompareProperties( const css::uno::Any& r1, const css::uno::Any& r2 ); + + +#endif // INCLUDED_TOOLKIT_HELPER_PROPERTY_HXX + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/helper/scrollabledialog.hxx b/toolkit/inc/helper/scrollabledialog.hxx new file mode 100644 index 0000000000..b93751c680 --- /dev/null +++ b/toolkit/inc/helper/scrollabledialog.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_AWT_SCROLLABLEDIALOG_HXX +#define INCLUDED_TOOLKIT_AWT_SCROLLABLEDIALOG_HXX + +#include <vcl/toolkit/dialog.hxx> +#include <vcl/toolkit/scrbar.hxx> + +namespace toolkit +{ + class ScrollableDialog final : public Dialog + { + public: + enum ScrollBarVisibility { None, Vert, Hori, Both }; + + private: + VclPtr<ScrollBar> maHScrollBar; + VclPtr<ScrollBar> maVScrollBar; + Size maScrollArea; + bool mbHasHoriBar; + bool mbHasVertBar; + Point mnScrollPos; + tools::Long mnScrWidth; + ScrollBarVisibility maScrollVis; + + void lcl_Scroll( tools::Long nX, tools::Long nY ); + DECL_LINK( ScrollBarHdl, ScrollBar*, void ); + + public: + ScrollableDialog( vcl::Window* pParent, WinBits nStyle, Dialog::InitFlag eFlag = Dialog::InitFlag::Default ); + virtual ~ScrollableDialog() override; + virtual void dispose() override; + // Window + virtual void Resize() override; + + void SetScrollWidth( tools::Long nWidth ); + void SetScrollHeight( tools::Long nHeight ); + void SetScrollLeft( tools::Long nLeft ); + void SetScrollTop( tools::Long Top ); + void setScrollVisibility( ScrollBarVisibility rState ); + void ResetScrollBars(); + }; + +} // namespacetoolkit + + +#endif // INCLUDED_TOOLKIT_AWT_SCROLLABLEDIALOG_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/helper/servicenames.hxx b/toolkit/inc/helper/servicenames.hxx new file mode 100644 index 0000000000..7fb7ab2a57 --- /dev/null +++ b/toolkit/inc/helper/servicenames.hxx @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +extern const char szServiceName_UnoControlDialog[]; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/helper/tkresmgr.hxx b/toolkit/inc/helper/tkresmgr.hxx new file mode 100644 index 0000000000..70cc4118c7 --- /dev/null +++ b/toolkit/inc/helper/tkresmgr.hxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_INC_HELPER_TKRESMGR_HXX +#define INCLUDED_TOOLKIT_INC_HELPER_TKRESMGR_HXX + +#include <rtl/ustring.hxx> + +class Image; + +namespace TkResMgr +{ +Image getImageFromURL(const OUString& i_rImageURL); +}; + +#endif // INCLUDED_TOOLKIT_INC_HELPER_TKRESMGR_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/helper/unopropertyarrayhelper.hxx b/toolkit/inc/helper/unopropertyarrayhelper.hxx new file mode 100644 index 0000000000..31940ec4c4 --- /dev/null +++ b/toolkit/inc/helper/unopropertyarrayhelper.hxx @@ -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 . + */ + +#ifndef INCLUDED_TOOLKIT_INC_HELPER_UNOPROPERTYARRAYHELPER_HXX +#define INCLUDED_TOOLKIT_INC_HELPER_UNOPROPERTYARRAYHELPER_HXX + +#include <cppuhelper/propshlp.hxx> + +#include <vector> +#include <o3tl/sorted_vector.hxx> + + + +class UnoPropertyArrayHelper final : public ::cppu::IPropertyArrayHelper +{ + o3tl::sorted_vector<sal_Int32> maIDs; + + bool ImplHasProperty( sal_uInt16 nPropId ) const; + +public: + UnoPropertyArrayHelper( const css::uno::Sequence<sal_Int32>& rIDs ); + UnoPropertyArrayHelper( const std::vector< sal_uInt16 > &rIDs ); + + // ::cppu::IPropertyArrayHelper + sal_Bool SAL_CALL fillPropertyMembersByHandle( OUString * pPropName, sal_Int16 * pAttributes, sal_Int32 nHandle ) override; + css::uno::Sequence< css::beans::Property > SAL_CALL getProperties() override; + css::beans::Property SAL_CALL getPropertyByName(const OUString& rPropertyName) override; + sal_Bool SAL_CALL hasPropertyByName(const OUString& rPropertyName) override; + sal_Int32 SAL_CALL getHandleByName( const OUString & rPropertyName ) override; + sal_Int32 SAL_CALL fillHandles( sal_Int32* pHandles, const css::uno::Sequence< OUString > & rPropNames ) override; +}; + + +#endif // INCLUDED_TOOLKIT_INC_HELPER_UNOPROPERTYARRAYHELPER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/inc/helper/unowrapper.hxx b/toolkit/inc/helper/unowrapper.hxx new file mode 100644 index 0000000000..dfed1b1215 --- /dev/null +++ b/toolkit/inc/helper/unowrapper.hxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_INC_HELPER_UNOWRAPPER_HXX +#define INCLUDED_TOOLKIT_INC_HELPER_UNOWRAPPER_HXX + +#include <com/sun/star/awt/XToolkit.hpp> +#include <com/sun/star/awt/XGraphics.hpp> +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> + +#include <vcl/toolkit/unowrap.hxx> +#include <vcl/window.hxx> + +#include <helper/accessibilityclient.hxx> + + + +class UnoWrapper final : public UnoWrapperBase +{ +private: + css::uno::Reference< css::awt::XToolkit> mxToolkit; + ::toolkit::AccessibilityClient maAccessibleFactoryAccess; + +public: + UnoWrapper( const css::uno::Reference< css::awt::XToolkit>& rxToolkit ); + + virtual void Destroy() override; + + // Toolkit + virtual css::uno::Reference< css::awt::XToolkit> GetVCLToolkit() override; + + // Graphics + virtual css::uno::Reference< css::awt::XGraphics> CreateGraphics( OutputDevice* pOutDev ) override; + virtual void ReleaseAllGraphics( OutputDevice* pOutDev ) override; + + // Window + virtual css::uno::Reference< css::awt::XVclWindowPeer> GetWindowInterface( vcl::Window* pWindow ) override; + virtual void SetWindowInterface( vcl::Window* pWindow, const css::uno::Reference< css::awt::XVclWindowPeer> & xIFace ) override; + virtual VclPtr<vcl::Window> GetWindow(const css::uno::Reference<css::awt::XWindow>& rxWindow) override; + + // Menu + virtual css::uno::Reference<css::awt::XPopupMenu> CreateMenuInterface( PopupMenu* pPopupMenu ) override; + + void WindowDestroyed( vcl::Window* pWindow ) override; + + // Accessibility + virtual css::uno::Reference< css::accessibility::XAccessible > + CreateAccessible( Menu* pMenu, bool bIsMenuBar ) override; + +private: + virtual ~UnoWrapper(); +}; + +#endif // INCLUDED_TOOLKIT_INC_HELPER_UNOWRAPPER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/qa/complex/toolkit/AccessibleStatusBarItem.java b/toolkit/qa/complex/toolkit/AccessibleStatusBarItem.java new file mode 100644 index 0000000000..d2c9b0f68a --- /dev/null +++ b/toolkit/qa/complex/toolkit/AccessibleStatusBarItem.java @@ -0,0 +1,359 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit; + +import java.util.logging.Logger; +import java.util.logging.Level; +import complex.toolkit.accessibility._XAccessibleEventBroadcaster; +import complex.toolkit.accessibility._XAccessibleExtendedComponent; +import complex.toolkit.accessibility._XAccessibleText; +import complex.toolkit.accessibility._XAccessibleComponent; +import complex.toolkit.accessibility._XAccessibleContext; +import util.SOfficeFactory; +import util.AccessibilityTools; +import com.sun.star.awt.XWindow; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.sheet.XSpreadsheetDocument; +import com.sun.star.text.XTextDocument; +import com.sun.star.uno.XInterface; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.XCloseable; +import com.sun.star.accessibility.AccessibleRole; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.awt.XExtendedToolkit; + + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openoffice.test.OfficeConnection; +import static org.junit.Assert.*; + +/** + * + */ +public class AccessibleStatusBarItem { + + XMultiServiceFactory xMSF = null; + XAccessibleContext testObject = null; + XWindow xWindow = null; + + /** + * Check document types + */ + @Test + public void checkDocs() + { + checkWriterDoc(); + checkMathDoc(); + checkDrawDoc(); + checkImpressDoc(); + checkCalcDoc(); + } + + private XMultiServiceFactory getMSF() + { + return UnoRuntime.queryInterface(XMultiServiceFactory.class, connection.getComponentContext().getServiceManager()); + } + + /** + * Test the interfaces on a writer document + */ + private void checkWriterDoc() { + xMSF = getMSF(); + SOfficeFactory xSOF = SOfficeFactory.getFactory(xMSF); + XTextDocument xTextDoc = null; + try { + System.out.println("****** Open a new writer document"); + xTextDoc = xSOF.createTextDoc("_blank"); + getTestObject(); + } + catch(com.sun.star.uno.Exception e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + runAllInterfaceTests(); + + if (xTextDoc != null) { + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, xTextDoc); + try { + xClose.close(false); + } + catch(com.sun.star.util.CloseVetoException e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + } + } + + /** + * Test the interfaces on a math document + */ + public void checkMathDoc() { + xMSF = getMSF(); + SOfficeFactory xSOF = SOfficeFactory.getFactory(xMSF); + XComponent xMathDoc = null; + try { + System.out.println("****** Open a new math document"); + xMathDoc = xSOF.createMathDoc("_blank"); + getTestObject(); + } + catch(com.sun.star.uno.Exception e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + runAllInterfaceTests(); + + if (xMathDoc != null) { + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, xMathDoc); + try { + xClose.close(false); + } + catch(com.sun.star.util.CloseVetoException e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + } + } + + /** + * Test the interfaces on a draw document + */ + public void checkDrawDoc() { + xMSF = getMSF(); + SOfficeFactory xSOF = SOfficeFactory.getFactory(xMSF); + XComponent xDrawDoc = null; + try { + System.out.println("****** Open a new draw document"); + xDrawDoc = xSOF.createDrawDoc("_blank"); + getTestObject(); + } + catch(com.sun.star.uno.Exception e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + runAllInterfaceTests(); + + if (xDrawDoc != null) { + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, xDrawDoc); + try { + xClose.close(false); + } + catch(com.sun.star.util.CloseVetoException e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + } + } + + /** + * Test the interfaces on an impress document + */ + public void checkImpressDoc() { + xMSF = getMSF(); + SOfficeFactory xSOF = SOfficeFactory.getFactory(xMSF); + XComponent xImpressDoc = null; + try { + System.out.println("****** Open a new impress document"); + xImpressDoc = xSOF.createImpressDoc("_blank"); + getTestObject(); + } + catch(com.sun.star.uno.Exception e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + runAllInterfaceTests(); + + if (xImpressDoc != null) { + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, xImpressDoc); + try { + xClose.close(false); + } + catch(com.sun.star.util.CloseVetoException e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + } + } + /** + * Test the interfaces on a calc document + */ + public void checkCalcDoc() { + xMSF = getMSF(); + SOfficeFactory xSOF = SOfficeFactory.getFactory(xMSF); + XSpreadsheetDocument xSpreadsheetDoc = null; + try { + System.out.println("****** Open a new calc document"); + xSpreadsheetDoc = xSOF.createCalcDoc("_blank"); + util.utils.waitForEventIdle(xMSF); + getTestObject(); + } + catch(com.sun.star.uno.Exception e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + runAllInterfaceTests(); + + if (xSpreadsheetDoc != null) { + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, xSpreadsheetDoc); + try { + xClose.close(false); + } + catch(com.sun.star.util.CloseVetoException e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + } + } + + public void getTestObject() { + try { + XInterface xIfc = (XInterface) xMSF.createInstance( + "com.sun.star.awt.Toolkit") ; + XExtendedToolkit tk = + UnoRuntime.queryInterface(XExtendedToolkit.class,xIfc); + + util.utils.waitForEventIdle(xMSF); + xWindow = UnoRuntime.queryInterface( + XWindow.class,tk.getActiveTopWindow()); + + util.utils.waitForEventIdle(xMSF); + XAccessible xRoot = AccessibilityTools.getAccessibleObject(xWindow); + XAccessibleContext parentContext = null; + + System.out.println("Get the accessible status bar."); + parentContext = AccessibilityTools.getAccessibleObjectForRole( + xRoot, AccessibleRole.STATUS_BAR, ""); + util.utils.waitForEventIdle(xMSF); + if ( parentContext == null ) { + fail("Could not create a test object."); + } + System.out.println("...OK."); + + testObject=parentContext; + } + catch(com.sun.star.uno.Exception e) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", e ); + } + catch(Throwable t) { + Logger.getLogger( this.getClass().getName() ).log( Level.SEVERE, "caught an exception", t ); + } + } + + public void runAllInterfaceTests() { + long count = testObject.getAccessibleChildCount(); + System.out.println("*****"); + System.out.println("**** Found items to test: " + count); + for (long i = 0; i < count; i++) { + System.out.println("**** Now testing StatusBarItem " + i + "."); + XAccessible object = null; + try { + object = testObject.getAccessibleChild(i); + } + catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("* Cannot get item Nr: " + i); + continue; + } + XServiceInfo xSI = UnoRuntime.queryInterface( + XServiceInfo.class,object); + String[] services = xSI.getSupportedServiceNames(); + System.out.println("* Implementation Name: " + xSI.getImplementationName()); + String accName = object.getAccessibleContext().getAccessibleName(); + System.out.println("* Accessible Name: " + accName); + for (int j=0; j<services.length; j++) + { + System.out.println("* ServiceName "+j+": "+ services[j]); + } + System.out.println("*****"); + + System.out.println("*** Now testing XAccessibleComponent ***"); + _XAccessibleComponent _xAccCompTest = + new _XAccessibleComponent(object); + assertTrue("failed: "+accName+" - XAccessibleComponent::getBounds", _xAccCompTest._getBounds()); + assertTrue("failed: "+accName+" - XAccessibleComponent::contains", _xAccCompTest._containsPoint()); + assertTrue("failed: "+accName+" - XAccessibleComponent::getAccessibleAt", _xAccCompTest._getAccessibleAtPoint()); + assertTrue("failed: "+accName+" - XAccessibleComponent::getBackground", _xAccCompTest._getBackground()); + assertTrue("failed: "+accName+" - XAccessibleComponent::getForeground", _xAccCompTest._getForeground()); + assertTrue("failed: "+accName+" - XAccessibleComponent::getLocation", _xAccCompTest._getLocation()); + assertTrue("failed: "+accName+" - XAccessibleComponent::getLocationOnScreen", _xAccCompTest._getLocationOnScreen()); + assertTrue("failed: "+accName+" - XAccessibleComponent::getSize", _xAccCompTest._getSize()); + assertTrue("failed: "+accName+" - XAccessibleComponent::grabFocus", _xAccCompTest._grabFocus()); + + System.out.println("*** Now testing XAccessibleContext ***"); + _XAccessibleContext _xAccContext = + new _XAccessibleContext(object); + assertTrue("failed: "+accName+" - XAccessibleContext::getAccessibleChildCount", _xAccContext._getAccessibleChildCount()); + assertTrue("failed: "+accName+" - XAccessibleContext::getAccessibleChild", _xAccContext._getAccessibleChild()); + assertTrue("failed: "+accName+" - XAccessibleContext::getAccessibleDescription", _xAccContext._getAccessibleDescription()); + assertTrue("failed: "+accName+" - XAccessibleContext::getAccessibleName", _xAccContext._getAccessibleName()); + assertTrue("failed: "+accName+" - XAccessibleContext::getAccessibleParent", _xAccContext._getAccessibleParent()); + assertTrue("failed: "+accName+" - XAccessibleContext::getAccessibleIndexInParent", _xAccContext._getAccessibleIndexInParent()); + assertTrue("failed: "+accName+" - XAccessibleContext::getAccessibleRelationSet", _xAccContext._getAccessibleRelationSet()); + assertTrue("failed: "+accName+" - XAccessibleContext::getAccessibleRole", _xAccContext._getAccessibleRole()); + assertTrue("failed: "+accName+" - XAccessibleContext::getAccessibleStateSet", _xAccContext._getAccessibleStateSet()); + assertTrue("failed: "+accName+" - XAccessibleContext::getLocale", _xAccContext._getLocale()); + + System.out.println("*** Now testing XAccessibleExtendedComponent ***"); + _XAccessibleExtendedComponent _xAccExtComp = + new _XAccessibleExtendedComponent(object); + assertTrue("failed: "+accName+" - XAccessibleExtendedComponent::getFont", _xAccExtComp._getFont()); + assertTrue("failed: "+accName+" - XAccessibleExtendedComponent::getTitledBorderText", _xAccExtComp._getTitledBorderText()); + assertTrue("failed: "+accName+" - XAccessibleExtendedComponent::getToolTipText", _xAccExtComp._getToolTipText()); + + System.out.println("*** Now testing XAccessibleEventBroadcaster ***"); + _XAccessibleEventBroadcaster _xAccEvBcast = + new _XAccessibleEventBroadcaster(object, xWindow); + assertTrue("failed: "+accName+" - XAccessibleEventBroadcaster::addEventListener", _xAccEvBcast._addEventListener(xMSF)); + assertTrue("failed: "+accName+" - XAccessibleEventBroadcaster::removeEventListener", _xAccEvBcast._removeEventListener(xMSF)); + + System.out.println("*** Now testing XAccessibleText ***"); + _XAccessibleText _xAccText = + new _XAccessibleText(object, xMSF, "true"); + assertTrue("failed: "+accName+" - XAccessibleText::getText", _xAccText._getText()); + assertTrue("failed: "+accName+" - XAccessibleText::getCharacterCount", _xAccText._getCharacterCount()); + assertTrue("failed: "+accName+" - XAccessibleText::getCharacterBounds", _xAccText._getCharacterBounds()); + assertTrue("failed: "+accName+" - XAccessibleText::setSelection", _xAccText._setSelection()); + assertTrue("failed: "+accName+" - XAccessibleText::copyText", _xAccText._copyText()); + assertTrue("failed: "+accName+" - XAccessibleText::getCharacter", _xAccText._getCharacter()); + assertTrue("failed: "+accName+" - XAccessibleText::getCharacterAttributes", _xAccText._getCharacterAttributes()); + assertTrue("failed: "+accName+" - XAccessibleText::getIndexAtPoint", _xAccText._getIndexAtPoint()); + assertTrue("failed: "+accName+" - XAccessibleText::getSelectedText", _xAccText._getSelectedText()); + assertTrue("failed: "+accName+" - XAccessibleText::getSelectionEnd", _xAccText._getSelectionEnd()); + assertTrue("failed: "+accName+" - XAccessibleText::getSelectionStart", _xAccText._getSelectionStart()); + assertTrue("failed: "+accName+" - XAccessibleText::getTextAtIndex", _xAccText._getTextAtIndex()); + assertTrue("failed: "+accName+" - XAccessibleText::getTextBeforeIndex", _xAccText._getTextBeforeIndex()); + assertTrue("failed: "+accName+" - XAccessibleText::getBehindIndex", _xAccText._getTextBehindIndex()); + assertTrue("failed: "+accName+" - XAccessibleText::getTextRange", _xAccText._getTextRange()); + assertTrue("failed: "+accName+" - XAccessibleText::setCaretPosition", _xAccText._setCaretPosition()); + assertTrue("failed: "+accName+" - XAccessibleText::getCaretPosition", _xAccText._getCaretPosition()); + } + } + + + + + @BeforeClass public static void setUpConnection() throws Exception { + System.out.println("setUpConnection()"); + connection.setUp(); + } + + @AfterClass public static void tearDownConnection() + throws InterruptedException, com.sun.star.uno.Exception + { + System.out.println("tearDownConnection()"); + connection.tearDown(); + } + + private static final OfficeConnection connection = new OfficeConnection(); + + +} diff --git a/toolkit/qa/complex/toolkit/Assert.java b/toolkit/qa/complex/toolkit/Assert.java new file mode 100644 index 0000000000..f7988270a8 --- /dev/null +++ b/toolkit/qa/complex/toolkit/Assert.java @@ -0,0 +1,185 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import static org.junit.Assert.*; + +/** + * provides assertion capabilities not found in {@link org.junit.Assert} + */ +public class Assert +{ + + /** invokes a given method on a given object, and assures a certain exception is caught + * @param i_message + * is the message to print when the check fails + * @param i_object + * is the object to invoke the method on + * @param i_methodName + * is the name of the method to invoke + * @param i_methodArgs + * are the arguments to pass to the method. + * @param i_argClasses + * are the classes to assume for the arguments of the methods + * @param i_expectedExceptionClass + * is the class of the exception to be caught. If this is null, + * it means that <em>no</em> exception must be throw by invoking the method. + */ + public static void assertException( final String i_message, final Object i_object, final String i_methodName, + final Class<?>[] i_argClasses, final Object[] i_methodArgs, final Class<?> i_expectedExceptionClass ) + { + Class<?> objectClass = i_object.getClass(); + + boolean noExceptionAllowed = ( i_expectedExceptionClass == null ); + + boolean caughtExpected = noExceptionAllowed; + try + { + Method method = impl_getMethod( objectClass, i_methodName, i_argClasses ); + method.invoke(i_object, i_methodArgs ); + } + catch ( NoSuchMethodException e ) + { + StringBuilder message = new StringBuilder(); + message.append( "no such method: " ).append( objectClass.getName() ).append( '.' ).append( i_methodName ).append( "( " ); + for ( int i=0; i<i_argClasses.length; ++i ) + { + message.append( i_argClasses[i].getName() ); + if ( i<i_argClasses.length - 1 ) + message.append( ", " ); + } + message.append( " )" ); + fail( message.toString() ); + } + catch ( InvocationTargetException e ) + { + caughtExpected = noExceptionAllowed + ? false + : ( e.getTargetException().getClass().equals( i_expectedExceptionClass ) ); + } + catch( Exception e ) + { + caughtExpected = false; + } + + assertTrue( i_message, caughtExpected ); + } + + /** + * retrieves a method, given by name and parameter signature, from the given class + * + * The method does somewhat more than simply calling {@link Class#getMethod}. In particular, it recognizes + * primitive parameter types, and attempts to find a method taking the given primitive type, instead of the + * type represented by the parameter class. + * + * For instance, if you have a method <code>foo( int )</code>, {@link Class#getMethod} would not return this + * method when you pass <code>Integer.class</code>. <code>impl_getMethod</code> will recognize this, and + * properly retrieve the method. + * + * Note: <code>impl_getMethod</code> is limited in that it will not try all possible combinations of primitive + * and non-primitive types. That is, a method like <code>foo( int, Integer, int )</code> is likely to not be + * found. + * + * @param i_objectClass + * @param i_methodName + * @param i_argClasses + * @return + */ + private static Method impl_getMethod( final Class<?> i_objectClass, final String i_methodName, final Class<?>[] i_argClasses ) throws NoSuchMethodException + { + try + { + return i_objectClass.getMethod( i_methodName, i_argClasses ); + } + catch ( NoSuchMethodException ex ) + { + } + + int substitutedTypes = 0; + int substitutedTypesLastRound = 0; + final Class<?>[][] substitutionTable = new Class[][] { + new Class[] { Long.class, long.class }, + new Class[] { Integer.class, int.class }, + new Class[] { Short.class, short.class }, + new Class[] { Byte.class, byte.class }, + new Class[] { Double.class, double.class }, + new Class[] { Float.class, float.class }, + new Class[] { Character.class, char.class } + }; + do + { + substitutedTypes = 0; + final Class<?>[] argClasses = new Class[ i_argClasses.length ]; + for ( int i=0; i < argClasses.length; ++i ) + { + argClasses[i] = i_argClasses[i]; + if ( substitutedTypes > substitutedTypesLastRound ) + continue; + + for ( int c=0; c<substitutionTable.length; ++c ) + { + if ( i_argClasses[i].equals( substitutionTable[c][0] ) ) + { + argClasses[i] = substitutionTable[c][1]; + ++substitutedTypes; + break; + } + } + } + if ( substitutedTypes == substitutedTypesLastRound ) + throw new NoSuchMethodException(); + substitutedTypesLastRound = substitutedTypes; + + try + { + return i_objectClass.getMethod( i_methodName, argClasses ); + } + catch ( NoSuchMethodException e ) + { + } + } + while ( substitutedTypes > 0 ); + throw new NoSuchMethodException(); + } + + /** invokes a given method on a given object, and assures a certain exception is caught + * @param i_message is the message to print when the check fails + * @param i_object is the object to invoke the method on + * @param i_methodName is the name of the method to invoke + * @param i_methodArgs are the arguments to pass to the method. Those implicitly define + * the classes of the arguments of the method which is called. + * @param i_expectedExceptionClass is the class of the exception to be caught. If this is null, + * it means that <em>no</em> exception must be throw by invoking the method. + */ + public static void assertException( final String i_message, final Object i_object, final String i_methodName, + final Object[] i_methodArgs, final Class<?> i_expectedExceptionClass ) + { + Class<?>[] argClasses = new Class[ i_methodArgs.length ]; + for ( int i=0; i<i_methodArgs.length; ++i ) + argClasses[i] = i_methodArgs[i].getClass(); + assertException( i_message, i_object, i_methodName, argClasses, i_methodArgs, i_expectedExceptionClass ); + } + + + + + +} diff --git a/toolkit/qa/complex/toolkit/GridControl.java b/toolkit/qa/complex/toolkit/GridControl.java new file mode 100644 index 0000000000..b2338ee68f --- /dev/null +++ b/toolkit/qa/complex/toolkit/GridControl.java @@ -0,0 +1,746 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit; + +import com.sun.star.awt.XControl; +import com.sun.star.awt.XControlContainer; +import com.sun.star.awt.XControlModel; +import com.sun.star.awt.XToolkit; +import com.sun.star.awt.grid.DefaultGridDataModel; +import com.sun.star.awt.grid.XGridColumn; +import com.sun.star.awt.grid.XGridColumnModel; +import com.sun.star.awt.grid.XGridControl; +import com.sun.star.awt.grid.XGridDataModel; +import com.sun.star.awt.grid.XMutableGridDataModel; +import com.sun.star.awt.grid.XSortableMutableGridDataModel; +import com.sun.star.beans.Pair; +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.ContainerEvent; +import com.sun.star.container.XContainerListener; +import com.sun.star.container.XNameContainer; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.IndexOutOfBoundsException; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.Exception; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.uno.XInterface; +import com.sun.star.util.XCloneable; +import complex.toolkit.awtgrid.DummyColumn; +import complex.toolkit.awtgrid.TMutableGridDataModel; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; +import org.openoffice.test.OfficeConnection; + +/** is a unit test for the grid control related implementations + */ +public class GridControl +{ + + public GridControl() + { + m_context = m_connection.getComponentContext(); + } + + + private static void impl_dispose( final Object... i_components ) + { + for ( int i=0; i<i_components.length; ++i ) + { + if ( i_components[i] != null ) + { + final XComponent component = UnoRuntime.queryInterface( XComponent.class, i_components[i] ); + component.dispose(); + } + } + } + + + private void impl_recreateGridModel() throws Exception + { + impl_dispose( m_gridControlModel, m_columnModel, m_dataModel ); + + // create a grid control model, and ensure it has a proper data and column model already + m_gridControlModel = UnoRuntime.queryInterface( XPropertySet.class, + createInstance( "com.sun.star.awt.grid.UnoControlGridModel" ) ); + assertNotNull( "grid control model does not provide XPropertySet interface", m_gridControlModel ); + + // ensure that the model has default column/data models + m_columnModel = UnoRuntime.queryInterface( XGridColumnModel.class, m_gridControlModel.getPropertyValue( "ColumnModel" ) ); + assertNotNull( "the control model is expected to have an initial column model", m_columnModel ); + final XGridDataModel dataModel = UnoRuntime.queryInterface( XGridDataModel.class, m_gridControlModel.getPropertyValue( "GridDataModel" ) ); + assertNotNull( "the control model is expected to have an initial data model", dataModel ); + m_dataModel = UnoRuntime.queryInterface( XSortableMutableGridDataModel.class, + dataModel ); + assertNotNull( "the out-of-the-box data model should be mutable and sortable", m_dataModel ); + } + + + @Test + public void testGridControlCloning() throws Exception + { + impl_recreateGridModel(); + + // give the test something to compare, actually + XGridColumnModel columnModel = UnoRuntime.queryInterface( XGridColumnModel.class, + m_gridControlModel.getPropertyValue( "ColumnModel" ) ); + columnModel.setDefaultColumns( 10 ); + + // clone the grid model + final XCloneable cloneable = UnoRuntime.queryInterface( XCloneable.class, m_gridControlModel ); + assertNotNull( "all UnoControlModel's are expected to be clonable", cloneable ); + + final XInterface clone = cloneable.createClone(); + final XPropertySet clonedProps = UnoRuntime.queryInterface( XPropertySet.class, clone ); + + // TODO: check all those generic properties for equality + + // the data model and the column model should have been cloned, too + // in particular, the clone should not share the sub models with the original + final XMutableGridDataModel originalDataModel = UnoRuntime.queryInterface( XMutableGridDataModel.class, + m_gridControlModel.getPropertyValue( "GridDataModel" ) ); + final XMutableGridDataModel clonedDataModel = UnoRuntime.queryInterface( XMutableGridDataModel.class, + clonedProps.getPropertyValue( "GridDataModel" ) ); + assertFalse( "data model should not be shared after cloning", UnoRuntime.areSame( originalDataModel, clonedDataModel ) ); + impl_assertEquality( originalDataModel, clonedDataModel ); + + final XGridColumnModel originalColumnModel = columnModel; + final XGridColumnModel clonedColumnModel = UnoRuntime.queryInterface( XGridColumnModel.class, + clonedProps.getPropertyValue( "ColumnModel" ) ); + assertFalse( "column model should not be shared after cloning", UnoRuntime.areSame( originalColumnModel, clonedColumnModel ) ); + impl_assertEquality( originalColumnModel, clonedColumnModel ); + } + + + @Test + public void testDisposal() throws Exception + { + impl_recreateGridModel(); + + final int columnCount = 3; + m_columnModel.setDefaultColumns( columnCount ); + + // add disposal listeners to all columns so far + final XGridColumn[] columns = m_columnModel.getColumns(); + assertEquals( "creating default columns resulted in unexpected column count", columnCount, columns.length ); + final DisposeListener[] columnListeners = new DisposeListener[columnCount]; + for ( int i=0; i<columnCount; ++i ) + columnListeners[i] = new DisposeListener( columns[i] ); + + // add another column, and check that upon removal, it is disposed + final int newColumnIndex = m_columnModel.addColumn( m_columnModel.createColumn() ); + final DisposeListener columnListener = new DisposeListener( m_columnModel.getColumn( newColumnIndex ) ); + m_columnModel.removeColumn( newColumnIndex ); + assertTrue( "explicit column removal is expected to dispose the column", columnListener.isDisposed() ); + + // by definition, the grid control model is the owner of both the column and the data model. So, setting + // a new column/data model should implicitly dispose the old models + final DisposeListener oldDataModelListener = new DisposeListener( m_dataModel ); + final DisposeListener oldColumnModelListener = new DisposeListener( m_columnModel ); + + final Object newDataModel = createInstance( "com.sun.star.awt.grid.DefaultGridDataModel" ); + final Object newColumnModel = createInstance( "com.sun.star.awt.grid.DefaultGridColumnModel" ); + final DisposeListener newDataModelListener = new DisposeListener( newDataModel ); + final DisposeListener newColumnModelListener = new DisposeListener( newColumnModel ); + + m_gridControlModel.setPropertyValue( "GridDataModel", newDataModel ); + assertTrue( "setting a new data model failed", impl_areSameInterface( newDataModel, m_gridControlModel.getPropertyValue( "GridDataModel" ) ) ); + m_gridControlModel.setPropertyValue( "ColumnModel", newColumnModel ); + assertTrue( "setting a new column model failed", impl_areSameInterface( newColumnModel, m_gridControlModel.getPropertyValue( "ColumnModel" ) ) ); + + assertTrue( "old data model has not been disposed", oldDataModelListener.isDisposed() ); + assertTrue( "old column model has not been disposed", oldColumnModelListener.isDisposed() ); + for ( int i=0; i<columnCount; ++i ) + assertTrue( "column no. " + i + " has not been disposed", columnListeners[i].isDisposed() ); + + // the same holds if the grid control model itself is disposed - it should dispose the depending models, too + assertFalse( "new data model is already disposed - this is unexpected", newDataModelListener.isDisposed() ); + assertFalse( "new column model is already disposed - this is unexpected", newColumnModelListener.isDisposed() ); + impl_dispose( m_gridControlModel ); + assertTrue( "new data model is not disposed after disposing the grid column model", newDataModelListener.isDisposed() ); + assertTrue( "new column model is not disposed after disposing the grid column model", newColumnModelListener.isDisposed() ); + } + + + /** + * tests various aspects of the <code>XMutableGridDataModel</code> interface + */ + @Test + public void testMutableGridDataModel() throws Exception + { + impl_recreateGridModel(); + + TMutableGridDataModel test = new TMutableGridDataModel( m_dataModel ); + test.testAddRow(); + test.testAddRows(); + test.testInsertRow(); + test.testInsertRows(); + test.testRemoveRow(); + test.testRemoveAllRows(); + test.testUpdateCellData(); + test.testUpdateRowData(); + test.testUpdateRowHeading(); + test.cleanup(); + + // a somewhat less straight-forward test: the data model is expected to implicitly increase its column count + // when you add a row which has more columns than currently known + final XMutableGridDataModel dataModel = DefaultGridDataModel.create( m_context ); + dataModel.addRow( 0, new Object[] { 1 } ); + assertEquals( "unexpected column count after adding the most simple row", 1, dataModel.getColumnCount() ); + dataModel.addRow( 1, new Object[] { 1, 2 } ); + assertEquals( "implicit extension of the column count doesn't work", 2, dataModel.getColumnCount() ); + } + + + @Test + public void testGridColumnModel() throws Exception + { + impl_recreateGridModel(); + + ColumnModelListener listener = new ColumnModelListener(); + m_columnModel.addContainerListener( listener ); + + // insert default columns into the previously empty model, ensure we get the right notifications + final int defaultColumnsCount = 3; + m_columnModel.setDefaultColumns( defaultColumnsCount ); + impl_assertColumnModelConsistency(); + List< ContainerEvent > events = listener.assertExclusiveInsertionEvents(); + listener.reset(); + assertEquals( "wrong number of events fired by setDefaultColumns", defaultColumnsCount, events.size() ); + for ( int i=0; i<defaultColumnsCount; ++i ) + { + final ContainerEvent event = events.get(i); + final int index = impl_assertInteger( event.Accessor ); + assertEquals( "unexpected Accessor value in insert notification", i, index ); + assertTrue( "wrong column object notified in insert notification", + impl_areSameInterface( event.Element, m_columnModel.getColumn(i) ) ); + } + + // insert some more default columns, ensure that all previously existing columns are removed + final int moreDefaultColumnsCount = 5; + m_columnModel.setDefaultColumns( moreDefaultColumnsCount ); + impl_assertColumnModelConsistency(); + assertEquals( "setting default columns is expected to remove all previously existing columns", + moreDefaultColumnsCount, m_columnModel.getColumnCount() ); + + // in this situation, both removal and insertion events have been notified + final List< ContainerEvent > removalEvents = listener.getRemovalEvents(); + final List< ContainerEvent > insertionEvents = listener.getInsertionEvents(); + listener.reset(); + + // for the removal events, check the indexes + assertEquals( "wrong number of columns removed (or notified) upon setting default columns", + defaultColumnsCount, removalEvents.size() ); + for ( int i=0; i<removalEvents.size(); ++i ) + { + final ContainerEvent event = removalEvents.get(i); + final int removedIndex = impl_assertInteger( event.Accessor ); + + // The implementation is allowed to remove the columns from the beginning, in which case the + // index of the removed column must always be 0, since e.g. the second column has index 0 + // after the first column (which previously had index 0) had been removed. + // Alternatively, the implementation is allowed to remove columns from the end, which means + // that the column index given in the event is steadily increasing. + assertTrue( "unexpected column removal event column index", + ( removedIndex == 0 ) || ( removedIndex == removalEvents.size() - 1 - i ) ); + } + + // for the insertion events, check the indexes as well + assertEquals( "wrong number of insertion events when setting default columns over existing columns", + moreDefaultColumnsCount, insertionEvents.size() ); + for ( int i=0; i<insertionEvents.size(); ++i ) + { + final ContainerEvent event = insertionEvents.get(i); + final int index = impl_assertInteger( event.Accessor ); + assertEquals( i, index ); + } + + // okay, remove all those columns + while ( m_columnModel.getColumnCount() != 0 ) + { + final int columnCount = m_columnModel.getColumnCount(); + final int removeColumnIndex = m_randomGenerator.nextInt( columnCount ); + m_columnModel.removeColumn( removeColumnIndex ); + events = listener.assertExclusiveRemovalEvents(); + listener.reset(); + assertEquals( "removing a single column should notify a single event", 1, events.size() ); + final ContainerEvent event = events.get(0); + final int removalIndex = impl_assertInteger( event.Accessor ); + assertEquals( "removing an arbitrary column does not notify the proper accessor", + removeColumnIndex, removalIndex ); + } + + // calling addColumn with a column not created by the given model/implementation should not succeed + boolean caughtExpected = false; + try + { + m_columnModel.addColumn( new DummyColumn() ); + } + catch( final com.sun.star.lang.IllegalArgumentException e ) + { + assertTrue( impl_areSameInterface( e.Context, m_columnModel ) ); + caughtExpected = true; + } + assertTrue( "adding a dummy (self-implemented) grid column to the model should not succeed", caughtExpected ); + + // adding a single column to the end should succeed, properly notify, and still be consistent + final XGridColumn newColumn = m_columnModel.createColumn(); + m_columnModel.addColumn( newColumn ); + impl_assertColumnModelConsistency(); + events = listener.assertExclusiveInsertionEvents(); + listener.reset(); + assertEquals( "addColumn notifies the wrong number of insertion events", 1, events.size() ); + final int insertionIndex = impl_assertInteger( events.get(0).Accessor ); + assertEquals( insertionIndex, newColumn.getIndex() ); + } + + + @Test + public void testDataModel() throws Exception + { + impl_recreateGridModel(); + + // ensure that getCellData and getRowData have the same opinion on the data they deliver + final Object[][] data = new Object[][] { + new Object[] { 15, 17, 0 }, + new Object[] { 9, 8, 14 }, + new Object[] { 17, 2, 16 }, + new Object[] { 0, 7, 14 }, + new Object[] { 10, 16, 16 }, + }; + m_dataModel.addRows( new Object[ data.length ], data ); + + for ( int row = 0; row < data.length; ++row ) + { + assertArrayEquals( "getRowData delivers wrong data in row " + row, data[row], m_dataModel.getRowData( row ) ); + for ( int col = 0; col < data[row].length; ++col ) + { + assertEquals( "getCellData delivers wrong data at position (" + col + ", " + row + ")", + data[row][col], m_dataModel.getCellData( col, row ) ); + } + } + } + + + @Test + public void testSortableDataModel() throws Exception + { + impl_recreateGridModel(); + + final int colCount = 3; + final int rowCount = 10; + // initialize with some data + final Object[][] data = new Object[][] { + new Object[] { 15, 17, 0 }, + new Object[] { 9, 8, 14 }, + new Object[] { 17, 2, 16 }, + new Object[] { 0, 7, 14 }, + new Object[] { 10, 16, 16 }, + new Object[] { 2, 8, 10 }, + new Object[] { 4, 8, 3 }, + new Object[] { 7, 9, 0 }, + new Object[] { 15, 6, 19 }, + new Object[] { 2, 14, 19 } + }; + final Object[] rowHeadings = new Object[] { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 + }; + // ensure consistency of the test data + assertEquals( rowHeadings.length, rowCount ); + assertEquals( data.length, rowCount ); + for ( Object[] rowData : data ) + assertEquals( rowData.length, colCount ); + + // add the test data + m_dataModel.addRows( rowHeadings, data ); + assertEquals( rowCount, m_dataModel.getRowCount() ); + assertEquals( colCount, m_dataModel.getColumnCount() ); + + // sort by each column + for ( int colIndex = 0; colIndex < colCount; ++colIndex ) + { + for ( boolean ascending : new boolean[] { true, false } ) + { + m_dataModel.sortByColumn( colIndex, ascending ); + Pair<Integer,Boolean> currentSortOrder = m_dataModel.getCurrentSortOrder(); + assertEquals( "invalid current sort column (column " + colIndex + ")", currentSortOrder.First.intValue(), colIndex ); + assertEquals( "invalid current sort direction", currentSortOrder.Second.booleanValue(), ascending ); + + /*for ( int i=0; i<rowCount; ++i ) + { + for ( int j=0; j<colCount; ++j ) + System.out.print( m_dataModel.getCellData( j, i ).toString() + ", " ); + System.out.println(); + }*/ + + // verify the data is actually sorted by this column + for ( int rowIndex = 0; rowIndex < rowCount - 1; ++rowIndex ) + { + final Object currentValue = m_dataModel.getCellData( colIndex, rowIndex ); + final int currentIntValue = impl_assertInteger( currentValue ); + final Object nextValue = m_dataModel.getCellData( colIndex, rowIndex + 1 ); + final int nextIntValue = impl_assertInteger( nextValue ); + assertTrue( "data in row " + rowIndex + " is actually not sorted " + ( ascending ? "ascending" : "descending" ), + ascending ? currentIntValue <= nextIntValue + : currentIntValue >= nextIntValue ); + + // ensure the data in the other columns, and the row headings, are sorted as well + final Object rowHeading = m_dataModel.getRowHeading( rowIndex ); + final int unsortedRowIndex = impl_assertInteger( rowHeading ); + for ( int innerColIndex = 0; innerColIndex < colCount; ++innerColIndex ) + { + assertEquals( "sorted row " + rowIndex + ", unsorted row " + unsortedRowIndex + ", col " + innerColIndex + + ": wrong data", + data[unsortedRowIndex][innerColIndex], m_dataModel.getCellData( innerColIndex, rowIndex ) ); + } + } + } + } + } + + + @Test + public void testView() throws Exception + { + final XControl control = impl_createDialogWithGridControl(); + final XPropertySet gridModelProps = + UnoRuntime.queryInterface( XPropertySet.class, control.getModel() ); + + // in the current implementation (not sure this is a good idea at all), the control (more precise: the peer) + // ensures that if there are no columns in the column model, but in the data model, then the column model + // will implicitly have the needed columns added. + // To ensure that clients which rely on this do not break in the future, check this here. + final XMutableGridDataModel dataModel = UnoRuntime.queryInterface( XMutableGridDataModel.class, + gridModelProps.getPropertyValue( "GridDataModel" ) ); + assertNotNull( dataModel ); + assertEquals( 0, dataModel.getColumnCount() ); + + final XGridColumnModel columnModel = UnoRuntime.queryInterface( XGridColumnModel.class, + gridModelProps.getPropertyValue( "ColumnModel" ) ); + assertNotNull( columnModel ); + assertEquals( 0, columnModel.getColumnCount() ); + + final int columnCount = 3; + final int rowCount = 2; + dataModel.addRow( null, new Object[] { 1, 2, 3 } ); + dataModel.addRow( null, new Object[] { 6, 5, 4 } ); + + assertEquals( columnCount, dataModel.getColumnCount() ); + assertEquals( columnCount, columnModel.getColumnCount() ); + + // some cursor traveling + final XGridControl gridControl = UnoRuntime.queryInterface( XGridControl.class, control ); + gridControl.goToCell( 0, 0 ); + assertEquals( "wrong 'current column' (1)", 0, gridControl.getCurrentColumn() ); + assertEquals( "wrong 'current row' (1)", 0, gridControl.getCurrentRow() ); + gridControl.goToCell( columnCount - 1, rowCount - 1 ); + assertEquals( "wrong 'current column' (2)", dataModel.getColumnCount() - 1, gridControl.getCurrentColumn() ); + assertEquals( "wrong 'current row' (2)", dataModel.getRowCount() - 1, gridControl.getCurrentRow() ); + + // removing the last column, while the active cell is in this very last column, is expected to adjust + // the active cell + columnModel.removeColumn( columnCount - 1 ); + assertEquals( "removed the last and active column, active column was not adjusted!", + columnCount - 2, gridControl.getCurrentColumn() ); + // same holds for rows + dataModel.removeRow( rowCount - 1 ); + assertEquals( "removed the last and active row, active row was not adjusted!", + rowCount - 2, gridControl.getCurrentRow() ); + } + + + private XControl impl_createDialogWithGridControl() throws Exception + { + // create a simple dialog model/control/peer trinity + final XControlModel dialogModel = createInstance( XControlModel.class, "com.sun.star.awt.UnoControlDialogModel" ); + m_disposables.add( dialogModel ); + final XPropertySet dialogProps = UnoRuntime.queryInterface( XPropertySet.class, dialogModel ); + dialogProps.setPropertyValue( "Width", 200 ); + dialogProps.setPropertyValue( "Height", 100 ); + dialogProps.setPropertyValue( "Title", "Grid Control Unit Test" ); + final XControl dialogControl = createInstance( XControl.class, "com.sun.star.awt.UnoControlDialog" ); + m_disposables.add( dialogControl ); + dialogControl.setModel( dialogModel ); + dialogControl.createPeer( createInstance( XToolkit.class, "com.sun.star.awt.Toolkit" ), null ); + + // insert a grid control model + final XMultiServiceFactory controlModelFactory = UnoRuntime.queryInterface( XMultiServiceFactory.class, + dialogModel ); + final XPropertySet gridModelProps = UnoRuntime.queryInterface( XPropertySet.class, + controlModelFactory.createInstance( "com.sun.star.awt.grid.UnoControlGridModel" ) ); + m_disposables.add( gridModelProps ); + gridModelProps.setPropertyValue( "PositionX", 6 ); + gridModelProps.setPropertyValue( "PositionY", 6 ); + gridModelProps.setPropertyValue( "Width", 188 ); + gridModelProps.setPropertyValue( "Height", 88 ); + final XNameContainer modelContainer = UnoRuntime.queryInterface( XNameContainer.class, dialogModel ); + modelContainer.insertByName( "grid", gridModelProps ); + + // check the respective control has been created + final XControlContainer controlContainer = UnoRuntime.queryInterface( XControlContainer.class, dialogControl ); + final XControl gridControl = controlContainer.getControl( "grid" ); + assertNotNull( "no grid control created in the dialog", gridControl ); + + return gridControl; + } + + + private int impl_assertInteger( final Object i_object ) + { + assertTrue( i_object instanceof Integer ); + return ((Integer)i_object).intValue(); + } + + + private void impl_assertColumnModelConsistency() throws IndexOutOfBoundsException + { + for ( int col = 0; col < m_columnModel.getColumnCount(); ++col ) + { + final XGridColumn column = m_columnModel.getColumn( col ); + assertNotNull( column ); + assertEquals( "column/model inconsistency: column " + col + " has a wrong index!", col, column.getIndex() ); + } + + final XGridColumn[] allColumns = m_columnModel.getColumns(); + assertEquals( "getColumns returns the wrong number of column objects", + m_columnModel.getColumnCount(), allColumns.length ); + for ( int col = 0; col < m_columnModel.getColumnCount(); ++col ) + { + assertTrue( "getColumns inconsistency", impl_areSameInterface( allColumns[col], m_columnModel.getColumn(col) ) ); + } + } + + + private void impl_assertEquality( final XGridDataModel i_reference, final XGridDataModel i_compare ) throws IndexOutOfBoundsException + { + assertNotNull( i_reference ); + assertNotNull( i_compare ); + + assertEquals( "data model comparison: wrong column counts", i_reference.getColumnCount(), i_compare.getColumnCount() ); + assertEquals( "data model comparison: wrong row counts", i_reference.getRowCount(), i_compare.getRowCount() ); + + for ( int row = 0; row < i_reference.getRowCount(); ++row ) + { + assertEquals( "data model comparison: wrong row heading content in row " + row, + i_reference.getRowHeading( row ) ); + for ( int col = 0; col < i_reference.getRowCount(); ++col ) + { + assertEquals( "data model comparison: wrong cell content in cell (" + col + ", " + row + ")", + i_reference.getCellData( col, row ) ); + assertEquals( "data model comparison: wrong tooltip content in cell (" + col + ", " + row + ")", + i_reference.getCellToolTip( col, row ) ); + } + } + } + + + private void impl_assertEquality( final XGridColumnModel i_reference, final XGridColumnModel i_compare ) throws IndexOutOfBoundsException + { + assertEquals( "column model comparison: wrong column counts", i_reference.getColumnCount(), i_compare.getColumnCount() ); + for ( int col = 0; col < i_reference.getColumnCount(); ++col ) + { + final XGridColumn referenceColumn = i_reference.getColumn( col ); + final XGridColumn compareColumn = i_compare.getColumn( col ); + impl_assertEquality( referenceColumn, compareColumn ); + } + } + + + private void impl_assertEquality( final XGridColumn i_reference, final XGridColumn i_compare ) + { + final Method[] methods = XGridColumn.class.getMethods(); + for ( int m=0; m<methods.length; ++m ) + { + if ( !methods[m].getName().startsWith( "get" ) ) + continue; + try + { + final Object referenceValue = methods[m].invoke( i_reference ); + final Object compareValue = methods[m].invoke( i_compare ); + assertEquals( "grid column comparison: column attribute '" + methods[m].getName().substring(3) + "' does not match", + referenceValue, compareValue ); + } + catch ( java.lang.Exception ex ) + { + fail( " could not retrieve object attributes: " + ex.toString() ); + } + } + } + + + private boolean impl_areSameInterface( final Object i_lhs, final Object i_rhs ) + { + final XInterface lhs = UnoRuntime.queryInterface( XInterface.class, i_lhs ); + final XInterface rhs = UnoRuntime.queryInterface( XInterface.class, i_rhs ); + return UnoRuntime.areSame( lhs, rhs ); + } + + + @Before + public void initTestCase() + { + m_disposables.clear(); + } + + + @After + public void cleanupTestCase() + { + impl_dispose( m_disposables.toArray() ); + } + + + @BeforeClass + public static void setUpConnection() throws java.lang.Exception + { + System.out.println( "--------------------------------------------------------------------------------" ); + System.out.println( "starting class: " + GridControl.class.getName() ); + System.out.print( "connecting ... " ); + m_connection.setUp(); + System.out.println( "done."); + + final long seed = m_randomGenerator.nextLong(); + m_randomGenerator.setSeed( seed ); + System.out.println( "seeding random number generator with " + seed ); + } + + + @AfterClass + public static void tearDownConnection() + throws InterruptedException, com.sun.star.uno.Exception + { + System.out.println(); + System.out.println( "tearing down connection" ); + m_connection.tearDown(); + System.out.println( "finished class: " + GridControl.class.getName() ); + System.out.println( "--------------------------------------------------------------------------------" ); + } + + + public <T> T createInstance( Class<T> i_interfaceClass, final String i_serviceIndentifier ) throws Exception + { + return UnoRuntime.queryInterface( i_interfaceClass, createInstance( i_serviceIndentifier ) ); + } + + + private Object createInstance( final String i_serviceName ) throws Exception + { + Object instance = m_context.getServiceManager().createInstanceWithContext( i_serviceName, m_context ); + assertNotNull( "could not create an instance of '" + i_serviceName + "'", instance ); + return instance; + } + + private static final class DisposeListener implements XEventListener + { + DisposeListener( final Object i_component ) + { + m_component = UnoRuntime.queryInterface( XComponent.class, i_component ); + assertNotNull( m_component ); + m_component.addEventListener( this ); + } + + public void disposing( EventObject i_event ) + { + assertTrue( UnoRuntime.areSame( i_event.Source, m_component ) ); + m_isDisposed = true; + } + + final boolean isDisposed() { return m_isDisposed; } + + private final XComponent m_component; + private boolean m_isDisposed; + } + + + private static final class ColumnModelListener implements XContainerListener + { + ColumnModelListener() + { + } + + public void elementInserted( ContainerEvent i_event ) + { + m_insertionEvents.add( i_event ); + } + + public void elementRemoved( ContainerEvent i_event ) + { + m_removalEvents.add( i_event ); + } + + public void elementReplaced( ContainerEvent i_event ) + { + m_replacementEvents.add( i_event ); + } + + public void disposing( EventObject eo ) + { + } + + private List< ContainerEvent > assertExclusiveInsertionEvents() + { + assertFalse( m_insertionEvents.isEmpty() ); + assertTrue( m_removalEvents.isEmpty() ); + assertTrue( m_replacementEvents.isEmpty() ); + return m_insertionEvents; + } + + private List< ContainerEvent > assertExclusiveRemovalEvents() + { + assertTrue( m_insertionEvents.isEmpty() ); + assertFalse( m_removalEvents.isEmpty() ); + assertTrue( m_replacementEvents.isEmpty() ); + return m_removalEvents; + } + + private void reset() + { + m_insertionEvents = new ArrayList< ContainerEvent >(); + m_removalEvents = new ArrayList< ContainerEvent >(); + m_replacementEvents = new ArrayList< ContainerEvent >(); + } + + private List< ContainerEvent > getInsertionEvents() { return m_insertionEvents; } + private List< ContainerEvent > getRemovalEvents() { return m_removalEvents; } + + private List< ContainerEvent > m_insertionEvents = new ArrayList< ContainerEvent >(); + private List< ContainerEvent > m_removalEvents = new ArrayList< ContainerEvent >(); + private List< ContainerEvent > m_replacementEvents = new ArrayList< ContainerEvent >(); + } + + + private static final OfficeConnection m_connection = new OfficeConnection(); + private static Random m_randomGenerator = new Random(); + private final XComponentContext m_context; + + private XPropertySet m_gridControlModel; + private XGridColumnModel m_columnModel; + private XSortableMutableGridDataModel m_dataModel; + private final List< Object > m_disposables = new ArrayList< Object >(); +} diff --git a/toolkit/qa/complex/toolkit/accessibility/_XAccessibleComponent.java b/toolkit/qa/complex/toolkit/accessibility/_XAccessibleComponent.java new file mode 100644 index 0000000000..e55cb1a22a --- /dev/null +++ b/toolkit/qa/complex/toolkit/accessibility/_XAccessibleComponent.java @@ -0,0 +1,463 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit.accessibility; + +import java.util.ArrayList; + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleComponent; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.awt.Point; +import com.sun.star.awt.Rectangle; +import com.sun.star.awt.Size; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; + +/** + * Testing <code>com.sun.star.accessibility.XAccessibleComponent</code> + * interface methods : + * <ul> + * <li><code> containsPoint()</code></li> + * <li><code> getAccessibleAtPoint()</code></li> + * <li><code> getBounds()</code></li> + * <li><code> getLocation()</code></li> + * <li><code> getLocationOnScreen()</code></li> + * <li><code> getSize()</code></li> + * <li><code> grabFocus()</code></li> + * </ul> <p> + * + * @see com.sun.star.accessibility.XAccessibleComponent + */ +public class _XAccessibleComponent { + + private final XAccessibleComponent oObj; + + private Rectangle bounds = null ; + + /** + * Constructor + */ + public _XAccessibleComponent(XInterface object/*, LogWriter log*/) { + oObj = UnoRuntime.queryInterface( + XAccessibleComponent.class, object); + } + + /** + * First checks 4 inner bounds (upper, lower, left and right) + * of component bounding box to contain + * at least one point of the component. Second 4 outer bounds + * are checked to not contain any component points.<p> + * + * Has <b> OK </b> status if inner bounds contain component points + * and outer bounds don't contain any component points. <p> + * + * The following method tests are to be completed successfully before : + * <ul> + * <li> <code> getBounds() </code> : to have size of a component.</li> + * </ul> + */ + public boolean _containsPoint() { + + boolean result = true ; + + int curX = 0; + while (!oObj.containsPoint(new Point(curX, 0)) && curX < bounds.Width) { + curX++; + } + if (curX < bounds.Width) { + System.out.println("Upper bound of box contains point (" + + curX + ",0) - OK"); + } else { + System.out.println + ("Upper bound of box contains no component points - FAILED"); + result = false; + } + + curX = 0; + while (!oObj.containsPoint(new Point(curX, bounds.Height - 1)) + && curX < bounds.Width) { + + System.out.println("Contains returns false for ("+curX+","+bounds.Height+")"); + curX++; + } + if (curX < bounds.Width) { + System.out.println("Lower bound of box contains point (" + + curX + "," + (bounds.Height - 1) + ") - OK"); + } else { + System.out.println + ("Lower bound of box contains no component points - FAILED"); + result = false; + } + + int curY = 0; + while (!oObj.containsPoint(new Point(0, curY)) && curY < bounds.Height) { + curY++; + } + if (curY < bounds.Height) { + System.out.println("Left bound of box contains point (0," + + curY + ") - OK"); + } else { + System.out.println + ("Left bound of box contains no component points - FAILED"); + result = false; + } + + curY = 0; + while (!oObj.containsPoint(new Point(bounds.Width - 1, curY)) && curY < bounds.Height) { + curY++; + } + if (curY < bounds.Height) { + System.out.println("Right bound of box contains point (" + + (bounds.Width - 1) + "," + curY + ") - OK"); + } else { + System.out.println + ("Right bound of box contains no component points - FAILED"); + result = false; + } + + boolean locRes = true; + for (int x = -1; x <= bounds.Width; x++) { + locRes &= !oObj.containsPoint(new Point(x, -1)); + locRes &= !oObj.containsPoint(new Point(x, bounds.Height+bounds.Y)); + } + if (locRes) { + System.out.println("Outer upper and lower bounds contain no component " + + "points - OK"); + } else { + System.out.println("Outer upper and lower bounds CONTAIN some component " + + "points - FAILED"); + result = false; + } + + locRes = true; + for (int y = -1; y <= bounds.Height; y++) { + locRes &= !oObj.containsPoint(new Point(-1, y)); + locRes &= !oObj.containsPoint(new Point(bounds.X+bounds.Width, y)); + } + if (locRes) { + System.out.println("Outer left and right bounds contain no component " + + "points - OK"); + } else { + System.out.println("Outer left and right bounds CONTAIN some component " + + "points - FAILED"); + result = false; + } + + return result; + } + + /** + * Iterates through all children which implement + * <code>XAccessibleComponent</code> (if they exist) determines their + * boundaries and tries to get each child by <code>getAccessibleAtPoint</code> + * passing point which belongs to the child. + * Also the point is checked which doesn't belong to child boundary + * box. <p> + * + * Has <b> OK </b> status if in the first cases the right children + * are returned, and in the second <code>null</code> or + * another child is returned. + */ + public boolean _getAccessibleAtPoint() { + + boolean result = true ; + XAccessibleComponent[] children = getChildrenComponents(); + + if (children.length > 0) { + for (int i = 0; i < children.length; i++) { + Rectangle chBnd = children[i].getBounds(); + if (chBnd.X == -1) + { + continue; + } + System.out.println("Checking child with bounds " + + "(" + chBnd.X + "," + chBnd.Y + "),(" + + chBnd.Width + "," + chBnd.Height + "): " + + util.AccessibilityTools.accessibleToString(children[i])); + + System.out.println("finding the point which lies on the component"); + int curX = 0; + int curY = 0; + while (!children[i].containsPoint(new Point(curX, curY)) + && curX < chBnd.Width) { + curX++; + curY++; + } + + if (curX==chBnd.Width) { + System.out.println("Couldn't find a point with contains"); + continue; + } + + // trying the point laying on child + XAccessible xAcc = oObj.getAccessibleAtPoint + (new Point(chBnd.X , chBnd.Y)); + if (xAcc == null) { + System.out.println("The child not found at point (" + + (chBnd.X ) + "," + chBnd.Y + ") - FAILED"); + result = false; + } else { + XAccessible xAccCh = UnoRuntime.queryInterface + (XAccessible.class, children[i]); + System.out.println("Child found at point (" + + (chBnd.X ) + "," + chBnd.Y + ") - OK"); + boolean res = util.AccessibilityTools.equals(xAccCh, xAcc); + if (!res) { + long expIndex = xAccCh.getAccessibleContext().getAccessibleIndexInParent(); + long gotIndex = xAcc.getAccessibleContext().getAccessibleIndexInParent(); + if (expIndex < gotIndex) { + System.out.println("The children found is not the same"); + System.out.println("The expected child " + + xAccCh.getAccessibleContext().getAccessibleName()); + System.out.println("is hidden behind the found Child "); + System.out.println(xAcc.getAccessibleContext().getAccessibleName()+" - OK"); + } else { + System.out.println("The children found is not the same - FAILED"); + System.out.println("Expected: " + +xAccCh.getAccessibleContext().getAccessibleName()); + System.out.println("Found: " + +xAcc.getAccessibleContext().getAccessibleName()); + result = false ; + } + } + } + + // trying the point NOT laying on child + xAcc = oObj.getAccessibleAtPoint + (new Point(chBnd.X - 1, chBnd.Y - 1)); + if (xAcc == null) { + System.out.println("No children found at point (" + + (chBnd.X - 1) + "," + (chBnd.Y - 1) + ") - OK"); + result &= true; + } else { + XAccessible xAccCh = UnoRuntime.queryInterface(XAccessible.class, children[i]); + boolean res = util.AccessibilityTools.equals(xAccCh, xAcc); + if (res) { + System.out.println("The same child found outside " + + "its bounds - FAILED"); + result = false ; + } + } + } + } else { + System.out.println("There are no children supporting " + + "XAccessibleComponent"); + } + + return result; + } + + /** + * Retrieves the component bounds and stores it. <p> + * + * Has <b> OK </b> status if boundary position (x,y) is not negative + * and size (Width, Height) is greater than 0. + */ + public boolean _getBounds() { + boolean result = true ; + + bounds = oObj.getBounds() ; + result &= bounds != null + && bounds.X >=0 && bounds.Y >=0 + && bounds.Width >0 && bounds.Height >0; + + System.out.println("Bounds = " + (bounds != null + ? "(" + bounds.X + "," + bounds.Y + "),(" + + bounds.Width + "," + bounds.Height + ")" : "null")); + + return result; + } + + /** + * Gets the location. <p> + * + * Has <b> OK </b> status if the location is the same as location + * of boundary obtained by <code>getBounds()</code> method. + * + * The following method tests are to be completed successfully before : + * <ul> + * <li> <code> getBounds() </code> : to have bounds </li> + * </ul> + */ + public boolean _getLocation() { + + boolean result = true ; + Point loc = oObj.getLocation() ; + + result &= loc.X == bounds.X && loc.Y == bounds.Y ; + + return result; + } + + /** + * Get the screen location of the component and its parent + * (if it exists and supports <code>XAccessibleComponent</code>). <p> + * + * Has <b> OK </b> status if component screen location equals + * to screen location of its parent plus location of the component + * relative to the parent. <p> + * + * The following method tests are to be completed successfully before : + * <ul> + * <li> <code> getBounds() </code> : to have location of the component + * relative to its parent</li> + * </ul> + */ + public boolean _getLocationOnScreen() { + + XAccessibleComponent parent = getParentComponent(); + + boolean result = true ; + Point loc = oObj.getLocationOnScreen(); + System.out.println("Location is (" + loc.X + "," + loc.Y + ")"); + + if (parent != null) { + Point parLoc = parent.getLocationOnScreen(); + System.out.println("Parent location is (" + + parLoc.X + "," + parLoc.Y + ")"); + + result &= parLoc.X + bounds.X == loc.X; + result &= parLoc.Y + bounds.Y == loc.Y; + } + + return result; + } + + /** + * Obtains the size of the component. <p> + * + * Has <b> OK </b> status if the size is the same as in bounds. <p> + * + * The following method tests are to be completed successfully before : + * <ul> + * <li> <code> getBounds() </code> </li> + * </ul> + */ + public boolean _getSize() { + + boolean result = true ; + Size size = oObj.getSize() ; + + result &= size.Width == bounds.Width; + result &= size.Height == bounds.Height; + + return result; + } + + /** + * Just calls the method. <p> + * + * Has <b> OK </b> status if no runtime exceptions occurred. + */ + public boolean _grabFocus() { + + boolean result = true ; + oObj.grabFocus() ; + + return result ; + } + + /** + * Retrieves all children (not more than 50) of the current + * component which support <code>XAccessibleComponent</code>. + * + * @return The array of children. Empty array returned if + * such children were not found or some error occurred. + */ + private XAccessibleComponent[] getChildrenComponents() { + XAccessible xAcc = UnoRuntime.queryInterface(XAccessible.class, oObj) ; + if (xAcc == null) { + System.out.println("Component doesn't support XAccessible."); + return new XAccessibleComponent[0]; + } + XAccessibleContext xAccCon = xAcc.getAccessibleContext(); + long cnt = xAccCon.getAccessibleChildCount(); + + // for cases when too many children exist checking only first 50 + if (cnt > 50) + { + cnt = 50; + } + + ArrayList<XAccessibleComponent> childComp = new ArrayList<XAccessibleComponent>(); + for (long i = 0; i < cnt; i++) { + try { + XAccessible child = xAccCon.getAccessibleChild(i); + XAccessibleContext xAccConCh = child.getAccessibleContext(); + XAccessibleComponent xChAccComp = UnoRuntime.queryInterface(XAccessibleComponent.class, xAccConCh); + if (xChAccComp != null) { + childComp.add(xChAccComp) ; + } + } catch (com.sun.star.lang.IndexOutOfBoundsException e) {} + } + + return childComp.toArray(new XAccessibleComponent[childComp.size()]); + } + + /** + * Gets the parent of the current component which support + * <code>XAccessibleComponent</code>. + * + * @return The parent or <code>null</code> if the component + * has no parent or some errors occurred. + */ + private XAccessibleComponent getParentComponent() { + XAccessible xAcc = UnoRuntime.queryInterface(XAccessible.class, oObj) ; + if (xAcc == null) { + System.out.println("Component doesn't support XAccessible."); + return null; + } + + XAccessibleContext xAccCon = xAcc.getAccessibleContext(); + XAccessible xAccPar = xAccCon.getAccessibleParent(); + + if (xAccPar == null) { + System.out.println("Component has no accessible parent."); + return null; + } + XAccessibleContext xAccConPar = xAccPar.getAccessibleContext(); + XAccessibleComponent parent = UnoRuntime.queryInterface(XAccessibleComponent.class, xAccConPar); + if (parent == null) { + System.out.println + ("Accessible parent doesn't support XAccessibleComponent"); + return null; + } + + return parent; + } + + /** + * Just calls the method. + */ + public boolean _getForeground() { + int forColor = oObj.getForeground(); + System.out.println("getForeground(): " + forColor); + return true; + } + + /** + * Just calls the method. + */ + public boolean _getBackground() { + int backColor = oObj.getBackground(); + System.out.println("getBackground(): " + backColor); + return true; + } + +} diff --git a/toolkit/qa/complex/toolkit/accessibility/_XAccessibleContext.java b/toolkit/qa/complex/toolkit/accessibility/_XAccessibleContext.java new file mode 100644 index 0000000000..645b3b03b9 --- /dev/null +++ b/toolkit/qa/complex/toolkit/accessibility/_XAccessibleContext.java @@ -0,0 +1,244 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit.accessibility; + +import com.sun.star.lang.Locale; +import com.sun.star.uno.XInterface; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.IllegalAccessibleComponentStateException; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleRelationSet; +import util.AccessibilityTools; + +/** + * Testing <code>com.sun.star.accessibility.XAccessibleContext</code> + * interface methods : + * <ul> + * <li><code> getAccessibleChildCount()</code></li> + * <li><code> getAccessibleChild()</code></li> + * <li><code> getAccessibleParent()</code></li> + * <li><code> getAccessibleIndexInParent()</code></li> + * <li><code> getAccessibleRole()</code></li> + * <li><code> getAccessibleDescription()</code></li> + * <li><code> getAccessibleName()</code></li> + * <li><code> getAccessibleRelationSet()</code></li> + * <li><code> getAccessibleStateSet()</code></li> + * <li><code> getLocale()</code></li> + * </ul> <p> + * + * @see com.sun.star.accessibility.XAccessibleContext + */ +public class _XAccessibleContext { + + private final XAccessibleContext oObj; + + private long childCount = 0; + private XAccessible parent = null ; + + public _XAccessibleContext(XInterface object) { + oObj = UnoRuntime.queryInterface(XAccessibleContext.class, object); + } + + /** + * Calls the method and stores the number of children. <p> + * Has <b> OK </b> status if non-negative number rutrned. + */ + public boolean _getAccessibleChildCount() { + childCount = oObj.getAccessibleChildCount(); + System.out.println(childCount + " children found."); + return childCount > -1; + } + + /** + * Tries to get every child and checks its parent. <p> + * + * Has <b> OK </b> status if parent of every child + * and the tested component are the same objects. + * + * The following method tests are to be completed successfully before : + * <ul> + * <li> <code> getAccessibleChildCount() </code> : to have a number of + * children </li> + * </ul> + */ + public boolean _getAccessibleChild() { + boolean bOK = true; + long counter = childCount; + if (childCount > 500) + { + counter = 500; + } + for (long i = 0; i < counter; i++) { + try { + XAccessible ch = oObj.getAccessibleChild(i) ; + XAccessibleContext chAC = ch.getAccessibleContext(); + + System.out.println(" Child " + i + ": " + + chAC.getAccessibleDescription()) ; + + if (!AccessibilityTools.equals + (chAC.getAccessibleParent().getAccessibleContext(), oObj)){ + + System.out.println("Role:"); + System.out.println("Getting: "+chAC.getAccessibleParent().getAccessibleContext().getAccessibleRole()); + System.out.println("Expected: "+oObj.getAccessibleRole()); + + System.out.println("ImplementationName:"); + System.out.println("Getting: "+util.utils.getImplName(chAC.getAccessibleParent().getAccessibleContext())); + System.out.println("Expected: "+util.utils.getImplName(oObj)); + + System.out.println("The parent of child and component "+ + "itself differ."); + System.out.println("Getting(Description): " + +chAC.getAccessibleParent().getAccessibleContext().getAccessibleDescription()); + System.out.println("Expected(Description): " + +oObj.getAccessibleDescription()); + + bOK = false; + } else { + System.out.println("Getting the expected Child -- OK"); + } + } catch (com.sun.star.lang.IndexOutOfBoundsException e) { + e.printStackTrace(); + bOK = false; + } + } + + return bOK; + } + + /** + * Just gets the parent. <p> + * + * Has <b> OK </b> status if parent is not null. + */ + public boolean _getAccessibleParent() { + // assume that the component is not ROOT + parent = oObj.getAccessibleParent(); + return parent != null; + } + + /** + * Retrieves the index of tested component in its parent. + * Then gets the parent's child by this index and compares + * it with tested component.<p> + * + * Has <b> OK </b> status if the parent's child and the tested + * component are the same objects. + * + * The following method tests are to be completed successfully before : + * <ul> + * <li> <code> getAccessibleParent() </code> : to have a parent </li> + * </ul> + */ + public boolean _getAccessibleIndexInParent() { + + boolean bOK = true; + long idx = oObj.getAccessibleIndexInParent(); + + XAccessibleContext parentAC = parent.getAccessibleContext() ; + try { + bOK &= AccessibilityTools.equals( + parentAC.getAccessibleChild(idx).getAccessibleContext(),oObj); + if (!bOK) { + System.out.println("Expected: "+util.utils.getImplName(oObj)); + System.out.println("Getting: "+util.utils.getImplName( + parentAC.getAccessibleChild(idx).getAccessibleContext())); + } + } catch (com.sun.star.lang.IndexOutOfBoundsException e) { + e.printStackTrace(); + bOK = false; + } + return bOK; + } + + /** + * Get the accessible role of component. <p> + * + * Has <b> OK </b> status if non-negative number rutrned. + */ + public boolean _getAccessibleRole() { + short role = oObj.getAccessibleRole(); + System.out.println("The role is " + role); + return role > -1; + } + + /** + * Get the accessible name of the component. <p> + * + * Has <b> OK </b> status if the name has non-zero length. + */ + public boolean _getAccessibleName() { + String name = oObj.getAccessibleName(); + System.out.println("The name is '" + name + "'"); + return name != null; + } + + /** + * Get the accessible description of the component. <p> + * + * Has <b> OK </b> status if the description has non-zero length. + */ + public boolean _getAccessibleDescription() { + String descr = oObj.getAccessibleDescription(); + System.out.println("The description is '" + descr + "'"); + return descr != null; + } + + /** + * Just gets the set. <p> + * + * Has <b> OK </b> status if the set is not null. + */ + public boolean _getAccessibleRelationSet() { + XAccessibleRelationSet set = oObj.getAccessibleRelationSet(); + return set != null; + } + + /** + * Just gets the set. <p> + * + * Has <b> OK </b> status if the set is not null. + */ + public boolean _getAccessibleStateSet() { + return true; + } + + /** + * Gets the locale. <p> + * + * Has <b> OK </b> status if <code>Country</code> and + * <code>Language</code> fields of locale structure + * are not empty. + */ + public boolean _getLocale() { + Locale loc = null ; + try { + loc = oObj.getLocale(); + System.out.println("The locale is " + loc.Language + "," + loc.Country); + } catch (IllegalAccessibleComponentStateException e) { + e.printStackTrace(); + } + + return loc != null && loc.Language.length() > 0 && + loc.Country.length() > 0; + } +} + diff --git a/toolkit/qa/complex/toolkit/accessibility/_XAccessibleEventBroadcaster.java b/toolkit/qa/complex/toolkit/accessibility/_XAccessibleEventBroadcaster.java new file mode 100644 index 0000000000..44b846615d --- /dev/null +++ b/toolkit/qa/complex/toolkit/accessibility/_XAccessibleEventBroadcaster.java @@ -0,0 +1,178 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit.accessibility; + +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.AccessibleStateType; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEventBroadcaster; +import com.sun.star.accessibility.XAccessibleEventListener; +import com.sun.star.awt.PosSize; +import com.sun.star.awt.Rectangle; +import com.sun.star.awt.XWindow; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; + +/** + * Testing <code> + * com.sun.star.accessibility.XAccessibleEventBroadcaster</code> + * interface methods : + * <ul> + * <li><code> addAccessibleEventListener()</code></li> + * <li><code> removeAccessibleEventListener()</code></li> + * </ul> + * + * <p>This test needs the following object relations :</p> + * <ul> + * <li> <code>'EventProducer'</code> (of type + * <code>ifc.accessibility._XAccessibleEventBroadcaster.EventProducer</code>): + * this must be an implementation of the interface which could perform + * some actions for generating any kind of <code>AccessibleEvent</code></li> + * </ul> + * + * @see com.sun.star.accessibility.XAccessibleEventBroadcaster + */ +public class _XAccessibleEventBroadcaster { + + private final XAccessibleEventBroadcaster oObj; + private final EventProducer prod; + private final EvListener list = new EvListener(); + + /** + * An event producer + */ + private static class EventProducer { + private final XWindow xWindow; + private EventProducer(XWindow window) { + xWindow = window; + } + + private void fireEvent() { + Rectangle newPosSize = xWindow.getPosSize(); + newPosSize.Width = newPosSize.Width - 20; + newPosSize.Height = newPosSize.Height - 20; + newPosSize.X = newPosSize.X + 20; + newPosSize.Y = newPosSize.Y + 20; + xWindow.setPosSize(newPosSize.X, newPosSize.Y, newPosSize.Width, + newPosSize.Height, PosSize.POSSIZE); + } + } + + /** + * Listener implementation which registers listener calls. + */ + private class EvListener implements XAccessibleEventListener { + public AccessibleEventObject notifiedEvent = null ; + public void notifyEvent(AccessibleEventObject ev) { + System.out.println("Listener, Event : " + ev.EventId); + System.out.println("EventID: " + ev.EventId); + Object old=ev.OldValue; + if (old instanceof com.sun.star.accessibility.XAccessible) { + System.out.println("Old: "+((XAccessible)old).getAccessibleContext().getAccessibleName()); + } + + Object nev=ev.NewValue; + if (nev instanceof com.sun.star.accessibility.XAccessible) { + System.out.println("New: "+((XAccessible)nev).getAccessibleContext().getAccessibleName()); + } + notifiedEvent = ev; + } + + public void disposing(EventObject ev) {} + } + + public _XAccessibleEventBroadcaster(XInterface object, XWindow window) { + oObj = UnoRuntime.queryInterface(XAccessibleEventBroadcaster.class, object); + prod = new EventProducer(window); + } + + /** + * Adds two listeners and fires event by mean of object relation. <p> + * Has <b> OK </b> status if both listeners were called + */ + public boolean _addEventListener(XMultiServiceFactory xMSF) { + System.out.println("adding two listeners"); + oObj.addAccessibleEventListener(list); + boolean isTransient = chkTransient(oObj); + System.out.println("fire event"); + prod.fireEvent() ; + + util.utils.waitForEventIdle(xMSF); + + boolean works = true; + + if (list.notifiedEvent == null) { + if (!isTransient) { + System.out.println("listener wasn't called"); + works = false; + } else { + System.out.println("Object is Transient, listener isn't expected to be called"); + } + oObj.removeAccessibleEventListener(list); + } + + return works; + } + + /** + * Removes one of two listeners added before and fires event + * by mean of object relation. <p> + * + * Has <b> OK </b> status if the removed listener wasn't called. <p> + * + * The following method tests are to be completed successfully before : + * <ul> + * <li> <code>addEventListener()</code> : to have added listeners </li> + * </ul> + */ + public boolean _removeEventListener(XMultiServiceFactory xMSF) { + + list.notifiedEvent = null; + + System.out.println("remove first listener"); + oObj.removeAccessibleEventListener(list); + + System.out.println("fire event"); + prod.fireEvent() ; + + util.utils.waitForEventIdle(xMSF); + + if (list.notifiedEvent == null) { + System.out.println("listener wasn't called -- OK"); + } + + return list.notifiedEvent == null; + + } + + private static boolean chkTransient(Object Testcase) { + XAccessibleContext accCon = UnoRuntime.queryInterface(XAccessibleContext.class, Testcase); + long nStateSet = accCon.getAccessibleStateSet(); + if ((nStateSet & AccessibleStateType.TRANSIENT) == 0) + return false; + long nParentStateSet = accCon.getAccessibleParent().getAccessibleContext() + .getAccessibleStateSet(); + return (nParentStateSet & AccessibleStateType.MANAGES_DESCENDANTS) != 0; + } + +} + diff --git a/toolkit/qa/complex/toolkit/accessibility/_XAccessibleExtendedComponent.java b/toolkit/qa/complex/toolkit/accessibility/_XAccessibleExtendedComponent.java new file mode 100644 index 0000000000..64dd66fcb7 --- /dev/null +++ b/toolkit/qa/complex/toolkit/accessibility/_XAccessibleExtendedComponent.java @@ -0,0 +1,75 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit.accessibility; + +import com.sun.star.accessibility.XAccessibleExtendedComponent; +import com.sun.star.awt.XFont; +import com.sun.star.uno.XInterface; +import com.sun.star.uno.UnoRuntime; + +/** + * Testing <code>com.sun.star.accessibility.XAccessibleExtendedComponent</code> + * interface methods : + * <ul> + * <li><code> getForeground()</code></li> + * <li><code> getBackground()</code></li> + * <li><code> getFont()</code></li> + * <li><code> isEnabled()</code></li> + * <li><code> getTitledBorderText()</code></li> + * <li><code> getToolTipText()</code></li> + * </ul> <p> + * @see com.sun.star.accessibility.XAccessibleExtendedComponent + */ +public class _XAccessibleExtendedComponent { + + private final XAccessibleExtendedComponent oObj; + + public _XAccessibleExtendedComponent(XInterface object/*, LogWriter log*/) { + oObj = UnoRuntime.queryInterface(XAccessibleExtendedComponent.class, object); + } + + /** + * Just calls the method. + */ + public boolean _getFont() { + XFont font = oObj.getFont(); + System.out.println("getFont(): " + font); + return true; + } + + /** + * Calls the method and checks returned value. + * Has OK status if returned value isn't null. + */ + public boolean _getTitledBorderText() { + String titleBorderText = oObj.getTitledBorderText(); + System.out.println("getTitledBorderText(): '" + titleBorderText + "'"); + return titleBorderText != null; + } + + /** + * Calls the method and checks returned value. + * Has OK status if returned value isn't null. + */ + public boolean _getToolTipText() { + String toolTipText = oObj.getToolTipText(); + System.out.println("getToolTipText(): '" + toolTipText + "'"); + return toolTipText != null; + } +} diff --git a/toolkit/qa/complex/toolkit/accessibility/_XAccessibleText.java b/toolkit/qa/complex/toolkit/accessibility/_XAccessibleText.java new file mode 100644 index 0000000000..1cd97d63ab --- /dev/null +++ b/toolkit/qa/complex/toolkit/accessibility/_XAccessibleText.java @@ -0,0 +1,1001 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit.accessibility; + +import com.sun.star.accessibility.XAccessibleText; +import com.sun.star.beans.PropertyValue; +import com.sun.star.awt.Rectangle; +import com.sun.star.awt.Point; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.accessibility.AccessibleTextType; +import com.sun.star.accessibility.TextSegment; +import com.sun.star.beans.UnknownPropertyException; +import com.sun.star.uno.XInterface; +import com.sun.star.uno.UnoRuntime; + +/** + * Testing <code>com.sun.star.accessibility.XAccessibleText</code> + * interface methods : + * <ul> + * <li><code> getCaretPosition()</code></li> + * <li><code> setCaretPosition()</code></li> + * <li><code> getCharacter()</code></li> + * <li><code> getCharacterAttributes()</code></li> + * <li><code> getCharacterBounds()</code></li> + * <li><code> getCharacterCount()</code></li> + * <li><code> getIndexAtPoint()</code></li> + * <li><code> getSelectedText()</code></li> + * <li><code> getSelectionStart()</code></li> + * <li><code> getSelectionEnd()</code></li> + * <li><code> setSelection()</code></li> + * <li><code> getText()</code></li> + * <li><code> getTextRange()</code></li> + * <li><code> getTextAtIndex()</code></li> + * <li><code> getTextBeforeIndex()</code></li> + * <li><code> getTextBehindIndex()</code></li> + * <li><code> copyText()</code></li> + * </ul> + * <p>This test needs the following object relations :</p> + * <ul> + * <li> <code>'XAccessibleText.Text'</code> (of type <code>String</code>) + * <b> optional </b> : + * the string presentation of component's text. If the relation + * is not specified, then text from method <code>getText()</code> + * is used. + * </li> + * </ul> + * @see com.sun.star.accessibility.XAccessibleText + */ +public class _XAccessibleText { + + private final XAccessibleText oObj; + private final XMultiServiceFactory xMSF; + + private Rectangle chBounds = null; + private int chCount = 0; + + private String text = null; + private final String editOnly; + + + public _XAccessibleText(XInterface object, XMultiServiceFactory xMSF, String editOnly) { + this.oObj = UnoRuntime.queryInterface(XAccessibleText.class, object); + this.xMSF = xMSF; + this.editOnly = editOnly; + } + + + /** + * Calls the method and checks returned value. + * Has OK status if returned value is equal to <code>chCount - 1</code>. + * The following method tests are to be executed before: + * <ul> + * <li> <code>setCaretPosition()</code> </li> + * </ul> + */ + public boolean _getCaretPosition() { + + if (editOnly != null) { + System.out.println(editOnly); + return true; + } + + boolean res = true; + if ( chCount > 0 ) { + try { + oObj.setCaretPosition(chCount - 1); + } catch (com.sun.star.lang.IndexOutOfBoundsException ie) { + + } + int carPos = oObj.getCaretPosition(); + System.out.println("getCaretPosition: " + carPos); + res = carPos == (chCount - 1); + } + return res; + } + + /** + * Calls the method with the wrong index and with the correct index + * <code>chCount - 1</code>. + * Has OK status if exception was thrown for wrong index and + * if exception wasn't thrown for the correct index. + * The following method tests are to be executed before: + * <ul> + * <li> <code>getCharacterCount()</code> </li> + * </ul> + */ + public boolean _setCaretPosition() { + boolean res = true; + + try { + System.out.println("setCaretPosition(-1):"); + oObj.setCaretPosition(-1); + res &= false; + System.out.println("exception was expected"); + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("expected exception"); + res &= true; + } + + try { + System.out.println("setCaretPosition(chCount+1):"); + oObj.setCaretPosition(chCount+1); + res &= false; + System.out.println("exception was expected"); + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("expected exception"); + res &= true; + } + if ( chCount > 0 ) { + try { + System.out.println("setCaretPosition(chCount - 1)"); + oObj.setCaretPosition(chCount - 1); + res &= true; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("unexpected exception"); + e.printStackTrace(); + res &= false; + } + } + + return res; + } + + /** + * Calls the method with the wrong index and with the correct indexes. + * Checks every character in the text. + * Has OK status if exception was thrown for wrong index, + * if exception wasn't thrown for the correct index and + * if every character is equal to corresponding character in the text. + * The following method tests are to be executed before: + * <ul> + * <li> <code>getCharacterCount()</code> </li> + * </ul> + */ + public boolean _getCharacter() { + boolean res = true; + + try { + System.out.println("getCharacter(-1)"); + oObj.getCharacter(-1); + System.out.println("Exception was expected"); + res = false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res = true; + } + + try { + System.out.println("getCharacter(chCount)"); + oObj.getCharacter(chCount); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } + + try { + System.out.println("Checking of every character in the text..."); + boolean isEqCh = true; + for(int i = 0; i < chCount; i++) { + char ch = oObj.getCharacter(i); + isEqCh = ch == text.charAt(i); + res &= isEqCh; + if (!isEqCh) { + System.out.println("At the position " + i + + "was expected character: " + text.charAt(i)); + System.out.println("but was returned: " + ch); + break; + } + } + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } + + return res; + } + + /** + * Calls the method with the wrong indexes and with the correct index, + * checks a returned value. + * Has OK status if exception was thrown for the wrong indexes, + * if exception wasn't thrown for the correct index and + * if returned value isn't <code>null</code>. + * The following method tests are to be executed before: + * <ul> + * <li> <code>getCharacterCount()</code> </li> + * </ul> + */ + public boolean _getCharacterAttributes() { + boolean res = true; + + try { + System.out.println("getCharacterAttributes(-1)"); + oObj.getCharacterAttributes(-1, new String[0]); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } catch(UnknownPropertyException e) { + System.out.println("unexpected exception => FAILED"); + res &= false; + } + + try { + System.out.println("getCharacterAttributes(chCount)"); + oObj.getCharacterAttributes(chCount, new String[0]); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } catch(UnknownPropertyException e) { + System.out.println("unexpected exception => FAILED"); + res &= false; + } + + try { + if ( chCount > 0 ) { + System.out.println("getCharacterAttributes(chCount-1)"); + PropertyValue[] props = oObj.getCharacterAttributes(chCount - 1, new String[0]); + res &= props != null; + } + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } catch(UnknownPropertyException e) { + System.out.println("unexpected exception => FAILED"); + res &= false; + } + + return res; + } + + + /** + * Calls the method with the wrong indexes and with the correct index. + * checks and stores a returned value. + * Has OK status if exception was thrown for the wrong indexes, + * if exception wasn't thrown for the correct index and + * if returned value isn't <code>null</code>. + * The following method tests are to be executed before: + * <ul> + * <li> <code>getCharacterCount()</code> </li> + * </ul> + */ + public boolean _getCharacterBounds() { + boolean res = true; + + try { + System.out.println("getCharacterBounds(-1)"); + oObj.getCharacterBounds(-1); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } + + try { + System.out.println("getCharacterBounds(chCount)"); + oObj.getCharacterBounds(chCount); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } + + try { + if (chCount > 0) { + System.out.println("getCharacterBounds(chCount-1)"); + chBounds = oObj.getCharacterBounds(chCount-1); + res &= chBounds != null; + System.out.println("rect: " + chBounds.X + ", " + chBounds.Y + ", " + + chBounds.Width + ", " + chBounds.Height); + } + + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } + + return res; + } + + + /** + * Calls the method and stores a returned value to the variable + * <code>chCount</code>. + * Has OK status if a returned value is equal to the text length. + */ + public boolean _getCharacterCount() { + chCount = oObj.getCharacterCount(); + System.out.println("Character count:" + chCount); + boolean res = chCount == text.length(); + return res; + } + + /** + * Calls the method for an invalid point and for the point of rectangle + * returned by the method <code>getCharacterBounds()</code>. + * Has OK status if returned value is equal to <code>-1</code> for an + * invalid point and if returned value is equal to <code>chCount-1</code> + * for a valid point. + * The following method tests are to be executed before: + * <ul> + * <li> <code>getCharacterBounds()</code> </li> + * </ul> + */ + public boolean _getIndexAtPoint() { + + boolean res = true; + System.out.println("getIndexAtPoint(-1, -1):"); + Point pt = new Point(-1, -1); + int index = oObj.getIndexAtPoint(pt); + System.out.println(Integer.toString(index)); + res &= index == -1; + + if (chBounds != null) { + pt = new Point(chBounds.X , chBounds.Y ); + System.out.println("getIndexAtPoint(" + pt.X + ", " + pt.Y + "):"); + index = oObj.getIndexAtPoint(pt); + System.out.println(Integer.toString(index)); + res &= index == (chCount - 1); + } + + return res; + } + + /** + * Checks a returned values after different calls of the method + * <code>setSelection()</code>. + * The following method tests are to be executed before: + * <ul> + * <li> <code>setSelection()</code> </li> + * </ul> + */ + public boolean _getSelectedText() { + if (editOnly != null) { + System.out.println(editOnly); + return true; + } + + boolean res = true; + + try { + System.out.println("setSelection(0, 0)"); + oObj.setSelection(0, 0); + System.out.println("getSelectedText():"); + String txt = oObj.getSelectedText(); + System.out.println("'" + txt + "'"); + res &= txt.length() == 0; + + System.out.println("setSelection(0, chCount)"); + oObj.setSelection(0, chCount); + System.out.println("getSelectedText():"); + txt = oObj.getSelectedText(); + System.out.println("'" + txt + "'"); + res &= txt.equals(text); + + if (chCount > 2) { + System.out.println("setSelection(1, chCount-1)"); + oObj.setSelection(1, chCount - 1); + System.out.println("getSelectedText():"); + txt = oObj.getSelectedText(); + System.out.println("'" + txt + "'"); + res &= txt.equals(text.substring(1, chCount - 1)); + } + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } + + return res; + } + + /** + * Checks a returned values after different calls of the method + * <code>setSelection()</code>. + * The following method tests are to be executed before: + * <ul> + * <li> <code>setSelection()</code> </li> + * </ul> + */ + public boolean _getSelectionStart() { + if (editOnly != null) { + System.out.println(editOnly); + return true; + } + + boolean res = true; + + try { + System.out.println("setSelection(0, chCount)"); + oObj.setSelection(0, chCount); + int start = oObj.getSelectionStart(); + System.out.println("getSelectionStart():" + start); + res &= start == 0; + + if (chCount > 2) { + System.out.println("setSelection(1, chCount-1)"); + oObj.setSelection(1, chCount - 1); + start = oObj.getSelectionStart(); + System.out.println("getSelectionStart():" + start); + res &= start == 1; + } + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } + + return res; + } + + /** + * Checks a returned values after different calls of the method + * <code>setSelection()</code>. + * The following method tests are to be executed before: + * <ul> + * <li> <code>setSelection()</code> </li> + * </ul> + */ + public boolean _getSelectionEnd() { + if (editOnly != null) { + System.out.println(editOnly); + return true; + } + + boolean res = true; + + try { + System.out.println("setSelection(0, chCount)"); + oObj.setSelection(0, chCount); + int end = oObj.getSelectionEnd(); + System.out.println("getSelectionEnd():" + end); + res &= end == chCount; + + if (chCount > 2) { + System.out.println("setSelection(1, chCount-1)"); + oObj.setSelection(1, chCount - 1); + end = oObj.getSelectionEnd(); + System.out.println("getSelectionEnd():" + end); + res &= end == chCount - 1; + } + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } + + return res; + } + + /** + * Calls the method with invalid parameters and with valid parameters. + * Has OK status if exception was thrown for invalid parameters, + * if exception wasn't thrown for valid parameters. + * The following method tests are to be executed before: + * <ul> + * <li> <code>getCharacterCount()</code> </li> + * </ul> + */ + public boolean _setSelection() { + boolean res = true; + boolean locRes = true; + + if (editOnly != null) { + System.out.println(editOnly); + return true; + } + + try { + System.out.println("setSelection(-1, chCount-1):"); + locRes = oObj.setSelection(-1, chCount - 1); + System.out.println(locRes + " exception was expected"); + res &= !locRes; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } + + try { + System.out.println("setSelection(0, chCount+1):"); + locRes = oObj.setSelection(0, chCount + 1); + System.out.println(locRes + " exception was expected"); + res &= !locRes; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } + + try { + if (chCount > 2) { + System.out.println("setSelection(1, chCount-1):"); + locRes = oObj.setSelection(1, chCount - 1); + System.out.println(Boolean.toString(locRes)); + res &= locRes; + + System.out.println("setSelection(chCount-1, 1):"); + locRes = oObj.setSelection(chCount - 1, 1); + System.out.println(Boolean.toString(locRes)); + res &= locRes; + } + + if (chCount > 1) { + System.out.println("setSelection(0, chCount-1):"); + locRes = oObj.setSelection(0, chCount-1); + System.out.println(Boolean.toString(locRes)); + res &= locRes; + + System.out.println("setSelection(chCount-1, 0):"); + locRes = oObj.setSelection(chCount-1, 0); + System.out.println(Boolean.toString(locRes)); + res &= locRes; + } + + System.out.println("setSelection(0, 0):"); + locRes = oObj.setSelection(0, 0); + System.out.println(Boolean.toString(locRes)); + res &= locRes; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } + + return res; + } + + /** + * Calls the method and checks returned value. + * Has OK status if returned string is not null + * received from relation. + */ + public boolean _getText() { + text = oObj.getText(); + System.out.println("getText: '" + text + "'"); + return (text != null); + } + + /** + * Calls the method with invalid parameters and with valid parameters, + * checks returned values. + * Has OK status if exception was thrown for invalid parameters, + * if exception wasn't thrown for valid parameters and if returned values + * are equal to corresponding substrings of the text received by relation. + * The following method tests are to be executed before: + * <ul> + * <li> <code>getCharacterCount()</code> </li> + * </ul> + */ + public boolean _getTextRange() { + boolean res = true; + boolean locRes = true; + + try { + if (chCount > 3) { + System.out.println("getTextRange(1, chCount - 2): "); + String txtRange = oObj.getTextRange(1, chCount - 2); + System.out.println(txtRange); + locRes = txtRange.equals(text.substring(1, chCount - 2)); + res &= locRes; + if (!locRes) { + System.out.println("Was expected: " + + text.substring(1, chCount - 2)); + } + } + + if (chCount > 0) { + System.out.println("getTextRange(0, chCount-1): "); + String txtRange = oObj.getTextRange(0, chCount-1); + System.out.println(txtRange); + locRes = txtRange.equals(text.substring(0, chCount - 1)); + res &= locRes; + if (!locRes) { + System.out.println("Was expected: " + + text.substring(0, chCount - 1)); + } + + System.out.println("getTextRange(chCount, 0): "); + txtRange = oObj.getTextRange(chCount, 0); + System.out.println(txtRange); + res &= txtRange.equals(text); + + System.out.println("getTextRange(0, 0): "); + txtRange = oObj.getTextRange(0, 0); + System.out.println(txtRange); + locRes = txtRange.length() == 0; + res &= locRes; + if (!locRes) { + System.out.println("Empty string was expected"); + } + } + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } + + try { + System.out.println("getTextRange(-1, chCount - 1): "); + oObj.getTextRange(-1, chCount - 1); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } + + try { + System.out.println("getTextRange(0, chCount + 1): "); + oObj.getTextRange(0, chCount + 1); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } + + try { + System.out.println("getTextRange(chCount+1, -1): "); + oObj.getTextRange(chCount+1, -1); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } + + return res; + } + + /** + * Calls the method with invalid parameters and with valid parameters, + * checks returned values. + * Has OK status if exception was thrown for invalid parameters, + * if exception wasn't thrown for valid parameters and if returned values + * are equal to corresponding substrings of the text received by relation. + * The following method tests are to be executed before: + * <ul> + * <li> <code>getCharacterCount()</code> </li> + * </ul> + */ + public boolean _getTextAtIndex() { + boolean res = true; + + try { + System.out.println("getTextAtIndex(-1, AccessibleTextType.PARAGRAPH):"); + oObj.getTextAtIndex(-1, AccessibleTextType.PARAGRAPH); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } catch(com.sun.star.lang.IllegalArgumentException e) { + System.out.println("Expected exception"); + res &= true; + } + + try { + System.out.println("getTextAtIndex(chCount+1," + + " AccessibleTextType.PARAGRAPH):"); + oObj.getTextAtIndex(chCount + 1, AccessibleTextType.PARAGRAPH); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } catch(com.sun.star.lang.IllegalArgumentException e) { + System.out.println("Expected exception"); + res &= true; + } + + + try { + if ( chCount > 0 ) { + System.out.println("getTextAtIndex(chCount," + + " AccessibleTextType.PARAGRAPH):"); + TextSegment txt = oObj.getTextAtIndex(chCount, + AccessibleTextType.PARAGRAPH); + System.out.println("'" + txt.SegmentText + "'"); + res &= txt.SegmentText.length() == 0; + + System.out.println("getTextAtIndex(1," + + " AccessibleTextType.PARAGRAPH):"); + txt = oObj.getTextAtIndex(1, + AccessibleTextType.PARAGRAPH); + System.out.println("'" + txt.SegmentText + "'"); + res &= txt.SegmentText.equals(text); + } + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } catch(com.sun.star.lang.IllegalArgumentException e) { + System.out.println("Unexpected exception"); + res &= false; + } + + + return res; + } + + /** + * Calls the method with invalid parameters and with valid parameters, + * checks returned values. + * Has OK status if exception was thrown for invalid parameters, + * if exception wasn't thrown for valid parameters and if returned values + * are equal to corresponding substrings of the text received by relation. + * The following method tests are to be executed before: + * <ul> + * <li> <code>getCharacterCount()</code> </li> + * </ul> + */ + public boolean _getTextBeforeIndex() { + boolean res = true; + + try { + System.out.println("getTextBeforeIndex(-1, AccessibleTextType.PARAGRAPH):"); + oObj.getTextBeforeIndex(-1, AccessibleTextType.PARAGRAPH); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } catch(com.sun.star.lang.IllegalArgumentException e) { + System.out.println("Expected exception"); + res &= true; + } + + + try { + System.out.println("getTextBeforeIndex(chCount+1, " + + "AccessibleTextType.PARAGRAPH):"); + oObj.getTextBeforeIndex(chCount + 1, AccessibleTextType.PARAGRAPH); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } catch(com.sun.star.lang.IllegalArgumentException e) { + System.out.println("Expected exception"); + res &= true; + } + + TextSegment txt = null; + try { + if (chCount > 0) { + System.out.println("getTextBeforeIndex(chCount," + + " AccessibleTextType.PARAGRAPH):"); + txt = oObj.getTextBeforeIndex(chCount, + AccessibleTextType.PARAGRAPH); + System.out.println("'" + txt.SegmentText + "'"); + res &= txt.SegmentText.length() == chCount ; + + System.out.println("getTextBeforeIndex(1," + + " AccessibleTextType.PARAGRAPH):"); + txt = oObj.getTextBeforeIndex(1, + AccessibleTextType.PARAGRAPH); + System.out.println("'" + txt.SegmentText + "'"); + res &= txt.SegmentText.length() == 0; + } + + if (chCount > 2) { + System.out.println("getTextBeforeIndex(chCount-1," + + " AccessibleTextType.CHARACTER):"); + txt = oObj.getTextBeforeIndex(chCount - 1, + AccessibleTextType.CHARACTER); + System.out.println("'" + txt.SegmentText + "'"); + res &= txt.SegmentText.equals(text.substring(chCount - 2, chCount - 1)); + System.out.println("getTextBeforeIndex(2," + + " AccessibleTextType.CHARACTER):"); + txt = oObj.getTextBeforeIndex(2, + AccessibleTextType.CHARACTER); + System.out.println("'" + txt.SegmentText + "'"); + res &= txt.SegmentText.equals(text.substring(1, 2)); + } + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } catch(com.sun.star.lang.IllegalArgumentException e) { + System.out.println("Unexpected exception"); + res &= false; + } + + + return res; + } + + /** + * Calls the method with invalid parameters and with valid parameters, + * checks returned values. + * Has OK status if exception was thrown for invalid parameters, + * if exception wasn't thrown for valid parameters and if returned values + * are equal to corresponding substrings of the text received by relation. + * The following method tests are to be executed before: + * <ul> + * <li> <code>getCharacterCount()</code> </li> + * </ul> + */ + public boolean _getTextBehindIndex() { + boolean res = true; + + try { + System.out.println("getTextBehindIndex(-1, AccessibleTextType.PARAGRAPH):"); + oObj.getTextBehindIndex(-1, AccessibleTextType.PARAGRAPH); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } catch(com.sun.star.lang.IllegalArgumentException e) { + System.out.println("Expected exception"); + res &= true; + } + + + try { + System.out.println("getTextBehindIndex(chCount+1, " + + "AccessibleTextType.PARAGRAPH):"); + oObj.getTextBehindIndex(chCount + 1, AccessibleTextType.PARAGRAPH); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } catch(com.sun.star.lang.IllegalArgumentException e) { + System.out.println("Expected exception"); + res &= true; + } + + + try { + if ( chCount > 0 ) { + System.out.println("getTextBehindIndex(chCount," + + " AccessibleTextType.PARAGRAPH):"); + TextSegment txt = oObj.getTextBehindIndex(chCount, + AccessibleTextType.PARAGRAPH); + System.out.println("'" + txt.SegmentText + "'"); + res &= txt.SegmentText.length() == 0; + + System.out.println("getTextBehindIndex(chCount-1," + + " AccessibleTextType.PARAGRAPH):"); + txt = oObj.getTextBehindIndex(chCount - 1, + AccessibleTextType.PARAGRAPH); + System.out.println("'" + txt.SegmentText + "'"); + res &= txt.SegmentText.length() == 0; + } + if ( chCount > 1 ) { + System.out.println("getTextBehindIndex(1," + + " AccessibleTextType.CHARACTER):"); + TextSegment txt = oObj.getTextBehindIndex(1, + AccessibleTextType.CHARACTER); + System.out.println("'" + txt.SegmentText + "'"); + res &= txt.SegmentText.equals(text.substring(2, 3)); + } + if (chCount > 2) { + System.out.println("getTextBehindIndex(chCount-2," + + " AccessibleTextType.CHARACTER):"); + TextSegment txt = oObj.getTextBehindIndex(chCount - 2, + AccessibleTextType.CHARACTER); + System.out.println("'" + txt.SegmentText + "'"); + res &= txt.SegmentText.equals(text.substring(chCount - 1, chCount)); + } + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } catch(com.sun.star.lang.IllegalArgumentException e) { + System.out.println("Unexpected exception"); + res &= false; + } + + + return res; + } + + /** + * Calls the method with invalid parameters and with valid parameter, + * checks returned values. + * Has OK status if exception was thrown for invalid parameters, + * if exception wasn't thrown for valid parameter and if returned value for + * valid parameter is equal to <code>true</code>. + */ + public boolean _copyText() { + boolean res = true; + boolean locRes = true; + + if (editOnly != null) { + System.out.println(editOnly); + return true; + } + + try { + System.out.println("copyText(-1,chCount):"); + oObj.copyText(-1, chCount); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } + + try { + System.out.println("copyText(0,chCount+1):"); + oObj.copyText(0, chCount + 1); + System.out.println("Exception was expected"); + res &= false; + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Expected exception"); + res &= true; + } + + try { + System.out.println("copyText(0,chCount):"); + locRes = oObj.copyText(0, chCount); + System.out.println(locRes); + res &= locRes; + + String cbText = null; + try { + cbText = + util.SysUtils.getSysClipboardText(xMSF); + } catch (com.sun.star.uno.Exception e) { + System.out.println("Couldn't access system clipboard :"); + e.printStackTrace(); + } + System.out.println("Clipboard: '" + cbText + "'"); + res &= text.equals(cbText); + + if (chCount > 2) { + System.out.println("copyText(1,chCount-1):"); + locRes = oObj.copyText(1, chCount - 1); + System.out.println(locRes); + res &= locRes; + + try { + cbText = util.SysUtils.getSysClipboardText(xMSF); + } catch (com.sun.star.uno.Exception e) { + System.out.println("Couldn't access system clipboard :"); + e.printStackTrace(); + } + + System.out.println("Clipboard: '" + cbText + "'"); + res &= text.substring(1, chCount - 1).equals(cbText); + } + + } catch(com.sun.star.lang.IndexOutOfBoundsException e) { + System.out.println("Unexpected exception"); + e.printStackTrace(); + res &= false; + } + + return res; + } +} diff --git a/toolkit/qa/complex/toolkit/awtgrid/DummyColumn.java b/toolkit/qa/complex/toolkit/awtgrid/DummyColumn.java new file mode 100644 index 0000000000..d86df3f1c3 --- /dev/null +++ b/toolkit/qa/complex/toolkit/awtgrid/DummyColumn.java @@ -0,0 +1,167 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit.awtgrid; + +import com.sun.star.awt.grid.XGridColumn; +import com.sun.star.awt.grid.XGridColumnListener; +import com.sun.star.lang.IllegalArgumentException; +import com.sun.star.lang.XEventListener; +import com.sun.star.style.HorizontalAlignment; +import com.sun.star.util.XCloneable; + +/** + * a dummy implementation of css.awt.grid.XGridColumn + */ +public class DummyColumn implements XGridColumn +{ + public Object getIdentifier() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setIdentifier( Object o ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public int getColumnWidth() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setColumnWidth( int i ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public int getMinWidth() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setMinWidth( int i ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public int getMaxWidth() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setMaxWidth( int i ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public boolean getResizeable() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setResizeable( boolean bln ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public int getFlexibility() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setFlexibility( int i ) throws IllegalArgumentException + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public HorizontalAlignment getHorizontalAlign() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setHorizontalAlign( HorizontalAlignment ha ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public String getTitle() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setTitle( String string ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public String getHelpText() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setHelpText( String string ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public int getIndex() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public int getDataColumnIndex() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setDataColumnIndex( int i ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void addGridColumnListener( XGridColumnListener xl ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void removeGridColumnListener( XGridColumnListener xl ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void dispose() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void addEventListener( XEventListener xl ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void removeEventListener( XEventListener xl ) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public XCloneable createClone() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } +} diff --git a/toolkit/qa/complex/toolkit/awtgrid/GridDataListener.java b/toolkit/qa/complex/toolkit/awtgrid/GridDataListener.java new file mode 100644 index 0000000000..800727bb34 --- /dev/null +++ b/toolkit/qa/complex/toolkit/awtgrid/GridDataListener.java @@ -0,0 +1,107 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit.awtgrid; + +import com.sun.star.awt.grid.GridDataEvent; +import com.sun.star.awt.grid.XGridDataListener; +import com.sun.star.lang.EventObject; +import static org.junit.Assert.*; + +final public class GridDataListener implements XGridDataListener +{ + public void rowsInserted( GridDataEvent i_event ) + { + assertNull( m_rowInsertionEvent ); + m_rowInsertionEvent = i_event; + } + + public void rowsRemoved( GridDataEvent i_event ) + { + assertNull( m_rowRemovalEvent ); + m_rowRemovalEvent = i_event; + } + + public void dataChanged( GridDataEvent i_event ) + { + assertNull( m_dataChangeEvent ); + m_dataChangeEvent = i_event; + } + + public void rowHeadingChanged( GridDataEvent i_event ) + { + assertNull( m_rowHeadingChangeEvent ); + m_rowHeadingChangeEvent = i_event; + } + + public void disposing( EventObject eo ) + { + m_disposed = true; + } + + public final GridDataEvent assertSingleRowInsertionEvent() + { + assertNotNull( m_rowInsertionEvent ); + assertNull( m_rowRemovalEvent ); + assertNull( m_dataChangeEvent ); + assertNull( m_rowHeadingChangeEvent ); + assertFalse( m_disposed ); + return m_rowInsertionEvent; + } + + public final GridDataEvent assertSingleRowRemovalEvent() + { + assertNull( m_rowInsertionEvent ); + assertNotNull( m_rowRemovalEvent ); + assertNull( m_dataChangeEvent ); + assertNull( m_rowHeadingChangeEvent ); + assertFalse( m_disposed ); + return m_rowRemovalEvent; + } + + public final GridDataEvent assertSingleDataChangeEvent() + { + assertNull( m_rowInsertionEvent ); + assertNull( m_rowRemovalEvent ); + assertNotNull( m_dataChangeEvent ); + assertNull( m_rowHeadingChangeEvent ); + assertFalse( m_disposed ); + return m_dataChangeEvent; + } + + public final GridDataEvent assertSingleRowHeadingChangeEvent() + { + assertNull( m_rowInsertionEvent ); + assertNull( m_rowRemovalEvent ); + assertNull( m_dataChangeEvent ); + assertNotNull( m_rowHeadingChangeEvent ); + assertFalse( m_disposed ); + return m_rowHeadingChangeEvent; + } + + public final void reset() + { + m_rowInsertionEvent = m_rowRemovalEvent = m_dataChangeEvent = m_rowHeadingChangeEvent = null; + // m_disposed is not reset intentionally + } + private GridDataEvent m_rowInsertionEvent = null; + private GridDataEvent m_rowRemovalEvent = null; + private GridDataEvent m_dataChangeEvent = null; + private GridDataEvent m_rowHeadingChangeEvent = null; + private boolean m_disposed = false; +} diff --git a/toolkit/qa/complex/toolkit/awtgrid/TMutableGridDataModel.java b/toolkit/qa/complex/toolkit/awtgrid/TMutableGridDataModel.java new file mode 100644 index 0000000000..83878e18c7 --- /dev/null +++ b/toolkit/qa/complex/toolkit/awtgrid/TMutableGridDataModel.java @@ -0,0 +1,449 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit.awtgrid; + +import com.sun.star.awt.grid.GridDataEvent; +import com.sun.star.awt.grid.XMutableGridDataModel; +import com.sun.star.lang.IllegalArgumentException; +import com.sun.star.lang.IndexOutOfBoundsException; +import static org.junit.Assert.*; +import static complex.toolkit.Assert.*; + +/** test for the <code>css.awt.grid.XMutableGridData</code> interface + * + */ +public class TMutableGridDataModel +{ + public TMutableGridDataModel( final XMutableGridDataModel i_dataModel ) + { + m_dataModel = i_dataModel; + + m_listener = new GridDataListener(); + m_dataModel.addGridDataListener( m_listener ); + } + + /* + * tests the XMutableGridDataModel.addRow method + */ + public void testAddRow() throws IndexOutOfBoundsException + { + m_dataModel.addRow( m_rowHeadings[0], m_rowValues[0] ); + GridDataEvent event = m_listener.assertSingleRowInsertionEvent(); + m_listener.reset(); + assertEquals( "row insertion: wrong FirstRow (1)", 0, event.FirstRow ); + assertEquals( "row insertion: wrong LastRow (1)", 0, event.LastRow ); + impl_assertRowData( 0 ); + + m_dataModel.addRow( m_rowHeadings[1], m_rowValues[1] ); + event = m_listener.assertSingleRowInsertionEvent(); + m_listener.reset(); + assertEquals( "row insertion: wrong FirstRow (2)", 1, event.FirstRow ); + assertEquals( "row insertion: wrong LastRow (2)", 1, event.LastRow ); + impl_assertRowData( 1 ); + } + + /** + * tests the XMutableGridDataModel.addRows method + */ + public void testAddRows() throws IndexOutOfBoundsException, IllegalArgumentException + { + assertEquals( "precondition not met: call this directly after testAddRow, please!", 2, m_dataModel.getRowCount() ); + + m_dataModel.addRows( + new Object[] { m_rowHeadings[2], m_rowHeadings[3], m_rowHeadings[4] }, + new Object[][] { m_rowValues[2], m_rowValues[3], m_rowValues[4] } ); + GridDataEvent event = m_listener.assertSingleRowInsertionEvent(); + assertEquals( "row insertion: wrong FirstRow (1)", 2, event.FirstRow ); + assertEquals( "row insertion: wrong LastRow (1)", 4, event.LastRow ); + m_listener.reset(); + + assertEquals( "data model's row count is not adjusted when adding rows", m_rowValues.length, m_dataModel.getRowCount() ); + assertEquals( "data model's column count is not adjusted when adding rows", m_rowValues[0].length, m_dataModel.getColumnCount() ); + for ( int row=0; row<m_rowValues.length; ++row ) + { + for ( int col=0; col<m_rowValues[row].length; ++col ) + { + assertEquals( "added row values are not preserved", + m_rowValues[row][col], m_dataModel.getCellData( col, row ) ); + } + } + + assertException( "addRows is expected to throw when invoked with different-sized arrays", + m_dataModel, "addRows", new Object[] { new Object[0], new Object[1][2] }, IllegalArgumentException.class ); + } + + /** + * tests the XMutableGridDataModel.insertRow method + */ + public void testInsertRow() throws IndexOutOfBoundsException + { + int expectedRowCount = m_rowValues.length; + assertEquals( "precondition not met: call this directly after testAddRows, please!", expectedRowCount, m_dataModel.getRowCount() ); + + // inserting some row somewhere between the other rows + final Object heading = "inbetweenRow"; + final Object[] inbetweenRow = new Object[] { "foo", "bar", 3, 4, 5 }; + final int insertionPos = 2; + m_dataModel.insertRow( insertionPos, heading, inbetweenRow ); + ++expectedRowCount; + assertEquals( "inserting a row is expected to increment the row count", + expectedRowCount, m_dataModel.getRowCount() ); + + final GridDataEvent event = m_listener.assertSingleRowInsertionEvent(); + assertEquals( "inserting a row results in wrong FirstRow being notified", insertionPos, event.FirstRow ); + assertEquals( "inserting a row results in wrong LastRow being notified", insertionPos, event.LastRow ); + m_listener.reset(); + + for ( int row=0; row<expectedRowCount; ++row ) + { + final Object[] actualRowData = m_dataModel.getRowData( row ); + final Object[] expectedRowData = + ( row < insertionPos ) + ? m_rowValues[ row ] + : ( row == insertionPos ) + ? inbetweenRow + : m_rowValues[ row - 1 ]; + assertArrayEquals( "row number " + row + " has wrong content after inserting a row", + expectedRowData, actualRowData ); + + final Object actualHeading = m_dataModel.getRowHeading(row); + final Object expectedHeading = + ( row < insertionPos ) + ? m_rowHeadings[ row ] + : ( row == insertionPos ) + ? heading + : m_rowHeadings[ row - 1 ]; + assertEquals( "row " + row + " has a wrong heading after invoking insertRow", + expectedHeading, actualHeading ); + } + + // exceptions + assertException( "inserting a row at a position > rowCount is expected to throw", + m_dataModel, "insertRow", + new Class[] { Integer.class, Object.class, Object[].class }, + new Object[] { expectedRowCount + 1, "", new Object[] { "1", 2, 3 } }, + IndexOutOfBoundsException.class ); + assertException( "inserting a row at a position < 0 is expected to throw", + m_dataModel, "insertRow", + new Class[] { Integer.class, Object.class, Object[].class }, + new Object[] { -1, "", new Object[] { "1", 2, 3 } }, + IndexOutOfBoundsException.class ); + + // remove the row, to create the situation expected by the next test + m_dataModel.removeRow( insertionPos ); + m_listener.reset(); + } + + /** + * tests the XMutableGridDataModel.insertRows method + */ + public void testInsertRows() throws IndexOutOfBoundsException, IllegalArgumentException + { + int expectedRowCount = m_rowValues.length; + assertEquals( "precondition not met: call this directly after testInsertRow, please!", expectedRowCount, m_dataModel.getRowCount() ); + + // inserting some rows somewhere between the other rows + final int insertionPos = 3; + final Object[] rowHeadings = new Object[] { "A", "B", "C" }; + final Object[][] rowData = new Object[][] { + new Object[] { "A", "B", "C", "D", "E" }, + new Object[] { "J", "I", "H", "G", "F" }, + new Object[] { "K", "L", "M", "N", "O" } + }; + final int insertedRowCount = rowData.length; + assertEquals( "invalid test data", rowHeadings.length, insertedRowCount ); + + m_dataModel.insertRows( insertionPos, rowHeadings, rowData ); + expectedRowCount += insertedRowCount; + + final GridDataEvent event = m_listener.assertSingleRowInsertionEvent(); + assertEquals( "inserting multiple rows results in wrong FirstRow being notified", + insertionPos, event.FirstRow ); + assertEquals( "inserting multiple rows results in wrong LastRow being notified", + insertionPos + insertedRowCount - 1, event.LastRow ); + m_listener.reset(); + + for ( int row=0; row<expectedRowCount; ++row ) + { + final Object[] actualRowData = m_dataModel.getRowData( row ); + final Object[] expectedRowData = + ( row < insertionPos ) + ? m_rowValues[ row ] + : ( row >= insertionPos ) && ( row < insertionPos + insertedRowCount ) + ? rowData[ row - insertionPos ] + : m_rowValues[ row - insertedRowCount ]; + assertArrayEquals( "row number " + row + " has wrong content after inserting multiple rows", + expectedRowData, actualRowData ); + + final Object actualHeading = m_dataModel.getRowHeading(row); + final Object expectedHeading = + ( row < insertionPos ) + ? m_rowHeadings[ row ] + : ( row >= insertionPos ) && ( row < insertionPos + insertedRowCount ) + ? rowHeadings[ row - insertionPos ] + : m_rowHeadings[ row - insertedRowCount ]; + assertEquals( "row " + row + " has a wrong heading after invoking insertRows", + expectedHeading, actualHeading ); + } + + // exceptions + assertException( "inserting multiple rows at a position > rowCount is expected to throw an IndexOutOfBoundsException", + m_dataModel, "insertRows", + new Class[] { Integer.class, Object[].class, Object[][].class }, + new Object[] { expectedRowCount + 1, new Object[0], new Object[][] { } }, + IndexOutOfBoundsException.class ); + assertException( "inserting multiple rows at a position < 0 is expected to throw an IndexOutOfBoundsException", + m_dataModel, "insertRows", + new Class[] { Integer.class, Object[].class, Object[][].class }, + new Object[] { -1, new Object[0], new Object[][] { } }, + IndexOutOfBoundsException.class ); + assertException( "inserting multiple rows with inconsistent array lengths is expected to throw an IllegalArgumentException", + m_dataModel, "insertRows", + new Class[] { Integer.class, Object[].class, Object[][].class }, + new Object[] { 0, new Object[0], new Object[][] { new Object[0] } }, + IllegalArgumentException.class ); + + // remove the row, to create the situation expected by the next test + for ( int i=0; i<insertedRowCount; ++i ) + { + m_dataModel.removeRow( insertionPos ); + m_listener.reset(); + } + } + + /** + * tests the XMutableGridDataModel.removeRow method + */ + public void testRemoveRow() throws IndexOutOfBoundsException + { + assertEquals( "precondition not met: call this directly after testAddRows, please!", m_rowValues.length, m_dataModel.getRowCount() ); + + final int rowToRemove = 2; + m_dataModel.removeRow( rowToRemove ); + GridDataEvent event = m_listener.assertSingleRowRemovalEvent(); + assertEquals( "incorrect notification of row removal (FirstRow)", rowToRemove, event.FirstRow ); + assertEquals( "incorrect notification of row removal (LastRow)", rowToRemove, event.LastRow ); + m_listener.reset(); + + assertEquals( "data model's row count does not reflect the removed row", m_rowValues.length - 1, m_dataModel.getRowCount() ); + for ( int row = rowToRemove; row<m_rowValues.length-1; ++row ) + { + for ( int col=0; col<m_rowValues[row].length; ++col ) + { + assertEquals( "unexpected row values after removing a row (col: " + col + ", row: " + row + ")", + m_rowValues[row+1][col], m_dataModel.getCellData( col, row ) ); + } + } + + assertException( "removeRow silently ignores an invalid index (1)", + m_dataModel, "removeRow", new Object[] { -1 }, IndexOutOfBoundsException.class ); + assertException( "removeRow silently ignores an invalid index (2)", + m_dataModel, "removeRow", new Object[] { m_dataModel.getRowCount() }, IndexOutOfBoundsException.class ); + } + + /** + * tests the XMutableGridDataModel.removeAllRows method + */ + public void testRemoveAllRows() + { + assertEquals( "precondition not met: call this directly after testRemoveRow, please!", m_rowValues.length - 1, m_dataModel.getRowCount() ); + + m_dataModel.removeAllRows(); + final GridDataEvent event = m_listener.assertSingleRowRemovalEvent(); + if ( event.FirstRow != -1 ) + { // notifying "-1" is allowed, this means "all rows affected", by definition + assertEquals( "removeAllRows is not notifying properly (1)", 0, event.FirstRow ); + assertEquals( "removeAllRows is not notifying properly (2)", m_rowValues.length - 1, event.LastRow ); + } + m_listener.reset(); + } + + /** + * tests the XMutableGridDataModel.updateCellData method + */ + public void testUpdateCellData() throws IndexOutOfBoundsException, IllegalArgumentException + { + assertEquals( "precondition not met: call this directly after testRemoveAllRows, please!", 0, m_dataModel.getRowCount() ); + + m_dataModel.addRows( m_rowHeadings, m_rowValues ); + m_listener.assertSingleRowInsertionEvent(); + m_listener.reset(); + + final Object[][] modifyValues = new Object[][] { + new Object[] { 2, 1, "text" }, + new Object[] { 3, 0, null }, + new Object[] { 0, 4, Double.valueOf( 33.0 ) } + }; + for ( int i = 0; i < modifyValues.length; ++i ) + { + final int row = ((Integer)modifyValues[i][0]).intValue(); + final int col = ((Integer)modifyValues[i][1]).intValue(); + final Object value = modifyValues[i][2]; + m_dataModel.updateCellData( col, row, value ); + + final GridDataEvent event = m_listener.assertSingleDataChangeEvent(); + assertEquals( "data change notification: FirstRow is invalid", row, event.FirstRow ); + assertEquals( "data change notification: LastRow is invalid", row, event.LastRow ); + assertEquals( "data change notification: FirstColumn is invalid", col, event.FirstColumn ); + assertEquals( "data change notification: LastColumn is invalid", col, event.LastColumn ); + m_listener.reset(); + + assertEquals( "data change at (" + col + ", " + row + ") not successful", value, m_dataModel.getCellData( col, row ) ); + } + + assertException( "updateCellData silently ignores an invalid index (1)", + m_dataModel, "updateCellData", new Class[] { int.class, int.class, Object.class }, + new Object[] { -1, -1, "text" }, IndexOutOfBoundsException.class ); + assertException( "updateCellData silently ignores an invalid index (2)", + m_dataModel, "updateCellData", new Class[] { int.class, int.class, Object.class }, + new Object[] { 0, m_dataModel.getRowCount(), "text" }, IndexOutOfBoundsException.class ); + assertException( "updateCellData silently ignores an invalid index (3)", + m_dataModel, "updateCellData", new Class[] { int.class, int.class, Object.class }, + new Object[] { m_dataModel.getColumnCount(), 0, "text" }, IndexOutOfBoundsException.class ); + } + + /** + * tests the XMutableGridDataModel.updateRowData method + */ + public void testUpdateRowData() throws IndexOutOfBoundsException, IllegalArgumentException + { + assertEquals( "precondition not met: call this directly after testRemoveAllRows, please!", m_rowValues.length, m_dataModel.getRowCount() ); + + // get data from before the update + final Object[][] preUpdateValues = impl_getCurrentData(); + + // do the update + final int[] colIndexes = new int[] { + 0, 3, 4 + }; + final Object[] values = new Object[] { + 13, null, 42.0 + }; + final int rowToUpdate = 2; + m_dataModel.updateRowData( colIndexes, rowToUpdate, values ); + final GridDataEvent event = m_listener.assertSingleDataChangeEvent(); + assertEquals( "row update notification: FirstRow is invalid", rowToUpdate, event.FirstRow ); + assertEquals( "row update notification: LastRow is invalid", rowToUpdate, event.LastRow ); + assertEquals( "row update notification: FirstColumn is invalid", 0, event.FirstColumn ); + assertEquals( "row update notification: LastColumn is invalid", 4, event.LastColumn ); + m_listener.reset(); + + // reflect the changes made in the pre-update data + for ( int i=0; i<colIndexes.length; ++i ) + { + preUpdateValues[rowToUpdate][colIndexes[i]] = values[i]; + } + + // get data from after the update + final Object[][] postUpdateValues = impl_getCurrentData(); + + // ensure both the manually updated pre-update data and the post-update data are identical + assertArrayEquals( preUpdateValues, postUpdateValues ); + + + assertException( "updateRowData silently ignores an invalid index (1)", + m_dataModel, "updateRowData", new Class[] { int[].class, int.class, Object[].class }, + new Object[] { new int[] { -1 }, 0, new Object[] { "text" } }, IndexOutOfBoundsException.class ); + assertException( "updateRowData silently ignores an invalid index (2)", + m_dataModel, "updateRowData", new Class[] { int[].class, int.class, Object[].class }, + new Object[] { new int[] { 0 }, -1, new Object[] { "" } }, IndexOutOfBoundsException.class ); + assertException( "updateRowData silently ignores different-sized arrays", + m_dataModel, "updateRowData", new Class[] { int[].class, int.class, Object[].class }, + new Object[] { new int[] { 0, 0 }, 0, new Object[] { "" } }, IllegalArgumentException.class ); + } + + /** + * tests the XMutableGridDataModel.updateRowHeading method + */ + public void testUpdateRowHeading() throws IndexOutOfBoundsException + { + assertEquals( "precondition not met: call this directly after testUpdateRowData, please!", m_rowValues.length, m_dataModel.getRowCount() ); + + final Object[] preUpdateHeadings = impl_getCurrentRowHeadings(); + + final int rowToUpdate = 2; + final String valueToUpdate = "some text"; + m_dataModel.updateRowHeading( rowToUpdate, valueToUpdate ); + final GridDataEvent event = m_listener.assertSingleRowHeadingChangeEvent(); + assertEquals( "row heading update notification: FirstRow is invalid", rowToUpdate, event.FirstRow ); + assertEquals( "row heading update notification: FirstRow is invalid", rowToUpdate, event.LastRow ); + m_listener.reset(); + + preUpdateHeadings[rowToUpdate] = valueToUpdate; + + final Object[] postUpdateHeadings = impl_getCurrentRowHeadings(); + assertArrayEquals( preUpdateHeadings, postUpdateHeadings ); + + assertException( "updateRowHeading silently ignores an invalid index", + m_dataModel, "updateRowHeading", new Class[] { int.class, Object.class }, + new Object[] { -1, "" }, IndexOutOfBoundsException.class ); + } + + public void cleanup() + { + m_dataModel.removeGridDataListener( m_listener ); + } + + private Object[][] impl_getCurrentData() throws IndexOutOfBoundsException + { + final int rowCount = m_dataModel.getRowCount(); + final int colCount = m_dataModel.getColumnCount(); + final Object[][] data = new Object[rowCount][colCount]; + for ( int row=0; row<rowCount; ++row ) + { + for ( int col=0; col<colCount; ++col ) + { + data[row][col] = m_dataModel.getCellData( col, row ); + } + } + return data; + } + + private Object[] impl_getCurrentRowHeadings() throws IndexOutOfBoundsException + { + final int rowCount = m_dataModel.getRowCount(); + final Object[] headings = new Object[rowCount]; + for ( int row=0; row<rowCount; ++row ) + headings[row] = m_dataModel.getRowHeading( row ); + return headings; + } + + private void impl_assertRowData( final int i_rowIndex ) throws IndexOutOfBoundsException + { + for ( int i=0; i<m_rowValues[i_rowIndex].length; ++i ) + { + assertEquals( m_rowValues[i_rowIndex][i], m_dataModel.getCellData( i, i_rowIndex ) ); + } + } + + private final XMutableGridDataModel m_dataModel; + private final GridDataListener m_listener; + + private static final Object[][] m_rowValues = new Object[][] { + new Object[] { 1, 2, "3", 4, 5 }, + new Object[] { 2, 3, 4, "5", 6 }, + new Object[] { "3", 4, 5, 6, 7 }, + new Object[] { 4, 5, 6, 7, "8" }, + new Object[] { 5, "6", 7, 8, 9 }, + }; + + private static final Object[] m_rowHeadings = new Object[] { + "1", 2, 3.0, "4", (float)5.0 + }; +} diff --git a/toolkit/qa/cppunit/Dialog.cxx b/toolkit/qa/cppunit/Dialog.cxx new file mode 100644 index 0000000000..6ddf3f7258 --- /dev/null +++ b/toolkit/qa/cppunit/Dialog.cxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <test/bootstrapfixture.hxx> +#include <unotest/macros_test.hxx> + +#include <com/sun/star/awt/UnoControlDialog.hpp> +#include <com/sun/star/awt/XUnoControlDialog.hpp> +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> + +#include <comphelper/processfactory.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/window.hxx> + +using namespace css; + +namespace +{ +/// Test dialogs constructed via UNO +class DialogTest : public test::BootstrapFixture, public unotest::MacrosTest +{ +protected: + uno::Reference<uno::XComponentContext> mxContext; + +public: + virtual void setUp() override; +}; + +void DialogTest::setUp() +{ + test::BootstrapFixture::setUp(); + + mxContext.set(comphelper::getComponentContext(getMultiServiceFactory())); +} + +CPPUNIT_TEST_FIXTURE(DialogTest, testDialogSizeable) +{ + uno::Reference<awt::XDialog> xDialog; + uno::Reference<lang::XMultiComponentFactory> xFactory(mxContext->getServiceManager(), + uno::UNO_SET_THROW); + uno::Reference<awt::XControlModel> xControlModel( + xFactory->createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", mxContext), + uno::UNO_QUERY_THROW); + + uno::Reference<beans::XPropertySet> xPropSet(xControlModel, uno::UNO_QUERY_THROW); + xPropSet->setPropertyValue("Sizeable", uno::Any(true)); + + uno::Reference<awt::XUnoControlDialog> xControl = awt::UnoControlDialog::create(mxContext); + xControl->setModel(xControlModel); + xControl->setVisible(true); + xDialog.set(xControl, uno::UNO_QUERY_THROW); + xDialog->execute(); + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xControl->getPeer()); + CPPUNIT_ASSERT(pWindow); + CPPUNIT_ASSERT(pWindow->GetStyle() & WB_SIZEABLE); + + xDialog->endExecute(); + css::uno::Reference<css::lang::XComponent>(xDialog, css::uno::UNO_QUERY_THROW)->dispose(); + css::uno::Reference<css::lang::XComponent>(xControlModel, css::uno::UNO_QUERY_THROW)->dispose(); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/qa/cppunit/EventContainer.cxx b/toolkit/qa/cppunit/EventContainer.cxx new file mode 100644 index 0000000000..ad62f93105 --- /dev/null +++ b/toolkit/qa/cppunit/EventContainer.cxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <test/bootstrapfixture.hxx> + +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <com/sun/star/script/XScriptEventsSupplier.hpp> +#include <com/sun/star/uno/Reference.hxx> + +#include <comphelper/processfactory.hxx> + +using namespace css; +using namespace css::awt; +using namespace css::container; +using namespace css::lang; +using namespace css::script; +using namespace css::uno; + +namespace +{ +/// Test EventContainer class +class EventContainerTest : public test::BootstrapFixture +{ +protected: + Reference<XComponentContext> mxContext; + +public: + virtual void setUp() override; +}; + +void EventContainerTest::setUp() +{ + test::BootstrapFixture::setUp(); + + mxContext.set(comphelper::getComponentContext(getMultiServiceFactory())); +} + +// Make sure that EventContainer keeps insertion order, and does not reorder its elements. +// Otherwise this would break macro signatures. +CPPUNIT_TEST_FIXTURE(EventContainerTest, testInsertOrder) +{ + Reference<XMultiComponentFactory> xFactory(mxContext->getServiceManager(), UNO_SET_THROW); + Reference<XControlModel> xControlModel( + xFactory->createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", mxContext), + UNO_QUERY_THROW); + + Reference<beans::XPropertySet> xPropSet(xControlModel, UNO_QUERY_THROW); + + Reference<XScriptEventsSupplier> xEventsSupplier(xPropSet, UNO_QUERY_THROW); + Reference<XNameContainer> xEvents(xEventsSupplier->getEvents(), UNO_SET_THROW); + script::ScriptEventDescriptor descr1; + script::ScriptEventDescriptor descr2; + script::ScriptEventDescriptor descr3; + script::ScriptEventDescriptor descr4; + xEvents->insertByName("b", Any(descr1)); + xEvents->insertByName("a", Any(descr2)); + xEvents->insertByName("1", Any(descr3)); + xEvents->insertByName("A", Any(descr4)); + + Sequence<OUString> aEventNames(xEvents->getElementNames()); + sal_Int32 nEventCount = aEventNames.getLength(); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), nEventCount); + + CPPUNIT_ASSERT_EQUAL(OUString("b"), aEventNames[0]); + CPPUNIT_ASSERT_EQUAL(OUString("a"), aEventNames[1]); + CPPUNIT_ASSERT_EQUAL(OUString("1"), aEventNames[2]); + CPPUNIT_ASSERT_EQUAL(OUString("A"), aEventNames[3]); + + css::uno::Reference<css::lang::XComponent>(xControlModel, css::uno::UNO_QUERY_THROW)->dispose(); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/qa/cppunit/UnitConversion.cxx b/toolkit/qa/cppunit/UnitConversion.cxx new file mode 100644 index 0000000000..5e28667f5a --- /dev/null +++ b/toolkit/qa/cppunit/UnitConversion.cxx @@ -0,0 +1,215 @@ +/* -*- 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 <test/bootstrapfixture.hxx> + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/WindowDescriptor.hpp> +#include <com/sun/star/awt/XUnitConversion.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/awt/XWindow.hpp> + +#ifdef _WIN32 +#include <windows.h> +#endif + +using namespace ::com::sun::star; + +namespace +{ +class ToolkitTest : public test::BootstrapFixture +{ +public: + void testXUnitConversion(); + + CPPUNIT_TEST_SUITE(ToolkitTest); + CPPUNIT_TEST(testXUnitConversion); + CPPUNIT_TEST_SUITE_END(); +}; + +/** + * Creates a floating XWindow on the given position and size. + * @return a floating XWindow + * @param X the X-Position of the floating XWindow + * @param Y the Y-Position of the floating XWindow + * @param width the width of the floating XWindow + * @param height the height of the floating XWindow + * @param xMSF the MultiServiceFactory + */ +uno::Reference<awt::XWindowPeer> +createFloatingWindow(uno::Reference<lang::XMultiServiceFactory> const& xMSF, sal_Int32 const nX, + sal_Int32 const nY, sal_Int32 const nWidth, sal_Int32 const nHeight) +{ + uno::Reference<awt::XToolkit> const xTk(xMSF->createInstance("com.sun.star.awt.Toolkit"), + uno::UNO_QUERY); + + awt::WindowDescriptor descriptor; + descriptor.Type = awt::WindowClass_TOP; + descriptor.WindowServiceName = "modelessdialog"; + descriptor.ParentIndex = -1; + descriptor.Bounds.X = nX; + descriptor.Bounds.Y = nY; + descriptor.Bounds.Width = nWidth; + descriptor.Bounds.Height = nHeight; + descriptor.WindowAttributes + = (awt::WindowAttribute::BORDER + awt::WindowAttribute::MOVEABLE + + awt::WindowAttribute::SIZEABLE + awt::WindowAttribute::CLOSEABLE + + awt::VclWindowPeerAttribute::CLIPCHILDREN); + + return xTk->createWindow(descriptor); +} + +/** + * Not really a check, + * only a simple test call to convertSizeToLogic(...) with different parameters + */ +void checkSize(uno::Reference<awt::XUnitConversion> const& xConv, awt::Size const& rSize, + sal_Int16 const nMeasureUnit, OUString const& rUnit) +{ + awt::Size const aSizeIn = xConv->convertSizeToLogic(rSize, nMeasureUnit); + std::cerr << "Window size:\n"; + std::cerr << "Width: " << aSizeIn.Width << " " << rUnit << "\n"; + std::cerr << "Height: " << aSizeIn.Height << " " << rUnit << "\n"; +} + +/** + * The real test function + * 2. try to create an empty window + * 3. try to convert the WindowPeer to an XWindow + * 4. try to resize and move the window to another position, so we get a well knowing position and size. + * 5. run some more tests + */ +void ToolkitTest::testXUnitConversion() +{ +#ifdef _WIN32 + HKEY hkey; + DWORD type; + DWORD data; + DWORD size(sizeof(data)); + LONG ret = ::RegOpenKeyW(HKEY_CURRENT_USER, L"Control Panel\\Desktop", &hkey); + if (ret == ERROR_SUCCESS) + { + ret = ::RegQueryValueExW(hkey, L"LogPixels", nullptr, &type, + reinterpret_cast<LPBYTE>(&data), &size); + if (ret == ERROR_SUCCESS && type == REG_DWORD && data != 96) + { + std::cerr << "non-default resolution, skipping textXUnitConversion\n"; + return; + } + } +#endif + + // create a window + sal_Int32 x = 100; + sal_Int32 y = 100; + sal_Int32 width = 640; + sal_Int32 height = 480; + uno::Reference<awt::XWindowPeer> const xWindowPeer + = createFloatingWindow(getMultiServiceFactory(), x, y, width, height); + CPPUNIT_ASSERT(xWindowPeer.is()); + + // resize and move the window to a well known position and size + uno::Reference<awt::XWindow> const xWindow(xWindowPeer, uno::UNO_QUERY); + CPPUNIT_ASSERT(xWindow.is()); + + xWindow->setVisible(true); + + awt::Rectangle aRect = xWindow->getPosSize(); + awt::Point aPoint(aRect.X, aRect.Y); + awt::Size aSize(aRect.Width, aRect.Height); + + std::cerr << "Window position and size in pixel:\n"; + std::cerr << "X: " << aPoint.X << "\n"; + std::cerr << "Y: " << aPoint.Y << "\n"; + std::cerr << "Width: " << aSize.Width << "\n"; + std::cerr << "Height: " << aSize.Height << "\n"; + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Window size wrong", width, aSize.Width); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Window size wrong", height, aSize.Height); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Window pos wrong", x, aPoint.X); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Window pos wrong", y, aPoint.Y); + + uno::Reference<awt::XUnitConversion> const xConv(xWindowPeer, uno::UNO_QUERY); + + // try to get the position of the window in 1/100mm with the XUnitConversion method + awt::Point const aPointInMM_100TH + = xConv->convertPointToLogic(aPoint, util::MeasureUnit::MM_100TH); + std::cerr << "Window position:\n"; + std::cerr << "X: " << aPointInMM_100TH.X << " 1/100mm\n"; + std::cerr << "Y: " << aPointInMM_100TH.Y << " 1/100mm\n"; + + // try to get the size of the window in 1/100mm with the XUnitConversion method + awt::Size const aSizeInMM_100TH = xConv->convertSizeToLogic(aSize, util::MeasureUnit::MM_100TH); + std::cerr << "Window size:\n"; + std::cerr << "Width: " << aSizeInMM_100TH.Width << " 1/100mm\n"; + std::cerr << "Height: " << aSizeInMM_100TH.Height << " 1/100mm\n"; + + // try to get the size of the window in 1/10mm with the XUnitConversion method + awt::Size const aSizeInMM_10TH = xConv->convertSizeToLogic(aSize, util::MeasureUnit::MM_10TH); + std::cerr << "Window size:\n"; + std::cerr << "Width: " << aSizeInMM_10TH.Width << " 1/10mm\n"; + std::cerr << "Height: " << aSizeInMM_10TH.Height << " 1/10mm\n"; + + // check the size with a delta which must be smaller a given difference + CPPUNIT_ASSERT_MESSAGE("Size.Width not correct", + std::abs(aSizeInMM_100TH.Width - aSizeInMM_10TH.Width * 10) < 10); + CPPUNIT_ASSERT_MESSAGE("Size.Height not correct", + std::abs(aSizeInMM_100TH.Height - aSizeInMM_10TH.Height * 10) < 10); + + // new + checkSize(xConv, aSize, util::MeasureUnit::PIXEL, "pixel"); + checkSize(xConv, aSize, util::MeasureUnit::APPFONT, "appfont"); + checkSize(xConv, aSize, util::MeasureUnit::SYSFONT, "sysfont"); + + // simply check some more parameters + checkSize(xConv, aSize, util::MeasureUnit::MM, "mm"); + checkSize(xConv, aSize, util::MeasureUnit::CM, "cm"); + checkSize(xConv, aSize, util::MeasureUnit::INCH_1000TH, "1/1000inch"); + checkSize(xConv, aSize, util::MeasureUnit::INCH_100TH, "1/100inch"); + checkSize(xConv, aSize, util::MeasureUnit::INCH_10TH, "1/10inch"); + checkSize(xConv, aSize, util::MeasureUnit::INCH, "inch"); + checkSize(xConv, aSize, util::MeasureUnit::POINT, "point"); + checkSize(xConv, aSize, util::MeasureUnit::TWIP, "twip"); + + // convert the 1/100mm window size back to pixel + awt::Size const aNewSize + = xConv->convertSizeToPixel(aSizeInMM_100TH, util::MeasureUnit::MM_100TH); + std::cerr << "Window size:\n"; + std::cerr << "Width: " << aNewSize.Width << " pixel\n"; + std::cerr << "Height: " << aNewSize.Height << " pixel\n"; + + // assure the pixels are the same as we already know + CPPUNIT_ASSERT_EQUAL_MESSAGE("failed: Size from pixel to 1/100mm to pixel", aNewSize.Width, + aSize.Width); + CPPUNIT_ASSERT_EQUAL_MESSAGE("failed: Size from pixel to 1/100mm to pixel", aNewSize.Height, + aSize.Height); + + // close the window. + xWindow->dispose(); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(ToolkitTest); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/AccessibleStatusBarTest.cxx b/toolkit/qa/cppunit/a11y/AccessibleStatusBarTest.cxx new file mode 100644 index 0000000000..5bf522fb2e --- /dev/null +++ b/toolkit/qa/cppunit/a11y/AccessibleStatusBarTest.cxx @@ -0,0 +1,139 @@ +/* -*- 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 <string> + +#include <com/sun/star/accessibility/AccessibleRole.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/awt/XWindow.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> + +#include <rtl/ustrbuf.hxx> +#include <test/a11y/accessibletestbase.hxx> +#include <vcl/scheduler.hxx> + +#include <test/a11y/AccessibilityTools.hxx> +#include "XAccessibleComponentTester.hxx" +#include "XAccessibleContextTester.hxx" +#include "XAccessibleExtendedComponentTester.hxx" +#include "XAccessibleEventBroadcasterTester.hxx" + +using namespace css; + +namespace +{ +class AccessibleStatusBarTest : public test::AccessibleTestBase +{ +private: + uno::Reference<accessibility::XAccessibleContext> + getTestObject(const uno::Reference<awt::XWindow>& xWindow); + void runAllTests(); + void testDocument(std::string_view sKind); + + void testWriterDoc() { testDocument("swriter"); } + void testMathDoc() { testDocument("smath"); } + void testDrawDoc() { testDocument("sdraw"); } + void testImpressDoc() { testDocument("simpress"); } + void testCalcDoc() { testDocument("scalc"); } + +public: + CPPUNIT_TEST_SUITE(AccessibleStatusBarTest); + CPPUNIT_TEST(testWriterDoc); + CPPUNIT_TEST(testMathDoc); + CPPUNIT_TEST(testDrawDoc); + CPPUNIT_TEST(testImpressDoc); + CPPUNIT_TEST(testCalcDoc); + CPPUNIT_TEST_SUITE_END(); +}; + +uno::Reference<accessibility::XAccessibleContext> +AccessibleStatusBarTest::getTestObject(const uno::Reference<awt::XWindow>& xWindow) +{ + uno::Reference<accessibility::XAccessible> xAccessible(xWindow, uno::UNO_QUERY_THROW); + std::cout << "got accessible: " << xAccessible << std::endl; + std::cout << "accessible name: " << AccessibilityTools::debugString(xAccessible) << std::endl; + + auto xContext = AccessibilityTools::getAccessibleObjectForRole( + xAccessible, accessibility::AccessibleRole::STATUS_BAR); + std::cout << "got context: " << xContext << std::endl; + std::cout << "context name: " << AccessibilityTools::debugString(xContext) << std::endl; + + Scheduler::ProcessEventsToIdle(); // not sure why? + + uno::Reference<lang::XServiceInfo> xSI(xContext, uno::UNO_QUERY_THROW); + std::cout << "implementation name: " << xSI->getImplementationName() << std::endl; + auto serviceNames = xSI->getSupportedServiceNames(); + std::cout << "has " << serviceNames.size() << " services:" << std::endl; + for (auto& service : serviceNames) + std::cout << " * service: " << service << std::endl; + + return xContext; +} + +void AccessibleStatusBarTest::runAllTests() +{ + auto xContext = getTestObject(mxWindow); + + uno::Reference<accessibility::XAccessibleComponent> xAccessibleComponent(xContext, + uno::UNO_QUERY_THROW); + XAccessibleComponentTester componentTester(xAccessibleComponent); + componentTester.testAll(); + + XAccessibleContextTester contextTester(xContext); + contextTester.testAll(); + + uno::Reference<accessibility::XAccessibleExtendedComponent> xAccessibleExtendedComponent( + xContext, uno::UNO_QUERY_THROW); + XAccessibleExtendedComponentTester extendedComponentTester(xAccessibleExtendedComponent); + extendedComponentTester.testAll(); + + uno::Reference<accessibility::XAccessibleEventBroadcaster> xAccessibleEventBroadcaster( + xContext, uno::UNO_QUERY_THROW); + XAccessibleEventBroadcasterTester eventBroadcasterTester(xAccessibleEventBroadcaster, mxWindow); + eventBroadcasterTester.testAll(); +} + +void AccessibleStatusBarTest::testDocument(std::string_view sKind) +{ + rtl::OUStringBuffer sURL("private:factory/"); + sURL.appendAscii(sKind.data(), sKind.length()); + + load(sURL.makeStringAndClear()); + + std::cout << "got document: " << mxDocument << std::endl; + + Scheduler::ProcessEventsToIdle(); + + runAllTests(); + + // close document + close(); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(AccessibleStatusBarTest); +} // namespace + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.cxx b/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.cxx new file mode 100644 index 0000000000..fa5fb9f705 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.cxx @@ -0,0 +1,291 @@ +/* -*- 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 "XAccessibleComponentTester.hxx" + +#include <cppunit/TestAssert.h> + +#include <com/sun/star/accessibility/AccessibleStateType.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/awt/Point.hpp> +#include <com/sun/star/uno/Reference.hxx> + +#include <tools/color.hxx> + +#include <test/a11y/AccessibilityTools.hxx> + +using namespace css; + +/** + * @brief Checks the component's bounds + * + * Checks the X and Y coordinates are non-negative, and that the width and + * height are greater than 0. + * + * Coherence with @c getLocation() is tested in the test for the + * latter. + */ +void XAccessibleComponentTester::testBounds() +{ + auto bounds = mxComponent->getBounds(); + std::cout << "bounds: " << bounds.Width << "x" << bounds.Height << std::showpos << bounds.X + << bounds.Y << std::noshowpos << std::endl; + CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int32>(0), bounds.X); + CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int32>(0), bounds.Y); + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), bounds.Width); + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), bounds.Height); +} + +/** + * @brief Tests results of XAccessibleComponent::getSize() + * + * Succeeds if the size is the same as in bounds. + */ +void XAccessibleComponentTester::testSize() +{ + auto bounds = mxComponent->getBounds(); + auto size = mxComponent->getSize(); + CPPUNIT_ASSERT_EQUAL(bounds.Width, size.Width); + CPPUNIT_ASSERT_EQUAL(bounds.Height, size.Height); +} + +/** + * @brief Tests results of XAccessibleComponent::getBounds() + * + * First checks 4 inner bounds (upper, lower, left and right) of component + * bounding box to contain at least one point of the component. Second 4 outer + * bounds are checked to not contain any component points. + * + * Succeeds if inner bounds contain component points and outer bounds don't + * contain any component points. + */ +void XAccessibleComponentTester::testContainsPoint() +{ + auto bounds = mxComponent->getBounds(); + + /* upper end */ + int curX = 0; + while (!mxComponent->containsPoint(awt::Point(curX, 0)) && curX < bounds.Width) + curX++; + CPPUNIT_ASSERT_MESSAGE("Upper bound of box contains no component points", curX < bounds.Width); + std::cout << "Upper bound of box contains point (" << curX << ",0)" << std::endl; + /* lower end */ + curX = 0; + while (!mxComponent->containsPoint(awt::Point(curX, bounds.Height - 1)) && curX < bounds.Width) + curX++; + CPPUNIT_ASSERT_MESSAGE("Lower bound of box contains no component points", curX < bounds.Width); + std::cout << "Lower bound of box contains point (" << curX << "," << (bounds.Height - 1) << ")" + << std::endl; + /* left end */ + int curY = 0; + while (!mxComponent->containsPoint(awt::Point(0, curY)) && curY < bounds.Height) + curY++; + CPPUNIT_ASSERT_MESSAGE("Left bound of box contains no component points", curY < bounds.Height); + std::cout << "Left bound of box contains point (0," << curY << ")" << std::endl; + /* right end */ + curY = 0; + while (!mxComponent->containsPoint(awt::Point(bounds.Width - 1, curY)) && curY < bounds.Height) + curY++; + CPPUNIT_ASSERT_MESSAGE("Right bound of box contains no component points", curY < bounds.Height); + std::cout << "Right bound of box contains point (" << (bounds.Width - 1) << "," << curY << ")" + << std::endl; + /* no match outside the bounds */ + for (int x = -1; x <= bounds.Width; x++) + { + CPPUNIT_ASSERT_MESSAGE("Outer upper bound CONTAINS a component point", + !mxComponent->containsPoint(awt::Point(x, -1))); + CPPUNIT_ASSERT_MESSAGE("Outer lower bound CONTAINS a component point", + !mxComponent->containsPoint(awt::Point(x, bounds.Height))); + } + for (int y = -1; y <= bounds.Height; y++) + { + CPPUNIT_ASSERT_MESSAGE("Outer left bound CONTAINS a component point", + !mxComponent->containsPoint(awt::Point(-1, y))); + CPPUNIT_ASSERT_MESSAGE("Outer right bound CONTAINS a component point", + !mxComponent->containsPoint(awt::Point(bounds.Width, y))); + } +} + +/** + * @brief Tests results of XAccessibleComponent::getAccessibleAtPoint() + * + * Iterates through all children which implement + * <code>XAccessibleComponent</code> (if they exist) determines their + * boundaries and tries to get each child by <code>getAccessibleAtPoint</code> + * passing point which belongs to the child. + * Also the point is checked which doesn't belong to child boundary box. + * + * Succeeds if in the first cases the right children are returned, and in the + * second <code>null</code> or another child is returned. + */ +void XAccessibleComponentTester::testAccessibleAtPoint() +{ + sal_Int64 count = mxContext->getAccessibleChildCount(); + std::cout << "Found " << count << " children" << std::endl; + for (sal_Int64 i = 0; i < count && i < AccessibilityTools::MAX_CHILDREN; i++) + { + auto child = mxContext->getAccessibleChild(i); + uno::Reference<accessibility::XAccessibleContext> childContext( + child->getAccessibleContext(), uno::UNO_SET_THROW); + std::cout << "* Found child: " << AccessibilityTools::debugString(child) << std::endl; + std::cout << " states: " + << AccessibilityTools::debugAccessibleStateSet( + childContext->getAccessibleStateSet()) + << std::endl; + uno::Reference<accessibility::XAccessibleComponent> xChildComponent(childContext, + uno::UNO_QUERY); + std::cout << " component: " << xChildComponent << std::endl; + if (!xChildComponent) + continue; + + auto childBounds = xChildComponent->getBounds(); + if (childBounds.X == -1) + continue; + std::cout << " bounds: " << childBounds.Width << "x" << childBounds.Height << std::showpos + << childBounds.X << childBounds.Y << std::noshowpos << std::endl; + + std::cout << "finding the point which lies on the component" << std::endl; + int curX = 0; + int curY = 0; + while (!xChildComponent->containsPoint(awt::Point(curX, curY)) && curX < childBounds.Width) + { + curX++; + curY++; + } + if (curX >= childBounds.Width) + { + std::cout << "Couldn't find a point with contains" << std::endl; + continue; + } + else + { + std::cout << "Child found at point +" << childBounds.X + curX << "+" + << childBounds.Y + curY << std::endl; + } + + // trying the point laying on child + auto xAccAtPoint + = mxComponent->getAccessibleAtPoint(awt::Point(childBounds.X, childBounds.Y)); + CPPUNIT_ASSERT_MESSAGE("Child not found at point", xAccAtPoint.is()); + if (!AccessibilityTools::equals(child, xAccAtPoint)) + { + auto idxExpected = childContext->getAccessibleIndexInParent(); + auto idxResult = xAccAtPoint->getAccessibleContext()->getAccessibleIndexInParent(); + std::cout << "The child found (" << AccessibilityTools::debugString(xAccAtPoint) + << ") is not the expected one (" << AccessibilityTools::debugString(child) + << ")" << std::endl; + if (idxExpected < idxResult) + { + std::cout << "-- it probably is hidden behind? Skipping." << std::endl; + } + else + { + CPPUNIT_ASSERT_EQUAL_MESSAGE("The child found is NOT the expected one", child, + xAccAtPoint); + } + } + + // trying the point NOT laying on child + xAccAtPoint + = mxComponent->getAccessibleAtPoint(awt::Point(childBounds.X - 1, childBounds.Y - 1)); + if (xAccAtPoint.is()) + { + CPPUNIT_ASSERT_MESSAGE("Child found OUTSIDE its bounds", + !AccessibilityTools::equals(child, xAccAtPoint)); + } + } +} + +/** + * @brief Tests results of XAccessibleComponent::getLocation() + * + * Succeeds if the location is the same as location of boundary obtained by + * the <code>getBounds()</code> method. + */ +void XAccessibleComponentTester::testLocation() +{ + auto bounds = mxComponent->getBounds(); + auto location = mxComponent->getLocation(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid X location", bounds.X, location.X); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid Y location", bounds.Y, location.Y); +} + +/** + * @brief Tests results of XAccessibleComponent::getLocationOnScreen() + * @returns @c true on success. + * + * Get the screen location of the component and its parent + * (if it exists and supports <code>XAccessibleComponent</code>). + * + * Succeeds component screen location equals to screen location of its parent + * plus location of the component relative to the parent. + */ +void XAccessibleComponentTester::testLocationOnScreen() +{ + auto location = mxComponent->getLocationOnScreen(); + std::cout << "location on screen: +" << location.X << "+" << location.Y << std::endl; + + auto xParent = mxContext->getAccessibleParent(); + if (!xParent.is()) + std::cout << "No parent" << std::endl; + else + { + std::cout << "Found parent: " << AccessibilityTools::debugString(xParent) << std::endl; + uno::Reference<accessibility::XAccessibleComponent> xParentComponent( + xParent->getAccessibleContext(), uno::UNO_QUERY); + if (!xParentComponent.is()) + std::cout << "Parent is not a Component" << std::endl; + else + { + auto bounds = mxComponent->getBounds(); + auto parentLocation = xParentComponent->getLocationOnScreen(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid X screen location", parentLocation.X + bounds.X, + location.X); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid Y screen location", parentLocation.Y + bounds.Y, + location.Y); + } + } +} + +/** + * Just calls the method. + */ +void XAccessibleComponentTester::testGrabFocus() { mxComponent->grabFocus(); } + +/** + * Just calls the method. + */ +void XAccessibleComponentTester::testGetForeground() +{ + auto color = mxComponent->getForeground(); + std::cout << "foreground color: " << Color(ColorAlpha, color) << std::endl; +} + +/** + * Just calls the method. + */ +void XAccessibleComponentTester::testGetBackground() +{ + auto color = mxComponent->getBackground(); + std::cout << "background color: " << Color(ColorAlpha, color) << std::endl; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.hxx b/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.hxx new file mode 100644 index 0000000000..5965374a3f --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.hxx @@ -0,0 +1,66 @@ +/* -*- 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 <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> + +#include <test/a11y/AccessibilityTools.hxx> + +class XAccessibleComponentTester +{ +private: + const css::uno::Reference<css::accessibility::XAccessibleComponent> mxComponent; + const css::uno::Reference<css::accessibility::XAccessibleContext> mxContext; + +public: + XAccessibleComponentTester( + const css::uno::Reference<css::accessibility::XAccessibleComponent>& component) + : mxComponent(component) + , mxContext(component, css::uno::UNO_QUERY_THROW) + { + } + + void testBounds(); + void testSize(); + void testContainsPoint(); + void testAccessibleAtPoint(); + void testLocation(); + void testLocationOnScreen(); + void testGrabFocus(); + void testGetForeground(); + void testGetBackground(); + + void testAll() + { + testBounds(); + testSize(); + testContainsPoint(); + testAccessibleAtPoint(); + testLocation(); + testLocationOnScreen(); + testGrabFocus(); + testGetForeground(); + testGetBackground(); + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleContextTester.cxx b/toolkit/qa/cppunit/a11y/XAccessibleContextTester.cxx new file mode 100644 index 0000000000..9d7fdb992e --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleContextTester.cxx @@ -0,0 +1,164 @@ +/* -*- 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 "XAccessibleContextTester.hxx" + +#include <cppunit/TestAssert.h> + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> + +#include <test/a11y/AccessibilityTools.hxx> + +/** + * @brief Tries to get every child and checks its parent. + * + * Checks that the parent of every child and the tested component are the same object. + */ +void XAccessibleContextTester::testGetAccessibleChild() +{ + sal_Int64 count = mxContext->getAccessibleChildCount(); + for (sal_Int64 i = 0; i < count && i < AccessibilityTools::MAX_CHILDREN; i++) + { + auto child = mxContext->getAccessibleChild(i); + auto childCtx = child->getAccessibleContext(); + + std::cout << " Child " << i << ": " << AccessibilityTools::debugString(childCtx) + << std::endl; + + CPPUNIT_ASSERT_EQUAL_MESSAGE("child's parent context is not parent's context!", + childCtx->getAccessibleParent()->getAccessibleContext(), + mxContext); + } +} + +/** + * @brief Calls the method. + * + * Checks that the child count is non-negative. + */ +void XAccessibleContextTester::testGetAccessibleChildCount() +{ + sal_Int64 childCount = mxContext->getAccessibleChildCount(); + std::cout << childCount << " children found." << std::endl; + CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int64>(0), childCount); +} + +/** + * @brief Get the accessible description of the component. + */ +void XAccessibleContextTester::testGetAccessibleDescription() +{ + auto desc = mxContext->getAccessibleDescription(); + std::cout << "The description is '" << desc << "'" << std::endl; +} + +/** + * @brief Checks the index in parent + * + * Checks that the parent's child and the tested component are the same objects. + * + * Retrieves the index of tested component in its parent. + * Then gets the parent's child by this index and compares + * it with tested component. + */ +void XAccessibleContextTester::testGetAccessibleIndexInParent() +{ + sal_Int64 idx = mxContext->getAccessibleIndexInParent(); + std::cout << "The index in parent is " << idx << std::endl; + + auto parent = mxContext->getAccessibleParent(); + CPPUNIT_ASSERT(parent.is()); + auto parentCtx = parent->getAccessibleContext(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Parent's child context at our index is not us!", mxContext, + parentCtx->getAccessibleChild(idx)->getAccessibleContext()); +} + +/** + * @brief Get the accessible name of the component. + */ +void XAccessibleContextTester::testGetAccessibleName() +{ + auto name = mxContext->getAccessibleName(); + std::cout << "The name is '" << name << "'" << std::endl; +} + +/** + * @brief Just gets the parent. + * + * Checks that the parent is not null. + */ +void XAccessibleContextTester::testGetAccessibleParent() +{ + // assume that the component is not ROOT + auto parent = mxContext->getAccessibleParent(); + std::cout << "The parent is " << AccessibilityTools::debugString(parent) << std::endl; + CPPUNIT_ASSERT_MESSAGE("parent is not set", parent.is()); +} + +/** + * @brief Just gets the relation set. + * + * Checks that the relation set is not null. + */ +void XAccessibleContextTester::testGetAccessibleRelationSet() +{ + auto relSet = mxContext->getAccessibleRelationSet(); + CPPUNIT_ASSERT_MESSAGE("relation set is not set", relSet.is()); +} + +/** + * @brief Get the accessible role of component. + * + * Checks that the role is a non-negative number. + */ +void XAccessibleContextTester::testGetAccessibleRole() +{ + sal_Int32 role = mxContext->getAccessibleRole(); + std::cout << "The role is " << role << " (" << AccessibilityTools::getRoleName(role) << ")" + << std::endl; + CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int32>(0), role); +} + +/** + * @brief Just gets the state set. + * + * Checks that the state set is not null. + */ +void XAccessibleContextTester::testGetAccessibleStateSet() +{ + sal_Int64 stateSet = mxContext->getAccessibleStateSet(); + std::cout << "The state set is: " << AccessibilityTools::debugAccessibleStateSet(stateSet) + << std::endl; +} + +/** + * @brief Gets the locale. + * + * Checks that @c Country and @c Language fields of locale structure are not empty. + */ +void XAccessibleContextTester::testGetLocale() +{ + auto loc = mxContext->getLocale(); + std::cout << "The locale is " << loc.Language << "," << loc.Country << std::endl; + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), loc.Language.getLength()); + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), loc.Country.getLength()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleContextTester.hxx b/toolkit/qa/cppunit/a11y/XAccessibleContextTester.hxx new file mode 100644 index 0000000000..b399028cf8 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleContextTester.hxx @@ -0,0 +1,62 @@ +/* -*- 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 <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> + +class XAccessibleContextTester +{ +protected: + const css::uno::Reference<css::accessibility::XAccessibleContext> mxContext; + +public: + XAccessibleContextTester(const css::uno::Reference<css::accessibility::XAccessibleContext>& ctx) + : mxContext(ctx) + { + } + + void testGetAccessibleChild(); + void testGetAccessibleChildCount(); + void testGetAccessibleDescription(); + void testGetAccessibleIndexInParent(); + void testGetAccessibleName(); + void testGetAccessibleParent(); + void testGetAccessibleRelationSet(); + void testGetAccessibleRole(); + void testGetAccessibleStateSet(); + void testGetLocale(); + + void testAll() + { + testGetAccessibleChild(); + testGetAccessibleChildCount(); + testGetAccessibleDescription(); + testGetAccessibleIndexInParent(); + testGetAccessibleName(); + testGetAccessibleParent(); + testGetAccessibleRelationSet(); + testGetAccessibleRole(); + testGetAccessibleStateSet(); + testGetLocale(); + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.cxx b/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.cxx new file mode 100644 index 0000000000..852c91c341 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.cxx @@ -0,0 +1,185 @@ +/* -*- 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 "XAccessibleEventBroadcasterTester.hxx" + +#include <iostream> + +#include <cppunit/TestAssert.h> + +#include <com/sun/star/accessibility/AccessibleEventObject.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/lang/EventObject.hpp> + +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> + +#include <test/a11y/AccessibilityTools.hxx> + +using namespace css; + +namespace +{ +class EvListener : public cppu::WeakImplHelper<accessibility::XAccessibleEventListener> +{ +public: + bool mbGotEvent; + + EvListener() + : mbGotEvent(false) + { + } + + // XEventListener + virtual void SAL_CALL disposing(const lang::EventObject&) override {} + + // XAccessibleEventListener + virtual void SAL_CALL notifyEvent(const accessibility::AccessibleEventObject& aEvent) override + { + std::cout << "Listener got event: " << AccessibilityTools::debugString(aEvent) << std::endl; + uno::Reference<accessibility::XAccessible> xOld(aEvent.OldValue, uno::UNO_QUERY); + if (xOld.is()) + std::cout << "Old: " << AccessibilityTools::debugString(xOld) << std::endl; + + uno::Reference<accessibility::XAccessible> xNew(aEvent.NewValue, uno::UNO_QUERY); + if (xNew.is()) + std::cout << "New: " << AccessibilityTools::debugString(xNew) << std::endl; + + mbGotEvent = true; + } +}; +} + +XAccessibleEventBroadcasterTester::XAccessibleEventBroadcasterTester( + const uno::Reference<accessibility::XAccessibleEventBroadcaster>& xBroadcaster, + const uno::Reference<awt::XWindow>& xWindow) + : mxBroadcaster(xBroadcaster) + , mxWindow(xWindow) +{ +} + +bool XAccessibleEventBroadcasterTester::isTransient( + const uno::Reference<accessibility::XAccessibleEventBroadcaster> xBroadcaster) +{ + uno::Reference<accessibility::XAccessibleContext> xCtx(xBroadcaster, uno::UNO_QUERY_THROW); + return isTransient(xCtx); +} + +bool XAccessibleEventBroadcasterTester::isTransient( + const uno::Reference<accessibility::XAccessibleContext> xCtx) +{ + return ((xCtx->getAccessibleStateSet() & accessibility::AccessibleStateType::TRANSIENT) + && (xCtx->getAccessibleParent()->getAccessibleContext()->getAccessibleStateSet() + & accessibility::AccessibleStateType::MANAGES_DESCENDANTS)); +} + +/** + * @brief Generates an event on @c mxBroadcaster. + * + * This method indirectly alters the state of @c mxBroadcaster so that an + * accessible event will be fired for it. It can be called as many times as + * needed and triggers an event each time. + */ +void XAccessibleEventBroadcasterTester::fireEvent() +{ + awt::Rectangle newPosSize = mxWindow->getPosSize(); + newPosSize.Width = newPosSize.Width - 20; + newPosSize.Height = newPosSize.Height - 20; + newPosSize.X = newPosSize.X + 20; + newPosSize.Y = newPosSize.Y + 20; + mxWindow->setPosSize(newPosSize.X, newPosSize.Y, newPosSize.Width, newPosSize.Height, + awt::PosSize::POSSIZE); +} + +/** + * @brief Adds a listener and fires events by mean of object relation. + * + * Asserts that the listener was properly called. + */ +void XAccessibleEventBroadcasterTester::testAddEventListener() +{ + rtl::Reference<EvListener> xListener(new EvListener); + mxBroadcaster->addAccessibleEventListener(xListener); + bool transient = isTransient(mxBroadcaster); + + std::cout << "firing event" << std::endl; + fireEvent(); + + AccessibilityTools::Await([&xListener]() { return xListener->mbGotEvent; }); + + if (!transient) + CPPUNIT_ASSERT_MESSAGE("listener wasn't called", xListener->mbGotEvent); + else + CPPUNIT_ASSERT_MESSAGE("Object is Transient, listener isn't expected to be called", + !xListener->mbGotEvent); + + mxBroadcaster->removeAccessibleEventListener(xListener); +} + +/** + * @brief Similar to @c testAddEventListener() but also removes the listener + * + * Adds an event listener just like @c testAddEventListener(), and then removes it and verifies an + * event doesn't trigger the supposedly removed listener. + * + * @see testAddEventListener() + */ +void XAccessibleEventBroadcasterTester::testRemoveEventListener() +{ + /* there is nothing we can really test for transient objects */ + if (isTransient(mxBroadcaster)) + { + std::cerr << "could not test removing listener on transient object " << mxBroadcaster + << std::endl; + return; + } + + rtl::Reference<EvListener> xListener(new EvListener); + mxBroadcaster->addAccessibleEventListener(xListener); + + std::cout << "firing event (with listener)" << std::endl; + fireEvent(); + + AccessibilityTools::Await([&xListener]() { return xListener->mbGotEvent; }); + + CPPUNIT_ASSERT_MESSAGE("listener wasn't called", xListener->mbGotEvent); + + /* reset listener, remove it and try again */ + xListener->mbGotEvent = false; + + std::cout << "removing listener" << std::endl; + mxBroadcaster->removeAccessibleEventListener(xListener); + + std::cout << "firing event (without listener)" << std::endl; + fireEvent(); + + AccessibilityTools::Wait(500); + + CPPUNIT_ASSERT_MESSAGE("removed listener was called", !xListener->mbGotEvent); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.hxx b/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.hxx new file mode 100644 index 0000000000..0fc7c23bd3 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.hxx @@ -0,0 +1,54 @@ +/* -*- 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 <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/awt/XWindow.hpp> + +class XAccessibleEventBroadcasterTester +{ +private: + const css::uno::Reference<css::accessibility::XAccessibleEventBroadcaster> mxBroadcaster; + const css::uno::Reference<css::awt::XWindow> mxWindow; + + static bool isTransient( + const css::uno::Reference<css::accessibility::XAccessibleEventBroadcaster> xBroadcaster); + static bool isTransient(const css::uno::Reference<css::accessibility::XAccessibleContext> xCtx); + + void fireEvent(); + +public: + XAccessibleEventBroadcasterTester( + const css::uno::Reference<css::accessibility::XAccessibleEventBroadcaster>& xBroadcaster, + const css::uno::Reference<css::awt::XWindow>& xWindow); + + void testAddEventListener(); + void testRemoveEventListener(); + + void testAll() + { + testAddEventListener(); + testRemoveEventListener(); + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.cxx b/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.cxx new file mode 100644 index 0000000000..a7137c4ba5 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.cxx @@ -0,0 +1,53 @@ +/* -*- 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 "XAccessibleExtendedComponentTester.hxx" + +#include <iostream> + +#include <com/sun/star/accessibility/XAccessibleExtendedComponent.hpp> + +/** + * @brief Just calls the method. + */ +void XAccessibleExtendedComponentTester::testGetFont() +{ + auto font = mxExtendedComponent->getFont(); + std::cout << "font: " << font << std::endl; +} + +/** + * @brief Just calls the method. + */ +void XAccessibleExtendedComponentTester::testGetTitledBorderText() +{ + auto titleBorderText = mxExtendedComponent->getTitledBorderText(); + std::cout << "getTitledBorderText(): '" << titleBorderText << "'" << std::endl; +} + +/** + * @brief Just calls the method. + */ +void XAccessibleExtendedComponentTester::testGetToolTipText() +{ + auto toolTipText = mxExtendedComponent->getToolTipText(); + std::cout << "getToolTipText(): '" << toolTipText << "'" << std::endl; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.hxx b/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.hxx new file mode 100644 index 0000000000..1297052822 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.hxx @@ -0,0 +1,50 @@ +/* -*- 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 <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleExtendedComponent.hpp> + +class XAccessibleExtendedComponentTester +{ +protected: + const css::uno::Reference<css::accessibility::XAccessibleExtendedComponent> mxExtendedComponent; + +public: + XAccessibleExtendedComponentTester( + const css::uno::Reference<css::accessibility::XAccessibleExtendedComponent>& + extendedComponent) + : mxExtendedComponent(extendedComponent) + { + } + + void testGetFont(); + void testGetTitledBorderText(); + void testGetToolTipText(); + + void testAll() + { + testGetFont(); + testGetTitledBorderText(); + testGetToolTipText(); + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/unoapi/knownissues.xcl b/toolkit/qa/unoapi/knownissues.xcl new file mode 100644 index 0000000000..7a2f95577f --- /dev/null +++ b/toolkit/qa/unoapi/knownissues.xcl @@ -0,0 +1,259 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +### i86545 ### +toolkit.AccessibleToolBoxItem::com::sun::star::accessibility::XAccessibleText + +### i30818 ### +toolkit.AccessibleToolBox::com::sun::star::accessibility::XAccessibleEventBroadcaster + +### i68852 ### +toolkit.TabControllerModel::com::sun::star::io::XPersistObject + +### i79111 ### +toolkit.UnoControlButtonModel::com::sun::star::awt::UnoControlButtonModel + +### i79110 ### +toolkit.UnoControlCheckBoxModel::com::sun::star::awt::UnoControlCheckBoxModel + +### i84314 ### +toolkit.UnoControlImageControlModel::com::sun::star::awt::UnoControlImageControlModel + +### i86542 ### +toolkit.UnoControlProgressBarModel::com::sun::star::awt::UnoControlProgressBarModel + +### i86543 ### +toolkit.UnoControlRadioButtonModel::com::sun::star::awt::UnoControlRadioButtonModel + +### i79098 ### +toolkit.UnoControlScrollBarModel::com::sun::star::awt::UnoControlScrollBarModel + +### i86544 ### +toolkit.UnoTreeModel::com::sun::star::awt::tree::TreeControlModel + +### i74011 ### +toolkit.UnoControlDateField::com::sun::star::awt::XWindow +toolkit.UnoSpinButtonControl::com::sun::star::awt::XWindow +toolkit.UnoControlPatternField::com::sun::star::awt::XWindow +toolkit.UnoControlRadioButton::com::sun::star::awt::XWindow +toolkit.UnoControlButton::com::sun::star::awt::XWindow +toolkit.UnoControlTimeField::com::sun::star::awt::XWindow +toolkit.UnoControlFormattedField::com::sun::star::awt::XWindow +toolkit.UnoControlContainer::com::sun::star::awt::XWindow +toolkit.UnoControlGroupBox::com::sun::star::awt::XWindow +toolkit.UnoControlFixedText::com::sun::star::awt::XWindow +toolkit.UnoControlListBox::com::sun::star::awt::XWindow +toolkit.UnoControlImageControl::com::sun::star::awt::XWindow +toolkit.UnoControlDialog::com::sun::star::awt::XWindow +toolkit.UnoControlEdit::com::sun::star::awt::XWindow +toolkit.UnoControlCurrencyField::com::sun::star::awt::XWindow +toolkit.UnoControlFileControl::com::sun::star::awt::XWindow +toolkit.UnoControlComboBox::com::sun::star::awt::XWindow +toolkit.UnoControlNumericField::com::sun::star::awt::XWindow +toolkit.UnoControlCheckBox::com::sun::star::awt::XWindow +toolkit.UnoScrollBarControl::com::sun::star::awt::XWindow + +### i86296 ### +toolkit.UnoControlContainerModel::com::sun::star::awt::UnoControlContainerModel + +### i86297 ### +toolkit.UnoControlFixedTextModel::com::sun::star::awt::UnoControlFixedTextModel + +### i86299 ### +toolkit.UnoTreeModel::com::sun::star::awt::UnoControlModel + +### i86300 ### +toolkit.UnoTreeModel::com::sun::star::beans::XMultiPropertySet + +### i86007 ### +toolkit.AccessibleButton +# -> disabled in toolkit.sce + +### i86008 ### +toolkit.AccessibleComboBox +# -> disabled in toolkit.sce + +### i86110 ### +toolkit.AccessibleEdit +toolkit.AccessibleList +toolkit.AccessibleListBox +toolkit.AccessibleListItem +toolkit.AccessibleRadioButton +# -> disabled in toolkit.sce + +### i86009 ### +toolkit.AccessibleMenuItem +# -> disabled in toolkit.sce + +### i86107 ### +toolkit.AccessibleRadioButton +# -> disabled in toolkit.sce + +### i86287 ### +toolkit.AccessibleToolBox +# -> disabled in toolkit.sce + +### i86011 ### +toolkit.UnoControlFileControl +# -> disabled in toolkit.sce + +### i86013 ### +toolkit.UnoControlFormattedField +# -> disabled in toolkit.sce + +### i86019 ### +toolkit.UnoControlListBox +# -> disabled in toolkit.sce + +### i86298 ### +toolkit.UnoTreeControl +# -> disabled in toolkit.sce + +### i52607 ### +toolkit.AccessiblePopupMenu +# -> disabled in toolkit.sce + +### i86660 ### +toolkit.UnoControlDateField::com::sun::star::awt::XView +toolkit.UnoSpinButtonControl::com::sun::star::awt::XView +toolkit.UnoControlPatternField::com::sun::star::awt::XView +toolkit.UnoControlRadioButton::com::sun::star::awt::XView +toolkit.UnoControlButton::com::sun::star::awt::XView +toolkit.UnoControlTimeField::com::sun::star::awt::XView +toolkit.UnoControlFormattedField::com::sun::star::awt::XView +toolkit.UnoControlGroupBox::com::sun::star::awt::XView +toolkit.UnoControlContainer::com::sun::star::awt::XView +toolkit.UnoControlFixedText::com::sun::star::awt::XView +toolkit.UnoControlListBox::com::sun::star::awt::XView +toolkit.UnoControlEdit::com::sun::star::awt::XView +toolkit.UnoControlImageControl::com::sun::star::awt::XView +toolkit.UnoControlDialog::com::sun::star::awt::XView +toolkit.UnoControlFileControl::com::sun::star::awt::XView +toolkit.UnoControlCurrencyField::com::sun::star::awt::XView +toolkit.UnoControlComboBox::com::sun::star::awt::XView +toolkit.UnoControlNumericField::com::sun::star::awt::XView +toolkit.UnoControlCheckBox::com::sun::star::awt::XView +toolkit.UnoScrollBarControl::com::sun::star::awt::XView + +### i37643 ### +toolkit.AccessibleStatusBarItem::com::sun::star::accessibility::XAccessibleEventBroadcaster + +### i87864 ### +toolkit.UnoControlContainerModel::com::sun::star::lang::XComponent +toolkit.UnoTreeModel::com::sun::star::lang::XComponent +toolkit.UnoControlTimeFieldModel::com::sun::star::lang::XComponent +toolkit.UnoControlDateField::com::sun::star::lang::XComponent +toolkit.UnoControlButtonModel::com::sun::star::lang::XComponent +toolkit.UnoControlPatternFieldModel::com::sun::star::lang::XComponent +toolkit.UnoSpinButtonControl::com::sun::star::lang::XComponent +toolkit.UnoControlDateFieldModel::com::sun::star::lang::XComponent +toolkit.UnoControlPatternField::com::sun::star::lang::XComponent +toolkit.UnoControlRadioButtonModel::com::sun::star::lang::XComponent +toolkit.UnoControlFormattedFieldModel::com::sun::star::lang::XComponent +toolkit.UnoControlRadioButton::com::sun::star::lang::XComponent +toolkit.UnoControlButton::com::sun::star::lang::XComponent +toolkit.UnoControlTimeField::com::sun::star::lang::XComponent +toolkit.UnoControlFormattedField::com::sun::star::lang::XComponent +toolkit.UnoControlFixedLineModel::com::sun::star::lang::XComponent +toolkit.UnoControlDialogModel::com::sun::star::lang::XComponent +toolkit.UnoControlFileControlModel::com::sun::star::lang::XComponent +toolkit.UnoControlGroupBox::com::sun::star::lang::XComponent +toolkit.UnoControlContainer::com::sun::star::lang::XComponent +toolkit.UnoControlFixedText::com::sun::star::lang::XComponent +toolkit.UnoControlListBox::com::sun::star::lang::XComponent +toolkit.UnoControlCheckBoxModel::com::sun::star::lang::XComponent +toolkit.UnoControlEditModel::com::sun::star::lang::XComponent +toolkit.UnoControlEdit::com::sun::star::lang::XComponent +toolkit.UnoControlImageControl::com::sun::star::lang::XComponent +toolkit.UnoControlDialog::com::sun::star::lang::XComponent +toolkit.UnoControlGroupBoxModel::com::sun::star::lang::XComponent +toolkit.UnoControlImageControlModel::com::sun::star::lang::XComponent +toolkit.UnoControlNumericFieldModel::com::sun::star::lang::XComponent +toolkit.UnoControlFileControl::com::sun::star::lang::XComponent +toolkit.UnoControlCurrencyField::com::sun::star::lang::XComponent +toolkit.UnoControlComboBoxModel::com::sun::star::lang::XComponent +toolkit.UnoControlComboBox::com::sun::star::lang::XComponent +toolkit.UnoControlNumericField::com::sun::star::lang::XComponent +toolkit.UnoControlScrollBarModel::com::sun::star::lang::XComponent +toolkit.UnoControlProgressBarModel::com::sun::star::lang::XComponent +toolkit.UnoControlListBoxModel::com::sun::star::lang::XComponent +toolkit.UnoControlFixedTextModel::com::sun::star::lang::XComponent +toolkit.UnoSpinButtonControlModel::com::sun::star::lang::XComponent +toolkit.UnoControlCurrencyFieldModel::com::sun::star::lang::XComponent +toolkit.UnoControlCheckBox::com::sun::star::lang::XComponent +toolkit.UnoScrollBarControl::com::sun::star::lang::XComponent + +### i89415 ### +toolkit.UnoControlContainer::com::sun::star::awt::XControl + +### i89417 ### +toolkit.UnoControlContainer::com::sun::star::accessibility::XAccessible + +### i89418 ### +toolkit.UnoSpinButtonControl::com::sun::star::awt::XSpinValue + +### i88332 ### +toolkit.AccessibleCheckBox +# -> disabled in toolkit.sce + +### i88605 ### +toolkit.AccessibleToolBoxItem +# -> disabled in toolkit.sce + +### i89019 ### +toolkit.AccessibleWindow::com::sun::star::accessibility::XAccessibleEventBroadcaster + +### i90354 ### +toolkit.AccessibleScrollBar::com::sun::star::accessibility::XAccessibleComponent + +### i90356 ### +toolkit.UnoScrollBarControl::com::sun::star::awt::XScrollBar + +### i94344 ### +toolkit.AccessibleTabPage::com::sun::star::accessibility::XAccessibleText + +### i109643 ### +toolkit.AccessibleMenu::com::sun::star::accessibility::XAccessibleValue +toolkit.AccessibleMenuBar::com::sun::star::accessibility::XAccessibleEventBroadcaster +toolkit.AccessibleMenuBar::com::sun::star::accessibility::XAccessibleSelection + +### i111113 ### +toolkit.AccessibleStatusBarItem::com::sun::star::accessibility::XAccessibleComponent +toolkit.AccessibleStatusBarItem::com::sun::star::accessibility::XAccessibleContext + +### i111195 ### +toolkit.AccessibleScrollBar::com::sun::star::accessibility::XAccessibleValue + +### i113489 ### +toolkit.AccessibleMenu::com::sun::star::accessibility::XAccessibleText +toolkit.AccessibleMenuBar::com::sun::star::accessibility::XAccessibleComponent +toolkit.AccessibleMenuSeparator::com::sun::star::accessibility::XAccessibleComponent + +### i114213 ### +toolkit.AccessibleMenu::com::sun::star::accessibility::XAccessibleSelection + +### i114636 ### +toolkit.AccessibleScrollBar::com::sun::star::accessibility::XAccessibleAction +toolkit.AccessibleScrollBar::com::sun::star::accessibility::XAccessibleContext +toolkit.AccessibleScrollBar::com::sun::star::accessibility::XAccessibleEventBroadcaster + +### fd#35666 ### +toolkit.UnoControlDialogModel::com::sun::star::io::XPersistObject + +### fd#35772 ### +toolkit.Toolkit::com::sun::star::awt::XMessageBoxFactory diff --git a/toolkit/qa/unoapi/testdocuments/poliball.gif b/toolkit/qa/unoapi/testdocuments/poliball.gif Binary files differnew file mode 100644 index 0000000000..dda461bd34 --- /dev/null +++ b/toolkit/qa/unoapi/testdocuments/poliball.gif diff --git a/toolkit/qa/unoapi/toolkit_1.sce b/toolkit/qa/unoapi/toolkit_1.sce new file mode 100644 index 0000000000..3cee05dc3e --- /dev/null +++ b/toolkit/qa/unoapi/toolkit_1.sce @@ -0,0 +1,40 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +#i86007 -o toolkit.AccessibleButton +#i88332 -o toolkit.AccessibleCheckBox +#i86008 -o toolkit.AccessibleComboBox +-o toolkit.AccessibleDropDownComboBox +-o toolkit.AccessibleDropDownListBox +#i86110 -o toolkit.AccessibleEdit +-o toolkit.AccessibleFixedText +#i86110 -o toolkit.AccessibleList +#i86110 -o toolkit.AccessibleListBox +#i86110 -o toolkit.AccessibleListItem +-o toolkit.AccessibleMenu +-o toolkit.AccessibleMenuBar +#i86009 -o toolkit.AccessibleMenuItem +-o toolkit.AccessibleMenuSeparator +#i52607 -o toolkit.AccessiblePopupMenu +#i86107,i86110 -o toolkit.AccessibleRadioButton +-o toolkit.AccessibleScrollBar +-o toolkit.AccessibleStatusBarItem +#i109643 -o toolkit.AccessibleTabControl +#i109643 -o toolkit.AccessibleTabPage +#i86287 -o toolkit.AccessibleToolBox +#i88605 -o toolkit.AccessibleToolBoxItem diff --git a/toolkit/qa/unoapi/toolkit_2.sce b/toolkit/qa/unoapi/toolkit_2.sce new file mode 100644 index 0000000000..2c5957f34f --- /dev/null +++ b/toolkit/qa/unoapi/toolkit_2.sce @@ -0,0 +1,36 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this 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 toolkit.AccessibleWindow +-o toolkit.MutableTreeDataModel +-o toolkit.MutableTreeNode +-o toolkit.TabController +-o toolkit.TabControllerModel +-o toolkit.Toolkit +-o toolkit.UnoControlButton +-o toolkit.UnoControlButtonModel +-o toolkit.UnoControlCheckBox +-o toolkit.UnoControlCheckBoxModel +-o toolkit.UnoControlComboBox +-o toolkit.UnoControlComboBoxModel +-o toolkit.UnoControlContainer +-o toolkit.UnoControlContainerModel +-o toolkit.UnoControlCurrencyField +-o toolkit.UnoControlCurrencyFieldModel +-o toolkit.UnoControlDateField +-o toolkit.UnoControlDateFieldModel diff --git a/toolkit/qa/unoapi/toolkit_3.sce b/toolkit/qa/unoapi/toolkit_3.sce new file mode 100644 index 0000000000..91f07709e2 --- /dev/null +++ b/toolkit/qa/unoapi/toolkit_3.sce @@ -0,0 +1,37 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +#allegedly was i80788, but still broken: -o toolkit.UnoControlDialog +-o toolkit.UnoControlDialogModel +-o toolkit.UnoControlEdit +-o toolkit.UnoControlEditModel +#i86011 -o toolkit.UnoControlFileControl +-o toolkit.UnoControlFileControlModel +-o toolkit.UnoControlFixedLineModel +-o toolkit.UnoControlFixedText +-o toolkit.UnoControlFixedTextModel +#i86013 -o toolkit.UnoControlFormattedField +-o toolkit.UnoControlFormattedFieldModel +-o toolkit.UnoControlGroupBox +-o toolkit.UnoControlGroupBoxModel +-o toolkit.UnoControlImageControl +-o toolkit.UnoControlImageControlModel +#i86019 -o toolkit.UnoControlListBox +-o toolkit.UnoControlListBoxModel +-o toolkit.UnoControlNumericField +-o toolkit.UnoControlNumericFieldModel diff --git a/toolkit/qa/unoapi/toolkit_4.sce b/toolkit/qa/unoapi/toolkit_4.sce new file mode 100644 index 0000000000..d8cdcbe380 --- /dev/null +++ b/toolkit/qa/unoapi/toolkit_4.sce @@ -0,0 +1,31 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this 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 toolkit.UnoControlPatternField +-o toolkit.UnoControlPatternFieldModel +-o toolkit.UnoControlProgressBarModel +-o toolkit.UnoControlRadioButton +-o toolkit.UnoControlRadioButtonModel +-o toolkit.UnoControlScrollBarModel +-o toolkit.UnoControlTimeField +-o toolkit.UnoControlTimeFieldModel +-o toolkit.UnoScrollBarControl +-o toolkit.UnoSpinButtonControl +-o toolkit.UnoSpinButtonControlModel +#i86298 -o toolkit.UnoTreeControl +-o toolkit.UnoTreeModel diff --git a/toolkit/source/awt/animatedimagespeer.cxx b/toolkit/source/awt/animatedimagespeer.cxx new file mode 100644 index 0000000000..379a82388b --- /dev/null +++ b/toolkit/source/awt/animatedimagespeer.cxx @@ -0,0 +1,476 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <awt/animatedimagespeer.hxx> +#include <helper/property.hxx> + +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/ImageScaleMode.hpp> + +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <o3tl/safeint.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/urlobj.hxx> +#include <vcl/toolkit/throbber.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <limits> +#include <string_view> + +namespace toolkit +{ + + + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::container::ContainerEvent; + using ::com::sun::star::awt::XAnimatedImages; + using ::com::sun::star::awt::Size; + using ::com::sun::star::graphic::XGraphicProvider; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::graphic::XGraphic; + + namespace ImageScaleMode = ::com::sun::star::awt::ImageScaleMode; + + //= helper + + namespace + { + + OUString lcl_getHighContrastURL( OUString const& i_imageURL ) + { + INetURLObject aURL( i_imageURL ); + if ( aURL.GetProtocol() != INetProtocol::PrivSoffice ) + { + OSL_VERIFY( aURL.insertName( u"sifr", false, 0 ) ); + return aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + // the private: scheme is not considered to be hierarchical by INetURLObject, so manually insert the + // segment + const sal_Int32 separatorPos = i_imageURL.indexOf( '/' ); + ENSURE_OR_RETURN( separatorPos != -1, "lcl_getHighContrastURL: unsupported URL scheme - cannot automatically determine HC version!", i_imageURL ); + + OUString composer = OUString::Concat(i_imageURL.subView(0, separatorPos)) + "/sifr" + + i_imageURL.subView(separatorPos); + return composer; + } + + + bool lcl_ensureImage_throw( Reference< XGraphicProvider > const& i_graphicProvider, const bool i_isHighContrast, const AnimatedImagesPeer::CachedImage& i_cachedImage ) + { + if ( !i_cachedImage.xGraphic.is() ) + { + ::comphelper::NamedValueCollection aMediaProperties; + if ( i_isHighContrast ) + { + // try (to find) the high-contrast version of the graphic first + aMediaProperties.put( "URL", lcl_getHighContrastURL( i_cachedImage.sImageURL ) ); + i_cachedImage.xGraphic = i_graphicProvider->queryGraphic( aMediaProperties.getPropertyValues() ); + } + if ( !i_cachedImage.xGraphic.is() ) + { + aMediaProperties.put( "URL", i_cachedImage.sImageURL ); + i_cachedImage.xGraphic = i_graphicProvider->queryGraphic( aMediaProperties.getPropertyValues() ); + } + } + return i_cachedImage.xGraphic.is(); + } + + + Size lcl_getGraphicSizePixel( Reference< XGraphic > const& i_graphic ) + { + Size aSizePixel; + try + { + if ( i_graphic.is() ) + { + const Reference< XPropertySet > xGraphicProps( i_graphic, UNO_QUERY_THROW ); + OSL_VERIFY( xGraphicProps->getPropertyValue("SizePixel") >>= aSizePixel ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + return aSizePixel; + } + + + void lcl_init( Sequence< OUString > const& i_imageURLs, ::std::vector< AnimatedImagesPeer::CachedImage >& o_images ) + { + o_images.resize(0); + size_t count = size_t( i_imageURLs.getLength() ); + o_images.reserve( count ); + for ( const auto& rImageURL : i_imageURLs ) + { + o_images.emplace_back( AnimatedImagesPeer::CachedImage{ rImageURL, nullptr } ); + } + } + + + } + + + //= AnimatedImagesPeer + + + AnimatedImagesPeer::AnimatedImagesPeer() + { + } + + + AnimatedImagesPeer::~AnimatedImagesPeer() + { + } + + + void SAL_CALL AnimatedImagesPeer::startAnimation() + { + SolarMutexGuard aGuard; + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if (pThrobber) + pThrobber->start(); + } + + void SAL_CALL AnimatedImagesPeer::stopAnimation() + { + SolarMutexGuard aGuard; + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if (pThrobber) + pThrobber->stop(); + } + + sal_Bool SAL_CALL AnimatedImagesPeer::isAnimationRunning() + { + SolarMutexGuard aGuard; + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if (pThrobber) + return pThrobber->isRunning(); + return false; + } + + void SAL_CALL AnimatedImagesPeer::setProperty( const OUString& i_propertyName, const Any& i_value ) + { + SolarMutexGuard aGuard; + + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if ( !pThrobber ) + { + VCLXWindow::setProperty( i_propertyName, i_value ); + return; + } + + const sal_uInt16 nPropertyId = GetPropertyId( i_propertyName ); + switch ( nPropertyId ) + { + case BASEPROPERTY_STEP_TIME: + { + sal_Int32 nStepTime( 0 ); + if ( i_value >>= nStepTime ) + pThrobber->setStepTime( nStepTime ); + break; + } + case BASEPROPERTY_AUTO_REPEAT: + { + bool bRepeat( true ); + if ( i_value >>= bRepeat ) + pThrobber->setRepeat( bRepeat ); + break; + } + + case BASEPROPERTY_IMAGE_SCALE_MODE: + { + sal_Int16 nScaleMode( ImageScaleMode::ANISOTROPIC ); + VclPtr<ImageControl> pImageControl = GetAsDynamic< ImageControl >(); + if ( pImageControl && ( i_value >>= nScaleMode ) ) + pImageControl->SetScaleMode( nScaleMode ); + } + break; + + default: + AnimatedImagesPeer_Base::setProperty( i_propertyName, i_value ); + break; + } + } + + + Any SAL_CALL AnimatedImagesPeer::getProperty( const OUString& i_propertyName ) + { + SolarMutexGuard aGuard; + + Any aReturn; + + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if ( !pThrobber ) + return VCLXWindow::getProperty( i_propertyName ); + + const sal_uInt16 nPropertyId = GetPropertyId( i_propertyName ); + switch ( nPropertyId ) + { + case BASEPROPERTY_STEP_TIME: + aReturn <<= pThrobber->getStepTime(); + break; + + case BASEPROPERTY_AUTO_REPEAT: + aReturn <<= pThrobber->getRepeat(); + break; + + case BASEPROPERTY_IMAGE_SCALE_MODE: + { + VclPtr<ImageControl> pImageControl = GetAsDynamic<ImageControl>(); + aReturn <<= ( pImageControl ? pImageControl->GetScaleMode() : ImageScaleMode::ANISOTROPIC ); + } + break; + + default: + aReturn = AnimatedImagesPeer_Base::getProperty( i_propertyName ); + break; + } + + return aReturn; + } + + + void AnimatedImagesPeer::ProcessWindowEvent( const VclWindowEvent& i_windowEvent ) + { + if ( i_windowEvent.GetId() == VclEventId::WindowResize ) + { + updateImageList_nothrow(); + } + + AnimatedImagesPeer_Base::ProcessWindowEvent( i_windowEvent ); + } + + + void AnimatedImagesPeer::impl_updateImages_nolck( const Reference< XInterface >& i_animatedImages ) + { + SolarMutexGuard aGuard; + + updateImageList_nothrow( Reference< XAnimatedImages >( i_animatedImages, UNO_QUERY_THROW ) ); + } + + + void SAL_CALL AnimatedImagesPeer::elementInserted( const ContainerEvent& i_event ) + { + SolarMutexGuard aGuard; + Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW ); + + sal_Int32 nPosition(0); + OSL_VERIFY( i_event.Accessor >>= nPosition ); + size_t position = size_t( nPosition ); + if ( position > maCachedImageSets.size() ) + { + OSL_ENSURE( false, "AnimatedImagesPeer::elementInserted: illegal accessor/index!" ); + updateImageList_nothrow( xAnimatedImages ); + } + + Sequence< OUString > aImageURLs; + OSL_VERIFY( i_event.Element >>= aImageURLs ); + ::std::vector< CachedImage > aImages; + lcl_init( aImageURLs, aImages ); + maCachedImageSets.insert( maCachedImageSets.begin() + position, aImages ); + updateImageList_nothrow(); + } + + + void SAL_CALL AnimatedImagesPeer::elementRemoved( const ContainerEvent& i_event ) + { + SolarMutexGuard aGuard; + Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW ); + + sal_Int32 nPosition(0); + OSL_VERIFY( i_event.Accessor >>= nPosition ); + size_t position = size_t( nPosition ); + if ( position >= maCachedImageSets.size() ) + { + OSL_ENSURE( false, "AnimatedImagesPeer::elementRemoved: illegal accessor/index!" ); + updateImageList_nothrow( xAnimatedImages ); + } + + maCachedImageSets.erase( maCachedImageSets.begin() + position ); + updateImageList_nothrow(); + } + + + void SAL_CALL AnimatedImagesPeer::elementReplaced( const ContainerEvent& i_event ) + { + SolarMutexGuard aGuard; + Reference< XAnimatedImages > xAnimatedImages( i_event.Source, UNO_QUERY_THROW ); + + sal_Int32 nPosition(0); + OSL_VERIFY( i_event.Accessor >>= nPosition ); + size_t position = size_t( nPosition ); + if ( position >= maCachedImageSets.size() ) + { + OSL_ENSURE( false, "AnimatedImagesPeer::elementReplaced: illegal accessor/index!" ); + updateImageList_nothrow( xAnimatedImages ); + } + + Sequence< OUString > aImageURLs; + OSL_VERIFY( i_event.Element >>= aImageURLs ); + ::std::vector< CachedImage > aImages; + lcl_init( aImageURLs, aImages ); + maCachedImageSets[ position ] = aImages; + updateImageList_nothrow(); + } + + + void SAL_CALL AnimatedImagesPeer::disposing( const EventObject& i_event ) + { + VCLXWindow::disposing( i_event ); + } + + + void SAL_CALL AnimatedImagesPeer::modified( const EventObject& i_event ) + { + impl_updateImages_nolck( i_event.Source ); + } + + + void SAL_CALL AnimatedImagesPeer::dispose( ) + { + AnimatedImagesPeer_Base::dispose(); + SolarMutexGuard aGuard; + maCachedImageSets.resize(0); + } + + void AnimatedImagesPeer::updateImageList_nothrow() + { + VclPtr<Throbber> pThrobber = GetAsDynamic<Throbber>(); + if ( !pThrobber ) + return; + + try + { + // collect the image sizes of the different image sets + const Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + const Reference< XGraphicProvider > xGraphicProvider( css::graphic::GraphicProvider::create(xContext) ); + + const bool isHighContrast = pThrobber->GetSettings().GetStyleSettings().GetHighContrastMode(); + + sal_Int32 nPreferredSet = -1; + const size_t nImageSetCount = maCachedImageSets.size(); + if ( nImageSetCount < 2 ) + { + nPreferredSet = sal_Int32( nImageSetCount ) - 1; + } + else + { + ::std::vector< Size > aImageSizes( nImageSetCount ); + for ( size_t nImageSet = 0; nImageSet < nImageSetCount; ++nImageSet ) + { + ::std::vector< CachedImage > const& rImageSet( maCachedImageSets[ nImageSet ] ); + if ( ( rImageSet.empty() ) + || ( !lcl_ensureImage_throw( xGraphicProvider, isHighContrast, rImageSet[0] ) ) + ) + { + aImageSizes[ nImageSet ] = Size( SAL_MAX_INT32, SAL_MAX_INT32 ); + } + else + { + aImageSizes[ nImageSet ] = lcl_getGraphicSizePixel( rImageSet[0].xGraphic ); + } + } + + // find the set with the smallest difference between window size and image size + const ::Size aWindowSizePixel = pThrobber->GetSizePixel(); + tools::Long nMinimalDistance = ::std::numeric_limits< tools::Long >::max(); + for ( ::std::vector< Size >::const_iterator check = aImageSizes.begin(); + check != aImageSizes.end(); + ++check + ) + { + if ( ( check->Width > aWindowSizePixel.Width() ) + || ( check->Height > aWindowSizePixel.Height() ) + ) + // do not use an image set which doesn't fit into the window + continue; + + const sal_Int64 distance = + ( aWindowSizePixel.Width() - check->Width ) * ( aWindowSizePixel.Width() - check->Width ) + + ( aWindowSizePixel.Height() - check->Height ) * ( aWindowSizePixel.Height() - check->Height ); + if ( distance < nMinimalDistance ) + { + nMinimalDistance = distance; + nPreferredSet = check - aImageSizes.begin(); + } + } + } + + // found a set? + std::vector< Image > aImages; + if ( ( nPreferredSet >= 0 ) && ( o3tl::make_unsigned( nPreferredSet ) < nImageSetCount ) ) + { + // => set the images + ::std::vector< CachedImage > const& rImageSet( maCachedImageSets[ nPreferredSet ] ); + aImages.resize( rImageSet.size() ); + sal_Int32 imageIndex = 0; + for ( const auto& rCachedImage : rImageSet ) + { + lcl_ensureImage_throw( xGraphicProvider, isHighContrast, rCachedImage ); + aImages[ imageIndex++ ] = Image(rCachedImage.xGraphic); + } + } + pThrobber->setImageList( std::move(aImages) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + } + + + void AnimatedImagesPeer::updateImageList_nothrow( const Reference< XAnimatedImages >& i_images ) + { + try + { + const sal_Int32 nImageSetCount = i_images->getImageSetCount(); + maCachedImageSets.resize(0); + for ( sal_Int32 set = 0; set < nImageSetCount; ++set ) + { + const Sequence< OUString > aImageURLs( i_images->getImageSet( set ) ); + ::std::vector< CachedImage > aImages; + lcl_init( aImageURLs, aImages ); + maCachedImageSets.push_back( aImages ); + } + + updateImageList_nothrow(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + } + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/asynccallback.cxx b/toolkit/source/awt/asynccallback.cxx new file mode 100644 index 0000000000..f6804d9c23 --- /dev/null +++ b/toolkit/source/awt/asynccallback.cxx @@ -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 . + */ + +#include <sal/config.h> + +#include <utility> +#include <vcl/svapp.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/awt/XRequestCallback.hpp> + +/// anonymous implementation namespace +namespace { + +class AsyncCallback: + public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::awt::XRequestCallback> +{ +public: + AsyncCallback() {} + AsyncCallback(const AsyncCallback&) = delete; + AsyncCallback& operator=(const AsyncCallback&) = delete; + + // css::lang::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; + + // css::awt::XRequestCallback: + virtual void SAL_CALL addCallback(const css::uno::Reference< css::awt::XCallback > & xCallback, const css::uno::Any & aData) override; + +private: + + struct CallbackData + { + CallbackData( css::uno::Reference< css::awt::XCallback > _xCallback, css::uno::Any aAny ) : + xCallback(std::move( _xCallback )), aData(std::move( aAny )) {} + + css::uno::Reference< css::awt::XCallback > xCallback; + css::uno::Any aData; + }; + + DECL_STATIC_LINK( AsyncCallback, Notify_Impl, void*, void ); + + virtual ~AsyncCallback() override {} +}; + +// com.sun.star.uno.XServiceInfo: +OUString SAL_CALL AsyncCallback::getImplementationName() +{ + return "com.sun.star.awt.comp.AsyncCallback"; +} + +sal_Bool SAL_CALL AsyncCallback::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > SAL_CALL AsyncCallback::getSupportedServiceNames() +{ + return { "com.sun.star.awt.AsyncCallback" }; +} + +// css::awt::XRequestCallback: +void SAL_CALL AsyncCallback::addCallback(const css::uno::Reference< css::awt::XCallback > & xCallback, const css::uno::Any & aData) +{ + if ( Application::IsInMain() ) + { + // NOTE: We don't need SolarMutexGuard here as Application::PostUserEvent is thread-safe + CallbackData* pCallbackData = new CallbackData( xCallback, aData ); + Application::PostUserEvent( LINK( this, AsyncCallback, Notify_Impl ), pCallbackData ); + } +} + +// private asynchronous link to call reference to the callback object +IMPL_STATIC_LINK( AsyncCallback, Notify_Impl, void*, p, void ) +{ + CallbackData* pCallbackData = static_cast<CallbackData*>(p); + try + { + // Asynchronous execution + // Check pointer and reference before! + if ( pCallbackData && pCallbackData->xCallback.is() ) + pCallbackData->xCallback->notify( pCallbackData->aData ); + } + catch ( css::uno::Exception& ) + { + } + + delete pCallbackData; +} + +} // closing anonymous implementation namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_awt_comp_AsyncCallback_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new AsyncCallback()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/non-interactable-containers.xml b/toolkit/source/awt/non-interactable-containers.xml new file mode 100644 index 0000000000..00f3f2a93e --- /dev/null +++ b/toolkit/source/awt/non-interactable-containers.xml @@ -0,0 +1,32 @@ +<?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 . + --> + +<dialog xmlns="http://openoffice.org/2007/layout" + xmlns:cnt="http://openoffice.org/2007/layout/container" + title="Interactable Containers" optimumsize="true" + border="true" sizeable="true" moveable="true"> + <hbox> + <table columns="3" cnt:title="Page 1"> + <pushbutton cnt:x-expand="false" cnt:row-span="2" label="1,1" /> + <pushbutton cnt:y-expand="false" label="1,2" /> + <pushbutton cnt:y-expand="false" label="1,3" /> + <pushbutton cnt:col-span="2" label="2,1" /> + </table> + </hbox> +</dialog> diff --git a/toolkit/source/awt/scrollabledialog.cxx b/toolkit/source/awt/scrollabledialog.cxx new file mode 100644 index 0000000000..bcc8d18e07 --- /dev/null +++ b/toolkit/source/awt/scrollabledialog.cxx @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <helper/scrollabledialog.hxx> +#include <vcl/settings.hxx> + +namespace toolkit +{ + +// Using WB_AUTOHSCROLL, WB_AUTOVSCROLL here sucks big time, there is a +// problem in the toolkit class where there are some clashing IDs +// ( css::awt::VclWindowPeerAttribute::VSCROLL has the same value +// as css::awt::WindowAttribute::NODECORATION and they are used +// in the same bitmap :-( WB_VSCROLL & WB_HSCROLL apparently are only for +// child classes ( whole thing is a mess if you ask me ) +ScrollableDialog::ScrollableDialog( vcl::Window* pParent, WinBits nStyle, Dialog::InitFlag eFlag ) + : Dialog( pParent, nStyle & ~( WB_AUTOHSCROLL | WB_AUTOVSCROLL ), eFlag ), + maHScrollBar( VclPtr<ScrollBar>::Create(this, WB_HSCROLL | WB_DRAG) ), + maVScrollBar( VclPtr<ScrollBar>::Create(this, WB_VSCROLL | WB_DRAG) ), + mbHasHoriBar( false ), + mbHasVertBar( false ), + maScrollVis( None ) +{ + Link<ScrollBar*,void> aLink( LINK( this, ScrollableDialog, ScrollBarHdl ) ); + maVScrollBar->SetScrollHdl( aLink ); + maHScrollBar->SetScrollHdl( aLink ); + + ScrollBarVisibility aVis = None; + + if ( nStyle & ( WB_AUTOHSCROLL | WB_AUTOVSCROLL ) ) + { + if ( nStyle & WB_AUTOHSCROLL ) + aVis = Hori; + if ( nStyle & WB_AUTOVSCROLL ) + { + if ( aVis == Hori ) + aVis = Both; + else + aVis = Vert; + } + } + setScrollVisibility( aVis ); + mnScrWidth = Dialog::GetSettings().GetStyleSettings().GetScrollBarSize(); +} + +void ScrollableDialog::setScrollVisibility( ScrollBarVisibility rVisState ) +{ + maScrollVis = rVisState; + if ( maScrollVis == Hori || maScrollVis == Both ) + { + mbHasHoriBar = true; + maHScrollBar->Show(); + } + if ( maScrollVis == Vert || maScrollVis == Both ) + { + mbHasVertBar = true; + maVScrollBar->Show(); + } + if ( mbHasHoriBar || mbHasVertBar ) + SetStyle( Dialog::GetStyle() | WB_CLIPCHILDREN ); +} + +ScrollableDialog::~ScrollableDialog() +{ + disposeOnce(); +} + +void ScrollableDialog::dispose() +{ + maHScrollBar.disposeAndClear(); + maVScrollBar.disposeAndClear(); + Dialog::dispose(); +} + +void ScrollableDialog::lcl_Scroll( tools::Long nX, tools::Long nY ) +{ + tools::Long nXScroll = mnScrollPos.X() - nX; + tools::Long nYScroll = mnScrollPos.Y() - nY; + mnScrollPos = Point( nX, nY ); + + tools::Rectangle aScrollableArea( 0, 0, maScrollArea.Width(), maScrollArea.Height() ); + Scroll(nXScroll, nYScroll, aScrollableArea ); + // Manually scroll all children ( except the scrollbars ) + for ( int index = 0; index < GetChildCount(); ++index ) + { + vcl::Window* pChild = GetChild( index ); + if ( pChild && pChild != maVScrollBar.get() && pChild != maHScrollBar.get() ) + { + Point aPos = pChild->GetPosPixel(); + aPos += Point( nXScroll, nYScroll ); + pChild->SetPosPixel( aPos ); + } + } +} + +IMPL_LINK( ScrollableDialog, ScrollBarHdl, ScrollBar*, pSB, void ) +{ + sal_uInt16 nPos = static_cast<sal_uInt16>(pSB->GetThumbPos()); + if( pSB == maVScrollBar.get() ) + lcl_Scroll(mnScrollPos.X(), nPos ); + else if( pSB == maHScrollBar.get() ) + lcl_Scroll(nPos, mnScrollPos.Y() ); +} + +void ScrollableDialog::SetScrollTop( tools::Long nTop ) +{ + Point aOld = mnScrollPos; + lcl_Scroll( mnScrollPos.X() , mnScrollPos.Y() - nTop ); + maHScrollBar->SetThumbPos( 0 ); + // new pos is 0,0 + mnScrollPos = aOld; +} +void ScrollableDialog::SetScrollLeft( tools::Long nLeft ) +{ + Point aOld = mnScrollPos; + lcl_Scroll( mnScrollPos.X() - nLeft , mnScrollPos.Y() ); + maVScrollBar->SetThumbPos( 0 ); + // new pos is 0,0 + mnScrollPos = aOld; +} + +void ScrollableDialog::SetScrollWidth( tools::Long nWidth ) +{ + maScrollArea.setWidth( nWidth ); + ResetScrollBars(); +} + +void ScrollableDialog::SetScrollHeight( tools::Long nHeight ) +{ + maScrollArea.setHeight( nHeight ); + ResetScrollBars(); +} + +void ScrollableDialog::Resize() +{ + ResetScrollBars(); +} + +void ScrollableDialog::ResetScrollBars() +{ + Size aOutSz = GetOutputSizePixel(); + + Point aVPos( aOutSz.Width() - mnScrWidth, 0 ); + Point aHPos( 0, aOutSz.Height() - mnScrWidth ); + + maVScrollBar->SetPosSizePixel( aVPos, Size( mnScrWidth, GetSizePixel().Height() - mnScrWidth ) ); + maHScrollBar->SetPosSizePixel( aHPos, Size( GetSizePixel().Width() - mnScrWidth, mnScrWidth ) ); + + maHScrollBar->SetRangeMax( maScrollArea.Width() + mnScrWidth ); + maHScrollBar->SetVisibleSize( GetSizePixel().Width() ); + + maVScrollBar->SetRangeMax( maScrollArea.Height() + mnScrWidth ); + maVScrollBar->SetVisibleSize( GetSizePixel().Height() ); +} + +} // toolkit +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/stylesettings.cxx b/toolkit/source/awt/stylesettings.cxx new file mode 100644 index 0000000000..75c2b687d2 --- /dev/null +++ b/toolkit/source/awt/stylesettings.cxx @@ -0,0 +1,926 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "stylesettings.hxx" +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <com/sun/star/lang/DisposedException.hpp> + +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <vcl/event.hxx> +#include <vcl/window.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + + +namespace toolkit +{ + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::lang::DisposedException; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::awt::FontDescriptor; + using ::com::sun::star::awt::XStyleChangeListener; + + + IMPL_LINK( WindowStyleSettings, OnWindowEvent, VclWindowEvent&, rEvent, void ) + { + if ( rEvent.GetId() != VclEventId::WindowDataChanged ) + return; + const DataChangedEvent* pDataChangedEvent = static_cast< const DataChangedEvent* >( rEvent.GetData() ); + if ( !pDataChangedEvent || ( pDataChangedEvent->GetType() != DataChangedEventType::SETTINGS ) ) + return; + if ( !( pDataChangedEvent->GetFlags() & AllSettingsFlags::STYLE ) ) + return; + + EventObject aEvent( *pOwningWindow ); + aStyleChangeListeners.notifyEach( &XStyleChangeListener::styleSettingsChanged, aEvent ); + } + + + //= StyleMethodGuard + + namespace { + + class StyleMethodGuard + { + public: + explicit StyleMethodGuard( const VCLXWindow* pOwningWindow ) + { + if ( pOwningWindow == nullptr ) + throw DisposedException(); + } + + private: + SolarMutexGuard m_aGuard; + }; + + } + + //= WindowStyleSettings + + + WindowStyleSettings::WindowStyleSettings(::osl::Mutex& i_rListenerMutex, VCLXWindow& i_rOwningWindow ) + : pOwningWindow( &i_rOwningWindow ) + ,aStyleChangeListeners( i_rListenerMutex ) + { + VclPtr<vcl::Window> pWindow = i_rOwningWindow.GetWindow(); + if ( !pWindow ) + throw RuntimeException(); + pWindow->AddEventListener( LINK( this, WindowStyleSettings, OnWindowEvent ) ); + } + + + WindowStyleSettings::~WindowStyleSettings() + { + } + + + void WindowStyleSettings::dispose() + { + StyleMethodGuard aGuard( pOwningWindow ); + + VclPtr<vcl::Window> pWindow = pOwningWindow->GetWindow(); + OSL_ENSURE( pWindow, "WindowStyleSettings::dispose: window has been reset before we could revoke the listener!" ); + if ( pWindow ) + pWindow->RemoveEventListener( LINK( this, WindowStyleSettings, OnWindowEvent ) ); + + EventObject aEvent( *this ); + aStyleChangeListeners.disposeAndClear( aEvent ); + + pOwningWindow = nullptr; + } + + + sal_Int32 WindowStyleSettings::ImplGetStyleColor( Color const & (StyleSettings::*i_pGetter)() const ) const + { + const VclPtr<vcl::Window>& pWindow = pOwningWindow->GetWindow(); + const AllSettings aAllSettings = pWindow->GetSettings(); + const StyleSettings& aStyleSettings = aAllSettings.GetStyleSettings(); + return sal_Int32((aStyleSettings.*i_pGetter)()); + } + + void WindowStyleSettings::ImplSetStyleColor( void (StyleSettings::*i_pSetter)( Color const & ), sal_Int32 i_nColor ) + { + VclPtr<vcl::Window> pWindow = pOwningWindow->GetWindow(); + AllSettings aAllSettings = pWindow->GetSettings(); + StyleSettings aStyleSettings = aAllSettings.GetStyleSettings(); + (aStyleSettings.*i_pSetter)( Color(ColorTransparency, i_nColor) ); + aAllSettings.SetStyleSettings( aStyleSettings ); + pWindow->SetSettings( aAllSettings ); + } + + FontDescriptor WindowStyleSettings::ImplGetStyleFont( vcl::Font const & (StyleSettings::*i_pGetter)() const ) const + { + const VclPtr<vcl::Window>& pWindow = pOwningWindow->GetWindow(); + const AllSettings aAllSettings = pWindow->GetSettings(); + const StyleSettings& aStyleSettings = aAllSettings.GetStyleSettings(); + return VCLUnoHelper::CreateFontDescriptor( (aStyleSettings.*i_pGetter)() ); + } + + void WindowStyleSettings::ImplSetStyleFont( void (StyleSettings::*i_pSetter)( vcl::Font const &), + vcl::Font const & (StyleSettings::*i_pGetter)() const, const FontDescriptor& i_rFont ) + { + VclPtr<vcl::Window> pWindow = pOwningWindow->GetWindow(); + AllSettings aAllSettings = pWindow->GetSettings(); + StyleSettings aStyleSettings = aAllSettings.GetStyleSettings(); + const vcl::Font aNewFont = VCLUnoHelper::CreateFont( i_rFont, (aStyleSettings.*i_pGetter)() ); + (aStyleSettings.*i_pSetter)( aNewFont ); + aAllSettings.SetStyleSettings( aStyleSettings ); + pWindow->SetSettings( aAllSettings ); + } + + ::sal_Int32 SAL_CALL WindowStyleSettings::getActiveBorderColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetActiveBorderColor ); + } + + + void SAL_CALL WindowStyleSettings::setActiveBorderColor( ::sal_Int32 _activebordercolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetActiveBorderColor, _activebordercolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getActiveColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetActiveColor ); + } + + + void SAL_CALL WindowStyleSettings::setActiveColor( ::sal_Int32 _activecolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetActiveColor, _activecolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getActiveTabColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetActiveTabColor ); + } + + + void SAL_CALL WindowStyleSettings::setActiveTabColor( ::sal_Int32 _activetabcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetActiveTabColor, _activetabcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getActiveTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetActiveTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setActiveTextColor( ::sal_Int32 _activetextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetActiveTextColor, _activetextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getButtonRolloverTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetButtonRolloverTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setButtonRolloverTextColor( ::sal_Int32 _buttonrollovertextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetButtonRolloverTextColor, _buttonrollovertextcolor ); + // Also need to set ActionButtonRolloverTextColor as this setting can't be + // set through the UNO interface otherwise. + // Previously this setting was used to set colors for both scenarios, + // but action button setting was added to differentiate the buttons from + // "normal" buttons in some themes. + ImplSetStyleColor( &StyleSettings::SetActionButtonRolloverTextColor, _buttonrollovertextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getButtonTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetButtonTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setButtonTextColor( ::sal_Int32 _buttontextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetButtonTextColor, _buttontextcolor ); + // Also need to set ActionButtonTextColor and DefaultActionButtonTextColor + // as this two settings can't be set through the UNO interface otherwise. + // Previously this setting was used to set colors for all three scenarios, + // but action button setting was added to differentiate the buttons from + // "normal" buttons in some themes. + ImplSetStyleColor( &StyleSettings::SetActionButtonTextColor, _buttontextcolor ); + ImplSetStyleColor( &StyleSettings::SetDefaultActionButtonTextColor, _buttontextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getCheckedColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetCheckedColor ); + } + + + void SAL_CALL WindowStyleSettings::setCheckedColor( ::sal_Int32 _checkedcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetCheckedColor, _checkedcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDarkShadowColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDarkShadowColor ); + } + + + void SAL_CALL WindowStyleSettings::setDarkShadowColor( ::sal_Int32 _darkshadowcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDarkShadowColor, _darkshadowcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDeactiveBorderColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDeactiveBorderColor ); + } + + + void SAL_CALL WindowStyleSettings::setDeactiveBorderColor( ::sal_Int32 _deactivebordercolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDeactiveBorderColor, _deactivebordercolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDeactiveColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDeactiveColor ); + } + + + void SAL_CALL WindowStyleSettings::setDeactiveColor( ::sal_Int32 _deactivecolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDeactiveColor, _deactivecolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDeactiveTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDeactiveTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setDeactiveTextColor( ::sal_Int32 _deactivetextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDeactiveTextColor, _deactivetextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDialogColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDialogColor ); + } + + + void SAL_CALL WindowStyleSettings::setDialogColor( ::sal_Int32 _dialogcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDialogColor, _dialogcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDialogTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDialogTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setDialogTextColor( ::sal_Int32 _dialogtextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDialogTextColor, _dialogtextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getDisableColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetDisableColor ); + } + + + void SAL_CALL WindowStyleSettings::setDisableColor( ::sal_Int32 _disablecolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetDisableColor, _disablecolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getFaceColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetFaceColor ); + } + + + void SAL_CALL WindowStyleSettings::setFaceColor( ::sal_Int32 _facecolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetFaceColor, _facecolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getFaceGradientColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + const VclPtr<vcl::Window>& pWindow = pOwningWindow->GetWindow(); + const AllSettings aAllSettings = pWindow->GetSettings(); + const StyleSettings& aStyleSettings = aAllSettings.GetStyleSettings(); + return sal_Int32(aStyleSettings.GetFaceGradientColor()); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getFieldColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetFieldColor ); + } + + + void SAL_CALL WindowStyleSettings::setFieldColor( ::sal_Int32 _fieldcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetFieldColor, _fieldcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getFieldRolloverTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetFieldRolloverTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setFieldRolloverTextColor( ::sal_Int32 _fieldrollovertextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetFieldRolloverTextColor, _fieldrollovertextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getFieldTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetFieldTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setFieldTextColor( ::sal_Int32 _fieldtextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetFieldTextColor, _fieldtextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getGroupTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetGroupTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setGroupTextColor( ::sal_Int32 _grouptextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetGroupTextColor, _grouptextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getHelpColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetHelpColor ); + } + + + void SAL_CALL WindowStyleSettings::setHelpColor( ::sal_Int32 _helpcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetHelpColor, _helpcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getHelpTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetHelpTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setHelpTextColor( ::sal_Int32 _helptextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetHelpTextColor, _helptextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getHighlightColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetHighlightColor ); + } + + + void SAL_CALL WindowStyleSettings::setHighlightColor( ::sal_Int32 _highlightcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetHighlightColor, _highlightcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getHighlightTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetHighlightTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setHighlightTextColor( ::sal_Int32 _highlighttextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetHighlightTextColor, _highlighttextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getInactiveTabColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetInactiveTabColor ); + } + + + void SAL_CALL WindowStyleSettings::setInactiveTabColor( ::sal_Int32 _inactivetabcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetInactiveTabColor, _inactivetabcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getLabelTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetLabelTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setLabelTextColor( ::sal_Int32 _labeltextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetLabelTextColor, _labeltextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getLightColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetLightColor ); + } + + + void SAL_CALL WindowStyleSettings::setLightColor( ::sal_Int32 _lightcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetLightColor, _lightcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuBarColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuBarColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuBarColor( ::sal_Int32 _menubarcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuBarColor, _menubarcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuBarTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuBarTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuBarTextColor( ::sal_Int32 _menubartextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuBarTextColor, _menubartextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuBorderColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuBorderColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuBorderColor( ::sal_Int32 _menubordercolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuBorderColor, _menubordercolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuColor( ::sal_Int32 _menucolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuColor, _menucolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuHighlightColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuHighlightColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuHighlightColor( ::sal_Int32 _menuhighlightcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuHighlightColor, _menuhighlightcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuHighlightTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuHighlightTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuHighlightTextColor( ::sal_Int32 _menuhighlighttextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuHighlightTextColor, _menuhighlighttextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMenuTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMenuTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setMenuTextColor( ::sal_Int32 _menutextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMenuTextColor, _menutextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getMonoColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetMonoColor ); + } + + + void SAL_CALL WindowStyleSettings::setMonoColor( ::sal_Int32 _monocolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetMonoColor, _monocolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getRadioCheckTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetRadioCheckTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setRadioCheckTextColor( ::sal_Int32 _radiochecktextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetRadioCheckTextColor, _radiochecktextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getSeparatorColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + const VclPtr<vcl::Window>& pWindow = pOwningWindow->GetWindow(); + const AllSettings aAllSettings = pWindow->GetSettings(); + const StyleSettings& aStyleSettings = aAllSettings.GetStyleSettings(); + return sal_Int32(aStyleSettings.GetSeparatorColor()); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getShadowColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetShadowColor ); + } + + + void SAL_CALL WindowStyleSettings::setShadowColor( ::sal_Int32 _shadowcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetShadowColor, _shadowcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getWindowColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetWindowColor ); + } + + + void SAL_CALL WindowStyleSettings::setWindowColor( ::sal_Int32 _windowcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetWindowColor, _windowcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getWindowTextColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetWindowTextColor ); + } + + + void SAL_CALL WindowStyleSettings::setWindowTextColor( ::sal_Int32 _windowtextcolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetWindowTextColor, _windowtextcolor ); + } + + + ::sal_Int32 SAL_CALL WindowStyleSettings::getWorkspaceColor() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleColor( &StyleSettings::GetWorkspaceColor ); + } + + + void SAL_CALL WindowStyleSettings::setWorkspaceColor( ::sal_Int32 _workspacecolor ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleColor( &StyleSettings::SetWorkspaceColor, _workspacecolor ); + } + + + sal_Bool SAL_CALL WindowStyleSettings::getHighContrastMode() + { + StyleMethodGuard aGuard( pOwningWindow ); + const VclPtr<vcl::Window>& pWindow = pOwningWindow->GetWindow(); + const AllSettings aAllSettings = pWindow->GetSettings(); + const StyleSettings& aStyleSettings = aAllSettings.GetStyleSettings(); + return aStyleSettings.GetHighContrastMode(); + } + + + void SAL_CALL WindowStyleSettings::setHighContrastMode( sal_Bool _highcontrastmode ) + { + StyleMethodGuard aGuard( pOwningWindow ); + VclPtr<vcl::Window> pWindow = pOwningWindow->GetWindow(); + AllSettings aAllSettings = pWindow->GetSettings(); + StyleSettings aStyleSettings = aAllSettings.GetStyleSettings(); + aStyleSettings.SetHighContrastMode( _highcontrastmode ); + aAllSettings.SetStyleSettings( aStyleSettings ); + pWindow->SetSettings( aAllSettings ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getApplicationFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetAppFont ); + } + + + void SAL_CALL WindowStyleSettings::setApplicationFont( const FontDescriptor& _applicationfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetAppFont, &StyleSettings::GetAppFont, _applicationfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getHelpFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetHelpFont ); + } + + + void SAL_CALL WindowStyleSettings::setHelpFont( const FontDescriptor& _helpfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetHelpFont, &StyleSettings::GetHelpFont, _helpfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getTitleFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetTitleFont ); + } + + + void SAL_CALL WindowStyleSettings::setTitleFont( const FontDescriptor& _titlefont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetTitleFont, &StyleSettings::GetTitleFont, _titlefont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getFloatTitleFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetFloatTitleFont ); + } + + + void SAL_CALL WindowStyleSettings::setFloatTitleFont( const FontDescriptor& _floattitlefont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetFloatTitleFont, &StyleSettings::GetFloatTitleFont, _floattitlefont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getMenuFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetMenuFont ); + } + + + void SAL_CALL WindowStyleSettings::setMenuFont( const FontDescriptor& _menufont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetMenuFont, &StyleSettings::GetMenuFont, _menufont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getToolFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetToolFont ); + } + + + void SAL_CALL WindowStyleSettings::setToolFont( const FontDescriptor& _toolfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetToolFont, &StyleSettings::GetToolFont, _toolfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getGroupFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetGroupFont ); + } + + + void SAL_CALL WindowStyleSettings::setGroupFont( const FontDescriptor& _groupfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetGroupFont, &StyleSettings::GetGroupFont, _groupfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getLabelFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetLabelFont ); + } + + + void SAL_CALL WindowStyleSettings::setLabelFont( const FontDescriptor& _labelfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetLabelFont, &StyleSettings::GetLabelFont, _labelfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getRadioCheckFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetRadioCheckFont ); + } + + + void SAL_CALL WindowStyleSettings::setRadioCheckFont( const FontDescriptor& _radiocheckfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetRadioCheckFont, &StyleSettings::GetRadioCheckFont, _radiocheckfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getPushButtonFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetPushButtonFont ); + } + + + void SAL_CALL WindowStyleSettings::setPushButtonFont( const FontDescriptor& _pushbuttonfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetPushButtonFont, &StyleSettings::GetPushButtonFont, _pushbuttonfont ); + } + + + FontDescriptor SAL_CALL WindowStyleSettings::getFieldFont() + { + StyleMethodGuard aGuard( pOwningWindow ); + return ImplGetStyleFont( &StyleSettings::GetFieldFont ); + } + + + void SAL_CALL WindowStyleSettings::setFieldFont( const FontDescriptor& _fieldfont ) + { + StyleMethodGuard aGuard( pOwningWindow ); + ImplSetStyleFont( &StyleSettings::SetFieldFont, &StyleSettings::GetFieldFont, _fieldfont ); + } + + + void SAL_CALL WindowStyleSettings::addStyleChangeListener( const Reference< XStyleChangeListener >& i_rListener ) + { + StyleMethodGuard aGuard( pOwningWindow ); + if ( i_rListener.is() ) + aStyleChangeListeners.addInterface( i_rListener ); + } + + + void SAL_CALL WindowStyleSettings::removeStyleChangeListener( const Reference< XStyleChangeListener >& i_rListener ) + { + StyleMethodGuard aGuard( pOwningWindow ); + if ( i_rListener.is() ) + aStyleChangeListeners.removeInterface( i_rListener ); + } + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/stylesettings.hxx b/toolkit/source/awt/stylesettings.hxx new file mode 100644 index 0000000000..4e3f0b2df9 --- /dev/null +++ b/toolkit/source/awt/stylesettings.hxx @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_SOURCE_AWT_STYLESETTINGS_HXX +#define INCLUDED_TOOLKIT_SOURCE_AWT_STYLESETTINGS_HXX + +#include <com/sun/star/awt/XStyleSettings.hpp> + +#include <comphelper/interfacecontainer3.hxx> +#include <cppuhelper/implbase.hxx> +#include <tools/link.hxx> + +namespace osl +{ + class Mutex; +} +class Color; +class VCLXWindow; +class VclWindowEvent; +class StyleSettings; +namespace vcl { class Font; } + +namespace toolkit +{ + + + //= WindowStyleSettings + + typedef ::cppu::WeakImplHelper < css::awt::XStyleSettings + > WindowStyleSettings_Base; + class WindowStyleSettings : public WindowStyleSettings_Base + { + public: + WindowStyleSettings( ::osl::Mutex& i_rListenerMutex, VCLXWindow& i_rOwningWindow ); + virtual ~WindowStyleSettings() override; + + void dispose(); + + // XStyleSettings + virtual ::sal_Int32 SAL_CALL getActiveBorderColor() override; + virtual void SAL_CALL setActiveBorderColor( ::sal_Int32 _activebordercolor ) override; + virtual ::sal_Int32 SAL_CALL getActiveColor() override; + virtual void SAL_CALL setActiveColor( ::sal_Int32 _activecolor ) override; + virtual ::sal_Int32 SAL_CALL getActiveTabColor() override; + virtual void SAL_CALL setActiveTabColor( ::sal_Int32 _activetabcolor ) override; + virtual ::sal_Int32 SAL_CALL getActiveTextColor() override; + virtual void SAL_CALL setActiveTextColor( ::sal_Int32 _activetextcolor ) override; + virtual ::sal_Int32 SAL_CALL getButtonRolloverTextColor() override; + virtual void SAL_CALL setButtonRolloverTextColor( ::sal_Int32 _buttonrollovertextcolor ) override; + virtual ::sal_Int32 SAL_CALL getButtonTextColor() override; + virtual void SAL_CALL setButtonTextColor( ::sal_Int32 _buttontextcolor ) override; + virtual ::sal_Int32 SAL_CALL getCheckedColor() override; + virtual void SAL_CALL setCheckedColor( ::sal_Int32 _checkedcolor ) override; + virtual ::sal_Int32 SAL_CALL getDarkShadowColor() override; + virtual void SAL_CALL setDarkShadowColor( ::sal_Int32 _darkshadowcolor ) override; + virtual ::sal_Int32 SAL_CALL getDeactiveBorderColor() override; + virtual void SAL_CALL setDeactiveBorderColor( ::sal_Int32 _deactivebordercolor ) override; + virtual ::sal_Int32 SAL_CALL getDeactiveColor() override; + virtual void SAL_CALL setDeactiveColor( ::sal_Int32 _deactivecolor ) override; + virtual ::sal_Int32 SAL_CALL getDeactiveTextColor() override; + virtual void SAL_CALL setDeactiveTextColor( ::sal_Int32 _deactivetextcolor ) override; + virtual ::sal_Int32 SAL_CALL getDialogColor() override; + virtual void SAL_CALL setDialogColor( ::sal_Int32 _dialogcolor ) override; + virtual ::sal_Int32 SAL_CALL getDialogTextColor() override; + virtual void SAL_CALL setDialogTextColor( ::sal_Int32 _dialogtextcolor ) override; + virtual ::sal_Int32 SAL_CALL getDisableColor() override; + virtual void SAL_CALL setDisableColor( ::sal_Int32 _disablecolor ) override; + virtual ::sal_Int32 SAL_CALL getFaceColor() override; + virtual void SAL_CALL setFaceColor( ::sal_Int32 _facecolor ) override; + virtual ::sal_Int32 SAL_CALL getFaceGradientColor() override; + virtual ::sal_Int32 SAL_CALL getFieldColor() override; + virtual void SAL_CALL setFieldColor( ::sal_Int32 _fieldcolor ) override; + virtual ::sal_Int32 SAL_CALL getFieldRolloverTextColor() override; + virtual void SAL_CALL setFieldRolloverTextColor( ::sal_Int32 _fieldrollovertextcolor ) override; + virtual ::sal_Int32 SAL_CALL getFieldTextColor() override; + virtual void SAL_CALL setFieldTextColor( ::sal_Int32 _fieldtextcolor ) override; + virtual ::sal_Int32 SAL_CALL getGroupTextColor() override; + virtual void SAL_CALL setGroupTextColor( ::sal_Int32 _grouptextcolor ) override; + virtual ::sal_Int32 SAL_CALL getHelpColor() override; + virtual void SAL_CALL setHelpColor( ::sal_Int32 _helpcolor ) override; + virtual ::sal_Int32 SAL_CALL getHelpTextColor() override; + virtual void SAL_CALL setHelpTextColor( ::sal_Int32 _helptextcolor ) override; + virtual ::sal_Int32 SAL_CALL getHighlightColor() override; + virtual void SAL_CALL setHighlightColor( ::sal_Int32 _highlightcolor ) override; + virtual ::sal_Int32 SAL_CALL getHighlightTextColor() override; + virtual void SAL_CALL setHighlightTextColor( ::sal_Int32 _highlighttextcolor ) override; + virtual ::sal_Int32 SAL_CALL getInactiveTabColor() override; + virtual void SAL_CALL setInactiveTabColor( ::sal_Int32 _inactivetabcolor ) override; + virtual ::sal_Int32 SAL_CALL getLabelTextColor() override; + virtual void SAL_CALL setLabelTextColor( ::sal_Int32 _labeltextcolor ) override; + virtual ::sal_Int32 SAL_CALL getLightColor() override; + virtual void SAL_CALL setLightColor( ::sal_Int32 _lightcolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuBarColor() override; + virtual void SAL_CALL setMenuBarColor( ::sal_Int32 _menubarcolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuBarTextColor() override; + virtual void SAL_CALL setMenuBarTextColor( ::sal_Int32 _menubartextcolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuBorderColor() override; + virtual void SAL_CALL setMenuBorderColor( ::sal_Int32 _menubordercolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuColor() override; + virtual void SAL_CALL setMenuColor( ::sal_Int32 _menucolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuHighlightColor() override; + virtual void SAL_CALL setMenuHighlightColor( ::sal_Int32 _menuhighlightcolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuHighlightTextColor() override; + virtual void SAL_CALL setMenuHighlightTextColor( ::sal_Int32 _menuhighlighttextcolor ) override; + virtual ::sal_Int32 SAL_CALL getMenuTextColor() override; + virtual void SAL_CALL setMenuTextColor( ::sal_Int32 _menutextcolor ) override; + virtual ::sal_Int32 SAL_CALL getMonoColor() override; + virtual void SAL_CALL setMonoColor( ::sal_Int32 _monocolor ) override; + virtual ::sal_Int32 SAL_CALL getRadioCheckTextColor() override; + virtual void SAL_CALL setRadioCheckTextColor( ::sal_Int32 _radiochecktextcolor ) override; + virtual ::sal_Int32 SAL_CALL getSeparatorColor() override; + virtual ::sal_Int32 SAL_CALL getShadowColor() override; + virtual void SAL_CALL setShadowColor( ::sal_Int32 _shadowcolor ) override; + virtual ::sal_Int32 SAL_CALL getWindowColor() override; + virtual void SAL_CALL setWindowColor( ::sal_Int32 _windowcolor ) override; + virtual ::sal_Int32 SAL_CALL getWindowTextColor() override; + virtual void SAL_CALL setWindowTextColor( ::sal_Int32 _windowtextcolor ) override; + virtual ::sal_Int32 SAL_CALL getWorkspaceColor() override; + virtual void SAL_CALL setWorkspaceColor( ::sal_Int32 _workspacecolor ) override; + virtual sal_Bool SAL_CALL getHighContrastMode() override; + virtual void SAL_CALL setHighContrastMode( sal_Bool _highcontrastmode ) override; + virtual css::awt::FontDescriptor SAL_CALL getApplicationFont() override; + virtual void SAL_CALL setApplicationFont( const css::awt::FontDescriptor& _applicationfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getHelpFont() override; + virtual void SAL_CALL setHelpFont( const css::awt::FontDescriptor& _helpfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getTitleFont() override; + virtual void SAL_CALL setTitleFont( const css::awt::FontDescriptor& _titlefont ) override; + virtual css::awt::FontDescriptor SAL_CALL getFloatTitleFont() override; + virtual void SAL_CALL setFloatTitleFont( const css::awt::FontDescriptor& _floattitlefont ) override; + virtual css::awt::FontDescriptor SAL_CALL getMenuFont() override; + virtual void SAL_CALL setMenuFont( const css::awt::FontDescriptor& _menufont ) override; + virtual css::awt::FontDescriptor SAL_CALL getToolFont() override; + virtual void SAL_CALL setToolFont( const css::awt::FontDescriptor& _toolfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getGroupFont() override; + virtual void SAL_CALL setGroupFont( const css::awt::FontDescriptor& _groupfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getLabelFont() override; + virtual void SAL_CALL setLabelFont( const css::awt::FontDescriptor& _labelfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getRadioCheckFont() override; + virtual void SAL_CALL setRadioCheckFont( const css::awt::FontDescriptor& _radiocheckfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getPushButtonFont() override; + virtual void SAL_CALL setPushButtonFont( const css::awt::FontDescriptor& _pushbuttonfont ) override; + virtual css::awt::FontDescriptor SAL_CALL getFieldFont() override; + virtual void SAL_CALL setFieldFont( const css::awt::FontDescriptor& _fieldfont ) override; + virtual void SAL_CALL addStyleChangeListener( const css::uno::Reference< css::awt::XStyleChangeListener >& Listener ) override; + virtual void SAL_CALL removeStyleChangeListener( const css::uno::Reference< css::awt::XStyleChangeListener >& Listener ) override; + + private: + void ImplSetStyleFont( void (StyleSettings::*i_pSetter)( vcl::Font const &), + vcl::Font const & (StyleSettings::*i_pGetter)() const, const css::awt::FontDescriptor& i_rFont ); + void ImplSetStyleColor( void (StyleSettings::*i_pSetter)( Color const & ), sal_Int32 i_nColor ); + sal_Int32 ImplGetStyleColor( Color const & (StyleSettings::*i_pGetter)() const ) const; + css::awt::FontDescriptor ImplGetStyleFont( vcl::Font const & (StyleSettings::*i_pGetter)() const ) const; + DECL_LINK( OnWindowEvent, VclWindowEvent&, void ); + + VCLXWindow* pOwningWindow; + ::comphelper::OInterfaceContainerHelper3<css::awt::XStyleChangeListener> aStyleChangeListeners; + }; + + +} // namespace toolkit + + +#endif // INCLUDED_TOOLKIT_SOURCE_AWT_STYLESETTINGS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxaccessiblecomponent.cxx b/toolkit/source/awt/vclxaccessiblecomponent.cxx new file mode 100644 index 0000000000..9c2321ede3 --- /dev/null +++ b/toolkit/source/awt/vclxaccessiblecomponent.cxx @@ -0,0 +1,854 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/accessiblecontexthelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <toolkit/awt/vclxaccessiblecomponent.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/helper/convert.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <vcl/vclevent.hxx> +#include <vcl/window.hxx> +#include <vcl/toolkit/edit.hxx> +#include <vcl/settings.hxx> +#include <tools/debug.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/menu.hxx> + +using namespace ::com::sun::star; +using namespace ::comphelper; + +VCLXAccessibleComponent::VCLXAccessibleComponent( VCLXWindow* pVCLXWindow ) +{ + m_xVCLXWindow = pVCLXWindow; + + DBG_ASSERT( pVCLXWindow->GetWindow(), "VCLXAccessibleComponent - no window!" ); + m_xEventSource = pVCLXWindow->GetWindow(); + if ( m_xEventSource ) + { + m_xEventSource->AddEventListener( LINK( this, VCLXAccessibleComponent, WindowEventListener ) ); + m_xEventSource->AddChildEventListener( LINK( this, VCLXAccessibleComponent, WindowChildEventListener ) ); + } + + // announce the XAccessible of our creator to the base class + lateInit( pVCLXWindow ); +} + +VCLXWindow* VCLXAccessibleComponent::GetVCLXWindow() const +{ + return m_xVCLXWindow.get(); +} + +void VCLXAccessibleComponent::DisconnectEvents() +{ + if ( m_xEventSource ) + { + m_xEventSource->RemoveEventListener( LINK( this, VCLXAccessibleComponent, WindowEventListener ) ); + m_xEventSource->RemoveChildEventListener( LINK( this, VCLXAccessibleComponent, WindowChildEventListener ) ); + m_xEventSource.clear(); + } +} + +VCLXAccessibleComponent::~VCLXAccessibleComponent() +{ + ensureDisposed(); + DisconnectEvents(); +} + +OUString VCLXAccessibleComponent::getImplementationName() +{ + return "com.sun.star.comp.toolkit.AccessibleWindow"; +} + +sal_Bool VCLXAccessibleComponent::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence< OUString > VCLXAccessibleComponent::getSupportedServiceNames() +{ + uno::Sequence< OUString > aNames { "com.sun.star.awt.AccessibleWindow" }; + return aNames; +} + +IMPL_LINK( VCLXAccessibleComponent, WindowEventListener, VclWindowEvent&, rEvent, void ) +{ + /* Ignore VclEventId::WindowEndPopupMode, because the UNO accessibility wrapper + * might have been destroyed by the previous VCLEventListener (if no AT tool + * is running), e.g. sub-toolbars in impress. + */ + if ( m_xVCLXWindow.is() /* #122218# */ && (rEvent.GetId() != VclEventId::WindowEndPopupMode) ) + { + DBG_ASSERT( rEvent.GetWindow(), "Window???" ); + if( !rEvent.GetWindow()->IsAccessibilityEventsSuppressed() || ( rEvent.GetId() == VclEventId::ObjectDying ) ) + { + ProcessWindowEvent( rEvent ); + } + } +} + +IMPL_LINK( VCLXAccessibleComponent, WindowChildEventListener, VclWindowEvent&, rEvent, void ) +{ + if ( m_xVCLXWindow.is() /* #i68079# */ ) + { + DBG_ASSERT( rEvent.GetWindow(), "Window???" ); + if( !rEvent.GetWindow()->IsAccessibilityEventsSuppressed() ) + { + // #103087# to prevent an early release of the component + uno::Reference< accessibility::XAccessibleContext > xHoldAlive = this; + + ProcessWindowChildEvent( rEvent ); + } + } +} + +uno::Reference< accessibility::XAccessible > VCLXAccessibleComponent::GetChildAccessible( const VclWindowEvent& rVclWindowEvent ) +{ + // checks if the data in the window event is our direct child + // and returns its accessible + + // MT: Change this later, normally a show/hide event shouldn't have the vcl::Window* in pData. + vcl::Window* pChildWindow = static_cast<vcl::Window *>(rVclWindowEvent.GetData()); + // tdf#141101/tdf#156561 Handle the event if this is either the a11y parent or the + // vcl::Window parent, since child events are sent for the vcl::Window hierarchy + // (s. Window::CallEventListeners) and e.g. DockingManager does manual partial reparenting + // that would cause child events to not be forwarded to the a11y level when + // not taking GetParent() into account here + if (pChildWindow && (GetWindow() == pChildWindow->GetAccessibleParentWindow() + || GetWindow() == pChildWindow->GetParent())) + return pChildWindow->GetAccessible( rVclWindowEvent.GetId() == VclEventId::WindowShow ); + else + return uno::Reference< accessibility::XAccessible > (); +} + +void VCLXAccessibleComponent::ProcessWindowChildEvent( const VclWindowEvent& rVclWindowEvent ) +{ + uno::Any aOldValue, aNewValue; + uno::Reference< accessibility::XAccessible > xAcc; + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::WindowShow: // send create on show for direct accessible children + { + xAcc = GetChildAccessible( rVclWindowEvent ); + if( xAcc.is() ) + { + aNewValue <<= xAcc; + NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + + // CHILD event above results in a11y event listeners getting registered, + // so send state change event for SHOWING event after that + uno::Reference<XAccessibleContext> xChildContext = xAcc->getAccessibleContext(); + if (xChildContext.is()) + { + VCLXAccessibleComponent* pChildComponent = dynamic_cast<VCLXAccessibleComponent*>(xChildContext.get()); + if (pChildComponent) + { + css::uno::Any aNewStateValue; + aNewStateValue <<= accessibility::AccessibleStateType::SHOWING; + pChildComponent->NotifyAccessibleEvent(accessibility::AccessibleEventId::STATE_CHANGED, css::uno::Any(), aNewStateValue); + } + } + } + } + break; + case VclEventId::WindowHide: // send destroy on hide for direct accessible children + { + xAcc = GetChildAccessible( rVclWindowEvent ); + if( xAcc.is() ) + { + // send state change event for SHOWING before sending the CHILD event below, + // since that one results in a11y event listeners getting removed + uno::Reference<XAccessibleContext> xChildContext = xAcc->getAccessibleContext(); + if (xChildContext.is()) + { + VCLXAccessibleComponent* pChildComponent = dynamic_cast<VCLXAccessibleComponent*>(xChildContext.get()); + if (pChildComponent) + { + css::uno::Any aOldStateValue; + aOldStateValue <<= accessibility::AccessibleStateType::SHOWING; + pChildComponent->NotifyAccessibleEvent(accessibility::AccessibleEventId::STATE_CHANGED, aOldStateValue, css::uno::Any()); + } + } + + aOldValue <<= xAcc; + NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + } + } + break; + default: break; + } +} + +void VCLXAccessibleComponent::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + uno::Any aOldValue, aNewValue; + + vcl::Window* pAccWindow = rVclWindowEvent.GetWindow(); + assert(pAccWindow && "VCLXAccessibleComponent::ProcessWindowEvent - Window?"); + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ObjectDying: + { + DisconnectEvents(); + m_xVCLXWindow.clear(); + } + break; + case VclEventId::WindowChildDestroyed: + { + vcl::Window* pWindow = static_cast<vcl::Window*>(rVclWindowEvent.GetData()); + DBG_ASSERT( pWindow, "VclEventId::WindowChildDestroyed - Window=?" ); + if ( pWindow->GetAccessible( false ).is() ) + { + aOldValue <<= pWindow->GetAccessible( false ); + NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + } + } + break; + case VclEventId::WindowActivate: + { + sal_Int16 aAccessibleRole = getAccessibleRole(); + // avoid notification if a child frame is already active + // only one frame may be active at a given time + if ( !pAccWindow->HasActiveChildFrame() && + ( aAccessibleRole == accessibility::AccessibleRole::FRAME || + aAccessibleRole == accessibility::AccessibleRole::ALERT || + aAccessibleRole == accessibility::AccessibleRole::DIALOG ) ) // #i18891# + { + aNewValue <<= accessibility::AccessibleStateType::ACTIVE; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + break; + case VclEventId::WindowDeactivate: + { + sal_Int16 aAccessibleRole = getAccessibleRole(); + if ( aAccessibleRole == accessibility::AccessibleRole::FRAME || + aAccessibleRole == accessibility::AccessibleRole::ALERT || + aAccessibleRole == accessibility::AccessibleRole::DIALOG ) // #i18891# + { + aOldValue <<= accessibility::AccessibleStateType::ACTIVE; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + break; + case VclEventId::WindowGetFocus: + case VclEventId::ControlGetFocus: + { + if( (pAccWindow->IsCompoundControl() && rVclWindowEvent.GetId() == VclEventId::ControlGetFocus) || + (!pAccWindow->IsCompoundControl() && rVclWindowEvent.GetId() == VclEventId::WindowGetFocus) ) + { + // if multiple listeners were registered it is possible that the + // focus was changed during event processing (eg SfxTopWindow ) + // #106082# allow ChildPathFocus only for CompoundControls, for windows the focus must be in the window itself + if( (pAccWindow->IsCompoundControl() && pAccWindow->HasChildPathFocus()) || + (!pAccWindow->IsCompoundControl() && pAccWindow->HasFocus()) ) + { + aNewValue <<= accessibility::AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + } + break; + case VclEventId::WindowLoseFocus: + case VclEventId::ControlLoseFocus: + { + if( (pAccWindow->IsCompoundControl() && rVclWindowEvent.GetId() == VclEventId::ControlLoseFocus) || + (!pAccWindow->IsCompoundControl() && rVclWindowEvent.GetId() == VclEventId::WindowLoseFocus) ) + { + aOldValue <<= accessibility::AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + break; + case VclEventId::WindowFrameTitleChanged: + { + OUString aOldName( *static_cast<OUString*>(rVclWindowEvent.GetData()) ); + OUString aNewName( getAccessibleName() ); + aOldValue <<= aOldName; + aNewValue <<= aNewName; + NotifyAccessibleEvent( accessibility::AccessibleEventId::NAME_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowEnabled: + { + aNewValue <<= accessibility::AccessibleStateType::ENABLED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + aNewValue <<= accessibility::AccessibleStateType::SENSITIVE; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowDisabled: + { + aOldValue <<= accessibility::AccessibleStateType::SENSITIVE; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + + aOldValue <<= accessibility::AccessibleStateType::ENABLED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowMove: + case VclEventId::WindowResize: + { + NotifyAccessibleEvent( accessibility::AccessibleEventId::BOUNDRECT_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowMenubarAdded: + { + MenuBar* pMenuBar = static_cast<MenuBar*>(rVclWindowEvent.GetData()); + if ( pMenuBar ) + { + uno::Reference< accessibility::XAccessible > xChild( pMenuBar->GetAccessible() ); + if ( xChild.is() ) + { + aNewValue <<= xChild; + NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + } + } + } + break; + case VclEventId::WindowMenubarRemoved: + { + MenuBar* pMenuBar = static_cast<MenuBar*>(rVclWindowEvent.GetData()); + if ( pMenuBar ) + { + uno::Reference< accessibility::XAccessible > xChild( pMenuBar->GetAccessible() ); + if ( xChild.is() ) + { + aOldValue <<= xChild; + NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + } + } + } + break; + case VclEventId::WindowMinimize: + { + aNewValue <<= accessibility::AccessibleStateType::ICONIFIED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowNormalize: + { + aOldValue <<= accessibility::AccessibleStateType::ICONIFIED; + NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowHide: + case VclEventId::WindowShow: + // WindowHide and WindowShow are handled in ProcessWindowChildEvent so the right order + // regarding the CHILD event can be taken into account + default: + { + } + break; + } +} + +void VCLXAccessibleComponent::disposing() +{ + DisconnectEvents(); + + OAccessibleExtendedComponentHelper::disposing(); + + m_xVCLXWindow.clear(); +} + +vcl::Window* VCLXAccessibleComponent::GetWindow() const +{ + return GetVCLXWindow() ? GetVCLXWindow()->GetWindow() + : nullptr; +} + +void VCLXAccessibleComponent::FillAccessibleRelationSet( utl::AccessibleRelationSetHelper& rRelationSet ) +{ + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( !pWindow ) + return; + + vcl::Window *pLabeledBy = pWindow->GetAccessibleRelationLabeledBy(); + if ( pLabeledBy && pLabeledBy != pWindow ) + { + uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pLabeledBy->GetAccessible() }; + rRelationSet.AddRelation( accessibility::AccessibleRelation( accessibility::AccessibleRelationType::LABELED_BY, aSequence ) ); + } + + vcl::Window* pLabelFor = pWindow->GetAccessibleRelationLabelFor(); + if ( pLabelFor && pLabelFor != pWindow ) + { + uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pLabelFor->GetAccessible() }; + rRelationSet.AddRelation( accessibility::AccessibleRelation( accessibility::AccessibleRelationType::LABEL_FOR, aSequence ) ); + } + + vcl::Window* pMemberOf = pWindow->GetAccessibleRelationMemberOf(); + if ( pMemberOf && pMemberOf != pWindow ) + { + uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pMemberOf->GetAccessible() }; + rRelationSet.AddRelation( accessibility::AccessibleRelation( accessibility::AccessibleRelationType::MEMBER_OF, aSequence ) ); + } +} + +void VCLXAccessibleComponent::FillAccessibleStateSet( sal_Int64& rStateSet ) +{ + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + if ( pWindow->IsVisible() ) + { + rStateSet |= accessibility::AccessibleStateType::VISIBLE; + rStateSet |= accessibility::AccessibleStateType::SHOWING; + } + else + { + rStateSet |= accessibility::AccessibleStateType::INVALID; + } + + if ( pWindow->IsEnabled() ) + { + rStateSet |= accessibility::AccessibleStateType::ENABLED; + rStateSet |= accessibility::AccessibleStateType::SENSITIVE; + } + + if ( pWindow->HasChildPathFocus() && + ( getAccessibleRole() == accessibility::AccessibleRole::FRAME || + getAccessibleRole() == accessibility::AccessibleRole::ALERT || + getAccessibleRole() == accessibility::AccessibleRole::DIALOG ) ) // #i18891# + rStateSet |= accessibility::AccessibleStateType::ACTIVE; + + if ( pWindow->HasFocus() || ( pWindow->IsCompoundControl() && pWindow->HasChildPathFocus() ) ) + rStateSet |= accessibility::AccessibleStateType::FOCUSED; + + if ( pWindow->IsWait() ) + rStateSet |= accessibility::AccessibleStateType::BUSY; + + if ( pWindow->GetStyle() & WB_SIZEABLE ) + rStateSet |= accessibility::AccessibleStateType::RESIZABLE; + // 6. frame doesn't have MOVABLE state + // 10. for password text, where is the sensitive state? + if( ( getAccessibleRole() == accessibility::AccessibleRole::FRAME ||getAccessibleRole() == accessibility::AccessibleRole::DIALOG )&& pWindow->GetStyle() & WB_MOVEABLE ) + rStateSet |= accessibility::AccessibleStateType::MOVEABLE; + if( pWindow->IsDialog() ) + { + Dialog *pDlg = static_cast< Dialog* >( pWindow.get() ); + if( pDlg->IsInExecute() ) + rStateSet |= accessibility::AccessibleStateType::MODAL; + } + //If a combobox or list's edit child isn't read-only,EDITABLE state + //should be set. + if( pWindow && pWindow->GetType() == WindowType::COMBOBOX ) + { + if( !( pWindow->GetStyle() & WB_READONLY) || + !static_cast<Edit*>(pWindow.get())->IsReadOnly() ) + rStateSet |= accessibility::AccessibleStateType::EDITABLE; + } + + VclPtr<vcl::Window> pChild = pWindow->GetWindow( GetWindowType::FirstChild ); + + while( pWindow && pChild ) + { + VclPtr<vcl::Window> pWinTemp = pChild->GetWindow( GetWindowType::FirstChild ); + if( pWinTemp && pWinTemp->GetType() == WindowType::EDIT ) + { + if( !( pWinTemp->GetStyle() & WB_READONLY) || + !static_cast<Edit*>(pWinTemp.get())->IsReadOnly() ) + rStateSet |= accessibility::AccessibleStateType::EDITABLE; + break; + } + if( pChild->GetType() == WindowType::EDIT ) + { + if( !( pChild->GetStyle() & WB_READONLY) || + !static_cast<Edit*>(pChild.get())->IsReadOnly()) + rStateSet |= accessibility::AccessibleStateType::EDITABLE; + break; + } + pChild = pChild->GetWindow( GetWindowType::Next ); + } + } + else + { + rStateSet |= accessibility::AccessibleStateType::DEFUNC; + } + +/* + +MUST BE SET FROM DERIVED CLASSES: + +CHECKABLE +CHECKED +COLLAPSED +EXPANDED +EXPANDABLE +EDITABLE +FOCUSABLE +HORIZONTAL +VERTICAL +ICONIFIED +MULTILINE +MULTI_SELECTABLE +PRESSED +SELECTABLE +SELECTED +SINGLE_LINE +TRANSIENT + + */ +} + + +// accessibility::XAccessibleContext +sal_Int64 VCLXAccessibleComponent::getAccessibleChildCount() +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nChildren = 0; + if ( GetWindow() ) + nChildren = GetWindow()->GetAccessibleChildWindowCount(); + + return nChildren; +} + +uno::Reference< accessibility::XAccessible > VCLXAccessibleComponent::getAccessibleChild( sal_Int64 i ) +{ + OExternalLockGuard aGuard( this ); + + if ( i >= getAccessibleChildCount() ) + throw lang::IndexOutOfBoundsException(); + + uno::Reference< accessibility::XAccessible > xAcc; + if ( GetWindow() ) + { + vcl::Window* pChild = GetWindow()->GetAccessibleChildWindow( static_cast<sal_uInt16>(i) ); + if ( pChild ) + xAcc = pChild->GetAccessible(); + } + + return xAcc; +} + +uno::Reference< accessibility::XAccessible > VCLXAccessibleComponent::getAccessibleParent( ) +{ + OExternalLockGuard aGuard( this ); + + uno::Reference< accessibility::XAccessible > xAcc; + if ( GetWindow() ) + { + vcl::Window* pParent = GetWindow()->GetAccessibleParentWindow(); + if ( pParent ) + xAcc = pParent->GetAccessible(); + } + return xAcc; +} + +sal_Int64 VCLXAccessibleComponent::getAccessibleIndexInParent( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nIndex = -1; + + if ( GetWindow() ) + { + vcl::Window* pParent = GetWindow()->GetAccessibleParentWindow(); + if ( pParent ) + { + // Iterate over all the parent's children and search for this object. + // this should be compatible with the code in SVX + uno::Reference< accessibility::XAccessible > xParentAcc( pParent->GetAccessible() ); + if ( xParentAcc.is() ) + { + uno::Reference< accessibility::XAccessibleContext > xParentContext ( xParentAcc->getAccessibleContext() ); + if ( xParentContext.is() ) + { + sal_Int64 nChildCount = xParentContext->getAccessibleChildCount(); + for ( sal_Int64 i = 0; i < nChildCount; i++ ) + { + uno::Reference< accessibility::XAccessible > xChild( xParentContext->getAccessibleChild(i) ); + if ( xChild.is() ) + { + uno::Reference< accessibility::XAccessibleContext > xChildContext = xChild->getAccessibleContext(); + if ( xChildContext == static_cast<accessibility::XAccessibleContext*>(this) ) + { + nIndex = i; + break; + } + } + } + } + } + } + } + return nIndex; +} + +sal_Int16 VCLXAccessibleComponent::getAccessibleRole( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int16 nRole = 0; + + if ( GetWindow() ) + nRole = GetWindow()->GetAccessibleRole(); + + return nRole; +} + +OUString VCLXAccessibleComponent::getAccessibleDescription( ) +{ + OExternalLockGuard aGuard( this ); + + OUString aDescription; + + if ( GetWindow() ) + aDescription = GetWindow()->GetAccessibleDescription(); + + return aDescription; +} + +OUString VCLXAccessibleComponent::getAccessibleName( ) +{ + OExternalLockGuard aGuard( this ); + + OUString aName; + if ( GetWindow() ) + { + aName = GetWindow()->GetAccessibleName(); +#if OSL_DEBUG_LEVEL > 0 + // append window type to accessible name for debugging purposes + // if LIBO_APPEND_WINDOW_TYPE_TO_ACCESSIBLE_NAME environment variable is set + static const char* pEnvAppendType = getenv("LIBO_APPEND_WINDOW_TYPE_TO_ACCESSIBLE_NAME"); + if (pEnvAppendType && OUString::createFromAscii(pEnvAppendType) != u"0") + aName += " (Type = " + OUString::number(static_cast<sal_Int32>(GetWindow()->GetType())) + ")"; +#endif + } + return aName; +} + +OUString VCLXAccessibleComponent::getAccessibleId( ) +{ + OExternalLockGuard aGuard( this ); + + OUString aId; + if ( GetWindow() ) + { + const OUString &aWindowId = GetWindow()->get_id(); + aId = aWindowId; + } + return aId; +} + +uno::Reference< accessibility::XAccessibleRelationSet > VCLXAccessibleComponent::getAccessibleRelationSet( ) +{ + OExternalLockGuard aGuard( this ); + + rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSetHelper = new utl::AccessibleRelationSetHelper; + FillAccessibleRelationSet( *pRelationSetHelper ); + return pRelationSetHelper; +} + +sal_Int64 VCLXAccessibleComponent::getAccessibleStateSet( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nStateSet = 0; + FillAccessibleStateSet( nStateSet ); + return nStateSet; +} + +lang::Locale VCLXAccessibleComponent::getLocale() +{ + OExternalLockGuard aGuard( this ); + + return Application::GetSettings().GetLanguageTag().getLocale(); +} + +uno::Reference< accessibility::XAccessible > VCLXAccessibleComponent::getAccessibleAtPoint( const awt::Point& rPoint ) +{ + OExternalLockGuard aGuard( this ); + + uno::Reference< accessibility::XAccessible > xChild; + for ( sal_Int64 i = 0, nCount = getAccessibleChildCount(); i < nCount; ++i ) + { + uno::Reference< accessibility::XAccessible > xAcc = getAccessibleChild( i ); + if ( xAcc.is() ) + { + uno::Reference< accessibility::XAccessibleComponent > xComp( xAcc->getAccessibleContext(), uno::UNO_QUERY ); + if ( xComp.is() ) + { + tools::Rectangle aRect = VCLRectangle( xComp->getBounds() ); + Point aPos = VCLPoint( rPoint ); + if ( aRect.Contains( aPos ) ) + { + xChild = xAcc; + break; + } + } + } + } + + return xChild; +} + +// accessibility::XAccessibleComponent +awt::Rectangle VCLXAccessibleComponent::implGetBounds() +{ + awt::Rectangle aBounds ( 0, 0, 0, 0 ); + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute(); + aBounds = AWTRectangle( aRect ); + vcl::Window* pParent = pWindow->GetAccessibleParentWindow(); + if ( pParent ) + { + AbsoluteScreenPixelRectangle aParentRect = pParent->GetWindowExtentsAbsolute(); + awt::Point aParentScreenLoc = AWTPoint( aParentRect.TopLeft() ); + aBounds.X -= aParentScreenLoc.X; + aBounds.Y -= aParentScreenLoc.Y; + } + } + + return aBounds; +} + +awt::Point VCLXAccessibleComponent::getLocationOnScreen( ) +{ + OExternalLockGuard aGuard( this ); + + awt::Point aPos; + if ( GetWindow() ) + { + AbsoluteScreenPixelRectangle aRect = GetWindow()->GetWindowExtentsAbsolute(); + aPos.X = aRect.Left(); + aPos.Y = aRect.Top(); + } + + return aPos; +} + +void VCLXAccessibleComponent::grabFocus( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nStates = getAccessibleStateSet(); + if ( m_xVCLXWindow.is() && ( nStates & accessibility::AccessibleStateType::FOCUSABLE ) ) + m_xVCLXWindow->setFocus(); +} + +sal_Int32 SAL_CALL VCLXAccessibleComponent::getForeground( ) +{ + OExternalLockGuard aGuard( this ); + + Color nColor; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + if ( pWindow->IsControlForeground() ) + nColor = pWindow->GetControlForeground(); + else + { + vcl::Font aFont; + if ( pWindow->IsControlFont() ) + aFont = pWindow->GetControlFont(); + else + aFont = pWindow->GetFont(); + nColor = aFont.GetColor(); + // COL_AUTO is not very meaningful for AT + if ( nColor == COL_AUTO) + nColor = pWindow->GetTextColor(); + } + } + + return sal_Int32(nColor); +} + +sal_Int32 SAL_CALL VCLXAccessibleComponent::getBackground( ) +{ + OExternalLockGuard aGuard( this ); + + Color nColor; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + if ( pWindow->IsControlBackground() ) + nColor = pWindow->GetControlBackground(); + else + nColor = pWindow->GetBackground().GetColor(); + } + + return sal_Int32(nColor); +} + +// XAccessibleExtendedComponent + +uno::Reference< awt::XFont > SAL_CALL VCLXAccessibleComponent::getFont( ) +{ + OExternalLockGuard aGuard( this ); + + uno::Reference< awt::XFont > xFont; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + uno::Reference< awt::XDevice > xDev( pWindow->GetComponentInterface(), uno::UNO_QUERY ); + if ( xDev.is() ) + { + vcl::Font aFont; + if ( pWindow->IsControlFont() ) + aFont = pWindow->GetControlFont(); + else + aFont = pWindow->GetFont(); + rtl::Reference<VCLXFont> pVCLXFont = new VCLXFont; + pVCLXFont->Init( *xDev, aFont ); + xFont = pVCLXFont; + } + } + + return xFont; +} + +OUString SAL_CALL VCLXAccessibleComponent::getTitledBorderText( ) +{ + OExternalLockGuard aGuard( this ); + + OUString sRet; + if ( GetWindow() ) + sRet = GetWindow()->GetText(); + + return sRet; +} + +OUString SAL_CALL VCLXAccessibleComponent::getToolTipText( ) +{ + OExternalLockGuard aGuard( this ); + + OUString sRet; + if ( GetWindow() ) + sRet = GetWindow()->GetQuickHelpText(); + + return sRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxbitmap.cxx b/toolkit/source/awt/vclxbitmap.cxx new file mode 100644 index 0000000000..1faedb6235 --- /dev/null +++ b/toolkit/source/awt/vclxbitmap.cxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <awt/vclxbitmap.hxx> +#include <tools/stream.hxx> +#include <vcl/dibtools.hxx> +#include <vcl/BitmapTools.hxx> + + + + +// css::awt::XBitmap +css::awt::Size VCLXBitmap::getSize() +{ + std::scoped_lock aGuard( GetMutex() ); + + css::awt::Size aSize( maBitmap.GetSizePixel().Width(), maBitmap.GetSizePixel().Height() ); + return aSize; +} + +css::uno::Sequence< sal_Int8 > VCLXBitmap::getDIB() +{ + std::scoped_lock aGuard( GetMutex() ); + + SvMemoryStream aMem; + WriteDIB(maBitmap.GetBitmap(), aMem, false, true); + return css::uno::Sequence<sal_Int8>( static_cast<sal_Int8 const *>(aMem.GetData()), aMem.Tell() ); +} + +css::uno::Sequence< sal_Int8 > VCLXBitmap::getMaskDIB() +{ + std::scoped_lock aGuard( GetMutex() ); + + return vcl::bitmap::GetMaskDIB(maBitmap); +} + +sal_Int64 SAL_CALL VCLXBitmap::estimateUsage() +{ + std::scoped_lock aGuard( GetMutex() ); + + return maBitmap.GetSizeBytes(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxcontainer.cxx b/toolkit/source/awt/vclxcontainer.cxx new file mode 100644 index 0000000000..de7518d418 --- /dev/null +++ b/toolkit/source/awt/vclxcontainer.cxx @@ -0,0 +1,264 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <awt/vclxcontainer.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <vcl/tabpage.hxx> +#include <tools/debug.hxx> +#include <helper/scrollabledialog.hxx> +#include <helper/property.hxx> + +void VCLXContainer::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXContainer::VCLXContainer() +{ +} + +VCLXContainer::~VCLXContainer() +{ +} + + +// css::awt::XVclContainer +void VCLXContainer::addVclContainerListener( const css::uno::Reference< css::awt::XVclContainerListener >& rxListener ) +{ + SolarMutexGuard aGuard; + + if (!IsDisposed()) + GetContainerListeners().addInterface( rxListener ); +} + +void VCLXContainer::removeVclContainerListener( const css::uno::Reference< css::awt::XVclContainerListener >& rxListener ) +{ + SolarMutexGuard aGuard; + + if (!IsDisposed()) + GetContainerListeners().removeInterface( rxListener ); +} + +css::uno::Sequence< css::uno::Reference< css::awt::XWindow > > VCLXContainer::getWindows( ) +{ + SolarMutexGuard aGuard; + + // Request container interface from all children + css::uno::Sequence< css::uno::Reference< css::awt::XWindow > > aSeq; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + sal_uInt16 nChildren = pWindow->GetChildCount(); + if ( nChildren ) + { + aSeq = css::uno::Sequence< css::uno::Reference< css::awt::XWindow > >( nChildren ); + css::uno::Reference< css::awt::XWindow > * pChildRefs = aSeq.getArray(); + for ( sal_uInt16 n = 0; n < nChildren; n++ ) + { + vcl::Window* pChild = pWindow->GetChild( n ); + css::uno::Reference< css::awt::XWindowPeer > xWP = pChild->GetComponentInterface(); + css::uno::Reference< css::awt::XWindow > xW( xWP, css::uno::UNO_QUERY ); + pChildRefs[n] = xW; + } + } + } + return aSeq; +} + + +// css::awt::XVclContainerPeer +void VCLXContainer::enableDialogControl( sal_Bool bEnable ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + if ( bEnable ) + nStyle |= WB_DIALOGCONTROL; + else + nStyle &= (~WB_DIALOGCONTROL); + pWindow->SetStyle( nStyle ); + } +} + +void VCLXContainer::setTabOrder( const css::uno::Sequence< css::uno::Reference< css::awt::XWindow > >& Components, const css::uno::Sequence< css::uno::Any >& Tabs, sal_Bool bGroupControl ) +{ + SolarMutexGuard aGuard; + + sal_uInt32 nCount = Components.getLength(); + DBG_ASSERT( nCount == static_cast<sal_uInt32>(Tabs.getLength()), "setTabOrder: TabCount != ComponentCount" ); + const css::uno::Reference< css::awt::XWindow > * pComps = Components.getConstArray(); + const css::uno::Any* pTabs = Tabs.getConstArray(); + + vcl::Window* pPrevWin = nullptr; + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + // css::style::TabStop + VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( pComps[n] ); + // May be NULL if a css::uno::Sequence is originated from TabController and is missing a peer! + if ( pWin ) + { + // Order windows before manipulating their style, because elements such as the + // RadioButton considers the PREV-window in StateChanged. + if ( pPrevWin ) + pWin->SetZOrder( pPrevWin, ZOrderFlags::Behind ); + + WinBits nStyle = pWin->GetStyle(); + nStyle &= ~(WB_TABSTOP|WB_NOTABSTOP|WB_GROUP); + if ( pTabs[n].getValueType().getTypeClass() == css::uno::TypeClass_BOOLEAN ) + { + bool bTab = false; + pTabs[n] >>= bTab; + nStyle |= ( bTab ? WB_TABSTOP : WB_NOTABSTOP ); + } + pWin->SetStyle( nStyle ); + + if ( bGroupControl ) + { + if ( n == 0 ) + pWin->SetDialogControlStart( true ); + else + pWin->SetDialogControlStart( false ); + } + + pPrevWin = pWin; + } + } +} + +void VCLXContainer::setGroup( const css::uno::Sequence< css::uno::Reference< css::awt::XWindow > >& Components ) +{ + SolarMutexGuard aGuard; + + sal_uInt32 nCount = Components.getLength(); + const css::uno::Reference< css::awt::XWindow > * pComps = Components.getConstArray(); + + vcl::Window* pPrevWin = nullptr; + vcl::Window* pPrevRadio = nullptr; + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( pComps[n] ); + if ( pWin ) + { + vcl::Window* pSortBehind = pPrevWin; + // #57096# Sort all radios consecutively + bool bNewPrevWin = true; + if ( pWin->GetType() == WindowType::RADIOBUTTON ) + { + if ( pPrevRadio ) + { + // This RadioButton was sorted before PrevWin + bNewPrevWin = ( pPrevWin == pPrevRadio ); + pSortBehind = pPrevRadio; + } + pPrevRadio = pWin; + } + + // Z-Order + if ( pSortBehind ) + pWin->SetZOrder( pSortBehind, ZOrderFlags::Behind ); + + WinBits nStyle = pWin->GetStyle(); + if ( n == 0 ) + nStyle |= WB_GROUP; + else + nStyle &= (~WB_GROUP); + pWin->SetStyle( nStyle ); + + // Add WB_GROUP after the last group + if ( n == ( nCount - 1 ) ) + { + vcl::Window* pBehindLast = pWin->GetWindow( GetWindowType::Next ); + if ( pBehindLast ) + { + WinBits nLastStyle = pBehindLast->GetStyle(); + nLastStyle |= WB_GROUP; + pBehindLast->SetStyle( nLastStyle ); + } + } + + if ( bNewPrevWin ) + pPrevWin = pWin; + } + } +} + +void SAL_CALL VCLXContainer::setProperty( + const OUString& PropertyName, + const css::uno::Any& Value ) +{ + SolarMutexGuard aGuard; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_SCROLLHEIGHT: + case BASEPROPERTY_SCROLLWIDTH: + case BASEPROPERTY_SCROLLTOP: + case BASEPROPERTY_SCROLLLEFT: + { + sal_Int32 nVal =0; + Value >>= nVal; + Size aSize( nVal, nVal ); + VclPtr<vcl::Window> pWindow = GetWindow(); + MapMode aMode( MapUnit::MapAppFont ); + toolkit::ScrollableDialog* pScrollable = dynamic_cast< toolkit::ScrollableDialog* >( pWindow.get() ); + TabPage* pScrollTabPage = dynamic_cast< TabPage* >( pWindow.get() ); + if ( pWindow && (pScrollable || pScrollTabPage) ) + { + aSize = pWindow->LogicToPixel( aSize, aMode ); + switch ( nPropType ) + { + case BASEPROPERTY_SCROLLHEIGHT: + pScrollable ? pScrollable->SetScrollHeight( aSize.Height() ) : (void)0; + pScrollTabPage ? pScrollTabPage->SetScrollHeight( aSize.Height() ) : (void)0; + break; + case BASEPROPERTY_SCROLLWIDTH: + pScrollable ? pScrollable->SetScrollWidth( aSize.Width() ) : (void)0; + pScrollTabPage ? pScrollTabPage->SetScrollWidth( aSize.Width() ) : (void)0; + break; + case BASEPROPERTY_SCROLLTOP: + pScrollable ? pScrollable->SetScrollTop( aSize.Height() ) : (void)0; + pScrollTabPage ? pScrollTabPage->SetScrollTop( aSize.Height() ) : (void)0; + break; + case BASEPROPERTY_SCROLLLEFT: + pScrollable ? pScrollable->SetScrollLeft( aSize.Width() ) : (void)0; + pScrollTabPage ? pScrollTabPage->SetScrollLeft( aSize.Width() ) : (void)0; + break; + default: + break; + } + break; + } + break; + } + + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxdevice.cxx b/toolkit/source/awt/vclxdevice.cxx new file mode 100644 index 0000000000..c4ed981467 --- /dev/null +++ b/toolkit/source/awt/vclxdevice.cxx @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/util/MeasureUnit.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include <toolkit/awt/vclxdevice.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <awt/vclxbitmap.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> +#include <vcl/virdev.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/metric.hxx> + + +VCLXDevice::VCLXDevice() +{ +} + +VCLXDevice::~VCLXDevice() +{ + //TODO: why was this empty, and everything done in ~VCLXVirtualDevice? + SolarMutexGuard g; + mpOutputDevice.reset(); +} + +// css::awt::XDevice, +css::uno::Reference< css::awt::XGraphics > VCLXDevice::createGraphics( ) +{ + SolarMutexGuard aGuard; + + css::uno::Reference< css::awt::XGraphics > xRef; + + if ( mpOutputDevice ) + xRef = mpOutputDevice->CreateUnoGraphics(); + + return xRef; +} + +css::uno::Reference< css::awt::XDevice > VCLXDevice::createDevice( sal_Int32 nWidth, sal_Int32 nHeight ) +{ + SolarMutexGuard aGuard; + + css::uno::Reference< css::awt::XDevice > xRef; + if ( GetOutputDevice() ) + { + rtl::Reference<VCLXVirtualDevice> pVDev = new VCLXVirtualDevice; + VclPtrInstance<VirtualDevice> pVclVDev( *GetOutputDevice() ); + pVclVDev->SetOutputSizePixel( Size( nWidth, nHeight ) ); + pVDev->SetVirtualDevice( pVclVDev ); + xRef = pVDev; + } + return xRef; +} + +css::awt::DeviceInfo VCLXDevice::getInfo() +{ + SolarMutexGuard aGuard; + + css::awt::DeviceInfo aInfo; + + if (mpOutputDevice) + aInfo = mpOutputDevice->GetDeviceInfo(); + + return aInfo; +} + +css::uno::Sequence< css::awt::FontDescriptor > VCLXDevice::getFontDescriptors( ) +{ + SolarMutexGuard aGuard; + + css::uno::Sequence< css::awt::FontDescriptor> aFonts; + if( mpOutputDevice ) + { + int nFonts = mpOutputDevice->GetFontFaceCollectionCount(); + if ( nFonts ) + { + aFonts = css::uno::Sequence< css::awt::FontDescriptor>( nFonts ); + css::awt::FontDescriptor* pFonts = aFonts.getArray(); + for ( int n = 0; n < nFonts; n++ ) + pFonts[n] = VCLUnoHelper::CreateFontDescriptor( mpOutputDevice->GetFontMetricFromCollection( n ) ); + } + } + return aFonts; +} + +css::uno::Reference< css::awt::XFont > VCLXDevice::getFont( const css::awt::FontDescriptor& rDescriptor ) +{ + SolarMutexGuard aGuard; + + css::uno::Reference< css::awt::XFont > xRef; + if( mpOutputDevice ) + { + rtl::Reference<VCLXFont> pMetric = new VCLXFont; + pMetric->Init( *this, VCLUnoHelper::CreateFont( rDescriptor, mpOutputDevice->GetFont() ) ); + xRef = pMetric; + } + return xRef; +} + +css::uno::Reference< css::awt::XBitmap > VCLXDevice::createBitmap( sal_Int32 nX, sal_Int32 nY, sal_Int32 nWidth, sal_Int32 nHeight ) +{ + SolarMutexGuard aGuard; + + css::uno::Reference< css::awt::XBitmap > xBmp; + if( mpOutputDevice ) + { + BitmapEx aBmp = mpOutputDevice->GetBitmapEx( Point( nX, nY ), Size( nWidth, nHeight ) ); + + rtl::Reference<VCLXBitmap> pBmp = new VCLXBitmap; + pBmp->SetBitmap( aBmp ); + xBmp = pBmp; + } + return xBmp; +} + +css::uno::Reference< css::awt::XDisplayBitmap > VCLXDevice::createDisplayBitmap( const css::uno::Reference< css::awt::XBitmap >& rxBitmap ) +{ + SolarMutexGuard aGuard; + + BitmapEx aBmp = VCLUnoHelper::GetBitmap( rxBitmap ); + rtl::Reference<VCLXBitmap> pBmp = new VCLXBitmap; + pBmp->SetBitmap( aBmp ); + return pBmp; +} + +VCLXVirtualDevice::~VCLXVirtualDevice() +{ + SolarMutexGuard aGuard; + + mpOutputDevice.disposeAndClear(); +} + +// Interface implementation of css::awt::XUnitConversion + +css::awt::Point SAL_CALL VCLXDevice::convertPointToLogic( const css::awt::Point& aPoint, ::sal_Int16 TargetUnit ) +{ + SolarMutexGuard aGuard; + if (TargetUnit == css::util::MeasureUnit::PERCENT ) + { + // percentage not allowed here + throw css::lang::IllegalArgumentException(); + } + + css::awt::Point aAWTPoint(0,0); + // X,Y + + if( mpOutputDevice ) + { + MapMode aMode(VCLUnoHelper::ConvertToMapModeUnit(TargetUnit)); + ::Point aVCLPoint = VCLUnoHelper::ConvertToVCLPoint(aPoint); + ::Point aDevPoint = mpOutputDevice->PixelToLogic(aVCLPoint, aMode ); + aAWTPoint = VCLUnoHelper::ConvertToAWTPoint(aDevPoint); + } + + return aAWTPoint; +} + + +css::awt::Point SAL_CALL VCLXDevice::convertPointToPixel( const css::awt::Point& aPoint, ::sal_Int16 SourceUnit ) +{ + SolarMutexGuard aGuard; + if (SourceUnit == css::util::MeasureUnit::PERCENT || + SourceUnit == css::util::MeasureUnit::PIXEL ) + { + // pixel or percentage not allowed here + throw css::lang::IllegalArgumentException(); + } + + css::awt::Point aAWTPoint(0,0); + + if( mpOutputDevice ) + { + MapMode aMode(VCLUnoHelper::ConvertToMapModeUnit(SourceUnit)); + ::Point aVCLPoint = VCLUnoHelper::ConvertToVCLPoint(aPoint); + ::Point aDevPoint = mpOutputDevice->LogicToPixel(aVCLPoint, aMode ); + aAWTPoint = VCLUnoHelper::ConvertToAWTPoint(aDevPoint); + } + + return aAWTPoint; +} + +css::awt::Size SAL_CALL VCLXDevice::convertSizeToLogic( const css::awt::Size& aSize, ::sal_Int16 TargetUnit ) +{ + SolarMutexGuard aGuard; + if (TargetUnit == css::util::MeasureUnit::PERCENT) + { + // percentage not allowed here + throw css::lang::IllegalArgumentException(); + } + + css::awt::Size aAWTSize(0,0); + // Width, Height + + + if( mpOutputDevice ) + { + MapMode aMode(VCLUnoHelper::ConvertToMapModeUnit(TargetUnit)); + ::Size aVCLSize = VCLUnoHelper::ConvertToVCLSize(aSize); + ::Size aDevSz = mpOutputDevice->PixelToLogic(aVCLSize, aMode ); + aAWTSize = VCLUnoHelper::ConvertToAWTSize(aDevSz); + } + + return aAWTSize; +} + +css::awt::Size SAL_CALL VCLXDevice::convertSizeToPixel( const css::awt::Size& aSize, ::sal_Int16 SourceUnit ) +{ + SolarMutexGuard aGuard; + if (SourceUnit == css::util::MeasureUnit::PERCENT || + SourceUnit == css::util::MeasureUnit::PIXEL) + { + // pixel or percentage not allowed here + throw css::lang::IllegalArgumentException(); + } + + css::awt::Size aAWTSize(0,0); + // Width, Height + if( mpOutputDevice ) + { + MapMode aMode(VCLUnoHelper::ConvertToMapModeUnit(SourceUnit)); + ::Size aVCLSize = VCLUnoHelper::ConvertToVCLSize(aSize); + ::Size aDevSz = mpOutputDevice->LogicToPixel(aVCLSize, aMode ); + aAWTSize = VCLUnoHelper::ConvertToAWTSize(aDevSz); + } + + return aAWTSize; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxfont.cxx b/toolkit/source/awt/vclxfont.cxx new file mode 100644 index 0000000000..01e7aaae79 --- /dev/null +++ b/toolkit/source/awt/vclxfont.cxx @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> + +#include <com/sun/star/awt/XDevice.hpp> + +#include <comphelper/sequence.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <vcl/kernarray.hxx> +#include <vcl/metric.hxx> +#include <vcl/outdev.hxx> +#include <vcl/svapp.hxx> + +VCLXFont::VCLXFont() +{ + mpFontMetric = nullptr; +} + +VCLXFont::~VCLXFont() +{ +} + +void VCLXFont::Init( css::awt::XDevice& rxDev, const vcl::Font& rFont ) +{ + mxDevice = &rxDev; + + mpFontMetric.reset(); + + maFont = rFont; +} + +bool VCLXFont::ImplAssertValidFontMetric() +{ + if ( !mpFontMetric && mxDevice.is() ) + { + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + vcl::Font aOldFont = pOutDev->GetFont(); + pOutDev->SetFont( maFont ); + mpFontMetric.reset( new FontMetric( pOutDev->GetFontMetric() ) ); + pOutDev->SetFont( aOldFont ); + } + } + return mpFontMetric != nullptr; +} + +css::awt::FontDescriptor VCLXFont::getFontDescriptor( ) +{ + std::unique_lock aGuard( maMutex ); + + return VCLUnoHelper::CreateFontDescriptor( maFont ); + +} + +css::awt::SimpleFontMetric VCLXFont::getFontMetric( ) +{ + std::unique_lock aGuard( maMutex ); + + css::awt::SimpleFontMetric aFM; + if ( ImplAssertValidFontMetric() ) + aFM = VCLUnoHelper::CreateFontMetric( *mpFontMetric ); + return aFM; +} + +sal_Int16 VCLXFont::getCharWidth( sal_Unicode c ) +{ + std::unique_lock aGuard( maMutex ); + + sal_Int16 nRet = -1; + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + vcl::Font aOldFont = pOutDev->GetFont(); + pOutDev->SetFont( maFont ); + + nRet = sal::static_int_cast< sal_Int16 >( + pOutDev->GetTextWidth( OUString(c) )); + + pOutDev->SetFont( aOldFont ); + } + return nRet; +} + +css::uno::Sequence< sal_Int16 > VCLXFont::getCharWidths( sal_Unicode nFirst, sal_Unicode nLast ) +{ + std::unique_lock aGuard( maMutex ); + + css::uno::Sequence<sal_Int16> aSeq; + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + vcl::Font aOldFont = pOutDev->GetFont(); + pOutDev->SetFont( maFont ); + + sal_Int16 nCount = nLast-nFirst + 1; + aSeq = css::uno::Sequence<sal_Int16>( nCount ); + for ( sal_uInt16 n = 0; n < nCount; n++ ) + { + aSeq.getArray()[n] = sal::static_int_cast< sal_Int16 >( + pOutDev->GetTextWidth( + OUString(static_cast< sal_Unicode >(nFirst+n)) )); + } + + pOutDev->SetFont( aOldFont ); + } + return aSeq; +} + +sal_Int32 VCLXFont::getStringWidth( const OUString& str ) +{ + std::unique_lock aGuard( maMutex ); + + sal_Int32 nRet = -1; + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + vcl::Font aOldFont = pOutDev->GetFont(); + pOutDev->SetFont( maFont ); + nRet = pOutDev->GetTextWidth( str ); + pOutDev->SetFont( aOldFont ); + } + return nRet; +} + +sal_Int32 VCLXFont::getStringWidthArray( const OUString& str, css::uno::Sequence< sal_Int32 >& rDXArray ) +{ + std::unique_lock aGuard( maMutex ); + + sal_Int32 nRet = -1; + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + vcl::Font aOldFont = pOutDev->GetFont(); + pOutDev->SetFont( maFont ); + KernArray aDXA; + nRet = pOutDev->GetTextArray( str, &aDXA ); + rDXArray.realloc(aDXA.size()); + sal_Int32* pArray = rDXArray.getArray(); + for (size_t i = 0, nLen = aDXA.size(); i < nLen; ++i) + pArray[i] = aDXA[i]; + pOutDev->SetFont( aOldFont ); + } + return nRet; +} + +void VCLXFont::getKernPairs( css::uno::Sequence< sal_Unicode >& /*rnChars1*/, css::uno::Sequence< sal_Unicode >& /*rnChars2*/, css::uno::Sequence< sal_Int16 >& /*rnKerns*/ ) +{ + // NOTE: this empty method is just used for keeping the related UNO-API stable +} + +// css::awt::XFont2 +sal_Bool VCLXFont::hasGlyphs( const OUString& aText ) +{ + std::unique_lock aGuard( maMutex ); + SolarMutexGuard aSolarGuard; + + OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( mxDevice ); + if ( pOutDev ) + { + if ( pOutDev->HasGlyphs( maFont, aText ) == -1 ) + { + return true; + } + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxgraphics.cxx b/toolkit/source/awt/vclxgraphics.cxx new file mode 100644 index 0000000000..06df80a421 --- /dev/null +++ b/toolkit/source/awt/vclxgraphics.cxx @@ -0,0 +1,487 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <memory> +#include <awt/vclxgraphics.hxx> +#include <toolkit/awt/vclxdevice.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> +#include <vcl/image.hxx> +#include <vcl/kernarray.hxx> +#include <vcl/gradient.hxx> +#include <vcl/metric.hxx> +#include <tools/debug.hxx> + +using namespace com::sun::star; + + + +VCLXGraphics::VCLXGraphics() + : mpOutputDevice(nullptr) + , meRasterOp(RasterOp::OverPaint) +{ +} + +VCLXGraphics::~VCLXGraphics() +{ + std::vector< VCLXGraphics* > *pLst = mpOutputDevice ? mpOutputDevice->GetUnoGraphicsList() : nullptr; + if ( pLst ) + { + auto it = std::find(pLst->begin(), pLst->end(), this); + if (it != pLst->end()) + pLst->erase( it ); + } + + mpClipRegion.reset(); + + SolarMutexGuard g; + mpOutputDevice.reset(); +} + +void VCLXGraphics::SetOutputDevice( OutputDevice* pOutDev ) +{ + mpOutputDevice = pOutDev; + mxDevice = nullptr; + initAttrs(); +} + +void VCLXGraphics::Init( OutputDevice* pOutDev ) +{ + DBG_ASSERT( !mpOutputDevice, "VCLXGraphics::Init already has pOutDev !" ); + mpOutputDevice = pOutDev; + + initAttrs(); + mpClipRegion = nullptr; + + // Register at OutputDevice + std::vector< VCLXGraphics* > *pLst = mpOutputDevice->GetUnoGraphicsList(); + if ( !pLst ) + pLst = mpOutputDevice->CreateUnoGraphicsList(); + pLst->push_back( this ); +} + +void VCLXGraphics::initAttrs() +{ + if ( !mpOutputDevice ) + return; + + maFont = mpOutputDevice->GetFont(); + maTextColor = mpOutputDevice->GetTextColor(); /* COL_BLACK */ + maTextFillColor = mpOutputDevice->GetTextFillColor(); /* COL_TRANSPARENT */ + maLineColor = mpOutputDevice->GetLineColor(); /* COL_BLACK */ + maFillColor = mpOutputDevice->GetFillColor(); /* COL_WHITE */ + meRasterOp = mpOutputDevice->GetRasterOp(); /* RasterOp::OverPaint */ +} + +void VCLXGraphics::InitOutputDevice( InitOutDevFlags nFlags ) +{ + if(!mpOutputDevice) + return; + + SolarMutexGuard aVclGuard; + + if ( nFlags & InitOutDevFlags::FONT ) + { + mpOutputDevice->SetFont( maFont ); + mpOutputDevice->SetTextColor( maTextColor ); + mpOutputDevice->SetTextFillColor( maTextFillColor ); + } + + if ( nFlags & InitOutDevFlags::COLORS ) + { + mpOutputDevice->SetLineColor( maLineColor ); + mpOutputDevice->SetFillColor( maFillColor ); + } + + mpOutputDevice->SetRasterOp( meRasterOp ); + + if( mpClipRegion ) + mpOutputDevice->SetClipRegion( *mpClipRegion ); + else + mpOutputDevice->SetClipRegion(); +} + +uno::Reference< awt::XDevice > VCLXGraphics::getDevice() +{ + SolarMutexGuard aGuard; + + if( !mxDevice.is() && mpOutputDevice ) + { + rtl::Reference<VCLXDevice> pDev = new VCLXDevice; + pDev->SetOutputDevice( mpOutputDevice ); + mxDevice = pDev; + } + return mxDevice; +} + +awt::SimpleFontMetric VCLXGraphics::getFontMetric() +{ + SolarMutexGuard aGuard; + + awt::SimpleFontMetric aM; + if( mpOutputDevice ) + { + mpOutputDevice->SetFont( maFont ); + aM = VCLUnoHelper::CreateFontMetric( mpOutputDevice->GetFontMetric() ); + } + return aM; +} + +void VCLXGraphics::setFont( const uno::Reference< awt::XFont >& rxFont ) +{ + SolarMutexGuard aGuard; + + maFont = VCLUnoHelper::CreateFont( rxFont ); +} + +void VCLXGraphics::selectFont( const awt::FontDescriptor& rDescription ) +{ + SolarMutexGuard aGuard; + + maFont = VCLUnoHelper::CreateFont( rDescription, vcl::Font() ); +} + +void VCLXGraphics::setTextColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + maTextColor = Color( ColorTransparency, nColor ); +} + +void VCLXGraphics::setTextFillColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + maTextFillColor = Color( ColorTransparency, nColor ); +} + +void VCLXGraphics::setLineColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + maLineColor = Color( ColorTransparency, nColor ); +} + +void VCLXGraphics::setFillColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + maFillColor = Color( ColorTransparency, nColor ); +} + +void VCLXGraphics::setRasterOp( awt::RasterOperation eROP ) +{ + SolarMutexGuard aGuard; + + meRasterOp = static_cast<RasterOp>(eROP); +} + +void VCLXGraphics::setClipRegion( const uno::Reference< awt::XRegion >& rxRegion ) +{ + SolarMutexGuard aGuard; + + if ( rxRegion.is() ) + mpClipRegion.reset( new vcl::Region( VCLUnoHelper::GetRegion( rxRegion ) ) ); + else + mpClipRegion.reset(); +} + +void VCLXGraphics::intersectClipRegion( const uno::Reference< awt::XRegion >& rxRegion ) +{ + SolarMutexGuard aGuard; + + if ( rxRegion.is() ) + { + vcl::Region aRegion( VCLUnoHelper::GetRegion( rxRegion ) ); + if ( !mpClipRegion ) + mpClipRegion.reset( new vcl::Region( aRegion ) ); + else + mpClipRegion->Intersect( aRegion ); + } +} + +void VCLXGraphics::push( ) +{ + SolarMutexGuard aGuard; + + + if( mpOutputDevice ) + mpOutputDevice->Push(); +} + +void VCLXGraphics::pop( ) +{ + SolarMutexGuard aGuard; + + + if( mpOutputDevice ) + mpOutputDevice->Pop(); +} + +void VCLXGraphics::clear( + const awt::Rectangle& aRect ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + const ::tools::Rectangle aVCLRect = VCLUnoHelper::ConvertToVCLRect( aRect ); + mpOutputDevice->Erase( aVCLRect ); + } +} + +void VCLXGraphics::copy( const uno::Reference< awt::XDevice >& rxSource, sal_Int32 nSourceX, sal_Int32 nSourceY, sal_Int32 nSourceWidth, sal_Int32 nSourceHeight, sal_Int32 nDestX, sal_Int32 nDestY, sal_Int32 nDestWidth, sal_Int32 nDestHeight ) +{ + SolarMutexGuard aGuard; + + if ( mpOutputDevice ) + { + VCLXDevice* pFromDev = dynamic_cast<VCLXDevice*>( rxSource.get() ); + DBG_ASSERT( pFromDev, "VCLXGraphics::copy - invalid device" ); + if ( pFromDev ) + { + InitOutputDevice( InitOutDevFlags::NONE ); + mpOutputDevice->DrawOutDev( Point( nDestX, nDestY ), Size( nDestWidth, nDestHeight ), + Point( nSourceX, nSourceY ), Size( nSourceWidth, nSourceHeight ), *pFromDev->GetOutputDevice() ); + } + } +} + +void VCLXGraphics::draw( const uno::Reference< awt::XDisplayBitmap >& rxBitmapHandle, sal_Int32 nSourceX, sal_Int32 nSourceY, sal_Int32 nSourceWidth, sal_Int32 nSourceHeight, sal_Int32 nDestX, sal_Int32 nDestY, sal_Int32 nDestWidth, sal_Int32 nDestHeight ) +{ + SolarMutexGuard aGuard; + + if( !mpOutputDevice ) + return; + + InitOutputDevice( InitOutDevFlags::NONE); + uno::Reference< awt::XBitmap > xBitmap( rxBitmapHandle, uno::UNO_QUERY ); + BitmapEx aBmpEx = VCLUnoHelper::GetBitmap( xBitmap ); + + Point aPos(nDestX - nSourceX, nDestY - nSourceY); + Size aSz = aBmpEx.GetSizePixel(); + + if(nDestWidth != nSourceWidth) + { + float zoomX = static_cast<float>(nDestWidth) / static_cast<float>(nSourceWidth); + aSz.setWidth( static_cast<tools::Long>(static_cast<float>(aSz.Width()) * zoomX) ); + } + + if(nDestHeight != nSourceHeight) + { + float zoomY = static_cast<float>(nDestHeight) / static_cast<float>(nSourceHeight); + aSz.setHeight( static_cast<tools::Long>(static_cast<float>(aSz.Height()) * zoomY) ); + } + + if(nSourceX || nSourceY || aSz.Width() != nSourceWidth || aSz.Height() != nSourceHeight) + mpOutputDevice->IntersectClipRegion(vcl::Region(tools::Rectangle(nDestX, nDestY, nDestX + nDestWidth - 1, nDestY + nDestHeight - 1))); + + mpOutputDevice->DrawBitmapEx( aPos, aSz, aBmpEx ); +} + +void VCLXGraphics::drawPixel( sal_Int32 x, sal_Int32 y ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawPixel( Point( x, y ) ); + } +} + +void VCLXGraphics::drawLine( sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawLine( Point( x1, y1 ), Point( x2, y2 ) ); + } +} + +void VCLXGraphics::drawRect( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawRect( tools::Rectangle( Point( x, y ), Size( width, height ) ) ); + } +} + +void VCLXGraphics::drawRoundedRect( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 nHorzRound, sal_Int32 nVertRound ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawRect( tools::Rectangle( Point( x, y ), Size( width, height ) ), nHorzRound, nVertRound ); + } +} + +void VCLXGraphics::drawPolyLine( const uno::Sequence< sal_Int32 >& DataX, const uno::Sequence< sal_Int32 >& DataY ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawPolyLine( VCLUnoHelper::CreatePolygon( DataX, DataY ) ); + } +} + +void VCLXGraphics::drawPolygon( const uno::Sequence< sal_Int32 >& DataX, const uno::Sequence< sal_Int32 >& DataY ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawPolygon( VCLUnoHelper::CreatePolygon( DataX, DataY ) ); + } +} + +void VCLXGraphics::drawPolyPolygon( const uno::Sequence< uno::Sequence< sal_Int32 > >& DataX, const uno::Sequence< uno::Sequence< sal_Int32 > >& DataY ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + sal_uInt16 nPolys = static_cast<sal_uInt16>(DataX.getLength()); + tools::PolyPolygon aPolyPoly( nPolys ); + for ( sal_uInt16 n = 0; n < nPolys; n++ ) + aPolyPoly[n] = VCLUnoHelper::CreatePolygon( DataX.getConstArray()[n], DataY.getConstArray()[n] ); + + mpOutputDevice->DrawPolyPolygon( aPolyPoly ); + } +} + +void VCLXGraphics::drawEllipse( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawEllipse( tools::Rectangle( Point( x, y ), Size( width, height ) ) ); + } +} + +void VCLXGraphics::drawArc( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawArc( tools::Rectangle( Point( x, y ), Size( width, height ) ), Point( x1, y1 ), Point( x2, y2 ) ); + } +} + +void VCLXGraphics::drawPie( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawPie( tools::Rectangle( Point( x, y ), Size( width, height ) ), Point( x1, y1 ), Point( x2, y2 ) ); + } +} + +void VCLXGraphics::drawChord( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawChord( tools::Rectangle( Point( x, y ), Size( width, height ) ), Point( x1, y1 ), Point( x2, y2 ) ); + } +} + +void VCLXGraphics::drawGradient( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, const awt::Gradient& rGradient ) +{ + SolarMutexGuard aGuard; + + if( !mpOutputDevice ) + return; + + InitOutputDevice( InitOutDevFlags::COLORS ); + Gradient aGradient(rGradient.Style, Color(ColorTransparency, rGradient.StartColor), Color(ColorTransparency, rGradient.EndColor)); + aGradient.SetAngle(Degree10(rGradient.Angle)); + aGradient.SetBorder(rGradient.Border); + aGradient.SetOfsX(rGradient.XOffset); + aGradient.SetOfsY(rGradient.YOffset); + aGradient.SetStartIntensity(rGradient.StartIntensity); + aGradient.SetEndIntensity(rGradient.EndIntensity); + aGradient.SetSteps(rGradient.StepCount); + mpOutputDevice->DrawGradient( tools::Rectangle( Point( x, y ), Size( width, height ) ), aGradient ); +} + +void VCLXGraphics::drawText( sal_Int32 x, sal_Int32 y, const OUString& rText ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS |InitOutDevFlags::FONT); + mpOutputDevice->DrawText( Point( x, y ), rText ); + } +} + +void VCLXGraphics::drawTextArray( sal_Int32 x, sal_Int32 y, const OUString& rText, const uno::Sequence< sal_Int32 >& rLongs ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice ) + { + InitOutputDevice( InitOutDevFlags::COLORS|InitOutDevFlags::FONT ); + KernArray aDXA; + aDXA.reserve(rText.getLength()); + for(int i = 0; i < rText.getLength(); ++i) + aDXA.push_back(rLongs[i]); + mpOutputDevice->DrawTextArray( Point( x, y ), rText, aDXA, {}, 0, rText.getLength()); + } +} + + +void VCLXGraphics::drawImage( sal_Int32 x, sal_Int32 y, sal_Int32 width, sal_Int32 height, sal_Int16 nStyle, const uno::Reference< graphic::XGraphic >& xGraphic ) +{ + SolarMutexGuard aGuard; + + if( mpOutputDevice && xGraphic.is() ) + { + Image aImage( xGraphic ); + if ( !!aImage ) + { + InitOutputDevice( InitOutDevFlags::COLORS ); + mpOutputDevice->DrawImage( Point( x, y ), Size( width, height ), aImage, static_cast<DrawImageFlags>(nStyle) ); + } + } +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxmenu.cxx b/toolkit/source/awt/vclxmenu.cxx new file mode 100644 index 0000000000..37785849c5 --- /dev/null +++ b/toolkit/source/awt/vclxmenu.cxx @@ -0,0 +1,869 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <toolkit/awt/vclxmenu.hxx> +#include <toolkit/helper/convert.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <tools/debug.hxx> +#include <vcl/dialoghelper.hxx> +#include <vcl/graph.hxx> +#include <vcl/menu.hxx> +#include <vcl/keycod.hxx> +#include <vcl/image.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> + +#include <com/sun/star/awt/KeyModifier.hpp> + +VCLXMenu::VCLXMenu() + : maMenuListeners( *this ) + , mnDefaultItem(0) +{ + mpMenu = nullptr; +} + +VCLXMenu::VCLXMenu( Menu* pMenu ) + : maMenuListeners( *this ) + , mnDefaultItem(0) +{ + mpMenu = pMenu; +} + +VCLXMenu::~VCLXMenu() +{ + maPopupMenuRefs.clear(); + if ( mpMenu ) + { + SolarMutexGuard g; + mpMenu->RemoveEventListener( LINK( this, VCLXMenu, MenuEventListener ) ); + mpMenu.disposeAndClear(); + } +} + +bool VCLXMenu::IsPopupMenu() const +{ + return (mpMenu && ! mpMenu->IsMenuBar()); +} + +void VCLXMenu::ImplCreateMenu( bool bPopup ) +{ + DBG_ASSERT( !mpMenu, "CreateMenu: Menu exists!" ); + + if ( bPopup ) + mpMenu = VclPtr<PopupMenu>::Create(); + else + mpMenu = VclPtr<MenuBar>::Create(); + + mpMenu->AddEventListener( LINK( this, VCLXMenu, MenuEventListener ) ); +} + +void VCLXMenu::ImplAddListener() +{ + assert(mpMenu); + mpMenu->AddEventListener( LINK( this, VCLXMenu, MenuEventListener ) ); +} + +IMPL_LINK( VCLXMenu, MenuEventListener, VclMenuEvent&, rMenuEvent, void ) +{ + DBG_ASSERT( rMenuEvent.GetMenu() && mpMenu, "Menu???" ); + + if ( rMenuEvent.GetMenu() != mpMenu ) // Also called for the root menu + return; + + switch ( rMenuEvent.GetId() ) + { + case VclEventId::MenuSelect: + { + if ( maMenuListeners.getLength() ) + { + css::awt::MenuEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.MenuId = mpMenu->GetCurItemId(); + maMenuListeners.itemSelected( aEvent ); + } + } + break; + case VclEventId::ObjectDying: + { + mpMenu = nullptr; + } + break; + case VclEventId::MenuHighlight: + { + if ( maMenuListeners.getLength() ) + { + css::awt::MenuEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.MenuId = mpMenu->GetCurItemId(); + maMenuListeners.itemHighlighted( aEvent ); + } + } + break; + case VclEventId::MenuActivate: + { + if ( maMenuListeners.getLength() ) + { + css::awt::MenuEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.MenuId = mpMenu->GetCurItemId(); + maMenuListeners.itemActivated( aEvent ); + } + } + break; + case VclEventId::MenuDeactivate: + { + if ( maMenuListeners.getLength() ) + { + css::awt::MenuEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.MenuId = mpMenu->GetCurItemId(); + maMenuListeners.itemDeactivated( aEvent ); + } + } + break; + + // ignore accessibility events + case VclEventId::MenuEnable: + case VclEventId::MenuInsertItem: + case VclEventId::MenuRemoveItem: + case VclEventId::MenuSubmenuActivate: + case VclEventId::MenuSubmenuDeactivate: + case VclEventId::MenuSubmenuChanged: + case VclEventId::MenuDehighlight: + case VclEventId::MenuDisable: + case VclEventId::MenuItemRoleChanged: + case VclEventId::MenuItemTextChanged: + case VclEventId::MenuItemChecked: + case VclEventId::MenuItemUnchecked: + case VclEventId::MenuShow: + case VclEventId::MenuHide: + break; + + default: OSL_FAIL( "MenuEventListener - Unknown event!" ); + } +} + + +OUString SAL_CALL VCLXMenu::getImplementationName( ) +{ + std::unique_lock aGuard( maMutex ); + const bool bIsPopupMenu = IsPopupMenu(); + aGuard.unlock(); + + OUString implName( "stardiv.Toolkit." ); + if ( bIsPopupMenu ) + implName += "VCLXPopupMenu"; + else + implName += "VCLXMenuBar"; + + return implName; +} + +css::uno::Sequence< OUString > SAL_CALL VCLXMenu::getSupportedServiceNames( ) +{ + std::unique_lock aGuard( maMutex ); + const bool bIsPopupMenu = IsPopupMenu(); + aGuard.unlock(); + + if ( bIsPopupMenu ) + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.PopupMenu", + "stardiv.vcl.PopupMenu"}; + else + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.MenuBar", + "stardiv.vcl.MenuBar"}; +} + +sal_Bool SAL_CALL VCLXMenu::supportsService(const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Any VCLXMenu::queryInterface( + const css::uno::Type & rType ) +{ + std::unique_lock aGuard( maMutex ); + const bool bIsPopupMenu = IsPopupMenu(); + aGuard.unlock(); + + css::uno::Any aRet; + + if ( bIsPopupMenu ) + aRet = ::cppu::queryInterface( rType, + static_cast< css::awt::XMenu* >(static_cast<css::awt::XMenuBar*>(this)), + static_cast< css::awt::XPopupMenu* >(this), + static_cast< css::lang::XTypeProvider* >(this), + static_cast< css::lang::XServiceInfo* >(this) ); + else + aRet = ::cppu::queryInterface( rType, + static_cast< css::awt::XMenu* >(static_cast<css::awt::XMenuBar*>(this)), + static_cast< css::awt::XMenuBar* >(this), + static_cast< css::lang::XTypeProvider* >(this), + static_cast< css::lang::XServiceInfo* >(this) ); + + return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType )); +} + + +css::uno::Sequence< css::uno::Type > VCLXMenu::getTypes() +{ + std::unique_lock aGuard( maMutex ); + const bool bIsPopupMenu = IsPopupMenu(); + aGuard.unlock(); + + if ( bIsPopupMenu ) + { + static cppu::OTypeCollection collectionPopupMenu( + cppu::UnoType<css::lang::XTypeProvider>::get(), cppu::UnoType<css::awt::XMenu>::get(), + cppu::UnoType<css::awt::XPopupMenu>::get(), + cppu::UnoType<css::lang::XServiceInfo>::get()); + return collectionPopupMenu.getTypes(); + } + else + { + static cppu::OTypeCollection collectionMenuBar( + cppu::UnoType<css::lang::XTypeProvider>::get(), cppu::UnoType<css::awt::XMenu>::get(), + cppu::UnoType<css::awt::XMenuBar>::get(), + cppu::UnoType<css::lang::XServiceInfo>::get()); + return collectionMenuBar.getTypes(); + } +} + + +css::uno::Sequence< sal_Int8 > VCLXMenu::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +void VCLXMenu::addMenuListener( + const css::uno::Reference< css::awt::XMenuListener >& rxListener ) +{ + std::unique_lock aGuard( maMutex ); + + maMenuListeners.addInterface( rxListener ); +} + +void VCLXMenu::removeMenuListener( + const css::uno::Reference< css::awt::XMenuListener >& rxListener ) +{ + std::unique_lock aGuard( maMutex ); + + maMenuListeners.removeInterface( rxListener ); +} + +void VCLXMenu::insertItem( + sal_Int16 nItemId, + const OUString& aText, + sal_Int16 nItemStyle, + sal_Int16 nPos ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->InsertItem(nItemId, aText, static_cast<MenuItemBits>(nItemStyle), {}, nPos); +} + +void VCLXMenu::removeItem( + sal_Int16 nPos, + sal_Int16 nCount ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if (!mpMenu) + return; + + sal_Int32 nItemCount = static_cast<sal_Int32>(mpMenu->GetItemCount()); + if ((nCount > 0) && (nPos >= 0) && (nPos < nItemCount)) + { + sal_Int16 nP = sal::static_int_cast< sal_Int16 >( + std::min( static_cast<int>(nPos+nCount), static_cast<int>(nItemCount) )); + while( nP-nPos > 0 ) + mpMenu->RemoveItem( --nP ); + } +} + +sal_Int16 VCLXMenu::getItemCount( ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + return mpMenu ? mpMenu->GetItemCount() : 0; +} + +sal_Int16 VCLXMenu::getItemId( + sal_Int16 nPos ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + return mpMenu ? mpMenu->GetItemId( nPos ) : 0; +} + +sal_Int16 VCLXMenu::getItemPos( + sal_Int16 nId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + return mpMenu ? mpMenu->GetItemPos( nId ) : 0; +} + +void VCLXMenu::enableItem( + sal_Int16 nItemId, + sal_Bool bEnable ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->EnableItem( nItemId, bEnable ); +} + +sal_Bool VCLXMenu::isItemEnabled( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + return mpMenu && mpMenu->IsItemEnabled( nItemId ); +} + +void VCLXMenu::setItemText( + sal_Int16 nItemId, + const OUString& aText ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->SetItemText( nItemId, aText ); +} + +OUString VCLXMenu::getItemText( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + OUString aItemText; + if ( mpMenu ) + aItemText = mpMenu->GetItemText( nItemId ); + return aItemText; +} + +void VCLXMenu::setPopupMenu( + sal_Int16 nItemId, + const css::uno::Reference< css::awt::XPopupMenu >& rxPopupMenu ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + VCLXMenu* pVCLMenu = dynamic_cast<VCLXMenu*>( rxPopupMenu.get() ); + DBG_ASSERT( pVCLMenu && pVCLMenu->GetMenu() && pVCLMenu->IsPopupMenu(), "setPopupMenu: Invalid Menu!" ); + + if ( mpMenu && pVCLMenu && pVCLMenu->GetMenu() && pVCLMenu->IsPopupMenu() ) + { + maPopupMenuRefs.push_back( rxPopupMenu ); + + mpMenu->SetPopupMenu( nItemId, static_cast<PopupMenu*>( pVCLMenu->GetMenu() ) ); + } +} + +css::uno::Reference< css::awt::XPopupMenu > VCLXMenu::getPopupMenu( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + css::uno::Reference< css::awt::XPopupMenu > aRef; + Menu* pMenu = mpMenu ? mpMenu->GetPopupMenu( nItemId ) : nullptr; + if ( pMenu ) + { + for ( size_t n = maPopupMenuRefs.size(); n; ) + { + css::uno::Reference< css::awt::XPopupMenu >& rRef = maPopupMenuRefs[ --n ]; + Menu* pM = static_cast<VCLXMenu*>(rRef.get())->GetMenu(); + if ( pM == pMenu ) + { + aRef = rRef; + break; + } + } + // it seems the popup menu is not insert into maPopupMenuRefs + // if the popup men is not created by stardiv.Toolkit.VCLXPopupMenu + if( !aRef.is() ) + { + aRef = new VCLXPopupMenu( static_cast<PopupMenu*>(pMenu) ); + } + } + return aRef; +} + +// css::awt::XPopupMenu +void VCLXMenu::insertSeparator( + sal_Int16 nPos ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->InsertSeparator({}, nPos); +} + +void VCLXMenu::setDefaultItem( + sal_Int16 nItemId ) +{ + std::unique_lock aGuard( maMutex ); + + mnDefaultItem = nItemId; +} + +sal_Int16 VCLXMenu::getDefaultItem( ) +{ + std::unique_lock aGuard( maMutex ); + + return mnDefaultItem; +} + +void VCLXMenu::checkItem( + sal_Int16 nItemId, + sal_Bool bCheck ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->CheckItem( nItemId, bCheck ); +} + +sal_Bool VCLXMenu::isItemChecked( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + return mpMenu && mpMenu->IsItemChecked( nItemId ); +} + +sal_Int16 VCLXMenu::execute( + const css::uno::Reference< css::awt::XWindowPeer >& rxWindowPeer, + const css::awt::Rectangle& rPos, + sal_Int16 nFlags ) +{ + SolarMutexGuard aSolarGuard; + auto pMenu = mpMenu; + { + std::unique_lock aGuard( maMutex ); + if ( !mpMenu || !IsPopupMenu() ) + return 0; + } + // cannot call this with mutex locked because it will call back into us + return static_cast<PopupMenu*>(pMenu.get())->Execute( + VCLUnoHelper::GetWindow( rxWindowPeer ), + VCLRectangle( rPos ), + static_cast<PopupMenuFlags>(nFlags) | PopupMenuFlags::NoMouseUpClose ); +} + + +void SAL_CALL VCLXMenu::setCommand( + sal_Int16 nItemId, + const OUString& aCommand ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->SetItemCommand( nItemId, aCommand ); +} + +OUString SAL_CALL VCLXMenu::getCommand( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + OUString aItemCommand; + if ( mpMenu ) + aItemCommand = mpMenu->GetItemCommand( nItemId ); + return aItemCommand; +} + +void SAL_CALL VCLXMenu::setHelpCommand( + sal_Int16 nItemId, + const OUString& aHelp ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu ) + mpMenu->SetHelpCommand( nItemId, aHelp ); +} + +OUString SAL_CALL VCLXMenu::getHelpCommand( + sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + OUString aHelpCommand; + if ( mpMenu ) + aHelpCommand = mpMenu->GetHelpCommand( nItemId ); + return aHelpCommand; +} + + +namespace +{ + Image lcl_XGraphic2VCLImage( + const css::uno::Reference< css::graphic::XGraphic >& xGraphic, + bool bResize ) + { + Image aImage; + if ( !xGraphic.is() ) + return aImage; + + aImage = Image( xGraphic ); + const ::Size aCurSize = aImage.GetSizePixel(); + const sal_Int32 nCurWidth = aCurSize.Width(); + const sal_Int32 nCurHeight = aCurSize.Height(); + constexpr sal_Int32 nIdeal( 16 ); + + if ( nCurWidth > 0 && nCurHeight > 0 ) + { + if ( bResize && ( nCurWidth > nIdeal || nCurHeight > nIdeal ) ) + { + sal_Int32 nIdealWidth = std::min(nCurWidth, nIdeal); + sal_Int32 nIdealHeight = std::min(nCurHeight, nIdeal); + + ::Size aNewSize( nIdealWidth, nIdealHeight ); + + bool bModified( false ); + BitmapEx aBitmapEx = aImage.GetBitmapEx(); + bModified = aBitmapEx.Scale( aNewSize, BmpScaleFlag::BestQuality ); + + if ( bModified ) + aImage = Image( aBitmapEx ); + } + } + return aImage; + } + + /** Copied from include/svtools/acceleratorexecute.hxx */ + css::awt::KeyEvent lcl_VCLKey2AWTKey( + const vcl::KeyCode& aVCLKey) + { + css::awt::KeyEvent aAWTKey; + aAWTKey.Modifiers = 0; + aAWTKey.KeyCode = static_cast<sal_Int16>(aVCLKey.GetCode()); + + if (aVCLKey.IsShift()) + aAWTKey.Modifiers |= css::awt::KeyModifier::SHIFT; + if (aVCLKey.IsMod1()) + aAWTKey.Modifiers |= css::awt::KeyModifier::MOD1; + if (aVCLKey.IsMod2()) + aAWTKey.Modifiers |= css::awt::KeyModifier::MOD2; + if (aVCLKey.IsMod3()) + aAWTKey.Modifiers |= css::awt::KeyModifier::MOD3; + + return aAWTKey; + } + + vcl::KeyCode lcl_AWTKey2VCLKey(const css::awt::KeyEvent& aAWTKey) + { + bool bShift = ((aAWTKey.Modifiers & css::awt::KeyModifier::SHIFT) == css::awt::KeyModifier::SHIFT ); + bool bMod1 = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD1 ) == css::awt::KeyModifier::MOD1 ); + bool bMod2 = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD2 ) == css::awt::KeyModifier::MOD2 ); + bool bMod3 = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD3 ) == css::awt::KeyModifier::MOD3 ); + sal_uInt16 nKey = static_cast<sal_uInt16>(aAWTKey.KeyCode); + + return vcl::KeyCode(nKey, bShift, bMod1, bMod2, bMod3); + } + +} + + +sal_Bool SAL_CALL VCLXMenu::isPopupMenu( ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + return IsPopupMenu(); +} + +void SAL_CALL VCLXMenu::clear( ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + if ( mpMenu ) + mpMenu->Clear(); +} + + +css::awt::MenuItemType SAL_CALL VCLXMenu::getItemType( + ::sal_Int16 nItemPos ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + css::awt::MenuItemType aMenuItemType = + css::awt::MenuItemType_DONTKNOW; + if ( mpMenu ) + { + aMenuItemType = static_cast<css::awt::MenuItemType>(mpMenu->GetItemType( nItemPos )); + } + + return aMenuItemType; +} + +void SAL_CALL VCLXMenu::hideDisabledEntries( + sal_Bool bHide ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + if ( mpMenu ) + { + if ( bHide ) + mpMenu->SetMenuFlags( mpMenu->GetMenuFlags() | MenuFlags::HideDisabledEntries ); + else + mpMenu->SetMenuFlags( mpMenu->GetMenuFlags() & ~MenuFlags::HideDisabledEntries ); + } +} + + +sal_Bool SAL_CALL VCLXMenu::isInExecute( ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && IsPopupMenu() ) + return vcl::IsInPopupMenuExecute(); + else + return false; +} + + +void SAL_CALL VCLXMenu::endExecute() +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && IsPopupMenu() ) + static_cast<PopupMenu*>( mpMenu.get() )->EndExecute(); +} + + +void SAL_CALL VCLXMenu::enableAutoMnemonics( + sal_Bool bEnable ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + if ( mpMenu ) + { + if ( !bEnable ) + mpMenu->SetMenuFlags( mpMenu->GetMenuFlags() | MenuFlags::NoAutoMnemonics ); + else + mpMenu->SetMenuFlags( mpMenu->GetMenuFlags() & ~MenuFlags::NoAutoMnemonics ); + } +} + + +void SAL_CALL VCLXMenu::setAcceleratorKeyEvent( + ::sal_Int16 nItemId, + const css::awt::KeyEvent& aKeyEvent ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && IsPopupMenu() && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + vcl::KeyCode aVCLKeyCode = lcl_AWTKey2VCLKey( aKeyEvent ); + mpMenu->SetAccelKey( nItemId, aVCLKeyCode ); + } +} + + +css::awt::KeyEvent SAL_CALL VCLXMenu::getAcceleratorKeyEvent( + ::sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + css::awt::KeyEvent aKeyEvent; + if ( mpMenu && IsPopupMenu() && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + vcl::KeyCode nKeyCode = mpMenu->GetAccelKey( nItemId ); + aKeyEvent = lcl_VCLKey2AWTKey( nKeyCode ); + } + + return aKeyEvent; +} + + +void SAL_CALL VCLXMenu::setHelpText( + ::sal_Int16 nItemId, + const OUString& sHelpText ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + mpMenu->SetHelpText( nItemId, sHelpText ); + } +} + + +OUString SAL_CALL VCLXMenu::getHelpText( + ::sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + OUString sHelpText; + if ( mpMenu && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + sHelpText = mpMenu->GetHelpText( nItemId ); + } + + return sHelpText; +} + + +void SAL_CALL VCLXMenu::setTipHelpText( + ::sal_Int16 nItemId, + const OUString& sTipHelpText ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + mpMenu->SetTipHelpText( nItemId, sTipHelpText ); + } +} + + +OUString SAL_CALL VCLXMenu::getTipHelpText( + ::sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + OUString sTipHelpText; + if ( mpMenu && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + sTipHelpText = mpMenu->GetTipHelpText( nItemId ); + } + return sTipHelpText; +} + + +void SAL_CALL VCLXMenu::setItemImage( + ::sal_Int16 nItemId, + const css::uno::Reference< css::graphic::XGraphic >& xGraphic, + sal_Bool bScale ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if ( mpMenu && IsPopupMenu() && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + Image aImage = lcl_XGraphic2VCLImage( xGraphic, bScale ); + mpMenu->SetItemImage( nItemId, aImage ); + } +} + + +css::uno::Reference< css::graphic::XGraphic > SAL_CALL +VCLXMenu::getItemImage( + ::sal_Int16 nItemId ) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + css::uno::Reference< css::graphic::XGraphic > rxGraphic; + + if ( mpMenu && IsPopupMenu() && MENU_ITEM_NOTFOUND != mpMenu->GetItemPos( nItemId ) ) + { + Image aImage = mpMenu->GetItemImage( nItemId ); + if ( !!aImage ) + rxGraphic = Graphic(aImage.GetBitmapEx()).GetXGraphic(); + } + return rxGraphic; +} + +void VCLXMenu::setUserValue(sal_uInt16 nItemId, void* nUserValue, MenuUserDataReleaseFunction aFunc) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard(maMutex); + + mpMenu->SetUserValue(nItemId, nUserValue, aFunc); +} + +void* VCLXMenu::getUserValue(sal_uInt16 nItemId) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard(maMutex); + + return mpMenu->GetUserValue(nItemId); +} + +VCLXMenuBar::VCLXMenuBar() +{ + ImplCreateMenu( false ); +} + +VCLXMenuBar::VCLXMenuBar( MenuBar* pMenuBar ) : VCLXMenu( static_cast<Menu *>(pMenuBar) ) +{ +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_VCLXMenuBar_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VCLXMenuBar()); +} + +VCLXPopupMenu::VCLXPopupMenu() +{ + ImplCreateMenu( true ); +} + +VCLXPopupMenu::VCLXPopupMenu( PopupMenu* pPopMenu ) : VCLXMenu( static_cast<Menu *>(pPopMenu) ) +{ + ImplAddListener(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_VCLXPopupMenu_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VCLXPopupMenu()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxpointer.cxx b/toolkit/source/awt/vclxpointer.cxx new file mode 100644 index 0000000000..b541610413 --- /dev/null +++ b/toolkit/source/awt/vclxpointer.cxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <awt/vclxpointer.hxx> +#include <cppuhelper/supportsservice.hxx> + +VCLXPointer::VCLXPointer() : maPointer(PointerStyle::Arrow) +{ +} + +VCLXPointer::~VCLXPointer() +{ +} + +void VCLXPointer::setType( sal_Int32 nType ) +{ + std::scoped_lock aGuard( maMutex ); + + maPointer = static_cast<PointerStyle>(nType); +} + +sal_Int32 VCLXPointer::getType() +{ + std::scoped_lock aGuard( maMutex ); + + return static_cast<sal_Int32>(maPointer); +} + +OUString VCLXPointer::getImplementationName() +{ + return "stardiv.Toolkit.VCLXPointer"; +} + +sal_Bool VCLXPointer::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> VCLXPointer::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.Pointer", "stardiv.vcl.Pointer"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_VCLXPointer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VCLXPointer()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxprinter.cxx b/toolkit/source/awt/vclxprinter.cxx new file mode 100644 index 0000000000..6ace061795 --- /dev/null +++ b/toolkit/source/awt/vclxprinter.cxx @@ -0,0 +1,379 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <awt/vclxprinter.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/string.hxx> + +#include <vcl/print.hxx> +#include <vcl/jobset.hxx> +#include <vcl/oldprintadaptor.hxx> +#include <vcl/svapp.hxx> + +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <o3tl/string_view.hxx> + +#include <toolkit/awt/vclxdevice.hxx> + + +#define BINARYSETUPMARKER 0x23864691 + +#define PROPERTY_Orientation 0 +#define PROPERTY_Horizontal 1 + +// ---------------------------------------------------- +// class VCLXPrinterPropertySet +// ---------------------------------------------------- + +IMPLEMENT_FORWARD_XINTERFACE2( VCLXPrinterPropertySet, VCLXPrinterPropertySet_Base, OPropertySetHelper ) +IMPLEMENT_FORWARD_XTYPEPROVIDER2( VCLXPrinterPropertySet, VCLXPrinterPropertySet_Base, ::cppu::OPropertySetHelper ) + +VCLXPrinterPropertySet::VCLXPrinterPropertySet( const OUString& rPrinterName ) + : OPropertySetHelper( m_aBHelper ) + , mxPrinter(VclPtrInstance< Printer >(rPrinterName)) +{ + SolarMutexGuard aSolarGuard; + + mnOrientation = 0; + mbHorizontal = false; +} + +VCLXPrinterPropertySet::~VCLXPrinterPropertySet() +{ + SolarMutexGuard aSolarGuard; + mxPrinter.reset(); +} + +css::uno::Reference< css::awt::XDevice > const & VCLXPrinterPropertySet::GetDevice() +{ + if ( !mxPrnDevice.is() ) + { + rtl::Reference<VCLXDevice> pDev = new VCLXDevice; + pDev->SetOutputDevice( GetPrinter() ); + mxPrnDevice = pDev; + } + return mxPrnDevice; +} + +css::uno::Reference< css::beans::XPropertySetInfo > VCLXPrinterPropertySet::getPropertySetInfo( ) +{ + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +::cppu::IPropertyArrayHelper& VCLXPrinterPropertySet::getInfoHelper() +{ + static ::cppu::OPropertyArrayHelper s_PropertyArrayHelper( + css::uno::Sequence<css::beans::Property>{ + css::beans::Property( "Orientation", PROPERTY_Orientation, cppu::UnoType<sal_Int16>::get(), 0 ), + css::beans::Property( "Horizontal", PROPERTY_Horizontal, cppu::UnoType<bool>::get(), 0 )}, + false); + + return s_PropertyArrayHelper; +} + +sal_Bool VCLXPrinterPropertySet::convertFastPropertyValue( css::uno::Any & rConvertedValue, css::uno::Any & rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + bool bDifferent = false; + switch ( nHandle ) + { + case PROPERTY_Orientation: + { + sal_Int16 n; + if( ( rValue >>= n ) && ( n != mnOrientation ) ) + { + rConvertedValue <<= n; + rOldValue <<= mnOrientation; + bDifferent = true; + } + } + break; + case PROPERTY_Horizontal: + { + bool b; + if( ( rValue >>= b ) && ( b != mbHorizontal ) ) + { + rConvertedValue <<= b; + rOldValue <<= mbHorizontal; + bDifferent = true; + } + } + break; + default: + { + OSL_FAIL( "VCLXPrinterPropertySet_Impl::convertFastPropertyValue - invalid Handle" ); + } + } + return bDifferent; +} + +void VCLXPrinterPropertySet::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + switch( nHandle ) + { + case PROPERTY_Orientation: + { + rValue >>= mnOrientation; + } + break; + case PROPERTY_Horizontal: + { + rValue >>= mbHorizontal; + } + break; + default: + { + OSL_FAIL( "VCLXPrinterPropertySet_Impl::convertFastPropertyValue - invalid Handle" ); + } + } +} + +void VCLXPrinterPropertySet::getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const +{ + ::osl::MutexGuard aGuard( const_cast<VCLXPrinterPropertySet*>(this)->m_aMutex ); + + switch( nHandle ) + { + case PROPERTY_Orientation: + rValue <<= mnOrientation; + break; + case PROPERTY_Horizontal: + rValue <<= mbHorizontal; + break; + default: + { + OSL_FAIL( "VCLXPrinterPropertySet_Impl::convertFastPropertyValue - invalid Handle" ); + } + } +} + +// css::awt::XPrinterPropertySet +void VCLXPrinterPropertySet::setHorizontal( sal_Bool bHorizontal ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + css::uno::Any aValue; + aValue <<= bHorizontal; + setFastPropertyValue( PROPERTY_Horizontal, aValue ); +} + +css::uno::Sequence< OUString > VCLXPrinterPropertySet::getFormDescriptions( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + const sal_uInt16 nPaperBinCount = GetPrinter()->GetPaperBinCount(); + css::uno::Sequence< OUString > aDescriptions( nPaperBinCount ); + for ( sal_uInt16 n = 0; n < nPaperBinCount; n++ ) + { + // Format: <DisplayFormName;FormNameId;DisplayPaperBinName;PaperBinNameId;DisplayPaperName;PaperNameId> + OUString aDescr = "*;*;" + GetPrinter()->GetPaperBinName( n ) + ";" + + OUString::number(n) + ";*;*"; + + aDescriptions.getArray()[n] = aDescr; + } + return aDescriptions; +} + +void VCLXPrinterPropertySet::selectForm( const OUString& rFormDescription ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + sal_uInt16 nPaperBin = sal::static_int_cast< sal_uInt16 >( + o3tl::toInt32(o3tl::getToken(rFormDescription, 3, ';' ))); + GetPrinter()->SetPaperBin( nPaperBin ); +} + +css::uno::Sequence< sal_Int8 > VCLXPrinterPropertySet::getBinarySetup( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + SvMemoryStream aMem; + aMem.WriteUInt32( BINARYSETUPMARKER ); + WriteJobSetup( aMem, GetPrinter()->GetJobSetup() ); + return css::uno::Sequence<sal_Int8>( static_cast<sal_Int8 const *>(aMem.GetData()), aMem.Tell() ); +} + +void VCLXPrinterPropertySet::setBinarySetup( const css::uno::Sequence< sal_Int8 >& data ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + SvMemoryStream aMem( const_cast<signed char*>(data.getConstArray()), data.getLength(), StreamMode::READ ); + sal_uInt32 nMarker; + aMem.ReadUInt32( nMarker ); + DBG_ASSERT( nMarker == BINARYSETUPMARKER, "setBinarySetup - invalid!" ); + if ( nMarker == BINARYSETUPMARKER ) + { + JobSetup aSetup; + ReadJobSetup( aMem, aSetup ); + GetPrinter()->SetJobSetup( aSetup ); + } +} + + +// ---------------------------------------------------- +// class VCLXPrinter +// ---------------------------------------------------- +VCLXPrinter::VCLXPrinter( const OUString& rPrinterName ) + : VCLXPrinter_Base( rPrinterName ) +{ +} + +VCLXPrinter::~VCLXPrinter() +{ +} + +sal_Bool VCLXPrinter::start( const OUString& /*rJobName*/, sal_Int16 /*nCopies*/, sal_Bool /*bCollate*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if (mxPrinter) + { + maInitJobSetup = mxPrinter->GetJobSetup(); + mxListener = std::make_shared<vcl::OldStylePrintAdaptor>(mxPrinter, nullptr); + } + + return true; +} + +void VCLXPrinter::end( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if (mxListener) + { + Printer::PrintJob(mxListener, maInitJobSetup); + mxListener.reset(); + } +} + +void VCLXPrinter::terminate( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + mxListener.reset(); +} + +css::uno::Reference< css::awt::XDevice > VCLXPrinter::startPage( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if (mxListener) + { + mxListener->StartPage(); + } + return GetDevice(); +} + +void VCLXPrinter::endPage( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if (mxListener) + { + mxListener->EndPage(); + } +} + + +// ---------------------------------------------------- +// class VCLXInfoPrinter +// ---------------------------------------------------- + +VCLXInfoPrinter::VCLXInfoPrinter( const OUString& rPrinterName ) + : VCLXInfoPrinter_Base( rPrinterName ) +{ +} + +VCLXInfoPrinter::~VCLXInfoPrinter() +{ +} + +// css::awt::XInfoPrinter +css::uno::Reference< css::awt::XDevice > VCLXInfoPrinter::createDevice( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + return GetDevice(); +} + +// ---------------------------------------------------- +// class VCLXPrinterServer +// ---------------------------------------------------- + +// css::awt::XPrinterServer2 +css::uno::Sequence< OUString > VCLXPrinterServer::getPrinterNames( ) +{ + const std::vector<OUString>& rQueues = Printer::GetPrinterQueues(); + sal_uInt32 nPrinters = rQueues.size(); + + css::uno::Sequence< OUString > aNames( nPrinters ); + for ( sal_uInt32 n = 0; n < nPrinters; n++ ) + aNames.getArray()[n] = rQueues[n]; + + return aNames; +} + +OUString VCLXPrinterServer::getDefaultPrinterName() +{ + return Printer::GetDefaultPrinterName(); +} + +css::uno::Reference< css::awt::XPrinter > VCLXPrinterServer::createPrinter( const OUString& rPrinterName ) +{ + css::uno::Reference< css::awt::XPrinter > xP = new VCLXPrinter( rPrinterName ); + return xP; +} + +css::uno::Reference< css::awt::XInfoPrinter > VCLXPrinterServer::createInfoPrinter( const OUString& rPrinterName ) +{ + css::uno::Reference< css::awt::XInfoPrinter > xP = new VCLXInfoPrinter( rPrinterName ); + return xP; +} + +OUString VCLXPrinterServer::getImplementationName() +{ + return "stardiv.Toolkit.VCLXPrinterServer"; +} + +sal_Bool VCLXPrinterServer::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> VCLXPrinterServer::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.PrinterServer", "stardiv.vcl.PrinterServer"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_VCLXPrinterServer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VCLXPrinterServer); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxregion.cxx b/toolkit/source/awt/vclxregion.cxx new file mode 100644 index 0000000000..fff7e7969d --- /dev/null +++ b/toolkit/source/awt/vclxregion.cxx @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <awt/vclxregion.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/helper/convert.hxx> + + + +VCLXRegion::VCLXRegion() +{ +} + +VCLXRegion::~VCLXRegion() +{ +} + +css::awt::Rectangle VCLXRegion::getBounds() +{ + std::scoped_lock aGuard( maMutex ); + + return AWTRectangle( maRegion.GetBoundRect() ); +} + +void VCLXRegion::clear() +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.SetEmpty(); +} + +void VCLXRegion::move( sal_Int32 nHorzMove, sal_Int32 nVertMove ) +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.Move( nHorzMove, nVertMove ); +} + +void VCLXRegion::unionRectangle( const css::awt::Rectangle& rRect ) +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.Union( VCLRectangle( rRect ) ); +} + +void VCLXRegion::intersectRectangle( const css::awt::Rectangle& rRect ) +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.Intersect( VCLRectangle( rRect ) ); +} + +void VCLXRegion::excludeRectangle( const css::awt::Rectangle& rRect ) +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.Exclude( VCLRectangle( rRect ) ); +} + +void VCLXRegion::xOrRectangle( const css::awt::Rectangle& rRect ) +{ + std::scoped_lock aGuard( maMutex ); + + maRegion.XOr( VCLRectangle( rRect ) ); +} + +void VCLXRegion::unionRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) +{ + std::scoped_lock aGuard( maMutex ); + + if ( rxRegion.is() ) + maRegion.Union( VCLUnoHelper::GetRegion( rxRegion ) ); +} + +void VCLXRegion::intersectRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) +{ + std::scoped_lock aGuard( maMutex ); + + if ( rxRegion.is() ) + maRegion.Intersect( VCLUnoHelper::GetRegion( rxRegion ) ); +} + +void VCLXRegion::excludeRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) +{ + std::scoped_lock aGuard( maMutex ); + + if ( rxRegion.is() ) + maRegion.Exclude( VCLUnoHelper::GetRegion( rxRegion ) ); +} + +void VCLXRegion::xOrRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) +{ + std::scoped_lock aGuard( maMutex ); + + if ( rxRegion.is() ) + maRegion.XOr( VCLUnoHelper::GetRegion( rxRegion ) ); +} + +css::uno::Sequence< css::awt::Rectangle > VCLXRegion::getRectangles() +{ + std::scoped_lock aGuard( maMutex ); + + RectangleVector aRectangles; + maRegion.GetRegionRectangles(aRectangles); + +// sal_uLong nRects = maRegion.GetRectCount(); + css::uno::Sequence< css::awt::Rectangle > aRects(aRectangles.size()); + sal_uInt32 a(0); + + for(const auto& rRect : aRectangles) + { + aRects.getArray()[a++] = AWTRectangle(rRect); + } + + //Rectangle aRect; + //sal_uInt32 nR = 0; + //RegionHandle h = maRegion.BeginEnumRects(); + //while ( maRegion.GetEnumRects( h, aRect ) ) + // aRects.getArray()[nR++] = AWTRectangle( aRect ); + //maRegion.EndEnumRects( h ); + + return aRects; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxspinbutton.cxx b/toolkit/source/awt/vclxspinbutton.cxx new file mode 100644 index 0000000000..cc4747dc20 --- /dev/null +++ b/toolkit/source/awt/vclxspinbutton.cxx @@ -0,0 +1,331 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <awt/vclxspinbutton.hxx> +#include <helper/property.hxx> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> + +#include <vcl/toolkit/spin.hxx> +#include <vcl/svapp.hxx> +#include "vclxwindows_internal.hxx" + +namespace toolkit +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + + + namespace + { + void lcl_modifyStyle( vcl::Window* _pWindow, WinBits _nStyleBits, bool _bShouldBePresent ) + { + WinBits nStyle = _pWindow->GetStyle(); + if ( _bShouldBePresent ) + nStyle |= _nStyleBits; + else + nStyle &= ~_nStyleBits; + _pWindow->SetStyle( nStyle ); + } + } + + VCLXSpinButton::VCLXSpinButton() + :maAdjustmentListeners( *this ) + { + } + + + VCLXSpinButton::~VCLXSpinButton() + { + } + + + IMPLEMENT_FORWARD_XINTERFACE2( VCLXSpinButton, VCLXWindow, VCLXSpinButton_Base ) + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( VCLXSpinButton, VCLXWindow, VCLXSpinButton_Base ) + + + void SAL_CALL VCLXSpinButton::dispose( ) + { + { + SolarMutexGuard aGuard; + + EventObject aDisposeEvent; + aDisposeEvent.Source = *this; + maAdjustmentListeners.disposeAndClear( aDisposeEvent ); + } + + VCLXWindow::dispose(); + } + + + void SAL_CALL VCLXSpinButton::addAdjustmentListener( const Reference< XAdjustmentListener >& listener ) + { + if ( listener.is() ) + maAdjustmentListeners.addInterface( listener ); + } + + + void SAL_CALL VCLXSpinButton::removeAdjustmentListener( const Reference< XAdjustmentListener >& listener ) + { + if ( listener.is() ) + maAdjustmentListeners.removeInterface( listener ); + } + + namespace + { + typedef void (SpinButton::*SetSpinButtonValue) (tools::Long); + typedef tools::Long (SpinButton::*GetSpinButtonValue) () const; + + + void lcl_setSpinButtonValue(vcl::Window* _pWindow, SetSpinButtonValue _pSetter, sal_Int32 _nValue ) + { + SolarMutexGuard aGuard; + SpinButton* pSpinButton = static_cast< SpinButton* >( _pWindow ); + if ( pSpinButton ) + (pSpinButton->*_pSetter)( _nValue ); + } + + + sal_Int32 lcl_getSpinButtonValue(const vcl::Window* _pWindow, GetSpinButtonValue _pGetter ) + { + SolarMutexGuard aGuard; + + sal_Int32 nValue = 0; + + const SpinButton* pSpinButton = static_cast< const SpinButton* >( _pWindow ); + if ( pSpinButton ) + nValue = (pSpinButton->*_pGetter)( ); + return nValue; + } + } + + + void SAL_CALL VCLXSpinButton::setValue( sal_Int32 n ) + { + lcl_setSpinButtonValue( GetWindow(), &SpinButton::SetValue, n ); + } + + + void SAL_CALL VCLXSpinButton::setValues( sal_Int32 minValue, sal_Int32 maxValue, sal_Int32 currentValue ) + { + SolarMutexGuard aGuard; + + setMinimum( minValue ); + setMaximum( maxValue ); + setValue( currentValue ); + } + + + sal_Int32 SAL_CALL VCLXSpinButton::getValue( ) + { + return lcl_getSpinButtonValue( GetWindow(), &SpinButton::GetValue ); + } + + + void SAL_CALL VCLXSpinButton::setMinimum( sal_Int32 minValue ) + { + lcl_setSpinButtonValue( GetWindow(), &SpinButton::SetRangeMin, minValue ); + } + + + void SAL_CALL VCLXSpinButton::setMaximum( sal_Int32 maxValue ) + { + lcl_setSpinButtonValue( GetWindow(), &SpinButton::SetRangeMax, maxValue ); + } + + + sal_Int32 SAL_CALL VCLXSpinButton::getMinimum( ) + { + return lcl_getSpinButtonValue( GetWindow(), &SpinButton::GetRangeMin ); + } + + + sal_Int32 SAL_CALL VCLXSpinButton::getMaximum( ) + { + return lcl_getSpinButtonValue( GetWindow(), &SpinButton::GetRangeMax ); + } + + + void SAL_CALL VCLXSpinButton::setSpinIncrement( sal_Int32 spinIncrement ) + { + lcl_setSpinButtonValue( GetWindow(), &SpinButton::SetValueStep, spinIncrement ); + } + + + sal_Int32 SAL_CALL VCLXSpinButton::getSpinIncrement( ) + { + return lcl_getSpinButtonValue( GetWindow(), &SpinButton::GetValueStep ); + } + + + void SAL_CALL VCLXSpinButton::setOrientation( sal_Int32 orientation ) + { + SolarMutexGuard aGuard; + + lcl_modifyStyle( GetWindow(), WB_HSCROLL, orientation == ScrollBarOrientation::HORIZONTAL ); + } + + + sal_Int32 SAL_CALL VCLXSpinButton::getOrientation( ) + { + return ( 0 != ( GetWindow()->GetStyle() & WB_HSCROLL ) ) + ? ScrollBarOrientation::HORIZONTAL + : ScrollBarOrientation::VERTICAL; + } + + + void VCLXSpinButton::ProcessWindowEvent( const VclWindowEvent& _rVclWindowEvent ) + { + SolarMutexClearableGuard aGuard; + Reference< XSpinValue > xKeepAlive( this ); + VclPtr<SpinButton> pSpinButton = GetAs<SpinButton>(); + if ( !pSpinButton ) + return; + + switch ( _rVclWindowEvent.GetId() ) + { + case VclEventId::SpinbuttonUp: + case VclEventId::SpinbuttonDown: + if ( maAdjustmentListeners.getLength() ) + { + AdjustmentEvent aEvent; + aEvent.Source = *this; + aEvent.Value = pSpinButton->GetValue(); + + aGuard.clear(); + maAdjustmentListeners.adjustmentValueChanged( aEvent ); + } + break; + + default: + xKeepAlive.clear(); + aGuard.clear(); + VCLXWindow::ProcessWindowEvent( _rVclWindowEvent ); + break; + } + } + + + void SAL_CALL VCLXSpinButton::setProperty( const OUString& PropertyName, const Any& Value ) + { + SolarMutexGuard aGuard; + + sal_Int32 nValue = 0; + bool bIsLongValue = ( Value >>= nValue ); + + if ( !GetWindow() ) + return; + + sal_uInt16 nPropertyId = GetPropertyId( PropertyName ); + switch ( nPropertyId ) + { + case BASEPROPERTY_BACKGROUNDCOLOR: + // the default implementation of the base class doesn't work here, since our + // interpretation for this property is slightly different + setButtonLikeFaceColor( GetWindow(), Value); + break; + + case BASEPROPERTY_SPINVALUE: + if ( bIsLongValue ) + setValue( nValue ); + break; + + case BASEPROPERTY_SPINVALUE_MIN: + if ( bIsLongValue ) + setMinimum( nValue ); + break; + + case BASEPROPERTY_SPINVALUE_MAX: + if ( bIsLongValue ) + setMaximum( nValue ); + break; + + case BASEPROPERTY_SPININCREMENT: + if ( bIsLongValue ) + setSpinIncrement( nValue ); + break; + + case BASEPROPERTY_ORIENTATION: + if ( bIsLongValue ) + lcl_modifyStyle( GetWindow(), WB_HSCROLL, nValue == ScrollBarOrientation::HORIZONTAL ); + break; + + default: + VCLXWindow::setProperty( PropertyName, Value ); + } + } + + + Any SAL_CALL VCLXSpinButton::getProperty( const OUString& PropertyName ) + { + SolarMutexGuard aGuard; + + Any aReturn; + + if ( GetWindow() ) + { + sal_uInt16 nPropertyId = GetPropertyId( PropertyName ); + switch ( nPropertyId ) + { + case BASEPROPERTY_BACKGROUNDCOLOR: + // the default implementation of the base class doesn't work here, since our + // interpretation for this property is slightly different + aReturn = getButtonLikeFaceColor( GetWindow() ); + break; + + case BASEPROPERTY_SPINVALUE: + aReturn <<= getValue( ); + break; + + case BASEPROPERTY_SPINVALUE_MIN: + aReturn <<= getMinimum( ); + break; + + case BASEPROPERTY_SPINVALUE_MAX: + aReturn <<= getMaximum( ); + break; + + case BASEPROPERTY_SPININCREMENT: + aReturn <<= getSpinIncrement( ); + break; + + case BASEPROPERTY_ORIENTATION: + aReturn <<= static_cast<sal_Int32>( ( 0 != ( GetWindow()->GetStyle() & WB_HSCROLL ) ) + ? ScrollBarOrientation::HORIZONTAL + : ScrollBarOrientation::VERTICAL + ); + break; + + default: + aReturn = VCLXWindow::getProperty( PropertyName ); + } + } + return aReturn; + } + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxsystemdependentwindow.cxx b/toolkit/source/awt/vclxsystemdependentwindow.cxx new file mode 100644 index 0000000000..2ba59470a8 --- /dev/null +++ b/toolkit/source/awt/vclxsystemdependentwindow.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 <com/sun/star/lang/SystemDependent.hpp> + +#if defined UNX && ! defined MACOSX +#include <com/sun/star/awt/SystemDependentXWindow.hpp> +#endif + +#include <awt/vclxsystemdependentwindow.hxx> + +#ifdef MACOSX +#include <premac.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> +#endif + +#include <vcl/svapp.hxx> +#include <vcl/syschild.hxx> +#include <vcl/sysdata.hxx> + + + +VCLXSystemDependentWindow::VCLXSystemDependentWindow() +{ +} + +VCLXSystemDependentWindow::~VCLXSystemDependentWindow() +{ +} + +css::uno::Any VCLXSystemDependentWindow::getWindowHandle( const css::uno::Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 SystemType ) +{ + SolarMutexGuard aGuard; + + // TODO, check the process id + css::uno::Any aRet; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + const SystemEnvData* pSysData = static_cast<SystemChildWindow *>(pWindow.get())->GetSystemData(); + if( pSysData ) + { +#if defined(_WIN32) + if( SystemType == css::lang::SystemDependent::SYSTEM_WIN32 ) + { + aRet <<= reinterpret_cast<sal_IntPtr>(pSysData->hWnd); + } +#elif defined(MACOSX) + if( SystemType == css::lang::SystemDependent::SYSTEM_MAC ) + { + aRet <<= reinterpret_cast<sal_IntPtr>(pSysData->mpNSView); + } +#elif defined(ANDROID) + // Nothing + (void) SystemType; +#elif defined(IOS) + // Nothing + (void) SystemType; +#elif defined(UNX) + if( SystemType == css::lang::SystemDependent::SYSTEM_XWINDOW ) + { + css::awt::SystemDependentXWindow aSD; + aSD.DisplayPointer = sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_IntPtr >(pSysData->pDisplay)); + aSD.WindowHandle = pSysData->GetWindowHandle(pWindow->ImplGetFrame()); + aRet <<= aSD; + } +#endif + } + } + return aRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxtabpagecontainer.cxx b/toolkit/source/awt/vclxtabpagecontainer.cxx new file mode 100644 index 0000000000..afd3d843b7 --- /dev/null +++ b/toolkit/source/awt/vclxtabpagecontainer.cxx @@ -0,0 +1,233 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <awt/vclxtabpagecontainer.hxx> +#include <com/sun/star/awt/tab/XTabPageModel.hpp> +#include <com/sun/star/awt/XControl.hpp> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <helper/property.hxx> +#include <vcl/image.hxx> +#include <vcl/tabpage.hxx> +#include <vcl/tabctrl.hxx> +#include <vcl/svapp.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <helper/tkresmgr.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::view; + + +void VCLXTabPageContainer::GetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXTabPageContainer::VCLXTabPageContainer() : + m_aTabPageListeners( *this ) +{ +} + +VCLXTabPageContainer::~VCLXTabPageContainer() +{ + SAL_INFO("toolkit", __FUNCTION__); +} + +void SAL_CALL VCLXTabPageContainer::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + VclPtr<TabControl> pTabControl = GetAs<TabControl>(); + if ( pTabControl ) + { + TabPage *pTabPage = pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( pTabControl->GetCurPageId( ) ) ); + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() ); + if (pTabPage && pDev) + { + ::Point aPos( nX, nY ); + aPos = pDev->PixelToLogic( aPos ); + pTabPage->Draw( pDev, aPos, SystemTextColorFlags::NONE ); + } + } + + VCLXWindow::draw( nX, nY ); +} + +void SAL_CALL VCLXTabPageContainer::setProperty(const OUString& PropertyName, const Any& Value ) +{ + SolarMutexGuard aGuard; + VclPtr<TabControl> pTabPage = GetAs<TabControl>(); + if ( pTabPage ) + VCLXWindow::setProperty( PropertyName, Value ); +} + +::sal_Int16 SAL_CALL VCLXTabPageContainer::getActiveTabPageID() +{ + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + return pTabCtrl ? pTabCtrl->GetCurPageId( ) : 0; +} + +void SAL_CALL VCLXTabPageContainer::setActiveTabPageID( ::sal_Int16 _activetabpageid ) +{ + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + if ( pTabCtrl ) + pTabCtrl->SelectTabPage(_activetabpageid); +} + +::sal_Int16 SAL_CALL VCLXTabPageContainer::getTabPageCount( ) +{ + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + return pTabCtrl ? pTabCtrl->GetPageCount() : 0; +} + +sal_Bool SAL_CALL VCLXTabPageContainer::isTabPageActive( ::sal_Int16 tabPageIndex ) +{ + return (getActiveTabPageID() == tabPageIndex); +} + +Reference< css::awt::tab::XTabPage > SAL_CALL VCLXTabPageContainer::getTabPage( ::sal_Int16 tabPageIndex ) +{ + return (tabPageIndex >= 0 && o3tl::make_unsigned(tabPageIndex) < m_aTabPages.size()) ? m_aTabPages[tabPageIndex] : nullptr; +} + +Reference< css::awt::tab::XTabPage > SAL_CALL VCLXTabPageContainer::getTabPageByID( ::sal_Int16 tabPageID ) +{ + SolarMutexGuard aGuard; + Reference< css::awt::tab::XTabPage > xTabPage; + for(const auto& rTabPage : m_aTabPages) + { + Reference< awt::XControl > xControl(rTabPage,UNO_QUERY ); + Reference< awt::tab::XTabPageModel > xP( xControl->getModel(), UNO_QUERY ); + if ( tabPageID == xP->getTabPageID() ) + { + xTabPage = rTabPage; + break; + } + } + return xTabPage; +} + +void SAL_CALL VCLXTabPageContainer::addTabPageContainerListener( const Reference< css::awt::tab::XTabPageContainerListener >& listener ) +{ + m_aTabPageListeners.addInterface( listener ); +} + +void SAL_CALL VCLXTabPageContainer::removeTabPageContainerListener( const Reference< css::awt::tab::XTabPageContainerListener >& listener ) +{ + m_aTabPageListeners.removeInterface( listener ); +} + +void VCLXTabPageContainer::ProcessWindowEvent( const VclWindowEvent& _rVclWindowEvent ) +{ + SolarMutexClearableGuard aGuard; + VclPtr<TabControl> pTabControl = GetAs<TabControl>(); + if ( !pTabControl ) + return; + + switch ( _rVclWindowEvent.GetId() ) + { + case VclEventId::TabpageActivate: + { + sal_uLong page = reinterpret_cast<sal_uLong>(_rVclWindowEvent.GetData()); + awt::tab::TabPageActivatedEvent aEvent(nullptr,page); + m_aTabPageListeners.tabPageActivated(aEvent); + break; + } + default: + aGuard.clear(); + VCLXWindow::ProcessWindowEvent( _rVclWindowEvent ); + break; + } +} +void SAL_CALL VCLXTabPageContainer::disposing( const css::lang::EventObject& /*Source*/ ) +{ +} +void SAL_CALL VCLXTabPageContainer::elementInserted( const css::container::ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + Reference< css::awt::tab::XTabPage > xTabPage(Event.Element,uno::UNO_QUERY); + if ( !pTabCtrl || !xTabPage.is() ) + return; + + Reference< awt::XControl > xControl(xTabPage,UNO_QUERY ); + Reference< awt::tab::XTabPageModel > xP( xControl->getModel(), UNO_QUERY ); + sal_Int16 nPageID = xP->getTabPageID(); + + if (!xControl->getPeer().is()) + throw RuntimeException("No peer for tabpage container!"); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xControl->getPeer()); + TabPage* pPage = static_cast<TabPage*>(pWindow.get()); + pTabCtrl->InsertPage(nPageID,pPage->GetText()); + + pPage->Hide(); + pTabCtrl->SetTabPage(nPageID,pPage); + pTabCtrl->SetHelpText(nPageID,xP->getToolTip()); + pTabCtrl->SetPageImage(nPageID,TkResMgr::getImageFromURL(xP->getImageURL())); + pTabCtrl->SelectTabPage(nPageID); + pTabCtrl->SetPageEnabled(nPageID,xP->getEnabled()); + m_aTabPages.push_back(xTabPage); + +} +void SAL_CALL VCLXTabPageContainer::elementRemoved( const css::container::ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + Reference< css::awt::tab::XTabPage > xTabPage(Event.Element,uno::UNO_QUERY); + if ( pTabCtrl && xTabPage.is() ) + { + Reference< awt::XControl > xControl(xTabPage,UNO_QUERY ); + Reference< awt::tab::XTabPageModel > xP( xControl->getModel(), UNO_QUERY ); + pTabCtrl->RemovePage(xP->getTabPageID()); + std::erase(m_aTabPages,xTabPage); + } +} +void SAL_CALL VCLXTabPageContainer::elementReplaced( const css::container::ContainerEvent& /*Event*/ ) +{ +} + +void VCLXTabPageContainer::propertiesChange(const::css::uno::Sequence<PropertyChangeEvent>& rEvents) +{ + SolarMutexGuard aGuard; + VclPtr<TabControl> pTabCtrl = GetAs<TabControl>(); + if (!pTabCtrl) + return; + + for (const beans::PropertyChangeEvent& rEvent : rEvents) { + // handle property changes for tab pages + Reference< css::awt::tab::XTabPageModel > xTabPageModel(rEvent.Source, uno::UNO_QUERY); + if (!xTabPageModel.is()) + continue; + + const sal_Int16 nId = xTabPageModel->getTabPageID(); + if (rEvent.PropertyName == GetPropertyName(BASEPROPERTY_ENABLED)) { + pTabCtrl->SetPageEnabled(nId, xTabPageModel->getEnabled()); + } else if (rEvent.PropertyName == GetPropertyName(BASEPROPERTY_TITLE)) { + pTabCtrl->SetPageText(nId, xTabPageModel->getTitle()); + } else if (rEvent.PropertyName == GetPropertyName(BASEPROPERTY_IMAGEURL)) { + pTabCtrl->SetPageImage(nId, TkResMgr::getImageFromURL(xTabPageModel->getImageURL())); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxtoolkit.cxx b/toolkit/source/awt/vclxtoolkit.cxx new file mode 100644 index 0000000000..fc6b7b9df8 --- /dev/null +++ b/toolkit/source/awt/vclxtoolkit.cxx @@ -0,0 +1,2624 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <string_view> +#include <thread> + +#ifdef _WIN32 +#include <prewin.h> +#include <postwin.h> +#endif +#include <config_features.h> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> +#include <com/sun/star/awt/WindowClass.hpp> +#include <com/sun/star/awt/MessageBoxButtons.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/SystemDependent.hpp> +#include <com/sun/star/awt/FocusEvent.hpp> +#include <com/sun/star/awt/KeyEvent.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> +#include <com/sun/star/lang/EventObject.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/awt/XToolkitExperimental.hpp> +#include <com/sun/star/awt/XToolkitRobot.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/bootstrap.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <osl/conditn.hxx> +#include <osl/module.h> +#include <osl/thread.hxx> +#include <osl/mutex.hxx> +#include <rtl/ref.hxx> +#include <rtl/process.h> +#include <sal/log.hxx> +#include <tools/link.hxx> +#include <vcl/idletask.hxx> +#include <vcl/wintypes.hxx> + +#ifdef MACOSX +#include <premac.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> +#endif + +#include <utility> +#include <vcl/sysdata.hxx> +#include <vcl/textrectinfo.hxx> +#include <vcl/toolkit/vclmedit.hxx> + +#include <toolkit/awt/vclxwindows.hxx> +#include <awt/vclxwindows.hxx> +#include <awt/vclxsystemdependentwindow.hxx> +#include <awt/vclxregion.hxx> +#include <awt/vclxtabpagecontainer.hxx> +#include <awt/vclxtopwindow.hxx> + +#include <awt/animatedimagespeer.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <helper/property.hxx> + +#include <toolkit/helper/convert.hxx> +#include <controls/filectrl.hxx> +#include <controls/svmedit.hxx> +#include <controls/table/tablecontrol.hxx> +#include <controls/treecontrolpeer.hxx> +#include <vcl/toolkit/button.hxx> +#include <vcl/toolkit/calendar.hxx> +#include <vcl/toolkit/combobox.hxx> +#include <vcl/ctrl.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <vcl/dockingarea.hxx> +#include <vcl/dockwin.hxx> +#include <vcl/toolkit/edit.hxx> +#include <vcl/event.hxx> +#include <vcl/toolkit/field.hxx> +#include <vcl/toolkit/fixed.hxx> +#include <vcl/toolkit/fixedhyper.hxx> +#include <vcl/toolkit/floatwin.hxx> +#include <vcl/toolkit/fmtfield.hxx> +#include <vcl/toolkit/prgsbar.hxx> +#include <vcl/scheduler.hxx> +#include <vcl/toolkit/lstbox.hxx> +#include <vcl/toolkit/longcurr.hxx> +#include <vcl/toolkit/menubtn.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/toolkit/scrbar.hxx> +#include <vcl/split.hxx> +#include <vcl/splitwin.hxx> +#include <vcl/status.hxx> +#include <vcl/svapp.hxx> +#include <vcl/syschild.hxx> +#include <vcl/tabctrl.hxx> +#include <vcl/tabpage.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/virdev.hxx> +#include <vcl/window.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/toolkit/group.hxx> +#include <vcl/toolkit/imgctrl.hxx> +#include <vcl/toolkit/morebtn.hxx> +#include <vcl/toolkit/roadmap.hxx> +#include <vcl/toolkit/spin.hxx> +#include <vcl/toolkit/tabdlg.hxx> +#include <vcl/toolkit/throbber.hxx> +#if HAVE_FEATURE_OPENGL +#include <vcl/opengl/OpenGLWrapper.hxx> +#endif +#include <awt/vclxspinbutton.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/profilezone.hxx> + +#include <helper/msgbox.hxx> +#include <helper/scrollabledialog.hxx> +#include <helper/unowrapper.hxx> + +#if defined(_WIN32) +#define SYSTEM_DEPENDENT_TYPE css::lang::SystemDependent::SYSTEM_WIN32 +#elif defined(MACOSX) +#define SYSTEM_DEPENDENT_TYPE css::lang::SystemDependent::SYSTEM_MAC +#elif defined(UNX) +#define SYSTEM_DEPENDENT_TYPE css::lang::SystemDependent::SYSTEM_XWINDOW +#endif + +void MessBox::ImplInitButtons() +{ + ButtonDialogFlags nOKFlags = ButtonDialogFlags::OK; + ButtonDialogFlags nCancelFlags = ButtonDialogFlags::Cancel; + ButtonDialogFlags nRetryFlags = ButtonDialogFlags::NONE; + ButtonDialogFlags nYesFlags = ButtonDialogFlags::NONE; + ButtonDialogFlags nNoFlags = ButtonDialogFlags::NONE; + + if ( mnMessBoxStyle & MessBoxStyle::OkCancel ) + { + if ( mnMessBoxStyle & MessBoxStyle::DefaultCancel ) + nCancelFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else // MessBoxStyle::DefaultOk + nOKFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + + AddButton( StandardButtonType::OK, RET_OK, nOKFlags ); + AddButton( StandardButtonType::Cancel, RET_CANCEL, nCancelFlags ); + } + else if ( mnMessBoxStyle & MessBoxStyle::YesNo ) + { + if ( mnMessBoxStyle & MessBoxStyle::DefaultYes ) + nYesFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else // MessBoxStyle::DefaultNo + nNoFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + nNoFlags |= ButtonDialogFlags::Cancel; + + AddButton( StandardButtonType::Yes, RET_YES, nYesFlags ); + AddButton( StandardButtonType::No, RET_NO, nNoFlags ); + } + else if ( mnMessBoxStyle & MessBoxStyle::YesNoCancel ) + { + if ( mnMessBoxStyle & MessBoxStyle::DefaultYes ) + nYesFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else if ( mnMessBoxStyle & MessBoxStyle::DefaultNo ) + nNoFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else + nCancelFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + + AddButton( StandardButtonType::Yes, RET_YES, nYesFlags ); + AddButton( StandardButtonType::No, RET_NO, nNoFlags ); + AddButton( StandardButtonType::Cancel, RET_CANCEL, nCancelFlags ); + } + else if ( mnMessBoxStyle & MessBoxStyle::RetryCancel ) + { + if ( mnMessBoxStyle & MessBoxStyle::DefaultCancel ) + nCancelFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else // MessBoxStyle::DefaultRetry + nRetryFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + + AddButton( StandardButtonType::Retry, RET_RETRY, nRetryFlags ); + AddButton( StandardButtonType::Cancel, RET_CANCEL, nCancelFlags ); + } + else if ( mnMessBoxStyle & MessBoxStyle::AbortRetryIgnore ) + { + ButtonDialogFlags nAbortFlags = ButtonDialogFlags::NONE; + ButtonDialogFlags nIgnoreFlags = ButtonDialogFlags::NONE; + + if ( mnMessBoxStyle & MessBoxStyle::DefaultCancel ) + nAbortFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else if ( mnMessBoxStyle & MessBoxStyle::DefaultRetry ) + nRetryFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + else if ( mnMessBoxStyle & MessBoxStyle::DefaultIgnore ) + nIgnoreFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + + AddButton( StandardButtonType::Abort, RET_CANCEL, nAbortFlags ); + AddButton( StandardButtonType::Retry, RET_RETRY, nRetryFlags ); + AddButton( StandardButtonType::Ignore, RET_IGNORE, nIgnoreFlags ); + } + else if ( mnMessBoxStyle & MessBoxStyle::Ok ) + { + nOKFlags |= ButtonDialogFlags::Default | ButtonDialogFlags::Focus; + + AddButton( StandardButtonType::OK, RET_OK, nOKFlags ); + } +} + +MessBox::MessBox(vcl::Window* pParent, MessBoxStyle nMessBoxStyle, WinBits nWinBits, + const OUString& rTitle, OUString aMessage) : + ButtonDialog( WindowType::MESSBOX ), + mbHelpBtn( false ), + mnMessBoxStyle( nMessBoxStyle ), + maMessText(std::move( aMessage )) +{ + ImplLOKNotifier(pParent); + ImplInitDialog(pParent, nWinBits | WB_MOVEABLE | WB_HORZ | WB_CENTER); + ImplInitButtons(); + + if ( !rTitle.isEmpty() ) + SetText( rTitle ); +} + +MessBox::~MessBox() +{ + disposeOnce(); +} + +void MessBox::dispose() +{ + mpVCLMultiLineEdit.disposeAndClear(); + mpFixedImage.disposeAndClear(); + ButtonDialog::dispose(); +} + +void MessBox::ImplPosControls() +{ + if ( !GetHelpId().isEmpty() ) + { + if ( !mbHelpBtn ) + { + AddButton( StandardButtonType::Help, RET_HELP, ButtonDialogFlags::Help, 3 ); + mbHelpBtn = true; + } + } + else + { + if ( mbHelpBtn ) + { + RemoveButton( RET_HELP ); + mbHelpBtn = false; + } + } + + TextRectInfo aTextInfo; + tools::Rectangle aRect( 0, 0, 30000, 30000 ); + tools::Rectangle aFormatRect; + Point aTextPos( IMPL_DIALOG_OFFSET, IMPL_DIALOG_OFFSET+IMPL_MSGBOX_OFFSET_EXTRA_Y ); + Size aImageSize; + Size aPageSize; + Size aMEditSize; + tools::Long nTitleWidth; + tools::Long nButtonSize = ImplGetButtonSize(); + tools::Long nMaxLineWidth; + tools::Long nWidth; + WinBits nWinStyle = WB_LEFT | WB_NOLABEL; + DrawTextFlags nTextStyle = DrawTextFlags::MultiLine | DrawTextFlags::Top | DrawTextFlags::Left; + + mpVCLMultiLineEdit.disposeAndClear(); + mpFixedImage.disposeAndClear(); + + // Clean up message text with tabs + OUString aMessText(maMessText.replaceAll("\t", " ")); + + //If window too small, we make dialog box be wider + tools::Long nMaxWidth = 630 * GetDPIScaleFactor(); + + // MessagBox should be at least as wide as to see the title + // Extra-Width for Close button, because Close button is set after this call + nTitleWidth = CalcTitleWidth(); + + nMaxWidth -= (IMPL_DIALOG_OFFSET*2)+(IMPL_MSGBOX_OFFSET_EXTRA_X*2); + + // for an image, get its size, create a suitable control and position it + aImageSize = maImage.GetSizePixel(); + if ( aImageSize.Width() ) + { + aImageSize.AdjustWidth(4 ); + aImageSize.AdjustHeight(4 ); + aTextPos.AdjustX(aImageSize.Width()+IMPL_SEP_MSGBOX_IMAGE ); + mpFixedImage = VclPtr<FixedImage>::Create( this ); + mpFixedImage->SetPosSizePixel( Point( IMPL_DIALOG_OFFSET-2+IMPL_MSGBOX_OFFSET_EXTRA_X, + IMPL_DIALOG_OFFSET-2+IMPL_MSGBOX_OFFSET_EXTRA_Y ), + aImageSize ); + mpFixedImage->SetImage( maImage ); + mpFixedImage->Show(); + nMaxWidth -= aImageSize.Width()+IMPL_SEP_MSGBOX_IMAGE; + } + else + aTextPos.AdjustX(IMPL_MSGBOX_OFFSET_EXTRA_X ); + + // Determine maximum line length without wordbreak + aFormatRect = GetTextRect( aRect, aMessText, nTextStyle, &aTextInfo ); + nMaxLineWidth = aFormatRect.GetWidth(); + nTextStyle |= DrawTextFlags::WordBreak; + + // Determine the width for text formatting + if ( nMaxLineWidth > 450 ) + nWidth = 450; + else if ( nMaxLineWidth > 300 ) + nWidth = nMaxLineWidth+5; + else + nWidth = 300; + + nWidth *= GetDPIScaleFactor(); + + if ( nButtonSize > nWidth ) + nWidth = nButtonSize-(aTextPos.X()-IMPL_DIALOG_OFFSET); + if ( nWidth > nMaxWidth ) + nWidth = nMaxWidth; + + aRect.SetRight( nWidth ); + aFormatRect = GetTextRect( aRect, aMessText, nTextStyle, &aTextInfo ); + if ( aTextInfo.GetMaxLineWidth() > nWidth ) + { + nWidth = aTextInfo.GetMaxLineWidth()+8; + aRect.SetRight( nWidth ); + aFormatRect = GetTextRect( aRect, aMessText, nTextStyle, &aTextInfo ); + } + + // get Style for VCLMultiLineEdit + aMEditSize.setWidth( aTextInfo.GetMaxLineWidth()+1 ); + aMEditSize.setHeight( aFormatRect.GetHeight() ); + aPageSize.setWidth( aImageSize.Width() ); + if ( aMEditSize.Height() < aImageSize.Height() ) + { + nWinStyle |= WB_VCENTER; + aPageSize.setHeight( aImageSize.Height() ); + aMEditSize.setHeight( aImageSize.Height() ); + } + else + { + nWinStyle |= WB_TOP; + aPageSize.setHeight( aMEditSize.Height() ); + } + if ( aImageSize.Width() ) + aPageSize.AdjustWidth(IMPL_SEP_MSGBOX_IMAGE ); + aPageSize.AdjustWidth((IMPL_DIALOG_OFFSET*2)+(IMPL_MSGBOX_OFFSET_EXTRA_X*2) ); + aPageSize.AdjustWidth(aMEditSize.Width()+1 ); + aPageSize.AdjustHeight((IMPL_DIALOG_OFFSET*2)+(IMPL_MSGBOX_OFFSET_EXTRA_Y*2) ); + + if ( aPageSize.Width() < IMPL_MINSIZE_MSGBOX_WIDTH ) + aPageSize.setWidth( IMPL_MINSIZE_MSGBOX_WIDTH ); + if ( aPageSize.Width() < nTitleWidth ) + aPageSize.setWidth( nTitleWidth ); + + mpVCLMultiLineEdit = VclPtr<VclMultiLineEdit>::Create( this, nWinStyle ); + mpVCLMultiLineEdit->SetText( aMessText ); + mpVCLMultiLineEdit->SetPosSizePixel( aTextPos, aMEditSize ); + mpVCLMultiLineEdit->Show(); + mpVCLMultiLineEdit->SetPaintTransparent(true); + mpVCLMultiLineEdit->EnableCursor(false); + SetPageSizePixel( aPageSize ); +} + +void MessBox::StateChanged( StateChangedType nType ) +{ + if ( nType == StateChangedType::InitShow ) + { + ImplPosControls(); + } + ButtonDialog::StateChanged( nType ); +} + +Size MessBox::GetOptimalSize() const +{ + // FIXME: base me on the font size ? + return Size( 250, 100 ); +} + + +namespace { + +class Pause : public Idle +{ +public: + explicit Pause(sal_Int32 nPauseMilliseconds) : + Idle("pause"), + m_nPauseMilliseconds(nPauseMilliseconds) + { + SetPriority(TaskPriority::HIGHEST); + Start(); + } + + virtual void Invoke() override + { + SolarMutexGuard aSolarGuard; + std::this_thread::sleep_for(std::chrono::milliseconds(m_nPauseMilliseconds)); + Stop(); + delete this; + } + + sal_Int32 m_nPauseMilliseconds; +}; + +class VCLXToolkit : public cppu::BaseMutex, + public cppu::WeakComponentImplHelper< + css::awt::XToolkitExperimental, + css::awt::XToolkitRobot, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::datatransfer::clipboard::XClipboard > mxClipboard; + css::uno::Reference< css::datatransfer::clipboard::XClipboard > mxSelection; + + ::comphelper::OInterfaceContainerHelper3<css::awt::XTopWindowListener> m_aTopWindowListeners; + ::comphelper::OInterfaceContainerHelper3<css::awt::XKeyHandler> m_aKeyHandlers; + ::comphelper::OInterfaceContainerHelper3<css::awt::XFocusListener> m_aFocusListeners; + ::Link<VclSimpleEvent&,void> m_aEventListenerLink; + ::Link<VclWindowEvent&,bool> m_aKeyListenerLink; + bool m_bEventListener; + bool m_bKeyListener; + + DECL_LINK(eventListenerHandler, ::VclSimpleEvent&, void); + + DECL_LINK(keyListenerHandler, ::VclWindowEvent&, bool); + + void callTopWindowListeners( + ::VclSimpleEvent const * pEvent, + void (SAL_CALL css::awt::XTopWindowListener::* pFn)( + css::lang::EventObject const &)); + + bool callKeyHandlers(::VclSimpleEvent const * pEvent, bool bPressed); + + void callFocusListeners(::VclSimpleEvent const * pEvent, bool bGained); + +protected: + ::osl::Mutex& GetMutex() { return m_aMutex; } + + virtual void SAL_CALL disposing() override; + + static vcl::Window* ImplCreateWindow( rtl::Reference<VCLXWindow>* ppNewComp, const css::awt::WindowDescriptor& rDescriptor, vcl::Window* pParent, + WinBits nWinBits, MessBoxStyle nMessBoxStyle ); + css::uno::Reference< css::awt::XWindowPeer > ImplCreateWindow( const css::awt::WindowDescriptor& Descriptor, + MessBoxStyle nForceMessBoxStyle ); + +public: + + VCLXToolkit(); + + // css::awt::XToolkitExperimental + virtual void SAL_CALL processEventsToIdle() override; + + virtual sal_Int64 SAL_CALL getOpenGLBufferSwapCounter() override; + + virtual void SAL_CALL setDeterministicScheduling(sal_Bool bDeterministicMode) override; + + virtual void SAL_CALL pause(sal_Int32 nMilliseconds) override; + + virtual void SAL_CALL startRecording() override; + + virtual void SAL_CALL stopRecording() override; + + css::uno::Sequence< OUString > SAL_CALL getRecordingAndClear() override; + + virtual void SAL_CALL waitUntilAllIdlesDispatched() override; + + // css::awt::XToolkit + css::uno::Reference< css::awt::XWindowPeer > SAL_CALL getDesktopWindow( ) override; + css::awt::Rectangle SAL_CALL getWorkArea( ) override; + css::uno::Reference< css::awt::XWindowPeer > SAL_CALL createWindow( const css::awt::WindowDescriptor& Descriptor ) override; + css::uno::Sequence< css::uno::Reference< css::awt::XWindowPeer > > SAL_CALL createWindows( const css::uno::Sequence< css::awt::WindowDescriptor >& Descriptors ) override; + css::uno::Reference< css::awt::XDevice > SAL_CALL createScreenCompatibleDevice( sal_Int32 Width, sal_Int32 Height ) override; + css::uno::Reference< css::awt::XRegion > SAL_CALL createRegion( ) override; + + // css::awt::XSystemChildFactory + css::uno::Reference< css::awt::XWindowPeer > SAL_CALL createSystemChild( const css::uno::Any& Parent, const css::uno::Sequence< sal_Int8 >& ProcessId, sal_Int16 SystemType ) override; + + // css::awt::XMessageBoxFactory + virtual css::uno::Reference< css::awt::XMessageBox > SAL_CALL createMessageBox( const css::uno::Reference< css::awt::XWindowPeer >& aParent, css::awt::MessageBoxType eType, ::sal_Int32 aButtons, const OUString& aTitle, const OUString& aMessage ) override; + + // css::awt::XDataTransfer + css::uno::Reference< css::datatransfer::dnd::XDragGestureRecognizer > SAL_CALL getDragGestureRecognizer( const css::uno::Reference< css::awt::XWindow >& window ) override; + css::uno::Reference< css::datatransfer::dnd::XDragSource > SAL_CALL getDragSource( const css::uno::Reference< css::awt::XWindow >& window ) override; + css::uno::Reference< css::datatransfer::dnd::XDropTarget > SAL_CALL getDropTarget( const css::uno::Reference< css::awt::XWindow >& window ) override; + css::uno::Reference< css::datatransfer::clipboard::XClipboard > SAL_CALL getClipboard( const OUString& clipboardName ) override; + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // css::awt::XExtendedToolkit: + + virtual ::sal_Int32 SAL_CALL getTopWindowCount() override; + + virtual css::uno::Reference< css::awt::XTopWindow > + SAL_CALL getTopWindow(::sal_Int32 nIndex) override; + + virtual css::uno::Reference< css::awt::XTopWindow > + SAL_CALL getActiveTopWindow() override; + + virtual void SAL_CALL addTopWindowListener( + css::uno::Reference< + css::awt::XTopWindowListener > const & rListener) override; + + virtual void SAL_CALL removeTopWindowListener( + css::uno::Reference< + css::awt::XTopWindowListener > const & rListener) override; + + virtual void SAL_CALL addKeyHandler( + css::uno::Reference< + css::awt::XKeyHandler > const & rHandler) override; + + virtual void SAL_CALL removeKeyHandler( + css::uno::Reference< + css::awt::XKeyHandler > const & rHandler) override; + + virtual void SAL_CALL addFocusListener( + css::uno::Reference< + css::awt::XFocusListener > const & rListener) override; + + virtual void SAL_CALL removeFocusListener( + css::uno::Reference< + css::awt::XFocusListener > const & rListener) override; + + virtual void SAL_CALL fireFocusGained( + css::uno::Reference< + css::uno::XInterface > const & source) override; + + virtual void SAL_CALL fireFocusLost( + css::uno::Reference< + css::uno::XInterface > const & source) override; + + // css::awt::XReschedule: + virtual void SAL_CALL reschedule() override; + + // css::awt::XFontMappingUse: + virtual void SAL_CALL startTrackingFontMappingUse() override; + + virtual css::uno::Sequence<css::awt::XFontMappingUseItem> SAL_CALL finishTrackingFontMappingUse() override; + + // css:awt:XToolkitRobot + virtual void SAL_CALL keyPress( const css::awt::KeyEvent & aKeyEvent ) override; + + virtual void SAL_CALL keyRelease( const css::awt::KeyEvent & aKeyEvent ) override; + + virtual void SAL_CALL mousePress( const css::awt::MouseEvent & aMouseEvent ) override; + + virtual void SAL_CALL mouseRelease( const css::awt::MouseEvent & aMouseEvent ) override; + + virtual void SAL_CALL mouseMove( const css::awt::MouseEvent & aMouseEvent ) override; + +}; + +std::pair<WinBits,MessBoxStyle> ImplGetWinBits( sal_uInt32 nComponentAttribs, WindowType nCompType ) +{ + WinBits nWinBits = 0; + MessBoxStyle nStyle = MessBoxStyle::NONE; + + bool bMessBox = false; + if ( ( nCompType == WindowType::INFOBOX ) || + ( nCompType == WindowType::MESSBOX ) || + ( nCompType == WindowType::QUERYBOX ) || + ( nCompType == WindowType::WARNINGBOX ) || + ( nCompType == WindowType::ERRORBOX ) ) + { + bMessBox = true; + } + + bool bDecoratedWindow = false; + if ( bMessBox + || ( nCompType == WindowType::DIALOG ) + || ( nCompType == WindowType::MODELESSDIALOG ) + || ( nCompType == WindowType::DOCKINGWINDOW ) + || ( nCompType == WindowType::TABDIALOG ) + || ( nCompType == WindowType::BUTTONDIALOG ) + || ( nCompType == WindowType::SYSTEMCHILDWINDOW ) + ) + { + bDecoratedWindow = true; + } + + if( nComponentAttribs & css::awt::WindowAttribute::BORDER ) + nWinBits |= WB_BORDER; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::NOBORDER ) + nWinBits |= WB_NOBORDER; + if( nComponentAttribs & css::awt::WindowAttribute::SIZEABLE ) + nWinBits |= WB_SIZEABLE; + if( nComponentAttribs & css::awt::WindowAttribute::MOVEABLE ) + nWinBits |= WB_MOVEABLE; + if( nComponentAttribs & css::awt::WindowAttribute::CLOSEABLE ) + nWinBits |= WB_CLOSEABLE; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::HSCROLL ) + nWinBits |= WB_HSCROLL; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::VSCROLL ) + nWinBits |= WB_VSCROLL; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::LEFT ) + nWinBits |= WB_LEFT; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::CENTER ) + nWinBits |= WB_CENTER; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::RIGHT ) + nWinBits |= WB_RIGHT; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::SPIN ) + nWinBits |= WB_SPIN; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::SORT ) + nWinBits |= WB_SORT; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DROPDOWN ) + nWinBits |= WB_DROPDOWN; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEFBUTTON ) + nWinBits |= WB_DEFBUTTON; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::READONLY ) + nWinBits |= WB_READONLY; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::CLIPCHILDREN ) + nWinBits |= WB_CLIPCHILDREN; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::GROUP ) + nWinBits |= WB_GROUP; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::NOLABEL ) //added for issue79712 + nWinBits |= WB_NOLABEL; + + // These bits are not unique + if ( bMessBox ) + { + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::OK ) + nStyle |= MessBoxStyle::Ok; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::OK_CANCEL ) + nStyle |= MessBoxStyle::OkCancel; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::YES_NO ) + nStyle |= MessBoxStyle::YesNo; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::YES_NO_CANCEL ) + nStyle |= MessBoxStyle::YesNoCancel; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::RETRY_CANCEL ) + nStyle |= MessBoxStyle::RetryCancel; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEF_OK ) + nStyle |= MessBoxStyle::DefaultOk; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEF_CANCEL ) + nStyle |= MessBoxStyle::DefaultCancel; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEF_RETRY ) + nStyle |= MessBoxStyle::DefaultRetry; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEF_YES ) + nStyle |= MessBoxStyle::DefaultYes; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::DEF_NO ) + nStyle |= MessBoxStyle::DefaultNo; + } + if ( nCompType == WindowType::MULTILINEEDIT || nCompType == WindowType::DIALOG + || nCompType == WindowType::GROUPBOX || nCompType == WindowType::TABPAGE ) + { + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::AUTOHSCROLL ) + nWinBits |= WB_AUTOHSCROLL; + if( nComponentAttribs & css::awt::VclWindowPeerAttribute::AUTOVSCROLL ) + nWinBits |= WB_AUTOVSCROLL; + } + + + if ( bDecoratedWindow ) + { + if( nComponentAttribs & css::awt::WindowAttribute::NODECORATION ) + { + // No decoration removes several window attributes and must + // set WB_NOBORDER! + nWinBits &= ~WB_BORDER; + nWinBits &= ~WB_SIZEABLE; + nWinBits &= ~WB_MOVEABLE; + nWinBits &= ~WB_CLOSEABLE; + nWinBits |= WB_NOBORDER; + } + } + + return { nWinBits, nStyle }; +} + +struct ComponentInfo +{ + std::u16string_view sName; + WindowType nWinType; +}; + +ComponentInfo const aComponentInfos [] = +{ + { std::u16string_view(u"animatedimages"), WindowType::CONTROL }, + { std::u16string_view(u"buttondialog"), WindowType::BUTTONDIALOG }, + { std::u16string_view(u"cancelbutton"), WindowType::CANCELBUTTON }, + { std::u16string_view(u"checkbox"), WindowType::CHECKBOX }, + { std::u16string_view(u"combobox"), WindowType::COMBOBOX }, + { std::u16string_view(u"control"), WindowType::CONTROL }, + { std::u16string_view(u"currencybox"), WindowType::CURRENCYBOX }, + { std::u16string_view(u"currencyfield"), WindowType::CURRENCYFIELD }, + { std::u16string_view(u"datebox"), WindowType::DATEBOX }, + { std::u16string_view(u"datefield"), WindowType::CONTROL }, + { std::u16string_view(u"dialog"), WindowType::DIALOG }, + { std::u16string_view(u"dockingarea"), WindowType::DOCKINGAREA }, + { std::u16string_view(u"dockingwindow"), WindowType::DOCKINGWINDOW }, + { std::u16string_view(u"edit"), WindowType::EDIT }, + { std::u16string_view(u"errorbox"), WindowType::ERRORBOX }, + { std::u16string_view(u"filecontrol"), WindowType::CONTROL }, + { std::u16string_view(u"fixedbitmap"), WindowType::FIXEDBITMAP }, + { std::u16string_view(u"fixedhyperlink"), WindowType::CONTROL }, + { std::u16string_view(u"fixedimage"), WindowType::FIXEDIMAGE }, + { std::u16string_view(u"fixedline"), WindowType::FIXEDLINE }, + { std::u16string_view(u"fixedtext"), WindowType::FIXEDTEXT }, + { std::u16string_view(u"floatingwindow"), WindowType::FLOATINGWINDOW }, + { std::u16string_view(u"formattedfield"), WindowType::CONTROL }, + { std::u16string_view(u"frame"), WindowType::GROUPBOX }, + { std::u16string_view(u"framewindow"), WindowType::TOOLKIT_FRAMEWINDOW }, + { std::u16string_view(u"grid"), WindowType::CONTROL }, + { std::u16string_view(u"groupbox"), WindowType::GROUPBOX }, + { std::u16string_view(u"helpbutton"), WindowType::HELPBUTTON }, + { std::u16string_view(u"imagebutton"), WindowType::IMAGEBUTTON }, + { std::u16string_view(u"infobox"), WindowType::INFOBOX }, + { std::u16string_view(u"listbox"), WindowType::LISTBOX }, + { std::u16string_view(u"longcurrencybox"), WindowType::LONGCURRENCYBOX }, + { std::u16string_view(u"longcurrencyfield"), WindowType::CONTROL }, + { std::u16string_view(u"menubutton"), WindowType::MENUBUTTON }, + { std::u16string_view(u"messbox"), WindowType::MESSBOX }, + { std::u16string_view(u"metricbox"), WindowType::METRICBOX }, + { std::u16string_view(u"metricfield"), WindowType::METRICFIELD }, + { std::u16string_view(u"modelessdialog"), WindowType::MODELESSDIALOG }, + { std::u16string_view(u"morebutton"), WindowType::MOREBUTTON }, + { std::u16string_view(u"multilineedit"), WindowType::MULTILINEEDIT }, + { std::u16string_view(u"multilistbox"), WindowType::MULTILISTBOX }, + { std::u16string_view(u"numericbox"), WindowType::NUMERICBOX }, + { std::u16string_view(u"numericfield"), WindowType::CONTROL }, + { std::u16string_view(u"okbutton"), WindowType::OKBUTTON }, + { std::u16string_view(u"patternbox"), WindowType::PATTERNBOX }, + { std::u16string_view(u"patternfield"), WindowType::PATTERNFIELD }, + { std::u16string_view(u"progressbar"), WindowType::CONTROL }, + { std::u16string_view(u"pushbutton"), WindowType::PUSHBUTTON }, + { std::u16string_view(u"querybox"), WindowType::QUERYBOX }, + { std::u16string_view(u"radiobutton"), WindowType::RADIOBUTTON }, + { std::u16string_view(u"roadmap"), WindowType::CONTROL }, + { std::u16string_view(u"scrollbar"), WindowType::SCROLLBAR }, + { std::u16string_view(u"scrollbarbox"), WindowType::SCROLLBARBOX }, + { std::u16string_view(u"spinbutton"), WindowType::SPINBUTTON }, + { std::u16string_view(u"spinfield"), WindowType::SPINFIELD }, + { std::u16string_view(u"splitter"), WindowType::SPLITTER }, + { std::u16string_view(u"splitwindow"), WindowType::SPLITWINDOW }, + { std::u16string_view(u"statusbar"), WindowType::STATUSBAR }, + { std::u16string_view(u"systemchildwindow"), WindowType::TOOLKIT_SYSTEMCHILDWINDOW }, + { std::u16string_view(u"tabcontrol"), WindowType::TABCONTROL }, + { std::u16string_view(u"tabdialog"), WindowType::TABDIALOG }, + { std::u16string_view(u"tabpage"), WindowType::TABPAGE }, + { std::u16string_view(u"tabpagecontainer"), WindowType::CONTROL }, + { std::u16string_view(u"tabpagemodel"), WindowType::TABPAGE }, + { std::u16string_view(u"timebox"), WindowType::TIMEBOX }, + { std::u16string_view(u"timefield"), WindowType::TIMEFIELD }, + { std::u16string_view(u"toolbox"), WindowType::TOOLBOX }, + { std::u16string_view(u"tree"), WindowType::CONTROL }, + { std::u16string_view(u"tristatebox"), WindowType::TRISTATEBOX }, + { std::u16string_view(u"warningbox"), WindowType::WARNINGBOX }, + { std::u16string_view(u"window"), WindowType::WINDOW }, + { std::u16string_view(u"workwindow"), WindowType::WORKWINDOW } +}; + +bool ComponentInfoFindCompare( const ComponentInfo & lhs, const OUString & s) +{ + return rtl_ustr_compareIgnoreAsciiCase_WithLength(s.pData->buffer, s.pData->length, + lhs.sName.data(), lhs.sName.size()) > 0; +} + +WindowType ImplGetComponentType( const OUString& rServiceName ) +{ + static bool bSorted = false; + if( !bSorted ) + { + assert( std::is_sorted( std::begin(aComponentInfos), std::end(aComponentInfos), + [](const ComponentInfo & lhs, const ComponentInfo & rhs) { + return + rtl_ustr_compare_WithLength( + lhs.sName.data(), lhs.sName.size(), rhs.sName.data(), + rhs.sName.size()) + < 0; + } ) ); + bSorted = true; + } + + OUString sSearch; + if ( !rServiceName.isEmpty() ) + sSearch = rServiceName; + else + sSearch = "window"; + + auto it = std::lower_bound( std::begin(aComponentInfos), std::end(aComponentInfos), sSearch, + ComponentInfoFindCompare ); + if (it != std::end(aComponentInfos) && + rtl_ustr_compareIgnoreAsciiCase_WithLength(sSearch.pData->buffer, sSearch.pData->length, it->sName.data(), it->sName.size()) == 0) + return it->nWinType; + return WindowType::NONE; +} + +struct MessageBoxTypeInfo +{ + css::awt::MessageBoxType eType; + const char *pName; + sal_Int32 nLen; +}; + +const MessageBoxTypeInfo aMessageBoxTypeInfo[] = +{ + { css::awt::MessageBoxType_MESSAGEBOX, RTL_CONSTASCII_STRINGPARAM("messbox") }, + { css::awt::MessageBoxType_INFOBOX, RTL_CONSTASCII_STRINGPARAM("infobox") }, + { css::awt::MessageBoxType_WARNINGBOX, RTL_CONSTASCII_STRINGPARAM("warningbox") }, + { css::awt::MessageBoxType_ERRORBOX, RTL_CONSTASCII_STRINGPARAM("errorbox") }, + { css::awt::MessageBoxType_QUERYBOX, RTL_CONSTASCII_STRINGPARAM("querybox") }, + { css::awt::MessageBoxType::MessageBoxType_MAKE_FIXED_SIZE, nullptr, 0 } +}; + +bool lcl_convertMessageBoxType( + OUString &sType, + css::awt::MessageBoxType eType ) +{ + const MessageBoxTypeInfo *pMap = aMessageBoxTypeInfo; + css::awt::MessageBoxType eVal = css::awt::MessageBoxType::MessageBoxType_MAKE_FIXED_SIZE; + + while ( pMap->pName ) + { + if ( pMap->eType == eType ) + { + eVal = eType; + sType = OUString( pMap->pName, pMap->nLen, RTL_TEXTENCODING_ASCII_US ); + break; + } + pMap++; + } + + return ( eVal != css::awt::MessageBoxType::MessageBoxType_MAKE_FIXED_SIZE ); +} + +#ifndef IOS + +sal_Int32 nVCLToolkitInstanceCount = 0; +bool bInitedByVCLToolkit = false; + +osl::Mutex & getInitMutex() +{ + static osl::Mutex aMutex; + return aMutex; +} + +osl::Condition & getInitCondition() +{ + static osl::Condition aCondition; + return aCondition; +} + +extern "C" +{ +static void ToolkitWorkerFunction( void* pArgs ) +{ + osl_setThreadName("VCLXToolkit VCL main thread"); + + css::uno::Reference<css::lang::XMultiServiceFactory> xServiceManager; + try + { + xServiceManager = ::comphelper::getProcessServiceFactory(); + } + catch (const css::uno::DeploymentException&) + { + } + if (!xServiceManager.is()) + { + css::uno::Reference<css::uno::XComponentContext> xContext = + ::cppu::defaultBootstrap_InitialComponentContext(); + + xServiceManager.set( xContext->getServiceManager(), css::uno::UNO_QUERY_THROW ); + // set global process service factory used by unotools config helpers + ::comphelper::setProcessServiceFactory( xServiceManager ); + } + + VCLXToolkit * pTk = static_cast<VCLXToolkit *>(pArgs); + bInitedByVCLToolkit = !IsVCLInit() && InitVCL(); + if( bInitedByVCLToolkit ) + { + UnoWrapper* pUnoWrapper = new UnoWrapper( pTk ); + UnoWrapperBase::SetUnoWrapper( pUnoWrapper ); + } + getInitCondition().set(); + if( bInitedByVCLToolkit ) + { + { + SolarMutexGuard aGuard; + Application::Execute(); + } + try + { + pTk->dispose(); + } + catch( css::uno::Exception & ) + { + } + DeInitVCL(); + } + else + { + // having the thread join itself is pretty stupid. + // but we can't get the osl_Thread to destroy here so just leak it. + } +} +} + +#endif + +// constructor, which might initialize VCL +VCLXToolkit::VCLXToolkit(): + cppu::WeakComponentImplHelper< + css::awt::XToolkitExperimental, + css::awt::XToolkitRobot, + css::lang::XServiceInfo>( GetMutex() ), + m_aTopWindowListeners(rBHelper.rMutex), + m_aKeyHandlers(rBHelper.rMutex), + m_aFocusListeners(rBHelper.rMutex), + m_aEventListenerLink(LINK(this, VCLXToolkit, eventListenerHandler)), + m_aKeyListenerLink(LINK(this, VCLXToolkit, keyListenerHandler)), + m_bEventListener(false), + m_bKeyListener(false) +{ +#ifndef IOS + osl::Guard< osl::Mutex > aGuard( getInitMutex() ); + nVCLToolkitInstanceCount++; + if( ( nVCLToolkitInstanceCount == 1 ) && ( !Application::IsInMain() ) ) + { + // setup execute thread + CreateMainLoopThread( ToolkitWorkerFunction, this ); + getInitCondition().wait(); + } +#endif +} + +void SAL_CALL VCLXToolkit::disposing() +{ +#ifndef IOS + { + osl::Guard< osl::Mutex > aGuard( getInitMutex() ); + if( --nVCLToolkitInstanceCount == 0 ) + { + if( bInitedByVCLToolkit ) + { + Application::Quit(); + JoinMainLoopThread(); + bInitedByVCLToolkit = false; + } + } + } +#endif + if (m_bEventListener) + { + ::Application::RemoveEventListener(m_aEventListenerLink); + m_bEventListener = false; + } + if (m_bKeyListener) + { + ::Application::RemoveKeyListener(m_aKeyListenerLink); + m_bKeyListener = false; + } + css::lang::EventObject aEvent( + getXWeak()); + m_aTopWindowListeners.disposeAndClear(aEvent); + m_aKeyHandlers.disposeAndClear(aEvent); + m_aFocusListeners.disposeAndClear(aEvent); +} + + +css::uno::Reference< css::awt::XWindowPeer > VCLXToolkit::getDesktopWindow( ) +{ + css::uno::Reference< css::awt::XWindowPeer > xRef; + // 07/00: AppWindow doesn't exist anymore... + return xRef; +} + +css::awt::Rectangle VCLXToolkit::getWorkArea( ) +{ + sal_Int32 nDisplay = Application::GetDisplayBuiltInScreen(); + AbsoluteScreenPixelRectangle aWorkRect = Application::GetScreenPosSizePixel( nDisplay ); + css::awt::Rectangle aNotherRect; + aNotherRect.X = aWorkRect.Left(); + aNotherRect.Y = aWorkRect.Top(); + aNotherRect.Width = aWorkRect.getOpenWidth(); + aNotherRect.Height = aWorkRect.getOpenHeight(); + return aNotherRect; +} + +css::uno::Reference< css::awt::XWindowPeer > VCLXToolkit::createWindow( const css::awt::WindowDescriptor& rDescriptor ) +{ + return ImplCreateWindow( rDescriptor, MessBoxStyle::NONE ); +} + +css::uno::Reference< css::awt::XDevice > VCLXToolkit::createScreenCompatibleDevice( sal_Int32 Width, sal_Int32 Height ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + rtl::Reference<VCLXVirtualDevice> pVDev = new VCLXVirtualDevice; + + SolarMutexGuard aSolarGuard; + + VclPtrInstance<VirtualDevice> pV; + pV->SetOutputSizePixel( Size( Width, Height ) ); + pVDev->SetVirtualDevice( pV ); + + return pVDev; +} + +css::uno::Reference< css::awt::XRegion > VCLXToolkit::createRegion( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + css::uno::Reference< css::awt::XRegion > xRef = new VCLXRegion; + return xRef; +} + +class InfoBox : public MessBox +{ +public: + InfoBox(vcl::Window* pParent, const OUString& rMessage) + : MessBox(pParent, MessBoxStyle::Ok | MessBoxStyle::DefaultOk, 0, OUString(), rMessage) + { + // Default Text is the display title from the application + if (GetText().isEmpty()) + SetText(GetStandardInfoBoxText()); + SetImage(GetStandardInfoBoxImage()); + } +}; + +class ErrorBox : public MessBox +{ +public: + ErrorBox(vcl::Window* pParent, MessBoxStyle nStyle, WinBits nWinBits, const OUString& rMessage) + : MessBox(pParent, nStyle, nWinBits, OUString(), rMessage) + { + // Default Text is the display title from the application + if (GetText().isEmpty()) + SetText(GetStandardErrorBoxText()); + SetImage(GetStandardErrorBoxImage()); + } +}; + +class QueryBox : public MessBox +{ +public: + QueryBox(vcl::Window* pParent, MessBoxStyle nStyle, WinBits nWinBits, const OUString& rMessage) + : MessBox(pParent, nStyle, nWinBits, OUString(), rMessage) + { + // Default Text is the display title from the application + if (GetText().isEmpty()) + SetText(GetStandardQueryBoxText()); + SetImage(GetStandardQueryBoxImage()); + } +}; + +class WarningBox : public MessBox +{ +public: + WarningBox(vcl::Window* pParent, MessBoxStyle nStyle, WinBits nWinBits, const OUString& rMessage) + : MessBox(pParent, nStyle, nWinBits, OUString(), rMessage) + { + // Default Text is the display title from the application + if (GetText().isEmpty()) + SetText(GetStandardWarningBoxText()); + SetImage(GetStandardWarningBoxImage()); + } +}; + + +struct RMItemData +{ + bool b_Enabled; + sal_Int32 n_ID; + OUString Label; +}; + +typedef ::cppu::ImplInheritanceHelper < VCLXGraphicControl + , css::container::XContainerListener + , css::beans::XPropertyChangeListener + , css::awt::XItemEventBroadcaster + > SVTXRoadmap_Base; +class SVTXRoadmap final : public SVTXRoadmap_Base +{ +public: + SVTXRoadmap(); + + void SAL_CALL disposing( const css::lang::EventObject& Source ) override { VCLXWindow::disposing( Source ); } + + // css::awt::XVclWindowPeer + void SAL_CALL setProperty( const OUString& PropertyName, const css::uno::Any& Value ) override; + + css::uno::Any SAL_CALL getProperty( const OUString& PropertyName ) override; + + // XContainerListener + void SAL_CALL elementInserted( const css::container::ContainerEvent& rEvent ) override; + void SAL_CALL elementRemoved( const css::container::ContainerEvent& rEvent ) override; + void SAL_CALL elementReplaced( const css::container::ContainerEvent& rEvent ) override; + + // XItemEventBroadcaster + virtual void SAL_CALL addItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) override; + virtual void SAL_CALL removeItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override; + +private: + + // VCLXGraphicControl overridables + virtual void ImplSetNewImage() override; + + static void ImplGetPropertyIds( std::vector< sal_uInt16 > &aIds ); + virtual void GetPropertyIds( std::vector< sal_uInt16 > &aIds ) override { return ImplGetPropertyIds( aIds ); } + + static RMItemData GetRMItemData( const css::container::ContainerEvent& _rEvent ); + + virtual void ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) override; + + virtual ~SVTXRoadmap() override; + + ItemListenerMultiplexer maItemListeners; +}; + + + + +SVTXRoadmap::SVTXRoadmap() : maItemListeners( *this ) +{ +} + +SVTXRoadmap::~SVTXRoadmap() +{ +} + +void SVTXRoadmap::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::RoadmapItemSelected: + { + SolarMutexGuard aGuard; + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + sal_Int16 CurItemID = pField->GetCurrentRoadmapItemID(); + css::awt::ItemEvent aEvent; + aEvent.Selected = CurItemID; + aEvent.Highlighted = CurItemID; + aEvent.ItemId = CurItemID; + maItemListeners.itemStateChanged( aEvent ); + } + } + break; + default: + SVTXRoadmap_Base::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +void SVTXRoadmap::propertyChange( const css::beans::PropertyChangeEvent& evt ) +{ + SolarMutexGuard aGuard; + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( !pField ) + return; + + css::uno::Reference< css::uno::XInterface > xRoadmapItem = evt.Source; + sal_Int32 nID = 0; + css::uno::Reference< css::beans::XPropertySet > xPropertySet( xRoadmapItem, css::uno::UNO_QUERY ); + css::uno::Any aValue = xPropertySet->getPropertyValue("ID"); + aValue >>= nID; + + OUString sPropertyName = evt.PropertyName; + if ( sPropertyName == "Enabled" ) + { + bool bEnable = false; + evt.NewValue >>= bEnable; + pField->EnableRoadmapItem( static_cast<vcl::RoadmapTypes::ItemId>(nID) , bEnable ); + } + else if ( sPropertyName == "Label" ) + { + OUString sLabel; + evt.NewValue >>= sLabel; + pField->ChangeRoadmapItemLabel( static_cast<vcl::RoadmapTypes::ItemId>(nID) , sLabel ); + } + else if ( sPropertyName == "ID" ) + { + sal_Int32 nNewID = 0; + evt.NewValue >>= nNewID; + evt.OldValue >>= nID; + pField->ChangeRoadmapItemID( static_cast<vcl::RoadmapTypes::ItemId>(nID), static_cast<vcl::RoadmapTypes::ItemId>(nNewID) ); + } +// else + // TODO handle Interactive appropriately +} + +void SVTXRoadmap::addItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) +{ + maItemListeners.addInterface( l ); +} + +void SVTXRoadmap::removeItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) +{ + maItemListeners.removeInterface( l ); +} + +RMItemData SVTXRoadmap::GetRMItemData( const css::container::ContainerEvent& _rEvent ) +{ + RMItemData aCurRMItemData; + css::uno::Reference< css::uno::XInterface > xRoadmapItem; + _rEvent.Element >>= xRoadmapItem; + css::uno::Reference< css::beans::XPropertySet > xPropertySet( xRoadmapItem, css::uno::UNO_QUERY ); + if ( xPropertySet.is() ) + { + css::uno::Any aValue = xPropertySet->getPropertyValue("Label"); + aValue >>= aCurRMItemData.Label; + aValue = xPropertySet->getPropertyValue("ID"); + aValue >>= aCurRMItemData.n_ID; + aValue = xPropertySet->getPropertyValue("Enabled"); + aValue >>= aCurRMItemData.b_Enabled; + } + else + { + aCurRMItemData.b_Enabled = false; + aCurRMItemData.n_ID = 0; + } + return aCurRMItemData; +} + +void SVTXRoadmap::elementInserted( const css::container::ContainerEvent& _rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + RMItemData CurItemData = GetRMItemData( _rEvent ); + sal_Int32 InsertIndex = 0; + _rEvent.Accessor >>= InsertIndex; + pField->InsertRoadmapItem( InsertIndex, CurItemData.Label, static_cast<vcl::RoadmapTypes::ItemId>(CurItemData.n_ID), CurItemData.b_Enabled ); + } +} + +void SVTXRoadmap::elementRemoved( const css::container::ContainerEvent& _rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + sal_Int32 DelIndex = 0; + _rEvent.Accessor >>= DelIndex; + pField->DeleteRoadmapItem(DelIndex); + } +} + +void SVTXRoadmap::elementReplaced( const css::container::ContainerEvent& _rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + RMItemData CurItemData = GetRMItemData( _rEvent ); + sal_Int32 ReplaceIndex = 0; + _rEvent.Accessor >>= ReplaceIndex; + pField->ReplaceRoadmapItem( ReplaceIndex, CurItemData.Label, static_cast<vcl::RoadmapTypes::ItemId>(CurItemData.n_ID), CurItemData.b_Enabled ); + } +} + +void SVTXRoadmap::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_COMPLETE: + { + bool b = false; + Value >>= b; + pField->SetRoadmapComplete( b); + } + break; + + case BASEPROPERTY_ACTIVATED: + { + bool b = false; + Value >>= b; + pField->SetRoadmapInteractive( b); + } + break; + + case BASEPROPERTY_CURRENTITEMID: + { + sal_Int32 nId = 0; + Value >>= nId; + pField->SelectRoadmapItemByID( static_cast<vcl::RoadmapTypes::ItemId>(nId) ); + } + break; + + case BASEPROPERTY_TEXT: + { + OUString aStr; + Value >>= aStr; + pField->SetText( aStr ); + pField->Invalidate(); + } + break; + + default: + SVTXRoadmap_Base::setProperty( PropertyName, Value ); + break; + } + + } + else + SVTXRoadmap_Base::setProperty( PropertyName, Value ); +} + + +css::uno::Any SVTXRoadmap::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aReturn; + + VclPtr<::vcl::ORoadmap> pField = GetAs< vcl::ORoadmap >(); + if ( pField ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_COMPLETE: + aReturn <<= pField->IsRoadmapComplete(); + break; + case BASEPROPERTY_ACTIVATED: + aReturn <<= pField->IsRoadmapInteractive(); + break; + case BASEPROPERTY_CURRENTITEMID: + aReturn <<= pField->GetCurrentRoadmapItemID(); + break; + default: + aReturn = SVTXRoadmap_Base::getProperty(PropertyName); + break; + } + } + return aReturn; +} + +void SVTXRoadmap::ImplSetNewImage() +{ + OSL_PRECOND( GetWindow(), "SVTXRoadmap::ImplSetNewImage: window is required to be not-NULL!" ); + VclPtr< ::vcl::ORoadmap > pButton = GetAs< ::vcl::ORoadmap >(); + pButton->SetRoadmapBitmap( GetImage().GetBitmapEx() ); +} + +void SVTXRoadmap::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_COMPLETE, + BASEPROPERTY_ACTIVATED, + BASEPROPERTY_CURRENTITEMID, + BASEPROPERTY_TEXT, + 0); + VCLXWindow::ImplGetPropertyIds( rIds, true ); + VCLXGraphicControl::ImplGetPropertyIds( rIds ); +} + +vcl::Window* VCLXToolkit::ImplCreateWindow( rtl::Reference<VCLXWindow>* ppNewComp, + const css::awt::WindowDescriptor& rDescriptor, + vcl::Window* pParent, WinBits nWinBits, MessBoxStyle nMessBoxStyle ) +{ + OUString aServiceName = rDescriptor.WindowServiceName.toAsciiLowerCase(); + + VclPtr<vcl::Window> pNewWindow; + WindowType nType = ImplGetComponentType( aServiceName ); + bool bFrameControl = false; + if ( aServiceName == "frame" ) + bFrameControl = true; + if ( aServiceName == "tabcontrolnotabs" ) + { + nWinBits |= WB_NOBORDER; + nType = ImplGetComponentType( "tabcontrol" ); + } + if ( !pParent ) + { + // If the component needs a parent, then return NULL, + // some time later css::uno::Exception... + bool bException = true; + if ( ( nType == WindowType::DIALOG ) + || ( nType == WindowType::MODELESSDIALOG ) + || ( nType == WindowType::MESSBOX ) + || ( nType == WindowType::INFOBOX ) + || ( nType == WindowType::WARNINGBOX ) + || ( nType == WindowType::ERRORBOX ) + || ( nType == WindowType::QUERYBOX ) + ) + bException = false; + else if ( ( nType == WindowType::WINDOW ) || + ( nType == WindowType::WORKWINDOW ) || + ( nType == WindowType::TOOLKIT_FRAMEWINDOW ) ) + { + if ( rDescriptor.Type == css::awt::WindowClass_TOP ) + bException = false; + } + + if ( bException ) + { + *ppNewComp = nullptr; + return nullptr; + } + } + + if ( nType != WindowType::NONE ) + { + SolarMutexGuard aVclGuard; + switch ( nType ) + { + case WindowType::CANCELBUTTON: + pNewWindow = VclPtr<CancelButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::CHECKBOX: + pNewWindow = VclPtr<CheckBox>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXCheckBox; + break; + case WindowType::COMBOBOX: + pNewWindow = VclPtr<ComboBox>::Create( pParent, nWinBits|WB_AUTOHSCROLL ); + static_cast<ComboBox*>(pNewWindow.get())->EnableAutoSize( false ); + *ppNewComp = new VCLXComboBox; + break; + case WindowType::CURRENCYBOX: + pNewWindow = VclPtr<CurrencyBox>::Create( pParent, nWinBits ); + break; + case WindowType::CURRENCYFIELD: + pNewWindow = VclPtr<CurrencyField>::Create( pParent, nWinBits ); + static_cast<CurrencyField*>(pNewWindow.get())->EnableEmptyFieldValue( true ); + *ppNewComp = new VCLXNumericField; + static_cast<VCLXFormattedSpinField*>((*ppNewComp).get())->SetFormatter( static_cast<FormatterBase*>(static_cast<CurrencyField*>(pNewWindow.get())) ); + break; + case WindowType::DATEBOX: + pNewWindow = VclPtr<DateBox>::Create( pParent, nWinBits ); + break; + case WindowType::DOCKINGAREA: + pNewWindow = VclPtr<DockingAreaWindow>::Create( pParent ); + break; + case WindowType::MULTILINEEDIT: + pNewWindow = VclPtr<MultiLineEdit>::Create(pParent, nWinBits|WB_IGNORETAB); + static_cast<MultiLineEdit*>(pNewWindow.get())->DisableSelectionOnFocus(); + *ppNewComp = new VCLXMultiLineEdit; + break; + case WindowType::EDIT: + pNewWindow = VclPtr<Edit>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXEdit; + break; + case WindowType::ERRORBOX: + pNewWindow = VclPtr<ErrorBox>::Create( pParent, nMessBoxStyle, nWinBits, OUString() ); + *ppNewComp = new VCLXMessageBox; + break; + case WindowType::FIXEDBITMAP: + pNewWindow = VclPtr<FixedBitmap>::Create( pParent, nWinBits ); + break; + case WindowType::FIXEDIMAGE: + pNewWindow = VclPtr<ImageControl>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXImageControl; + break; + case WindowType::FIXEDLINE: + pNewWindow = VclPtr<FixedLine>::Create( pParent, nWinBits ); + break; + case WindowType::FIXEDTEXT: + pNewWindow = VclPtr<FixedText>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXFixedText; + break; + case WindowType::FLOATINGWINDOW: + pNewWindow = VclPtr<FloatingWindow>::Create( pParent, nWinBits ); + break; + case WindowType::GROUPBOX: + pNewWindow = VclPtr<GroupBox>::Create( pParent, nWinBits ); + if ( bFrameControl ) + { + GroupBox* pGroupBox = static_cast< GroupBox* >( pNewWindow.get() ); + *ppNewComp = new VCLXFrame; + // Frame control needs to receive + // Mouse events + pGroupBox->SetMouseTransparent( false ); + } + break; + case WindowType::HELPBUTTON: + pNewWindow = VclPtr<HelpButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::IMAGEBUTTON: + pNewWindow = VclPtr<ImageButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::INFOBOX: + pNewWindow = VclPtr<InfoBox>::Create( pParent, OUString() ); + *ppNewComp = new VCLXMessageBox; + break; + case WindowType::LISTBOX: + pNewWindow = VclPtr<ListBox>::Create( pParent, nWinBits|WB_SIMPLEMODE|WB_AUTOHSCROLL ); + static_cast<ListBox*>(pNewWindow.get())->EnableAutoSize( false ); + *ppNewComp = new VCLXListBox; + break; + case WindowType::LONGCURRENCYBOX: + pNewWindow = VclPtr<LongCurrencyBox>::Create( pParent, nWinBits ); + break; + case WindowType::MENUBUTTON: + pNewWindow = VclPtr<MenuButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::MESSBOX: + pNewWindow = VclPtr<MessBox>::Create( pParent, nMessBoxStyle, nWinBits, OUString(), OUString() ); + *ppNewComp = new VCLXMessageBox; + break; + case WindowType::METRICBOX: + pNewWindow = VclPtr<MetricBox>::Create( pParent, nWinBits ); + break; + case WindowType::METRICFIELD: + pNewWindow = VclPtr<MetricField>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXMetricField; + static_cast<VCLXFormattedSpinField*>((*ppNewComp).get())->SetFormatter( static_cast<FormatterBase*>(static_cast<MetricField*>(pNewWindow.get())) ); + break; + case WindowType::DIALOG: + case WindowType::MODELESSDIALOG: + { + // Modal/Modeless only via Show/Execute + if ( (pParent == nullptr ) && ( rDescriptor.ParentIndex == -1 ) ) + pNewWindow = VclPtr<toolkit::ScrollableDialog>::Create( nullptr, nWinBits, Dialog::InitFlag::NoParent ); + else + pNewWindow = VclPtr<toolkit::ScrollableDialog>::Create( pParent, nWinBits ); + // #i70217# Don't always create a new component object. It's possible that VCL has called + // GetComponentInterface( sal_True ) in the Dialog ctor itself (see Window::IsTopWindow() ) + // which creates a component object. + css::uno::Reference< css::awt::XWindowPeer > xWinPeer = pNewWindow->GetComponentInterface( false ); + if ( xWinPeer.is() ) + *ppNewComp = dynamic_cast< VCLXDialog* >( xWinPeer.get() ); + else + *ppNewComp = new VCLXDialog; + } + break; + case WindowType::MOREBUTTON: + pNewWindow = VclPtr<MoreButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::MULTILISTBOX: + pNewWindow = VclPtr<MultiListBox>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXListBox; + break; + case WindowType::NUMERICBOX: + pNewWindow = VclPtr<NumericBox>::Create( pParent, nWinBits ); + break; + case WindowType::OKBUTTON: + pNewWindow = VclPtr<OKButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::PATTERNBOX: + pNewWindow = VclPtr<PatternBox>::Create( pParent, nWinBits ); + break; + case WindowType::PATTERNFIELD: + pNewWindow = VclPtr<PatternField>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXPatternField; + static_cast<VCLXFormattedSpinField*>((*ppNewComp).get())->SetFormatter( static_cast<FormatterBase*>(static_cast<PatternField*>(pNewWindow.get())) ); + break; + case WindowType::PUSHBUTTON: + pNewWindow = VclPtr<PushButton>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXButton; + break; + case WindowType::QUERYBOX: + pNewWindow = VclPtr<QueryBox>::Create( pParent, nMessBoxStyle, nWinBits, OUString() ); + *ppNewComp = new VCLXMessageBox; + break; + case WindowType::RADIOBUTTON: + pNewWindow = VclPtr<RadioButton>::Create(pParent, false, nWinBits); + *ppNewComp = new VCLXRadioButton; + + // by default, disable RadioCheck + // Since the VCLXRadioButton really cares for its RadioCheck settings, this is important: + // if we enable it, the VCLXRadioButton will use RadioButton::Check instead of RadioButton::SetState + // This leads to a strange behaviour if the control is newly created: when settings the initial + // state to "checked", the RadioButton::Check (called because RadioCheck=sal_True) will uncheck + // _all_other_ radio buttons in the same group. However, at this moment the grouping of the controls + // is not really valid: the controls are grouped after they have been created, but we're still in + // the creation process, so the RadioButton::Check relies on invalid grouping information. + // 07.08.2001 - #87254# - frank.schoenheit@sun.com + static_cast<RadioButton*>(pNewWindow.get())->EnableRadioCheck( false ); + break; + case WindowType::SCROLLBAR: + pNewWindow = VclPtr<ScrollBar>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXScrollBar; + break; + case WindowType::SCROLLBARBOX: + pNewWindow = VclPtr<ScrollBarBox>::Create( pParent, nWinBits ); + break; + case WindowType::SPINBUTTON: + pNewWindow = VclPtr<SpinButton>::Create( pParent, nWinBits ); + *ppNewComp = new ::toolkit::VCLXSpinButton; + break; + case WindowType::SPINFIELD: + pNewWindow = VclPtr<SpinField>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXNumericField; + break; + case WindowType::SPLITTER: + pNewWindow = VclPtr<Splitter>::Create( pParent, nWinBits ); + break; + case WindowType::SPLITWINDOW: + pNewWindow = VclPtr<SplitWindow>::Create( pParent, nWinBits ); + break; + case WindowType::STATUSBAR: + pNewWindow = VclPtr<StatusBar>::Create( pParent, nWinBits ); + break; + case WindowType::TOOLKIT_SYSTEMCHILDWINDOW: + pNewWindow = VclPtr<SystemChildWindow>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXSystemDependentWindow(); + break; + case WindowType::TABCONTROL: + pNewWindow = VclPtr<TabControl>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXMultiPage; + break; + case WindowType::TABDIALOG: + pNewWindow = VclPtr<TabDialog>::Create( pParent, nWinBits ); + break; + case WindowType::TABPAGE: + { + pNewWindow = VclPtr<TabPage>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXTabPage; + } + break; + case WindowType::TIMEBOX: + pNewWindow = VclPtr<TimeBox>::Create( pParent, nWinBits ); + break; + case WindowType::TIMEFIELD: + pNewWindow = VclPtr<TimeField>::Create( pParent, nWinBits ); + static_cast<TimeField*>(pNewWindow.get())->EnableEmptyFieldValue( true ); + *ppNewComp = new VCLXTimeField; + static_cast<VCLXFormattedSpinField*>((*ppNewComp).get())->SetFormatter( static_cast<FormatterBase*>(static_cast<TimeField*>(pNewWindow.get())) ); + break; + case WindowType::TOOLBOX: + pNewWindow = VclPtr<ToolBox>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXToolBox; + break; + case WindowType::TRISTATEBOX: + pNewWindow = VclPtr<CheckBox>::Create( pParent, nWinBits ); + static_cast<CheckBox*>(pNewWindow.get())->EnableTriState(true); + break; + case WindowType::WARNINGBOX: + pNewWindow = VclPtr<WarningBox>::Create( pParent, nMessBoxStyle, nWinBits, OUString() ); + *ppNewComp = new VCLXMessageBox; + break; + case WindowType::WORKWINDOW: + case WindowType::WINDOW: + case WindowType::TOOLKIT_FRAMEWINDOW: + case WindowType::DOCKINGWINDOW: + if ( rDescriptor.Type == css::awt::WindowClass_TOP ) + { + if (nType == WindowType::DOCKINGWINDOW ) + pNewWindow = VclPtr<DockingWindow>::Create( pParent, nWinBits ); + else + { + if ((pParent == nullptr) && rDescriptor.Parent.is()) + { + // try to get a system dependent window handle + css::uno::Reference< css::awt::XSystemDependentWindowPeer > xSystemDepParent(rDescriptor.Parent, css::uno::UNO_QUERY); + + if (xSystemDepParent.is()) + { + sal_Int8 processID[16]; + + rtl_getGlobalProcessId( reinterpret_cast<sal_uInt8*>(processID) ); + + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + css::uno::Sequence<sal_Int8> processIdSeq(processID, 16); + + css::uno::Any anyHandle = xSystemDepParent->getWindowHandle(processIdSeq, SYSTEM_DEPENDENT_TYPE); + + // use sal_Int64 here to accommodate all int types + // uno::Any shift operator will upcast if necessary + sal_Int64 nWindowHandle = 0; + bool bXEmbed = false; + + bool bUseParentData = true; + if( ! (anyHandle >>= nWindowHandle) ) + { + css::uno::Sequence< css::beans::NamedValue > aProps; + if( anyHandle >>= aProps ) + { + for( const css::beans::NamedValue& rProp : std::as_const(aProps) ) + { + if ( rProp.Name == "WINDOW" ) + rProp.Value >>= nWindowHandle; + else if ( rProp.Name == "XEMBED" ) + rProp.Value >>= bXEmbed; + } + } + else + bUseParentData = false; + } + + if( bUseParentData ) + { + SystemParentData aParentData; + aParentData.nSize = sizeof( aParentData ); + #if defined MACOSX + aParentData.pView = reinterpret_cast<NSView*>(nWindowHandle); + #elif defined ANDROID + // Nothing + #elif defined IOS + // Nothing + #elif defined UNX + aParentData.aWindow = nWindowHandle; + aParentData.bXEmbedSupport = bXEmbed; + #elif defined _WIN32 + aParentData.hWnd = reinterpret_cast<HWND>(nWindowHandle); + #endif + pNewWindow = VclPtr<WorkWindow>::Create( &aParentData ); + } + } + } + + if (!pNewWindow) + pNewWindow = VclPtr<WorkWindow>::Create( pParent, nWinBits ); + } + + *ppNewComp = new VCLXTopWindow(); + } + else if ( rDescriptor.Type == css::awt::WindowClass_CONTAINER ) + { + if (nType == WindowType::DOCKINGWINDOW ) + pNewWindow = VclPtr<DockingWindow>::Create( pParent, nWinBits ); + else + pNewWindow = VclPtr<vcl::Window>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXContainer; + } + else + { + if (nType == WindowType::DOCKINGWINDOW ) + pNewWindow = VclPtr<DockingWindow>::Create( pParent, nWinBits ); + else + pNewWindow = VclPtr<vcl::Window>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXWindow; + } + break; + case WindowType::CONTROL: + if ( aServiceName == "tabpagecontainer" ) + { + // TabControl has a special case for tabs without border: they are displayed + // in a different way, so we need to ensure that this style is not set, so + // we can guarantee normal tab behavior + pNewWindow = VclPtr<TabControl>::Create( pParent, nWinBits & (~WB_NOBORDER)); + *ppNewComp = new VCLXTabPageContainer; + } + else if ( aServiceName == "animatedimages" ) + { + pNewWindow = VclPtr<Throbber>::Create( pParent, nWinBits ); + *ppNewComp = new ::toolkit::AnimatedImagesPeer; + } + else if (aServiceName == "roadmap") + { + pNewWindow = VclPtr<::vcl::ORoadmap>::Create( pParent, WB_TABSTOP ); + *ppNewComp = new SVTXRoadmap; + } + else if (aServiceName == "fixedhyperlink") + { + pNewWindow = VclPtr<FixedHyperlink>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXFixedHyperlink; + } + else if (aServiceName == "progressbar") + { + pNewWindow = VclPtr<ProgressBar>::Create( pParent, nWinBits, ProgressBar::BarStyle::Progress ); + *ppNewComp = new VCLXProgressBar; + } + else if (aServiceName == "filecontrol") + { + pNewWindow = VclPtr<FileControl>::Create( pParent, nWinBits ); + *ppNewComp = new VCLXFileControl; + } + else if (aServiceName == "tree") + { + rtl::Reference<TreeControlPeer> pPeer = new TreeControlPeer; + *ppNewComp = pPeer; + pNewWindow = pPeer->createVclControl( pParent, nWinBits ); + } + else if (aServiceName == "formattedfield") + { + pNewWindow = VclPtr<FormattedField>::Create( pParent, nWinBits ); + *ppNewComp = new SVTXFormattedField; + } + else if (aServiceName == "numericfield") + { + pNewWindow = VclPtr<DoubleNumericField>::Create( pParent, nWinBits ); + *ppNewComp = new SVTXNumericField; + } + else if (aServiceName == "longcurrencyfield") + { + pNewWindow = VclPtr<DoubleCurrencyField>::Create( pParent, nWinBits ); + *ppNewComp = new SVTXCurrencyField; + } + else if (aServiceName == "datefield") + { + pNewWindow = VclPtr<CalendarField>::Create(pParent, nWinBits); + static_cast<CalendarField*>(pNewWindow.get())->EnableToday(); + static_cast<CalendarField*>(pNewWindow.get())->EnableNone(); + static_cast<CalendarField*>(pNewWindow.get())->EnableEmptyFieldValue( true ); + rtl::Reference<SVTXDateField> newComp = new SVTXDateField; + *ppNewComp = newComp; + newComp->SetFormatter( static_cast<FormatterBase*>(static_cast<DateField*>(pNewWindow.get())) ); + } + else if (aServiceName == "grid") + { + pNewWindow = VclPtr<::svt::table::TableControl>::Create(pParent, nWinBits); + *ppNewComp = new SVTXGridControl; + } + break; + default: + OSL_ENSURE( false, "VCLXToolkit::ImplCreateWindow: unknown window type!" ); + break; + } + } + + // tdf#126717 default that formcontrols show accelerators + if (Control* pControl = dynamic_cast<Control*>(pNewWindow.get())) + pControl->SetShowAccelerator(true); + return pNewWindow; +} + +css::uno::Reference< css::awt::XWindowPeer > VCLXToolkit::ImplCreateWindow( + const css::awt::WindowDescriptor& rDescriptor, + MessBoxStyle nForceMessBoxStyle ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + SolarMutexGuard aSolarGuard; + + css::uno::Reference< css::awt::XVclWindowPeer > xRef; + + VclPtr<vcl::Window> pParent; + if ( rDescriptor.Parent.is() ) + { + VCLXWindow* pParentComponent = dynamic_cast<VCLXWindow*>( rDescriptor.Parent.get() ); + + // #103939# Don't throw assertion, may be it's a system dependent window, used in ImplCreateWindow. + // DBG_ASSERT( pParentComponent, "ParentComponent not valid" ); + + if ( pParentComponent ) + pParent = pParentComponent->GetWindow(); + } + std::pair<WinBits, MessBoxStyle> aPair = ImplGetWinBits( rDescriptor.WindowAttributes, + ImplGetComponentType( rDescriptor.WindowServiceName ) ); + WinBits nWinBits = aPair.first; + aPair.second |= nForceMessBoxStyle; + + rtl::Reference<VCLXWindow> pNewComp; + + vcl::Window* pNewWindow = ImplCreateWindow( &pNewComp, rDescriptor, pParent, nWinBits, aPair.second ); + + DBG_ASSERT( pNewWindow, "createWindow: Unknown Component!" ); + SAL_INFO_IF( !pNewComp, "toolkit", "createWindow: No special Interface!" ); + + if ( pNewWindow ) + { + pNewWindow->SetCreatedWithToolkit( true ); + //pNewWindow->SetPosPixel( Point() ); // do not force (0,0) position, keep default pos instead + + if ( rDescriptor.WindowAttributes & css::awt::WindowAttribute::MINSIZE ) + { + pNewWindow->SetSizePixel( Size() ); + } + else if ( rDescriptor.WindowAttributes & css::awt::WindowAttribute::FULLSIZE ) + { + if ( pParent ) + pNewWindow->SetSizePixel( pParent->GetOutputSizePixel() ); + } + else if ( !VCLUnoHelper::IsZero( rDescriptor.Bounds ) ) + { + tools::Rectangle aRect = VCLRectangle( rDescriptor.Bounds ); + pNewWindow->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() ); + } + + if ( !pNewComp ) + { + // Default-Interface + xRef = pNewWindow->GetComponentInterface(); + } + else + { + xRef = pNewComp; + pNewWindow->SetComponentInterface( xRef ); + } + DBG_ASSERT( pNewWindow->GetComponentInterface( false ) == xRef, + "VCLXToolkit::createWindow: did #133706# resurge?" ); + + if ( rDescriptor.WindowAttributes & css::awt::WindowAttribute::SHOW ) + pNewWindow->Show(); + } + + return xRef; +} + +css::uno::Sequence< css::uno::Reference< css::awt::XWindowPeer > > VCLXToolkit::createWindows( const css::uno::Sequence< css::awt::WindowDescriptor >& rDescriptors ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + sal_uInt32 nComponents = rDescriptors.getLength(); + css::uno::Sequence< css::uno::Reference< css::awt::XWindowPeer > > aSeq( nComponents ); + for ( sal_uInt32 n = 0; n < nComponents; n++ ) + { + css::awt::WindowDescriptor aDescr = rDescriptors.getConstArray()[n]; + + if ( aDescr.ParentIndex == -1 ) + aDescr.Parent = nullptr; + else if ( ( aDescr.ParentIndex >= 0 ) && ( o3tl::make_unsigned(aDescr.ParentIndex) < n ) ) + aDescr.Parent = aSeq.getConstArray()[aDescr.ParentIndex]; + aSeq.getArray()[n] = createWindow( aDescr ); + } + return aSeq; +} + +// css::awt::XSystemChildFactory +css::uno::Reference< css::awt::XWindowPeer > VCLXToolkit::createSystemChild( const css::uno::Any& Parent, const css::uno::Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 nSystemType ) +{ + VclPtr<vcl::Window> pChildWindow; + if ( nSystemType == SYSTEM_DEPENDENT_TYPE ) + { + // use sal_Int64 here to accommodate all int types + // uno::Any shift operator will upcast if necessary + sal_Int64 nWindowHandle = 0; + bool bXEmbed = false; + + bool bUseParentData = true; + if( ! (Parent >>= nWindowHandle) ) + { + css::uno::Sequence< css::beans::NamedValue > aProps; + if( Parent >>= aProps ) + { + for( const css::beans::NamedValue& rProp : std::as_const(aProps) ) + { + if ( rProp.Name == "WINDOW" ) + rProp.Value >>= nWindowHandle; + else if ( rProp.Name == "XEMBED" ) + rProp.Value >>= bXEmbed; + } + } + else + bUseParentData = false; + } + + if( bUseParentData ) + { + SystemParentData aParentData; + aParentData.nSize = sizeof( aParentData ); + #if defined MACOSX + aParentData.pView = reinterpret_cast<NSView*>(nWindowHandle); + #elif defined ANDROID + // Nothing + #elif defined IOS + // Nothing + #elif defined UNX + aParentData.aWindow = nWindowHandle; + aParentData.bXEmbedSupport = bXEmbed; + #elif defined _WIN32 + aParentData.hWnd = reinterpret_cast<HWND>(nWindowHandle); + #endif + SolarMutexGuard aGuard; + try + { + pChildWindow.reset( VclPtr<WorkWindow>::Create( &aParentData ) ); + } + catch ( const css::uno::RuntimeException & ) + { + // system child window could not be created + DBG_UNHANDLED_EXCEPTION("toolkit"); + pChildWindow.clear(); + } + } + } + else if (nSystemType == css::lang::SystemDependent::SYSTEM_JAVA) + { + SolarMutexGuard aGuard; + pChildWindow.reset(VclPtr<WorkWindow>::Create(nullptr, Parent)); + } + + css::uno::Reference< css::awt::XVclWindowPeer > xPeer; + if ( pChildWindow ) + { + rtl::Reference<VCLXTopWindow> pPeer = new VCLXTopWindow; + SolarMutexGuard aGuard; + pPeer->SetWindow( pChildWindow ); + xPeer = pPeer; + pChildWindow->SetWindowPeer(xPeer, pPeer.get()); + } + + return xPeer; +} + +// css::awt::XMessageBoxFactory +css::uno::Reference< css::awt::XMessageBox > SAL_CALL VCLXToolkit::createMessageBox( + const css::uno::Reference< css::awt::XWindowPeer >& aParent, + css::awt::MessageBoxType eType, + ::sal_Int32 aButtons, + const OUString& aTitle, + const OUString& aMessage ) +{ + css::awt::WindowDescriptor aDescriptor; + + sal_Int32 nWindowAttributes = css::awt::WindowAttribute::BORDER|css::awt::WindowAttribute::MOVEABLE|css::awt::WindowAttribute::CLOSEABLE; + + // Map button definitions to window attributes + if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_OK ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::OK; + else if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_OK_CANCEL ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::OK_CANCEL; + else if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_YES_NO ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::YES_NO; + else if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_YES_NO_CANCEL ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::YES_NO_CANCEL; + else if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_RETRY_CANCEL ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::RETRY_CANCEL; + + // Map default button definitions to window attributes + if (sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_OK ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::DEF_OK; + else if (sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_CANCEL ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::DEF_CANCEL; + else if (sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_YES ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::DEF_YES; + else if (sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_NO ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::DEF_NO; + else if (sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_RETRY ) + nWindowAttributes |= css::awt::VclWindowPeerAttribute::DEF_RETRY; + + // No more bits for VclWindowPeerAttribute possible. Mapping must be + // done explicitly using VCL methods + MessBoxStyle nAddWinBits = MessBoxStyle::NONE; + if (( aButtons & 0x0000ffffL ) == css::awt::MessageBoxButtons::BUTTONS_ABORT_IGNORE_RETRY ) + nAddWinBits |= MessBoxStyle::AbortRetryIgnore; + if ( sal_Int32( aButtons & 0xffff0000L ) == css::awt::MessageBoxButtons::DEFAULT_BUTTON_IGNORE ) + nAddWinBits |= MessBoxStyle::DefaultIgnore; + + OUString aType; + lcl_convertMessageBoxType( aType, eType ); + + aDescriptor.Type = css::awt::WindowClass_MODALTOP; + aDescriptor.WindowServiceName = aType; + aDescriptor.ParentIndex = -1; + aDescriptor.Parent = aParent; + aDescriptor.WindowAttributes = nWindowAttributes; + css::uno::Reference< css::awt::XMessageBox > xMsgBox( + ImplCreateWindow( aDescriptor, nAddWinBits ), css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XWindow > xWindow( xMsgBox, css::uno::UNO_QUERY ); + if ( xMsgBox.is() && xWindow.is() ) + { + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + { + SolarMutexGuard aGuard; + xMsgBox->setCaptionText( aTitle ); + xMsgBox->setMessageText( aMessage ); + } + } + + return xMsgBox; +} + +css::uno::Reference< css::datatransfer::dnd::XDragGestureRecognizer > SAL_CALL VCLXToolkit::getDragGestureRecognizer( const css::uno::Reference< css::awt::XWindow >& window ) +{ + SolarMutexGuard g; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( window ); + + if( pWindow ) + return pWindow->GetDragGestureRecognizer(); + + return css::uno::Reference< css::datatransfer::dnd::XDragGestureRecognizer >(); +} + +css::uno::Reference< css::datatransfer::dnd::XDragSource > SAL_CALL VCLXToolkit::getDragSource( const css::uno::Reference< css::awt::XWindow >& window ) +{ + SolarMutexGuard g; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( window ); + + if( pWindow ) + return pWindow->GetDragSource(); + + return css::uno::Reference< css::datatransfer::dnd::XDragSource >(); +} + +css::uno::Reference< css::datatransfer::dnd::XDropTarget > SAL_CALL VCLXToolkit::getDropTarget( const css::uno::Reference< css::awt::XWindow >& window ) +{ + SolarMutexGuard g; + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( window ); + + if( pWindow ) + return pWindow->GetDropTarget(); + + return css::uno::Reference< css::datatransfer::dnd::XDropTarget >(); +} + +css::uno::Reference< css::datatransfer::clipboard::XClipboard > SAL_CALL VCLXToolkit::getClipboard( const OUString& clipboardName ) +{ + if( clipboardName.isEmpty() ) + { + if( !mxClipboard.is() ) + { + // remember clipboard here + mxClipboard = css::datatransfer::clipboard::SystemClipboard::create( + comphelper::getProcessComponentContext()); + } + + return mxClipboard; + } + + else if( clipboardName == "Selection" ) + { + return mxSelection; + } + + return css::uno::Reference< css::datatransfer::clipboard::XClipboard >(); +} + +// XServiceInfo +OUString VCLXToolkit::getImplementationName() +{ + return "stardiv.Toolkit.VCLXToolkit"; +} + +sal_Bool VCLXToolkit::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > VCLXToolkit::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.Toolkit", "stardiv.vcl.VclToolkit"}; +} + +// css::awt::XExtendedToolkit: + +// virtual +::sal_Int32 SAL_CALL VCLXToolkit::getTopWindowCount() +{ + return static_cast< ::sal_Int32 >(::Application::GetTopWindowCount()); + // XXX numeric overflow +} + +// virtual +css::uno::Reference< css::awt::XTopWindow > SAL_CALL +VCLXToolkit::getTopWindow(::sal_Int32 nIndex) +{ + vcl::Window * p = ::Application::GetTopWindow(static_cast< tools::Long >(nIndex)); + // XXX numeric overflow + return css::uno::Reference< css::awt::XTopWindow >( + p == nullptr ? nullptr : static_cast< css::awt::XWindow * >(p->GetWindowPeer()), + css::uno::UNO_QUERY); +} + +// virtual +css::uno::Reference< css::awt::XTopWindow > SAL_CALL +VCLXToolkit::getActiveTopWindow() +{ + vcl::Window * p = ::Application::GetActiveTopWindow(); + return css::uno::Reference< css::awt::XTopWindow >( + p == nullptr ? nullptr : static_cast< css::awt::XWindow * >(p->GetWindowPeer()), + css::uno::UNO_QUERY); +} + +// virtual +void SAL_CALL VCLXToolkit::addTopWindowListener( + css::uno::Reference< css::awt::XTopWindowListener > const & rListener) +{ + OSL_ENSURE(rListener.is(), "Null rListener"); + ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + aGuard.clear(); + rListener->disposing( + css::lang::EventObject( + getXWeak())); + } + else if (m_aTopWindowListeners.addInterface(rListener) == 1 + && !m_bEventListener) + { + m_bEventListener = true; + ::Application::AddEventListener(m_aEventListenerLink); + } +} + +// virtual +void SAL_CALL VCLXToolkit::removeTopWindowListener( + css::uno::Reference< css::awt::XTopWindowListener > const & rListener) +{ + ::osl::MutexGuard aGuard(rBHelper.rMutex); + if (!(rBHelper.bDisposed || rBHelper.bInDispose) + && m_aTopWindowListeners.removeInterface(rListener) == 0 + && m_aFocusListeners.getLength() == 0 && m_bEventListener) + { + ::Application::RemoveEventListener(m_aEventListenerLink); + m_bEventListener = false; + } +} + +// virtual +void SAL_CALL VCLXToolkit::addKeyHandler( + css::uno::Reference< css::awt::XKeyHandler > const & rHandler) +{ + OSL_ENSURE(rHandler.is(), "Null rHandler"); + ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + aGuard.clear(); + rHandler->disposing( + css::lang::EventObject( + getXWeak())); + } + else if (m_aKeyHandlers.addInterface(rHandler) == 1 && !m_bKeyListener) + { + m_bKeyListener = true; + ::Application::AddKeyListener(m_aKeyListenerLink); + } +} + +// virtual +void SAL_CALL VCLXToolkit::removeKeyHandler( + css::uno::Reference< css::awt::XKeyHandler > const & rHandler) +{ + ::osl::MutexGuard aGuard(rBHelper.rMutex); + if (!(rBHelper.bDisposed || rBHelper.bInDispose) + && m_aKeyHandlers.removeInterface(rHandler) == 0 && m_bKeyListener) + { + ::Application::RemoveKeyListener(m_aKeyListenerLink); + m_bKeyListener = false; + } +} + +// virtual +void SAL_CALL VCLXToolkit::addFocusListener( + css::uno::Reference< css::awt::XFocusListener > const & rListener) +{ + OSL_ENSURE(rListener.is(), "Null rListener"); + ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + aGuard.clear(); + rListener->disposing( + css::lang::EventObject( + getXWeak())); + } + else if (m_aFocusListeners.addInterface(rListener) == 1 + && !m_bEventListener) + { + m_bEventListener = true; + ::Application::AddEventListener(m_aEventListenerLink); + } +} + +// virtual +void SAL_CALL VCLXToolkit::removeFocusListener( + css::uno::Reference< css::awt::XFocusListener > const & rListener) +{ + ::osl::MutexGuard aGuard(rBHelper.rMutex); + if (!(rBHelper.bDisposed || rBHelper.bInDispose) + && m_aFocusListeners.removeInterface(rListener) == 0 + && m_aTopWindowListeners.getLength() == 0 && m_bEventListener) + { + ::Application::RemoveEventListener(m_aEventListenerLink); + m_bEventListener = false; + } +} + +// virtual +void SAL_CALL VCLXToolkit::fireFocusGained( + css::uno::Reference< + css::uno::XInterface > const &) +{ +} + +// virtual +void SAL_CALL VCLXToolkit::fireFocusLost( + css::uno::Reference< + css::uno::XInterface > const &) +{ +} + + +IMPL_LINK(VCLXToolkit, eventListenerHandler, ::VclSimpleEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowShow: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowOpened); + break; + case VclEventId::WindowHide: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowClosed); + break; + case VclEventId::WindowActivate: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowActivated); + break; + case VclEventId::WindowDeactivate: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowDeactivated); + break; + case VclEventId::WindowClose: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowClosing); + break; + case VclEventId::WindowGetFocus: + callFocusListeners(&rEvent, true); + break; + case VclEventId::WindowLoseFocus: + callFocusListeners(&rEvent, false); + break; + case VclEventId::WindowMinimize: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowMinimized); + break; + case VclEventId::WindowNormalize: + callTopWindowListeners( + &rEvent, &css::awt::XTopWindowListener::windowNormalized); + break; + default: break; + } +} + +IMPL_LINK(VCLXToolkit, keyListenerHandler, ::VclWindowEvent&, rEvent, bool) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowKeyInput: + return callKeyHandlers(&rEvent, true); + case VclEventId::WindowKeyUp: + return callKeyHandlers(&rEvent, false); + default: break; + } + return false; +} + +void VCLXToolkit::callTopWindowListeners( + ::VclSimpleEvent const * pEvent, + void (SAL_CALL css::awt::XTopWindowListener::* pFn)( + css::lang::EventObject const &)) +{ + vcl::Window * pWindow + = static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow(); + if (!pWindow->IsTopWindow()) + return; + + std::vector< css::uno::Reference< css::awt::XTopWindowListener > > + aListeners(m_aTopWindowListeners.getElements()); + if (aListeners.empty()) + return; + + css::lang::EventObject aAwtEvent( + static_cast< css::awt::XWindow * >(pWindow->GetWindowPeer())); + for (const css::uno::Reference<css::awt::XTopWindowListener> & xListener : aListeners) + { + try + { + (xListener.get()->*pFn)(aAwtEvent); + } + catch (const css::uno::RuntimeException &) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + } +} + +bool VCLXToolkit::callKeyHandlers(::VclSimpleEvent const * pEvent, + bool bPressed) +{ + std::vector< css::uno::Reference< css::awt::XKeyHandler > > + aHandlers(m_aKeyHandlers.getElements()); + + if (!aHandlers.empty()) + { + vcl::Window * pWindow = static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow(); + + // See implementation in vclxwindow.cxx for mapping between VCL and UNO AWT event + ::KeyEvent * pKeyEvent = static_cast< ::KeyEvent * >( + static_cast< ::VclWindowEvent const * >(pEvent)->GetData()); + css::awt::KeyEvent aAwtEvent( + static_cast< css::awt::XWindow * >(pWindow->GetWindowPeer()), + (pKeyEvent->GetKeyCode().IsShift() + ? css::awt::KeyModifier::SHIFT : 0) + | (pKeyEvent->GetKeyCode().IsMod1() + ? css::awt::KeyModifier::MOD1 : 0) + | (pKeyEvent->GetKeyCode().IsMod2() + ? css::awt::KeyModifier::MOD2 : 0) + | (pKeyEvent->GetKeyCode().IsMod3() + ? css::awt::KeyModifier::MOD3 : 0), + pKeyEvent->GetKeyCode().GetCode(), pKeyEvent->GetCharCode(), + sal::static_int_cast< sal_Int16 >( + pKeyEvent->GetKeyCode().GetFunction())); + for (const css::uno::Reference<css::awt::XKeyHandler> & xHandler : aHandlers) + { + try + { + if (bPressed ? xHandler->keyPressed(aAwtEvent) + : xHandler->keyReleased(aAwtEvent)) + return true; + } + catch (const css::uno::RuntimeException &) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + } + } + return false; +} + +void VCLXToolkit::callFocusListeners(::VclSimpleEvent const * pEvent, + bool bGained) +{ + vcl::Window * pWindow + = static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow(); + if (!pWindow->IsTopWindow()) + return; + + std::vector< css::uno::Reference< css::awt::XFocusListener > > + aListeners(m_aFocusListeners.getElements()); + if (aListeners.empty()) + return; + + // Ignore the interior of compound controls when determining the + // window that gets the focus next (see implementation in + // vclxwindow.cxx for mapping between VCL and UNO AWT event): + css::uno::Reference< css::uno::XInterface > xNext; + vcl::Window * pFocus = ::Application::GetFocusWindow(); + for (vcl::Window * p = pFocus; p != nullptr; p = p->GetParent()) + if (!p->IsCompoundControl()) + { + pFocus = p; + break; + } + if (pFocus != nullptr) + xNext = pFocus->GetComponentInterface(); + css::awt::FocusEvent aAwtEvent( + static_cast< css::awt::XWindow * >(pWindow->GetWindowPeer()), + static_cast<sal_Int16>(pWindow->GetGetFocusFlags()), + xNext, false); + for (const css::uno::Reference<css::awt::XFocusListener> & xListener : aListeners) + { + try + { + bGained ? xListener->focusGained(aAwtEvent) + : xListener->focusLost(aAwtEvent); + } + catch (const css::uno::RuntimeException &) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + } +} + +// css::awt::XReschedule: + +void SAL_CALL VCLXToolkit::reschedule() +{ + SolarMutexGuard aSolarGuard; + Application::Reschedule(true); +} + +// css::awt::XFontMappingUse: +void SAL_CALL VCLXToolkit::startTrackingFontMappingUse() +{ + SolarMutexGuard aSolarGuard; + OutputDevice::StartTrackingFontMappingUse(); +} + +css::uno::Sequence<css::awt::XFontMappingUseItem> +SAL_CALL VCLXToolkit::finishTrackingFontMappingUse() +{ + SolarMutexGuard aSolarGuard; + OutputDevice::FontMappingUseData data = OutputDevice::FinishTrackingFontMappingUse(); + css::uno::Sequence<css::awt::XFontMappingUseItem> ret( data.size()); + css::awt::XFontMappingUseItem* retData = ret.getArray(); + for( size_t i = 0; i < data.size(); ++i ) + { + retData[ i ].originalFont = data[ i ].mOriginalFont; + retData[ i ].usedFonts = comphelper::arrayToSequence<OUString,OUString> + (data[ i ].mUsedFonts.data(), data[ i ].mUsedFonts.size()); + retData[ i ].count = data[ i ].mCount; + } + return ret; +} + +// css::awt::XToolkitExperimental + +void SAL_CALL VCLXToolkit::processEventsToIdle() +{ + SolarMutexGuard aSolarGuard; + comphelper::ProfileZone aZone("processEvents"); + Scheduler::ProcessEventsToIdle(); +} + +sal_Int64 SAL_CALL VCLXToolkit::getOpenGLBufferSwapCounter() +{ +#if HAVE_FEATURE_OPENGL + return OpenGLWrapper::getBufferSwapCounter(); +#else + return 0; +#endif +} + +void SAL_CALL VCLXToolkit::setDeterministicScheduling(sal_Bool bDeterministicMode) +{ + SolarMutexGuard aSolarGuard; + Scheduler::SetDeterministicMode(bDeterministicMode); +} + +void SAL_CALL VCLXToolkit::pause(sal_Int32 nMilliseconds) +{ + new Pause(nMilliseconds); +} + +void SAL_CALL VCLXToolkit::startRecording() +{ + comphelper::TraceEvent::startRecording(); +} + +void SAL_CALL VCLXToolkit::stopRecording() +{ + comphelper::TraceEvent::stopRecording(); +} + +css::uno::Sequence< OUString > VCLXToolkit::getRecordingAndClear() +{ + return comphelper::ProfileZone::getRecordingAndClear(); +} + +void VCLXToolkit::waitUntilAllIdlesDispatched() +{ + IdleTask::waitUntilIdleDispatched(); +} + +// css:awt:XToolkitRobot + +void SAL_CALL VCLXToolkit::keyPress( const css::awt::KeyEvent & aKeyEvent ) +{ + css::uno::Reference<css::awt::XWindow> xWindow ( aKeyEvent.Source, css::uno::UNO_QUERY_THROW ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( !pWindow ) + throw css::uno::RuntimeException( "invalid event source" ); + + ::KeyEvent aVCLKeyEvent = VCLUnoHelper::createVCLKeyEvent( aKeyEvent ); + ::Application::PostKeyEvent( VclEventId::WindowKeyInput, pWindow, &aVCLKeyEvent ); +} + +void SAL_CALL VCLXToolkit::keyRelease( const css::awt::KeyEvent & aKeyEvent ) +{ + css::uno::Reference<css::awt::XWindow> xWindow ( aKeyEvent.Source, css::uno::UNO_QUERY_THROW ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( !pWindow ) + throw css::uno::RuntimeException( "invalid event source" ); + + ::KeyEvent aVCLKeyEvent = VCLUnoHelper::createVCLKeyEvent( aKeyEvent ); + ::Application::PostKeyEvent( VclEventId::WindowKeyUp, pWindow, &aVCLKeyEvent ); +} + + +void SAL_CALL VCLXToolkit::mousePress( const css::awt::MouseEvent & aMouseEvent ) +{ + css::uno::Reference<css::awt::XWindow> xWindow ( aMouseEvent.Source, css::uno::UNO_QUERY_THROW ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( !pWindow ) + throw css::uno::RuntimeException( "invalid event source" ); + + ::MouseEvent aVCLMouseEvent = VCLUnoHelper::createVCLMouseEvent( aMouseEvent ); + ::Application::PostMouseEvent( VclEventId::WindowMouseButtonDown, pWindow, &aVCLMouseEvent ); +} + +void SAL_CALL VCLXToolkit::mouseRelease( const css::awt::MouseEvent & aMouseEvent ) +{ + css::uno::Reference<css::awt::XWindow> xWindow ( aMouseEvent.Source, css::uno::UNO_QUERY_THROW ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( !pWindow ) + throw css::uno::RuntimeException( "invalid event source" ); + + ::MouseEvent aVCLMouseEvent = VCLUnoHelper::createVCLMouseEvent( aMouseEvent ); + ::Application::PostMouseEvent( VclEventId::WindowMouseButtonUp, pWindow, &aVCLMouseEvent ); +} + +void SAL_CALL VCLXToolkit::mouseMove( const css::awt::MouseEvent & aMouseEvent ) +{ + css::uno::Reference<css::awt::XWindow> xWindow ( aMouseEvent.Source, css::uno::UNO_QUERY_THROW ); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( !pWindow ) + throw css::uno::RuntimeException( "invalid event source" ); + + ::MouseEvent aVCLMouseEvent = VCLUnoHelper::createVCLMouseEvent( aMouseEvent ); + ::Application::PostMouseEvent( VclEventId::WindowMouseMove, pWindow, &aVCLMouseEvent ); +} + + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_VCLXToolkit_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VCLXToolkit()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxtopwindow.cxx b/toolkit/source/awt/vclxtopwindow.cxx new file mode 100644 index 0000000000..c0ce4d891d --- /dev/null +++ b/toolkit/source/awt/vclxtopwindow.cxx @@ -0,0 +1,229 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/SystemDependent.hpp> +#include <com/sun/star/awt/SystemDependentXWindow.hpp> + +#if defined ( MACOSX ) +#include <premac.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> +#endif + +#include <vcl/sysdata.hxx> +#include <o3tl/safeint.hxx> + +#include <awt/vclxtopwindow.hxx> +#include <toolkit/awt/vclxmenu.hxx> + +#include <vcl/wrkwin.hxx> +#include <vcl/syswin.hxx> +#include <vcl/menu.hxx> +#include <vcl/svapp.hxx> + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; +using ::com::sun::star::lang::IndexOutOfBoundsException; + + +css::uno::Any VCLXTopWindow::getWindowHandle( const css::uno::Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 SystemType ) +{ + SolarMutexGuard aGuard; + + // TODO, check the process id + css::uno::Any aRet; + vcl::Window* pWindow = VCLXContainer::GetWindow(); + if ( pWindow ) + { + const SystemEnvData* pSysData = static_cast<SystemWindow *>(pWindow)->GetSystemData(); + if( pSysData ) + { +#if defined (_WIN32) + if( SystemType == css::lang::SystemDependent::SYSTEM_WIN32 ) + { + aRet <<= reinterpret_cast<sal_IntPtr>(pSysData->hWnd); + } +#elif defined(MACOSX) + if( SystemType == css::lang::SystemDependent::SYSTEM_MAC ) + { + aRet <<= reinterpret_cast<sal_IntPtr>(pSysData->mpNSView); + } +#elif defined(ANDROID) + // Nothing + (void) SystemType; +#elif defined(IOS) + // Nothing + (void) SystemType; +#elif defined(UNX) + if( SystemType == css::lang::SystemDependent::SYSTEM_XWINDOW ) + { + css::awt::SystemDependentXWindow aSD; + aSD.DisplayPointer = sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_IntPtr >(pSysData->pDisplay)); + aSD.WindowHandle = pSysData->GetWindowHandle(pWindow->ImplGetFrame()); + aRet <<= aSD; + } +#endif + } + } + return aRet; +} + +void VCLXTopWindow::addTopWindowListener( const css::uno::Reference< css::awt::XTopWindowListener >& rxListener ) +{ + SolarMutexGuard aGuard; + + if (!IsDisposed()) + GetTopWindowListeners().addInterface( rxListener ); +} + +void VCLXTopWindow::removeTopWindowListener( const css::uno::Reference< css::awt::XTopWindowListener >& rxListener ) +{ + SolarMutexGuard aGuard; + + if (!IsDisposed()) + GetTopWindowListeners().removeInterface( rxListener ); +} + +void VCLXTopWindow::toFront( ) +{ + SolarMutexGuard aGuard; + + vcl::Window* pWindow = VCLXContainer::GetWindow(); + if (pWindow) + pWindow->ToTop( ToTopFlags::RestoreWhenMin ); +} + +void VCLXTopWindow::toBack( ) +{ +} + +void VCLXTopWindow::setMenuBar( const css::uno::Reference< css::awt::XMenuBar >& rxMenu ) +{ + SolarMutexGuard aGuard; + + vcl::Window* pWindow = VCLXContainer::GetWindow(); + if ( pWindow ) + { + SystemWindow* pSystemWindow = static_cast<SystemWindow*>( pWindow ); + pSystemWindow->SetMenuBar( nullptr ); + if ( rxMenu.is() ) + { + VCLXMenu* pMenu = dynamic_cast<VCLXMenu*>( rxMenu.get() ); + if ( pMenu && !pMenu->IsPopupMenu() ) + pSystemWindow->SetMenuBar( static_cast<MenuBar*>( pMenu->GetMenu() )); + } + } +} + + +sal_Bool SAL_CALL VCLXTopWindow::getIsMaximized() +{ + SolarMutexGuard aGuard; + + const WorkWindow* pWindow = VCLXContainer::GetAsDynamic<WorkWindow>(); + if ( !pWindow ) + return false; + + return pWindow->IsMaximized(); +} + + +void SAL_CALL VCLXTopWindow::setIsMaximized( sal_Bool _ismaximized ) +{ + SolarMutexGuard aGuard; + + WorkWindow* pWindow = VCLXContainer::GetAsDynamic<WorkWindow>(); + if ( !pWindow ) + return; + + pWindow->Maximize( _ismaximized ); +} + + +sal_Bool SAL_CALL VCLXTopWindow::getIsMinimized() +{ + SolarMutexGuard aGuard; + + const WorkWindow* pWindow = VCLXContainer::GetAsDynamic<WorkWindow>(); + if ( !pWindow ) + return false; + + return pWindow->IsMinimized(); +} + + +void SAL_CALL VCLXTopWindow::setIsMinimized( sal_Bool _isMinimized ) +{ + SolarMutexGuard aGuard; + + WorkWindow* pWindow = VCLXContainer::GetAsDynamic<WorkWindow>(); + if ( !pWindow ) + return; + + _isMinimized ? pWindow->Minimize() : pWindow->Restore(); +} + + +::sal_Int32 SAL_CALL VCLXTopWindow::getDisplay() +{ + SolarMutexGuard aGuard; + + const SystemWindow* pWindow = VCLXContainer::GetAsDynamic<SystemWindow>(); + if ( !pWindow ) + return 0; + + return pWindow->GetScreenNumber(); +} + + +void SAL_CALL VCLXTopWindow::setDisplay( ::sal_Int32 _display ) +{ + SolarMutexGuard aGuard; + + if ( ( _display < 0 ) || ( o3tl::make_unsigned(_display) >= Application::GetScreenCount() ) ) + throw IndexOutOfBoundsException(); + + SystemWindow* pWindow = VCLXContainer::GetAsDynamic<SystemWindow>(); + if ( !pWindow ) + return; + + pWindow->SetScreenNumber( _display ); +} + + + + +void VCLXTopWindow::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXContainer::ImplGetPropertyIds( rIds ); +} + +VCLXTopWindow::VCLXTopWindow() +{ +} + +VCLXTopWindow::~VCLXTopWindow() +{ +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxwindow.cxx b/toolkit/source/awt/vclxwindow.cxx new file mode 100644 index 0000000000..2448eaf646 --- /dev/null +++ b/toolkit/source/awt/vclxwindow.cxx @@ -0,0 +1,2611 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <stdarg.h> +#include <memory> +#include <com/sun/star/awt/WindowEvent.hpp> +#include <com/sun/star/awt/KeyEvent.hpp> +#include <com/sun/star/awt/MouseEvent.hpp> +#include <com/sun/star/awt/MouseWheelBehavior.hpp> +#include <com/sun/star/awt/Style.hpp> +#include <com/sun/star/awt/DockingEvent.hpp> +#include <com/sun/star/awt/EndDockingEvent.hpp> +#include <com/sun/star/awt/EndPopupModeEvent.hpp> +#include <com/sun/star/awt/XWindowListener2.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <toolkit/awt/vclxwindow.hxx> +#include <awt/vclxpointer.hxx> +#include <toolkit/awt/vclxwindows.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/helper/convert.hxx> +#include <helper/property.hxx> +#include <rtl/math.hxx> +#include <sal/log.hxx> +#include <utility> +#include <vcl/toolkit/floatwin.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <tools/color.hxx> +#include <tools/fract.hxx> +#include <tools/debug.hxx> +#include <vcl/event.hxx> +#include <vcl/dockwin.hxx> +#include <vcl/pdfextoutdevdata.hxx> +#include <vcl/tabpage.hxx> +#include <vcl/ctrl.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandevent.hxx> +#include <comphelper/flagguard.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/profilezone.hxx> +#include "stylesettings.hxx" +#include <tools/urlobj.hxx> + +#include <helper/accessibilityclient.hxx> +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::lang::EventObject; +using ::com::sun::star::awt::XWindowListener2; +using ::com::sun::star::awt::XDockableWindowListener; +using ::com::sun::star::awt::XDevice; +using ::com::sun::star::awt::XStyleSettings; +using ::com::sun::star::lang::DisposedException; +using ::com::sun::star::style::VerticalAlignment; +using ::com::sun::star::style::VerticalAlignment_TOP; +using ::com::sun::star::style::VerticalAlignment_MIDDLE; +using ::com::sun::star::style::VerticalAlignment_BOTTOM; + +namespace WritingMode2 = ::com::sun::star::text::WritingMode2; + + +//= VCLXWindowImpl + +class VCLXWindowImpl +{ +private: + typedef ::std::vector< VCLXWindow::Callback > CallbackArray; + +private: + VCLXWindow& mrAntiImpl; + ::toolkit::AccessibilityClient maAccFactory; + bool mbDisposed; + bool mbDrawingOntoParent; // no bit mask, is passed around by reference + bool mbEnableVisible; + bool mbDirectVisible; + + ::osl::Mutex maListenerContainerMutex; + ::comphelper::OInterfaceContainerHelper3<css::awt::XWindowListener2> maWindow2Listeners; + ::comphelper::OInterfaceContainerHelper3<XDockableWindowListener> maDockableWindowListeners; + EventListenerMultiplexer maEventListeners; + FocusListenerMultiplexer maFocusListeners; + WindowListenerMultiplexer maWindowListeners; + KeyListenerMultiplexer maKeyListeners; + MouseListenerMultiplexer maMouseListeners; + MouseMotionListenerMultiplexer maMouseMotionListeners; + PaintListenerMultiplexer maPaintListeners; + VclContainerListenerMultiplexer maContainerListeners; + TopWindowListenerMultiplexer maTopWindowListeners; + + CallbackArray maCallbackEvents; + ImplSVEvent * mnCallbackEventId; + +public: + bool mbDisposing : 1; + bool mbDesignMode : 1; + bool mbSynthesizingVCLEvent : 1; + bool mbWithDefaultProps : 1; + + sal_uLong mnListenerLockLevel; + sal_Int16 mnWritingMode; + sal_Int16 mnContextWritingMode; + + std::unique_ptr<UnoPropertyArrayHelper> + mpPropHelper; + + css::uno::Reference< css::accessibility::XAccessibleContext > + mxAccessibleContext; + css::uno::Reference< css::awt::XGraphics > + mxViewGraphics; + rtl::Reference< toolkit::WindowStyleSettings > + mxWindowStyleSettings; + +public: + bool& getDrawingOntoParent_ref() { return mbDrawingOntoParent; } + +public: + /** ctor + @param _pAntiImpl + the <type>VCLXWindow</type> instance which the object belongs to. Must + live longer then the object just being constructed. + */ + VCLXWindowImpl( VCLXWindow& _rAntiImpl, bool _bWithDefaultProps ); + + VCLXWindowImpl( const VCLXWindowImpl& ) = delete; + const VCLXWindowImpl& operator=(const VCLXWindowImpl&) = delete; + + /** synchronously mbEnableVisible + */ + void setEnableVisible( bool bEnableVisible ) { mbEnableVisible = bEnableVisible; } + bool isEnableVisible() const { return mbEnableVisible; } + /** synchronously mbDirectVisible; + */ + void setDirectVisible( bool bDirectVisible ) { mbDirectVisible = bDirectVisible; } + bool isDirectVisible() const { return mbDirectVisible; } + + /** impl-version of VCLXWindow::ImplExecuteAsyncWithoutSolarLock + */ + void callBackAsync( const VCLXWindow::Callback& i_callback ); + + /** notifies the object that its VCLXWindow is being disposed + */ + void disposing(); + + ::toolkit::AccessibilityClient& getAccessibleFactory() + { + return maAccFactory; + } + + Reference< XStyleSettings > getStyleSettings(); + + /** returns the container of registered XWindowListener2 listeners + */ + ::comphelper::OInterfaceContainerHelper3<css::awt::XWindowListener2>& getWindow2Listeners() { return maWindow2Listeners; } + ::comphelper::OInterfaceContainerHelper3<XDockableWindowListener>& getDockableWindowListeners() { return maDockableWindowListeners; } + EventListenerMultiplexer& getEventListeners() { return maEventListeners; } + FocusListenerMultiplexer& getFocusListeners() { return maFocusListeners; } + WindowListenerMultiplexer& getWindowListeners() { return maWindowListeners; } + KeyListenerMultiplexer& getKeyListeners() { return maKeyListeners; } + MouseListenerMultiplexer& getMouseListeners() { return maMouseListeners; } + MouseMotionListenerMultiplexer& getMouseMotionListeners() { return maMouseMotionListeners; } + PaintListenerMultiplexer& getPaintListeners() { return maPaintListeners; } + VclContainerListenerMultiplexer& getContainerListeners() { return maContainerListeners; } + TopWindowListenerMultiplexer& getTopWindowListeners() { return maTopWindowListeners; } + +private: + DECL_LINK( OnProcessCallbacks, void*, void ); +}; + + +VCLXWindowImpl::VCLXWindowImpl( VCLXWindow& _rAntiImpl, bool _bWithDefaultProps ) + :mrAntiImpl( _rAntiImpl ) + ,mbDisposed( false ) + ,mbDrawingOntoParent( false ) + ,mbEnableVisible(true) + ,mbDirectVisible(true) + ,maWindow2Listeners( maListenerContainerMutex ) + ,maDockableWindowListeners( maListenerContainerMutex ) + ,maEventListeners( _rAntiImpl ) + ,maFocusListeners( _rAntiImpl ) + ,maWindowListeners( _rAntiImpl ) + ,maKeyListeners( _rAntiImpl ) + ,maMouseListeners( _rAntiImpl ) + ,maMouseMotionListeners( _rAntiImpl ) + ,maPaintListeners( _rAntiImpl ) + ,maContainerListeners( _rAntiImpl ) + ,maTopWindowListeners( _rAntiImpl ) + ,mnCallbackEventId( nullptr ) + ,mbDisposing( false ) + ,mbDesignMode( false ) + ,mbSynthesizingVCLEvent( false ) + ,mbWithDefaultProps( _bWithDefaultProps ) + ,mnListenerLockLevel( 0 ) + ,mnWritingMode( WritingMode2::CONTEXT ) + ,mnContextWritingMode( WritingMode2::CONTEXT ) +{ +} + +void VCLXWindowImpl::disposing() +{ + SolarMutexGuard aGuard; + + assert(!mbDisposed); + + mbDisposed = true; + + if ( mnCallbackEventId ) + { + Application::RemoveUserEvent( mnCallbackEventId ); + mnCallbackEventId = nullptr; + // we acquired our VCLXWindow once before posting the event, release this one ref now + mrAntiImpl.release(); + } + maCallbackEvents.clear(); + + css::lang::EventObject aEvent; + aEvent.Source = mrAntiImpl; + + maDockableWindowListeners.disposeAndClear( aEvent ); + maEventListeners.disposeAndClear( aEvent ); + maFocusListeners.disposeAndClear( aEvent ); + maWindowListeners.disposeAndClear( aEvent ); + maKeyListeners.disposeAndClear( aEvent ); + maMouseListeners.disposeAndClear( aEvent ); + maMouseMotionListeners.disposeAndClear( aEvent ); + maPaintListeners.disposeAndClear( aEvent ); + maContainerListeners.disposeAndClear( aEvent ); + maTopWindowListeners.disposeAndClear( aEvent ); + maWindow2Listeners.disposeAndClear( aEvent ); + + if ( mxWindowStyleSettings ) + mxWindowStyleSettings->dispose(); + mxWindowStyleSettings.clear(); +} + + +void VCLXWindowImpl::callBackAsync( const VCLXWindow::Callback& i_callback ) +{ + DBG_TESTSOLARMUTEX(); + maCallbackEvents.push_back( i_callback ); + if ( !mnCallbackEventId ) + { + // ensure our VCLXWindow is not destroyed while the event is underway + mrAntiImpl.acquire(); + mnCallbackEventId = Application::PostUserEvent( LINK( this, VCLXWindowImpl, OnProcessCallbacks ) ); + } +} + + +IMPL_LINK_NOARG(VCLXWindowImpl, OnProcessCallbacks, void*, void) +{ + const Reference< uno::XInterface > xKeepAlive( mrAntiImpl ); + + SAL_INFO("toolkit.controls", "OnProcessCallbacks grabbing solarmutex"); + + // work on a copy of the callback array + CallbackArray aCallbacksCopy; + { + SolarMutexGuard aGuard; + aCallbacksCopy.swap(maCallbackEvents); + + // we acquired our VCLXWindow once before posting the event, release this one ref now + mrAntiImpl.release(); + + assert( mnCallbackEventId && "should not be possible to call us if the event was removed"); + + mnCallbackEventId = nullptr; + } + + { + SAL_INFO("toolkit.controls", "OnProcessCallbacks relinquished solarmutex"); + SolarMutexReleaser aReleaseSolar; + for (const auto& rCallback : aCallbacksCopy) + { + rCallback(); + } + } +} + +Reference< XStyleSettings > VCLXWindowImpl::getStyleSettings() +{ + SolarMutexGuard aGuard; + if ( mbDisposed ) + throw DisposedException( OUString(), mrAntiImpl ); + if ( !mxWindowStyleSettings.is() ) + mxWindowStyleSettings = new ::toolkit::WindowStyleSettings( maListenerContainerMutex, mrAntiImpl ); + return mxWindowStyleSettings; +} + + +// Uses an out-parameter instead of return value, due to the object reference + +static void ImplInitWindowEvent( css::awt::WindowEvent& rEvent, vcl::Window const * pWindow ) +{ + Point aPos = pWindow->GetPosPixel(); + Size aSz = pWindow->GetSizePixel(); + + rEvent.X = aPos.X(); + rEvent.Y = aPos.Y(); + + rEvent.Width = aSz.Width(); + rEvent.Height = aSz.Height(); + + pWindow->GetBorder( rEvent.LeftInset, rEvent.TopInset, rEvent.RightInset, rEvent.BottomInset ); +} + +VCLXWindow::VCLXWindow( bool _bWithDefaultProps ) +{ + mpImpl.reset( new VCLXWindowImpl( *this, _bWithDefaultProps ) ); +} + +VCLXWindow::~VCLXWindow() +{ + assert(mpImpl->mbDisposing && "forgot to call dispose()"); +} + + +void VCLXWindow::ImplExecuteAsyncWithoutSolarLock( const Callback& i_callback ) +{ + if (mpImpl->mbDisposing) + return; + mpImpl->callBackAsync( i_callback ); +} + + +::toolkit::IAccessibleFactory& VCLXWindow::getAccessibleFactory() +{ + return mpImpl->getAccessibleFactory().getFactory(); +} + +void VCLXWindow::SetWindow( const VclPtr<vcl::Window> &pWindow ) +{ + assert(!mpImpl->mbDisposing || !pWindow); + + if ( GetWindow() ) + { + GetWindow()->RemoveEventListener( LINK( this, VCLXWindow, WindowEventListener ) ); +// GetWindow()->DbgAssertNoEventListeners(); + } + + SetOutputDevice( pWindow ? pWindow->GetOutDev() : nullptr ); + + if ( GetWindow() ) + { + GetWindow()->AddEventListener( LINK( this, VCLXWindow, WindowEventListener ) ); + bool bDirectVisible = pWindow && pWindow->IsVisible(); + mpImpl->setDirectVisible( bDirectVisible ); + } +} + +void VCLXWindow::suspendVclEventListening( ) +{ + ++mpImpl->mnListenerLockLevel; +} + +void VCLXWindow::resumeVclEventListening( ) +{ + DBG_ASSERT( mpImpl->mnListenerLockLevel, "VCLXWindow::resumeVclEventListening: not suspended!" ); + --mpImpl->mnListenerLockLevel; +} + +void VCLXWindow::notifyWindowRemoved( vcl::Window const & _rWindow ) +{ + if ( mpImpl->getContainerListeners().getLength() ) + { + awt::VclContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Child = static_cast< XWindow* >( _rWindow.GetWindowPeer() ); + mpImpl->getContainerListeners().windowRemoved( aEvent ); + } +} + +IMPL_LINK( VCLXWindow, WindowEventListener, VclWindowEvent&, rEvent, void ) +{ + if ( mpImpl->mbDisposing || mpImpl->mnListenerLockLevel ) + return; + + DBG_ASSERT( rEvent.GetWindow() && GetWindow(), "Window???" ); + ProcessWindowEvent( rEvent ); +} + +namespace +{ + struct CallWindow2Listener + { + CallWindow2Listener( ::comphelper::OInterfaceContainerHelper3<css::awt::XWindowListener2>& i_rWindow2Listeners, const bool i_bEnabled, EventObject i_Event ) + :m_rWindow2Listeners( i_rWindow2Listeners ) + ,m_bEnabled( i_bEnabled ) + ,m_aEvent(std::move( i_Event )) + { + } + + void operator()() + { + m_rWindow2Listeners.notifyEach( m_bEnabled ? &XWindowListener2::windowEnabled : &XWindowListener2::windowDisabled, m_aEvent ); + } + + ::comphelper::OInterfaceContainerHelper3<css::awt::XWindowListener2>& m_rWindow2Listeners; + const bool m_bEnabled; + const EventObject m_aEvent; + }; +} + +void VCLXWindow::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + if (mpImpl->mbDisposing) + return; + css::uno::Reference< css::uno::XInterface > xThis( getXWeak() ); + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::WindowEnabled: + case VclEventId::WindowDisabled: + { + Callback aCallback = CallWindow2Listener( + mpImpl->getWindow2Listeners(), + ( VclEventId::WindowEnabled == rVclWindowEvent.GetId() ), + EventObject( *this ) + ); + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + break; + + case VclEventId::WindowPaint: + { + if ( mpImpl->getPaintListeners().getLength() ) + { + css::awt::PaintEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.UpdateRect = AWTRectangle( *static_cast<tools::Rectangle*>(rVclWindowEvent.GetData()) ); + aEvent.Count = 0; + mpImpl->getPaintListeners().windowPaint( aEvent ); + } + } + break; + case VclEventId::WindowMove: + { + if ( mpImpl->getWindowListeners().getLength() ) + { + css::awt::WindowEvent aEvent; + aEvent.Source = getXWeak(); + ImplInitWindowEvent( aEvent, rVclWindowEvent.GetWindow() ); + mpImpl->getWindowListeners().windowMoved( aEvent ); + } + } + break; + case VclEventId::WindowResize: + { + if ( mpImpl->getWindowListeners().getLength() ) + { + css::awt::WindowEvent aEvent; + aEvent.Source = getXWeak(); + ImplInitWindowEvent( aEvent, rVclWindowEvent.GetWindow() ); + mpImpl->getWindowListeners().windowResized( aEvent ); + } + } + break; + case VclEventId::WindowShow: + { + if ( mpImpl->getWindowListeners().getLength() ) + { + css::awt::WindowEvent aEvent; + aEvent.Source = getXWeak(); + ImplInitWindowEvent( aEvent, rVclWindowEvent.GetWindow() ); + mpImpl->getWindowListeners().windowShown( aEvent ); + } + + // For TopWindows this means opened... + if ( mpImpl->getTopWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getTopWindowListeners().windowOpened( aEvent ); + } + } + break; + case VclEventId::WindowHide: + { + if ( mpImpl->getWindowListeners().getLength() ) + { + css::awt::WindowEvent aEvent; + aEvent.Source = getXWeak(); + ImplInitWindowEvent( aEvent, rVclWindowEvent.GetWindow() ); + mpImpl->getWindowListeners().windowHidden( aEvent ); + } + + // For TopWindows this means closed... + if ( mpImpl->getTopWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getTopWindowListeners().windowClosed( aEvent ); + } + } + break; + case VclEventId::WindowActivate: + case VclEventId::WindowDeactivate: + { + if (!mpImpl->getTopWindowListeners().getLength()) + return; + + // Suppress events which are unlikely to be interesting to our listeners. + vcl::Window* pWin = static_cast<vcl::Window*>(rVclWindowEvent.GetData()); + bool bSuppress = false; + + while (pWin) + { + // Either the event came from the same window, from its + // child, or from a child of its border window (e.g. + // menubar or notebookbar). + if (pWin->GetWindow(GetWindowType::Client) == GetWindow()) + return; + + if (pWin->IsMenuFloatingWindow()) + bSuppress = true; + + if (pWin->GetType() == WindowType::FLOATINGWINDOW && + static_cast<FloatingWindow*>(pWin)->IsInPopupMode()) + bSuppress = true; + + // Otherwise, don't suppress if the event came from a different frame. + if (!bSuppress && pWin->GetWindow(GetWindowType::Frame) == pWin) + break; + + pWin = pWin->GetWindow(GetWindowType::RealParent); + } + + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + if (rVclWindowEvent.GetId() == VclEventId::WindowActivate) + mpImpl->getTopWindowListeners().windowActivated( aEvent ); + else + mpImpl->getTopWindowListeners().windowDeactivated( aEvent ); + } + break; + case VclEventId::WindowClose: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getDockableWindowListeners().notifyEach( &XDockableWindowListener::closed, aEvent ); + } + if ( mpImpl->getTopWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getTopWindowListeners().windowClosing( aEvent ); + } + } + break; + case VclEventId::ControlGetFocus: + case VclEventId::WindowGetFocus: + { + if ( ( rVclWindowEvent.GetWindow()->IsCompoundControl() + && rVclWindowEvent.GetId() == VclEventId::ControlGetFocus + ) + || ( !rVclWindowEvent.GetWindow()->IsCompoundControl() + && rVclWindowEvent.GetId() == VclEventId::WindowGetFocus + ) + ) + { + if ( mpImpl->getFocusListeners().getLength() ) + { + css::awt::FocusEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.FocusFlags = static_cast<sal_Int16>(rVclWindowEvent.GetWindow()->GetGetFocusFlags()); + aEvent.Temporary = false; + mpImpl->getFocusListeners().focusGained( aEvent ); + } + } + } + break; + case VclEventId::ControlLoseFocus: + case VclEventId::WindowLoseFocus: + { + if ( ( rVclWindowEvent.GetWindow()->IsCompoundControl() + && rVclWindowEvent.GetId() == VclEventId::ControlLoseFocus + ) + || ( !rVclWindowEvent.GetWindow()->IsCompoundControl() + && rVclWindowEvent.GetId() == VclEventId::WindowLoseFocus + ) + ) + { + if ( mpImpl->getFocusListeners().getLength() ) + { + css::awt::FocusEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.FocusFlags = static_cast<sal_Int16>(rVclWindowEvent.GetWindow()->GetGetFocusFlags()); + aEvent.Temporary = false; + + vcl::Window* pNext = Application::GetFocusWindow(); + if ( pNext ) + { + // Don't care about internals if this control is compound + vcl::Window* pNextC = pNext; + while ( pNextC && !pNextC->IsCompoundControl() ) + pNextC = pNextC->GetParent(); + if ( pNextC ) + pNext = pNextC; + + pNext->GetComponentInterface(); + aEvent.NextFocus = cppu::getXWeak(pNext->GetWindowPeer()); + } + mpImpl->getFocusListeners().focusLost( aEvent ); + } + } + } + break; + case VclEventId::WindowMinimize: + { + if ( mpImpl->getTopWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getTopWindowListeners().windowMinimized( aEvent ); + } + } + break; + case VclEventId::WindowNormalize: + { + if ( mpImpl->getTopWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getTopWindowListeners().windowNormalized( aEvent ); + } + } + break; + case VclEventId::WindowKeyInput: + { + if ( mpImpl->getKeyListeners().getLength() ) + { + css::awt::KeyEvent aEvent( VCLUnoHelper::createKeyEvent( + *static_cast<KeyEvent*>(rVclWindowEvent.GetData()), *this + ) ); + mpImpl->getKeyListeners().keyPressed( aEvent ); + } + } + break; + case VclEventId::WindowKeyUp: + { + if ( mpImpl->getKeyListeners().getLength() ) + { + css::awt::KeyEvent aEvent( VCLUnoHelper::createKeyEvent( + *static_cast<KeyEvent*>(rVclWindowEvent.GetData()), *this + ) ); + mpImpl->getKeyListeners().keyReleased( aEvent ); + } + } + break; + case VclEventId::WindowCommand: + { + CommandEvent* pCmdEvt = static_cast<CommandEvent*>(rVclWindowEvent.GetData()); + if ( mpImpl->getMouseListeners().getLength() && ( pCmdEvt->GetCommand() == CommandEventId::ContextMenu ) ) + { + // CommandEventId::ContextMenu: send as mousePressed with PopupTrigger = true ... + Point aWhere = static_cast< CommandEvent* >( rVclWindowEvent.GetData() )->GetMousePosPixel(); + if ( !pCmdEvt->IsMouseEvent() ) + { // for keyboard events, we set the coordinates to -1,-1. This is a slight HACK, but the current API + // handles a context menu command as special case of a mouse event, which is simply wrong. + // Without extending the API, we would not have another chance to notify listeners of a + // keyboard-triggered context menu request + aWhere = Point( -1, -1 ); + } + + MouseEvent aMEvt( aWhere, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT, 0 ); + awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( aMEvt, *this ) ); + aEvent.PopupTrigger = true; + + Callback aCallback = [ this, aEvent ]() + { this->mpImpl->getMouseListeners().mousePressed( aEvent ); }; + + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + } + break; + case VclEventId::WindowMouseMove: + { + MouseEvent* pMouseEvt = static_cast<MouseEvent*>(rVclWindowEvent.GetData()); + if ( mpImpl->getMouseListeners().getLength() && ( pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow() ) ) + { + awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( *pMouseEvt, *this ) ); + bool const isEnter(pMouseEvt->IsEnterWindow()); + Callback aCallback = [ this, isEnter, aEvent ]() + { MouseListenerMultiplexer& rMouseListeners = this->mpImpl->getMouseListeners(); + isEnter + ? rMouseListeners.mouseEntered(aEvent) + : rMouseListeners.mouseExited(aEvent); }; + + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + + if ( mpImpl->getMouseMotionListeners().getLength() && !pMouseEvt->IsEnterWindow() && !pMouseEvt->IsLeaveWindow() ) + { + awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( *pMouseEvt, *this ) ); + aEvent.ClickCount = 0; + if ( pMouseEvt->GetMode() & MouseEventModifiers::SIMPLEMOVE ) + mpImpl->getMouseMotionListeners().mouseMoved( aEvent ); + else + mpImpl->getMouseMotionListeners().mouseDragged( aEvent ); + } + } + break; + case VclEventId::WindowMouseButtonDown: + { + if ( mpImpl->getMouseListeners().getLength() ) + { + awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( *static_cast<MouseEvent*>(rVclWindowEvent.GetData()), *this ) ); + Callback aCallback = [ this, aEvent ]() + { this->mpImpl->getMouseListeners().mousePressed( aEvent ); }; + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + } + break; + case VclEventId::WindowMouseButtonUp: + { + if ( mpImpl->getMouseListeners().getLength() ) + { + awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( *static_cast<MouseEvent*>(rVclWindowEvent.GetData()), *this ) ); + + Callback aCallback = [ this, aEvent ]() + { this->mpImpl->getMouseListeners().mouseReleased( aEvent ); }; + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + } + break; + case VclEventId::WindowStartDocking: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + DockingData *pData = static_cast<DockingData*>(rVclWindowEvent.GetData()); + + if( pData ) + { + css::awt::DockingEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.TrackingRectangle = AWTRectangle( pData->maTrackRect ); + aEvent.MousePos.X = pData->maMousePos.X(); + aEvent.MousePos.Y = pData->maMousePos.Y(); + aEvent.bLiveMode = false; + aEvent.bInteractive = true; + + mpImpl->getDockableWindowListeners().notifyEach( &XDockableWindowListener::startDocking, aEvent ); + } + } + } + break; + case VclEventId::WindowDocking: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + DockingData *pData = static_cast<DockingData*>(rVclWindowEvent.GetData()); + + if( pData ) + { + css::awt::DockingEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.TrackingRectangle = AWTRectangle( pData->maTrackRect ); + aEvent.MousePos.X = pData->maMousePos.X(); + aEvent.MousePos.Y = pData->maMousePos.Y(); + aEvent.bLiveMode = false; + aEvent.bInteractive = true; + + Reference< XDockableWindowListener > xFirstListener; + ::comphelper::OInterfaceIteratorHelper3 aIter( mpImpl->getDockableWindowListeners() ); + while ( aIter.hasMoreElements() && !xFirstListener.is() ) + { + xFirstListener = aIter.next(); + } + + css::awt::DockingData aDockingData = + xFirstListener->docking( aEvent ); + pData->maTrackRect = VCLRectangle( aDockingData.TrackingRectangle ); + pData->mbFloating = aDockingData.bFloating; + } + } + } + break; + case VclEventId::WindowEndDocking: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + EndDockingData *pData = static_cast<EndDockingData*>(rVclWindowEvent.GetData()); + + if( pData ) + { + css::awt::EndDockingEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.WindowRectangle = AWTRectangle( pData->maWindowRect ); + aEvent.bFloating = pData->mbFloating; + aEvent.bCancelled = pData->mbCancelled; + mpImpl->getDockableWindowListeners().notifyEach( &XDockableWindowListener::endDocking, aEvent ); + } + } + } + break; + case VclEventId::WindowPrepareToggleFloating: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + sal_Bool *p_bFloating = static_cast<sal_Bool*>(rVclWindowEvent.GetData()); + + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + + Reference< XDockableWindowListener > xFirstListener; + ::comphelper::OInterfaceIteratorHelper3 aIter( mpImpl->getDockableWindowListeners() ); + while ( aIter.hasMoreElements() && !xFirstListener.is() ) + { + xFirstListener = aIter.next(); + } + + *p_bFloating = xFirstListener->prepareToggleFloatingMode( aEvent ); + } + } + break; + case VclEventId::WindowToggleFloating: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + css::lang::EventObject aEvent; + aEvent.Source = getXWeak(); + mpImpl->getDockableWindowListeners().notifyEach( &XDockableWindowListener::toggleFloatingMode, aEvent ); + } + } + break; + case VclEventId::WindowEndPopupMode: + { + if ( mpImpl->getDockableWindowListeners().getLength() ) + { + EndPopupModeData *pData = static_cast<EndPopupModeData*>(rVclWindowEvent.GetData()); + + if( pData ) + { + css::awt::EndPopupModeEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.FloatingPosition.X = pData->maFloatingPos.X(); + aEvent.FloatingPosition.Y = pData->maFloatingPos.Y(); + aEvent.bTearoff = pData->mbTearoff; + mpImpl->getDockableWindowListeners().notifyEach( &XDockableWindowListener::endPopupMode, aEvent ); + } + } + } + break; + default: break; + } +} + +uno::Reference< accessibility::XAccessibleContext > VCLXWindow::CreateAccessibleContext() +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return nullptr; + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXWindow::SetSynthesizingVCLEvent( bool _b ) +{ + mpImpl->mbSynthesizingVCLEvent = _b; +} + +bool VCLXWindow::IsSynthesizingVCLEvent() const +{ + return mpImpl->mbSynthesizingVCLEvent; +} + +Size VCLXWindow::ImplCalcWindowSize( const Size& rOutSz ) const +{ + Size aSz = rOutSz; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + sal_Int32 nLeft, nTop, nRight, nBottom; + pWindow->GetBorder( nLeft, nTop, nRight, nBottom ); + aSz.AdjustWidth(nLeft+nRight ); + aSz.AdjustHeight(nTop+nBottom ); + } + return aSz; +} + + +// css::lang::Component +void VCLXWindow::dispose( ) +{ + SolarMutexGuard aGuard; + + if ( mpImpl->mbDisposing ) + return; + + mpImpl->mbDisposing = true; + + mpImpl->mxViewGraphics = nullptr; + + mpImpl->disposing(); + + if ( VclPtr<vcl::Window> pWindow = GetWindow() ) + { + pWindow->RemoveEventListener( LINK( this, VCLXWindow, WindowEventListener ) ); + pWindow->SetWindowPeer( nullptr, nullptr ); + pWindow->SetAccessible( nullptr ); + + SetOutputDevice( nullptr ); + pWindow.disposeAndClear(); + } + + // #i14103# dispose the accessible context after the window has been destroyed, + // otherwise the old value in the child event fired in VCLXAccessibleComponent::ProcessWindowEvent() + // for VclEventId::WindowChildDestroyed contains a reference to an already disposed accessible object + try + { + css::uno::Reference< css::lang::XComponent > xComponent( mpImpl->mxAccessibleContext, css::uno::UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch ( const css::uno::Exception& ) + { + OSL_FAIL( "VCLXWindow::dispose: could not dispose the accessible context!" ); + } + mpImpl->mxAccessibleContext.clear(); +} + +void VCLXWindow::addEventListener( const css::uno::Reference< css::lang::XEventListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) // called during dispose by accessibility stuff + return; + mpImpl->getEventListeners().addInterface( rxListener ); +} + +void VCLXWindow::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getEventListeners().removeInterface( rxListener ); +} + + +// css::awt::XWindow +void VCLXWindow::setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) +{ + SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("setPosSize"); + + if ( GetWindow() ) + { + if( vcl::Window::GetDockingManager()->IsDockable( GetWindow() ) ) + vcl::Window::GetDockingManager()->SetPosSizePixel( GetWindow() , X, Y, Width, Height, static_cast<PosSizeFlags>(Flags) ); + else + GetWindow()->setPosSizePixel( X, Y, Width, Height, static_cast<PosSizeFlags>(Flags) ); + } +} + +css::awt::Rectangle VCLXWindow::getPosSize( ) +{ + SolarMutexGuard aGuard; + + css::awt::Rectangle aBounds; + if ( GetWindow() ) + { + if( vcl::Window::GetDockingManager()->IsDockable( GetWindow() ) ) + aBounds = AWTRectangle( vcl::Window::GetDockingManager()->GetPosSizePixel( GetWindow() ) ); + else + aBounds = AWTRectangle( tools::Rectangle( GetWindow()->GetPosPixel(), GetWindow()->GetSizePixel() ) ); + } + + return aBounds; +} + +void VCLXWindow::setVisible( sal_Bool bVisible ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + mpImpl->setDirectVisible( bVisible ); + pWindow->Show( bVisible && mpImpl->isEnableVisible() ); + } +} + +void VCLXWindow::setEnable( sal_Bool bEnable ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->Enable( bEnable, false ); // #95824# without children! + pWindow->EnableInput( bEnable ); + } +} + +void VCLXWindow::setFocus( ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + GetWindow()->GrabFocus(); +} + +void VCLXWindow::addWindowListener( const css::uno::Reference< css::awt::XWindowListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + + mpImpl->getWindowListeners().addInterface( rxListener ); + + Reference< XWindowListener2 > xListener2( rxListener, UNO_QUERY ); + if ( xListener2.is() ) + mpImpl->getWindow2Listeners().addInterface( xListener2 ); + + // #100119# Get all resize events, even if height or width 0, or invisible + if ( GetWindow() ) + GetWindow()->EnableAllResize(); +} + +void VCLXWindow::removeWindowListener( const css::uno::Reference< css::awt::XWindowListener >& rxListener ) +{ + SolarMutexGuard aGuard; + + if (mpImpl->mbDisposing) + return; + + Reference< XWindowListener2 > xListener2( rxListener, UNO_QUERY ); + if ( xListener2.is() ) + mpImpl->getWindow2Listeners().removeInterface( xListener2 ); + + mpImpl->getWindowListeners().removeInterface( rxListener ); +} + +void VCLXWindow::addFocusListener( const css::uno::Reference< css::awt::XFocusListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getFocusListeners().addInterface( rxListener ); +} + +void VCLXWindow::removeFocusListener( const css::uno::Reference< css::awt::XFocusListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getFocusListeners().removeInterface( rxListener ); +} + +void VCLXWindow::addKeyListener( const css::uno::Reference< css::awt::XKeyListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getKeyListeners().addInterface( rxListener ); +} + +void VCLXWindow::removeKeyListener( const css::uno::Reference< css::awt::XKeyListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getKeyListeners().removeInterface( rxListener ); +} + +void VCLXWindow::addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getMouseListeners().addInterface( rxListener ); +} + +void VCLXWindow::removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getMouseListeners().removeInterface( rxListener ); +} + +void VCLXWindow::addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getMouseMotionListeners().addInterface( rxListener ); +} + +void VCLXWindow::removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getMouseMotionListeners().removeInterface( rxListener ); +} + +void VCLXWindow::addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getPaintListeners().addInterface( rxListener ); +} + +void VCLXWindow::removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (mpImpl->mbDisposing) + return; + mpImpl->getPaintListeners().removeInterface( rxListener ); +} + +// css::awt::XWindowPeer +css::uno::Reference< css::awt::XToolkit > VCLXWindow::getToolkit( ) +{ + // no guard. nothing to guard here. + // 82463 - 12/21/00 - fs + return Application::GetVCLToolkit(); +} + +void VCLXWindow::setPointer( const css::uno::Reference< css::awt::XPointer >& rxPointer ) +{ + SolarMutexGuard aGuard; + + VCLXPointer* pPointer = dynamic_cast<VCLXPointer*>( rxPointer.get() ); + if ( pPointer && GetWindow() ) + GetWindow()->SetPointer( pPointer->GetPointer() ); +} + +void VCLXWindow::setBackground( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + if ( !GetWindow() ) + return; + + Color aColor(ColorTransparency, nColor); + GetWindow()->SetBackground( aColor ); + GetWindow()->SetControlBackground( aColor ); + + WindowType eWinType = GetWindow()->GetType(); + if ( ( eWinType == WindowType::WINDOW ) || + ( eWinType == WindowType::WORKWINDOW ) || + ( eWinType == WindowType::FLOATINGWINDOW ) ) + { + GetWindow()->Invalidate(); + } +} + +void VCLXWindow::invalidate( sal_Int16 nInvalidateFlags ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + GetWindow()->Invalidate( static_cast<InvalidateFlags>(nInvalidateFlags) ); +} + +void VCLXWindow::invalidateRect( const css::awt::Rectangle& rRect, sal_Int16 nInvalidateFlags ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + GetWindow()->Invalidate( VCLRectangle(rRect), static_cast<InvalidateFlags>(nInvalidateFlags) ); +} + + +// css::awt::XVclWindowPeer +sal_Bool VCLXWindow::isChild( const css::uno::Reference< css::awt::XWindowPeer >& rxPeer ) +{ + SolarMutexGuard aGuard; + + bool bIsChild = false; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + VclPtr<vcl::Window> pPeerWindow = VCLUnoHelper::GetWindow( rxPeer ); + bIsChild = pPeerWindow && pWindow->IsChild( pPeerWindow ); + } + + return bIsChild; +} + +void VCLXWindow::setDesignMode( sal_Bool bOn ) +{ + SolarMutexGuard aGuard; + + mpImpl->mbDesignMode = bOn; +} + +sal_Bool VCLXWindow::isDesignMode( ) +{ + SolarMutexGuard aGuard; + return mpImpl->mbDesignMode; +} + +void VCLXWindow::enableClipSiblings( sal_Bool bClip ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + GetWindow()->EnableClipSiblings( bClip ); +} + +void VCLXWindow::setForeground( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + { + GetWindow()->SetControlForeground( Color(ColorTransparency, nColor) ); + } +} + +void VCLXWindow::setControlFont( const css::awt::FontDescriptor& rFont ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + GetWindow()->SetControlFont( VCLUnoHelper::CreateFont( rFont, GetWindow()->GetControlFont() ) ); +} + +void VCLXWindow::getStyles( sal_Int16 nType, css::awt::FontDescriptor& Font, sal_Int32& ForegroundColor, sal_Int32& BackgroundColor ) +{ + SolarMutexGuard aGuard; + + if ( !GetWindow() ) + return; + + const StyleSettings& rStyleSettings = GetWindow()->GetSettings().GetStyleSettings(); + + switch ( nType ) + { + case css::awt::Style::FRAME: + { + Font = VCLUnoHelper::CreateFontDescriptor( rStyleSettings.GetAppFont() ); + ForegroundColor = sal_Int32(rStyleSettings.GetWindowTextColor()); + BackgroundColor = sal_Int32(rStyleSettings.GetWindowColor()); + } + break; + case css::awt::Style::DIALOG: + { + Font = VCLUnoHelper::CreateFontDescriptor( rStyleSettings.GetAppFont() ); + ForegroundColor = sal_Int32(rStyleSettings.GetDialogTextColor()); + BackgroundColor = sal_Int32(rStyleSettings.GetDialogColor()); + } + break; + default: OSL_FAIL( "VCLWindow::getStyles() - unknown Type" ); + } +} + +namespace toolkit +{ + static void setColorSettings( vcl::Window* _pWindow, const css::uno::Any& _rValue, + void (StyleSettings::*pSetter)( const Color& ), const Color& (StyleSettings::*pGetter)( ) const ) + { + sal_Int32 nColor = 0; + if ( !( _rValue >>= nColor ) ) + nColor = sal_Int32((Application::GetSettings().GetStyleSettings().*pGetter)()); + + AllSettings aSettings = _pWindow->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + + (aStyleSettings.*pSetter)( Color( ColorTransparency, nColor ) ); + + aSettings.SetStyleSettings( aStyleSettings ); + _pWindow->SetSettings( aSettings, true ); + } +} + +// Terminated by BASEPROPERTY_NOTFOUND (or 0) +void VCLXWindow::PushPropertyIds( std::vector< sal_uInt16 > &rIds, + int nFirstId, ...) +{ + va_list pVarArgs; + va_start( pVarArgs, nFirstId ); + + for ( int nId = nFirstId; nId != BASEPROPERTY_NOTFOUND; + nId = va_arg( pVarArgs, int ) ) + rIds.push_back( static_cast<sal_uInt16>(nId) ); + + va_end( pVarArgs ); +} + +void VCLXWindow::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds, bool bWithDefaults ) +{ + // These are common across ~all VCLXWindow derived classes + if( bWithDefaults ) + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_TEXT, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_ENABLEVISIBLE, // for visibility + BASEPROPERTY_TABSTOP, + 0); + + // lovely hack from: + // void UnoControlModel::ImplRegisterProperty( sal_uInt16 nPropId ) + if( std::find(rIds.begin(), rIds.end(), BASEPROPERTY_FONTDESCRIPTOR) != rIds.end() ) + { + // some properties are not included in the FontDescriptor, but every time + // when we have a FontDescriptor we want to have these properties too. + // => Easier to register the here, instead everywhere where I register the FontDescriptor... + + rIds.push_back( BASEPROPERTY_TEXTCOLOR ); + rIds.push_back( BASEPROPERTY_TEXTLINECOLOR ); + rIds.push_back( BASEPROPERTY_FONTRELIEF ); + rIds.push_back( BASEPROPERTY_FONTEMPHASISMARK ); + } +} + +void VCLXWindow::GetPropertyIds( std::vector< sal_uInt16 >& _out_rIds ) +{ + return ImplGetPropertyIds( _out_rIds, mpImpl->mbWithDefaultProps ); +} + +ListenerMultiplexerBase<css::awt::XVclContainerListener>& VCLXWindow::GetContainerListeners() +{ + return mpImpl->getContainerListeners(); +} + +ListenerMultiplexerBase<css::awt::XTopWindowListener>& VCLXWindow::GetTopWindowListeners() +{ + return mpImpl->getTopWindowListeners(); +} + +namespace +{ + void lcl_updateWritingMode( vcl::Window& _rWindow, const sal_Int16 _nWritingMode, const sal_Int16 _nContextWritingMode ) + { + bool bEnableRTL = false; + switch ( _nWritingMode ) + { + case WritingMode2::LR_TB: bEnableRTL = false; break; + case WritingMode2::RL_TB: bEnableRTL = true; break; + case WritingMode2::CONTEXT: + { + // consult our ContextWritingMode. If it has an explicit RTL/LTR value, then use + // it. If it doesn't (but is CONTEXT itself), then just ask the parent window of our + // own window for its RTL mode + switch ( _nContextWritingMode ) + { + case WritingMode2::LR_TB: bEnableRTL = false; break; + case WritingMode2::RL_TB: bEnableRTL = true; break; + case WritingMode2::CONTEXT: + { + const vcl::Window* pParent = _rWindow.GetParent(); + OSL_ENSURE( pParent, "lcl_updateWritingMode: cannot determine context's writing mode!" ); + if ( pParent ) + bEnableRTL = pParent->IsRTLEnabled(); + } + break; + } + } + break; + default: + OSL_FAIL( "lcl_updateWritingMode: unsupported WritingMode!" ); + } // switch ( nWritingMode ) + + _rWindow.EnableRTL( bEnableRTL ); + } +} + +void VCLXWindow::setProperty( const OUString& PropertyName, const css::uno::Any& Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( !pWindow ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + WindowType eWinType = pWindow->GetType(); + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_REFERENCE_DEVICE: + { + Control* pControl = dynamic_cast< Control* >( pWindow.get() ); + OSL_ENSURE( pControl, "VCLXWindow::setProperty( RefDevice ): need a Control for this!" ); + if ( !pControl ) + break; + Reference< XDevice > xDevice( Value, UNO_QUERY ); + OutputDevice* pDevice = VCLUnoHelper::GetOutputDevice( xDevice ); + pControl->SetReferenceDevice( pDevice ); + } + break; + + case BASEPROPERTY_CONTEXT_WRITING_MODE: + { + OSL_VERIFY( Value >>= mpImpl->mnContextWritingMode ); + if ( mpImpl->mnWritingMode == WritingMode2::CONTEXT ) + lcl_updateWritingMode( *pWindow, mpImpl->mnWritingMode, mpImpl->mnContextWritingMode ); + } + break; + + case BASEPROPERTY_WRITING_MODE: + { + bool bProperType = ( Value >>= mpImpl->mnWritingMode ); + OSL_ENSURE( bProperType, "VCLXWindow::setProperty( 'WritingMode' ): illegal value type!" ); + if ( bProperType ) + lcl_updateWritingMode( *pWindow, mpImpl->mnWritingMode, mpImpl->mnContextWritingMode ); + } + break; + + case BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR: + { + sal_uInt16 nWheelBehavior( css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY ); + OSL_VERIFY( Value >>= nWheelBehavior ); + + AllSettings aSettings = pWindow->GetSettings(); + MouseSettings aMouseSettings = aSettings.GetMouseSettings(); + + MouseWheelBehaviour nVclBehavior( MouseWheelBehaviour::FocusOnly ); + switch ( nWheelBehavior ) + { + case css::awt::MouseWheelBehavior::SCROLL_DISABLED: nVclBehavior = MouseWheelBehaviour::Disable; break; + case css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY: nVclBehavior = MouseWheelBehaviour::FocusOnly; break; + case css::awt::MouseWheelBehavior::SCROLL_ALWAYS: nVclBehavior = MouseWheelBehaviour::ALWAYS; break; + default: + OSL_FAIL( "VCLXWindow::setProperty( 'MouseWheelBehavior' ): illegal property value!" ); + } + + aMouseSettings.SetWheelBehavior( nVclBehavior ); + aSettings.SetMouseSettings( aMouseSettings ); + pWindow->SetSettings( aSettings, true ); + } + break; + + case BASEPROPERTY_NATIVE_WIDGET_LOOK: + { + bool bEnable( true ); + OSL_VERIFY( Value >>= bEnable ); + pWindow->EnableNativeWidget( bEnable ); + } + break; + + case BASEPROPERTY_PLUGINPARENT: + { + // set parent handle + SetSystemParent_Impl( Value ); + } + break; + + case BASEPROPERTY_ENABLED: + { + bool b = bool(); + if ( Value >>= b ) + setEnable( b ); + } + break; + case BASEPROPERTY_ENABLEVISIBLE: + { + bool b = false; + if ( Value >>= b ) + { + if( b != mpImpl->isEnableVisible() ) + { + mpImpl->setEnableVisible( b ); + pWindow->Show( b && mpImpl->isDirectVisible() ); + } + } + } + break; + case BASEPROPERTY_TEXT: + case BASEPROPERTY_LABEL: + case BASEPROPERTY_TITLE: + { + OUString aText; + if ( Value >>= aText ) + { + switch (eWinType) + { + case WindowType::OKBUTTON: + case WindowType::CANCELBUTTON: + case WindowType::HELPBUTTON: + // Standard Button: overwrite only if not empty. + if (!aText.isEmpty()) + pWindow->SetText( aText ); + break; + + default: + pWindow->SetText( aText ); + break; + } + } + } + break; + case BASEPROPERTY_ACCESSIBLENAME: + { + OUString aText; + if ( Value >>= aText ) + pWindow->SetAccessibleName( aText ); + } + break; + case BASEPROPERTY_HELPURL: + { + OUString aURL; + if ( Value >>= aURL ) + { + INetURLObject aHelpURL( aURL ); + if ( aHelpURL.GetProtocol() == INetProtocol::Hid ) + pWindow->SetHelpId( aHelpURL.GetURLPath() ); + else + pWindow->SetHelpId( aURL ); + } + } + break; + case BASEPROPERTY_HELPTEXT: + { + OUString aHelpText; + if ( Value >>= aHelpText ) + { + pWindow->SetQuickHelpText( aHelpText ); + } + } + break; + case BASEPROPERTY_FONTDESCRIPTOR: + { + if ( bVoid ) + pWindow->SetControlFont( vcl::Font() ); + else + { + css::awt::FontDescriptor aFont; + if ( Value >>= aFont ) + pWindow->SetControlFont( VCLUnoHelper::CreateFont( aFont, pWindow->GetControlFont() ) ); + } + } + break; + case BASEPROPERTY_FONTRELIEF: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + { + vcl::Font aFont = pWindow->GetControlFont(); + aFont.SetRelief( static_cast<FontRelief>(n) ); + pWindow->SetControlFont( aFont ); + } + } + break; + case BASEPROPERTY_FONTEMPHASISMARK: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + { + vcl::Font aFont = pWindow->GetControlFont(); + aFont.SetEmphasisMark( static_cast<FontEmphasisMark>(n) ); + pWindow->SetControlFont( aFont ); + } + } + break; + case BASEPROPERTY_BACKGROUNDCOLOR: + if ( bVoid ) + { + switch ( eWinType ) + { + // set dialog color for default + case WindowType::DIALOG: + case WindowType::MESSBOX: + case WindowType::INFOBOX: + case WindowType::WARNINGBOX: + case WindowType::ERRORBOX: + case WindowType::QUERYBOX: + case WindowType::TABPAGE: + { + Color aColor = pWindow->GetSettings().GetStyleSettings().GetDialogColor(); + pWindow->SetBackground( aColor ); + pWindow->SetControlBackground( aColor ); + break; + } + + case WindowType::FIXEDTEXT: + case WindowType::CHECKBOX: + case WindowType::RADIOBUTTON: + case WindowType::GROUPBOX: + case WindowType::FIXEDLINE: + { + // support transparency only for special controls + pWindow->SetBackground(); + pWindow->SetControlBackground(); + pWindow->SetPaintTransparent( true ); + break; + } + + default: + { + // default code which enables transparency for + // compound controls. It's not real transparency + // as most of these controls repaint their client + // area completely new. + if ( pWindow->IsCompoundControl() ) + pWindow->SetBackground(); + pWindow->SetControlBackground(); + break; + } + } + } + else + { + Color aColor; + if ( Value >>= aColor ) + { + pWindow->SetControlBackground( aColor ); + pWindow->SetBackground( aColor ); + switch ( eWinType ) + { + // reset paint transparent mode + case WindowType::FIXEDTEXT: + case WindowType::CHECKBOX: + case WindowType::RADIOBUTTON: + case WindowType::GROUPBOX: + case WindowType::FIXEDLINE: + pWindow->SetPaintTransparent( false ); + break; + default: + break; + } + pWindow->Invalidate(); // Invalidate if control does not respond to it + } + } + break; + case BASEPROPERTY_TEXTCOLOR: + if ( bVoid ) + { + pWindow->SetControlForeground(); + } + else + { + Color nColor ; + if ( Value >>= nColor ) + { + pWindow->SetTextColor( nColor ); + pWindow->SetControlForeground( nColor ); + } + } + break; + case BASEPROPERTY_TEXTLINECOLOR: + if ( bVoid ) + { + pWindow->SetTextLineColor(); + } + else + { + Color nColor; + if ( Value >>= nColor ) + pWindow->SetTextLineColor( nColor ); + } + break; + case BASEPROPERTY_FILLCOLOR: + if ( bVoid ) + pWindow->GetOutDev()->SetFillColor(); + else + { + Color nColor; + if ( Value >>= nColor ) + pWindow->GetOutDev()->SetFillColor( nColor ); + } + break; + case BASEPROPERTY_LINECOLOR: + if ( bVoid ) + pWindow->GetOutDev()->SetLineColor(); + else + { + Color nColor; + if ( Value >>= nColor ) + pWindow->GetOutDev()->SetLineColor( nColor ); + } + break; + case BASEPROPERTY_HIGHLIGHT_COLOR: + { + Color nColor = 0; + if ( bVoid ) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + + AllSettings aSettings(pWindow->GetSettings()); + StyleSettings aStyle(aSettings.GetStyleSettings()); + aStyle.SetHighlightColor(nColor); + aSettings.SetStyleSettings(aStyle); + pWindow->SetSettings(aSettings); + } + break; + case BASEPROPERTY_HIGHLIGHT_TEXT_COLOR: + { + Color nColor = 0; + if (bVoid) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightTextColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + + AllSettings aSettings(pWindow->GetSettings()); + StyleSettings aStyle(aSettings.GetStyleSettings()); + aStyle.SetHighlightTextColor(nColor); + aSettings.SetStyleSettings(aStyle); + pWindow->SetSettings(aSettings); + } + break; + case BASEPROPERTY_BORDER: + { + WinBits nStyle = pWindow->GetStyle(); + sal_uInt16 nTmp = 0; + Value >>= nTmp; + // clear any dodgy bits passed in, can come from dodgy extensions + nTmp &= o3tl::typed_flags<WindowBorderStyle>::mask; + WindowBorderStyle nBorder = static_cast<WindowBorderStyle>(nTmp); + if ( !bool(nBorder) ) + { + pWindow->SetStyle( nStyle & ~WB_BORDER ); + } + else + { + pWindow->SetStyle( nStyle | WB_BORDER ); + pWindow->SetBorderStyle( nBorder ); + } + } + break; + case BASEPROPERTY_TABSTOP: + { + WinBits nStyle = pWindow->GetStyle() & ~WB_TABSTOP; + if ( !bVoid ) + { + bool bTab = false; + Value >>= bTab; + if ( bTab ) + nStyle |= WB_TABSTOP; + else + nStyle |= WB_NOTABSTOP; + } + pWindow->SetStyle( nStyle ); + } + break; + case BASEPROPERTY_VERTICALALIGN: + { + VerticalAlignment eAlign = css::style::VerticalAlignment::VerticalAlignment_MAKE_FIXED_SIZE; + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_TOP|WB_VCENTER|WB_BOTTOM); + if ( !bVoid ) + Value >>= eAlign; + switch ( eAlign ) + { + case VerticalAlignment_TOP: + nStyle |= WB_TOP; + break; + case VerticalAlignment_MIDDLE: + nStyle |= WB_VCENTER; + break; + case VerticalAlignment_BOTTOM: + nStyle |= WB_BOTTOM; + break; + default: ; // for warning free code, MAKE_FIXED_SIZE + } + pWindow->SetStyle( nStyle ); + } + break; + case BASEPROPERTY_ALIGN: + { + sal_Int16 nAlign = PROPERTY_ALIGN_LEFT; + switch ( eWinType ) + { + case WindowType::COMBOBOX: + case WindowType::PUSHBUTTON: + case WindowType::OKBUTTON: + case WindowType::CANCELBUTTON: + case WindowType::HELPBUTTON: + nAlign = PROPERTY_ALIGN_CENTER; + [[fallthrough]]; + case WindowType::FIXEDTEXT: + case WindowType::EDIT: + case WindowType::MULTILINEEDIT: + case WindowType::CHECKBOX: + case WindowType::RADIOBUTTON: + case WindowType::LISTBOX: + { + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_LEFT|WB_CENTER|WB_RIGHT); + if ( !bVoid ) + Value >>= nAlign; + if ( nAlign == PROPERTY_ALIGN_LEFT ) + nStyle |= WB_LEFT; + else if ( nAlign == PROPERTY_ALIGN_CENTER ) + nStyle |= WB_CENTER; + else + nStyle |= WB_RIGHT; + pWindow->SetStyle( nStyle ); + } + break; + default: break; + } + } + break; + case BASEPROPERTY_MULTILINE: + { + if ( ( eWinType == WindowType::FIXEDTEXT ) + || ( eWinType == WindowType::CHECKBOX ) + || ( eWinType == WindowType::RADIOBUTTON ) + || ( eWinType == WindowType::PUSHBUTTON ) + || ( eWinType == WindowType::OKBUTTON ) + || ( eWinType == WindowType::CANCELBUTTON ) + || ( eWinType == WindowType::HELPBUTTON ) + ) + { + WinBits nStyle = pWindow->GetStyle(); + bool bMulti = false; + Value >>= bMulti; + if ( bMulti ) + nStyle |= WB_WORDBREAK; + else + nStyle &= ~WB_WORDBREAK; + pWindow->SetStyle( nStyle ); + } + } + break; + case BASEPROPERTY_ORIENTATION: + { + if ( eWinType == WindowType::FIXEDLINE) + { + sal_Int32 nOrientation = 0; + if ( Value >>= nOrientation ) + { + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_HORZ|WB_VERT); + if ( nOrientation == 0 ) + nStyle |= WB_HORZ; + else + nStyle |= WB_VERT; + + pWindow->SetStyle( nStyle ); + } + } + } + break; + case BASEPROPERTY_AUTOMNEMONICS: + { + bool bAutoMnemonics = false; + Value >>= bAutoMnemonics; + AllSettings aSettings = pWindow->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + if ( aStyleSettings.GetAutoMnemonic() != bAutoMnemonics ) + { + aStyleSettings.SetAutoMnemonic( bAutoMnemonics ); + aSettings.SetStyleSettings( aStyleSettings ); + pWindow->SetSettings( aSettings ); + } + } + break; + case BASEPROPERTY_MOUSETRANSPARENT: + { + bool bMouseTransparent = false; + Value >>= bMouseTransparent; + pWindow->SetMouseTransparent( bMouseTransparent ); + } + break; + case BASEPROPERTY_PAINTTRANSPARENT: + { + bool bPaintTransparent = false; + Value >>= bPaintTransparent; + pWindow->SetPaintTransparent( bPaintTransparent ); +// pWindow->SetBackground(); + } + break; + + case BASEPROPERTY_REPEAT: + { + bool bRepeat( false ); + Value >>= bRepeat; + + WinBits nStyle = pWindow->GetStyle(); + if ( bRepeat ) + nStyle |= WB_REPEAT; + else + nStyle &= ~WB_REPEAT; + pWindow->SetStyle( nStyle ); + } + break; + + case BASEPROPERTY_REPEAT_DELAY: + { + sal_Int32 nRepeatDelay = 0; + if ( Value >>= nRepeatDelay ) + { + AllSettings aSettings = pWindow->GetSettings(); + MouseSettings aMouseSettings = aSettings.GetMouseSettings(); + + aMouseSettings.SetButtonRepeat( nRepeatDelay ); + aSettings.SetMouseSettings( aMouseSettings ); + + pWindow->SetSettings( aSettings, true ); + } + } + break; + + case BASEPROPERTY_SYMBOL_COLOR: + ::toolkit::setColorSettings( pWindow, Value, &StyleSettings::SetButtonTextColor, &StyleSettings::GetButtonTextColor ); + break; + + case BASEPROPERTY_BORDERCOLOR: + ::toolkit::setColorSettings( pWindow, Value, &StyleSettings::SetMonoColor, &StyleSettings::GetMonoColor); + break; + } +} + +css::uno::Any VCLXWindow::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + if ( GetWindow() ) + { + if (PropertyName == "ParentIs100thmm") + { + bool bParentIs100thmm = false; + VclPtr<vcl::Window> pWindow = GetWindow(); + if (pWindow) + { + pWindow = pWindow->GetParent(); + if(pWindow && MapUnit::Map100thMM == pWindow->GetMapMode().GetMapUnit()) + { + bParentIs100thmm = true; + } + } + aProp <<= bParentIs100thmm; + return aProp; + } + WindowType eWinType = GetWindow()->GetType(); + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_REFERENCE_DEVICE: + { + VclPtr<Control> pControl = GetAsDynamic<Control >(); + OSL_ENSURE( pControl, "VCLXWindow::setProperty( RefDevice ): need a Control for this!" ); + if ( !pControl ) + break; + + rtl::Reference<VCLXDevice> pDevice = new VCLXDevice; + pDevice->SetOutputDevice( pControl->GetReferenceDevice() ); + aProp <<= Reference< XDevice >( pDevice ); + } + break; + + case BASEPROPERTY_CONTEXT_WRITING_MODE: + aProp <<= mpImpl->mnContextWritingMode; + break; + + case BASEPROPERTY_WRITING_MODE: + aProp <<= mpImpl->mnWritingMode; + break; + + case BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR: + { + MouseWheelBehaviour nVclBehavior = GetWindow()->GetSettings().GetMouseSettings().GetWheelBehavior(); + sal_uInt16 nBehavior = css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY; + switch ( nVclBehavior ) + { + case MouseWheelBehaviour::Disable: nBehavior = css::awt::MouseWheelBehavior::SCROLL_DISABLED; break; + case MouseWheelBehaviour::FocusOnly: nBehavior = css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY; break; + case MouseWheelBehaviour::ALWAYS: nBehavior = css::awt::MouseWheelBehavior::SCROLL_ALWAYS; break; + default: + OSL_FAIL( "VCLXWindow::getProperty( 'MouseWheelBehavior' ): illegal VCL value!" ); + } + aProp <<= nBehavior; + } + break; + + case BASEPROPERTY_NATIVE_WIDGET_LOOK: + aProp <<= GetWindow()->IsNativeWidgetEnabled(); + break; + + case BASEPROPERTY_ENABLED: + aProp <<= GetWindow()->IsEnabled(); + break; + + case BASEPROPERTY_ENABLEVISIBLE: + aProp <<= mpImpl->isEnableVisible(); + break; + + case BASEPROPERTY_HIGHCONTRASTMODE: + aProp <<= GetWindow()->GetSettings().GetStyleSettings().GetHighContrastMode(); + break; + + case BASEPROPERTY_TEXT: + case BASEPROPERTY_LABEL: + case BASEPROPERTY_TITLE: + { + OUString aText = GetWindow()->GetText(); + aProp <<= aText; + } + break; + case BASEPROPERTY_ACCESSIBLENAME: + { + OUString aText = GetWindow()->GetAccessibleName(); + aProp <<= aText; + } + break; + case BASEPROPERTY_HELPTEXT: + { + OUString aText = GetWindow()->GetQuickHelpText(); + aProp <<= aText; + } + break; + case BASEPROPERTY_HELPURL: + aProp <<= GetWindow()->GetHelpId(); + break; + case BASEPROPERTY_FONTDESCRIPTOR: + { + vcl::Font aFont = GetWindow()->GetControlFont(); + css::awt::FontDescriptor aFD = VCLUnoHelper::CreateFontDescriptor( aFont ); + aProp <<= aFD; + } + break; + case BASEPROPERTY_BACKGROUNDCOLOR: + aProp <<= GetWindow()->GetControlBackground(); + break; + case BASEPROPERTY_DISPLAYBACKGROUNDCOLOR: + aProp <<= GetWindow()->GetBackgroundColor(); + break; + case BASEPROPERTY_FONTRELIEF: + aProp <<= static_cast<sal_Int16>(GetWindow()->GetControlFont().GetRelief()); + break; + case BASEPROPERTY_FONTEMPHASISMARK: + aProp <<= static_cast<sal_Int16>(GetWindow()->GetControlFont().GetEmphasisMark()); + break; + case BASEPROPERTY_TEXTCOLOR: + aProp <<= GetWindow()->GetControlForeground(); + break; + case BASEPROPERTY_TEXTLINECOLOR: + aProp <<= GetWindow()->GetTextLineColor(); + break; + case BASEPROPERTY_FILLCOLOR: + aProp <<= GetWindow()->GetOutDev()->GetFillColor(); + break; + case BASEPROPERTY_LINECOLOR: + aProp <<= GetWindow()->GetOutDev()->GetLineColor(); + break; + case BASEPROPERTY_HIGHLIGHT_COLOR: + aProp <<= GetWindow()->GetSettings().GetStyleSettings().GetHighlightColor(); + break; + case BASEPROPERTY_HIGHLIGHT_TEXT_COLOR: + aProp <<= GetWindow()->GetSettings().GetStyleSettings().GetHighlightTextColor(); + break; + case BASEPROPERTY_BORDER: + { + WindowBorderStyle nBorder = WindowBorderStyle::NONE; + if ( GetWindow()->GetStyle() & WB_BORDER ) + nBorder = GetWindow()->GetBorderStyle(); + aProp <<= static_cast<sal_uInt16>(nBorder); + } + break; + case BASEPROPERTY_TABSTOP: + aProp <<= ( GetWindow()->GetStyle() & WB_TABSTOP ) != 0; + break; + case BASEPROPERTY_VERTICALALIGN: + { + WinBits nStyle = GetWindow()->GetStyle(); + if ( nStyle & WB_TOP ) + aProp <<= VerticalAlignment_TOP; + else if ( nStyle & WB_VCENTER ) + aProp <<= VerticalAlignment_MIDDLE; + else if ( nStyle & WB_BOTTOM ) + aProp <<= VerticalAlignment_BOTTOM; + } + break; + case BASEPROPERTY_ALIGN: + { + switch ( eWinType ) + { + case WindowType::FIXEDTEXT: + case WindowType::EDIT: + case WindowType::MULTILINEEDIT: + case WindowType::CHECKBOX: + case WindowType::RADIOBUTTON: + case WindowType::LISTBOX: + case WindowType::COMBOBOX: + case WindowType::PUSHBUTTON: + case WindowType::OKBUTTON: + case WindowType::CANCELBUTTON: + case WindowType::HELPBUTTON: + { + WinBits nStyle = GetWindow()->GetStyle(); + if ( nStyle & WB_LEFT ) + aProp <<= sal_Int16(PROPERTY_ALIGN_LEFT); + else if ( nStyle & WB_CENTER ) + aProp <<= sal_Int16(PROPERTY_ALIGN_CENTER); + else if ( nStyle & WB_RIGHT ) + aProp <<= sal_Int16(PROPERTY_ALIGN_RIGHT); + } + break; + default: break; + } + } + break; + case BASEPROPERTY_MULTILINE: + { + if ( ( eWinType == WindowType::FIXEDTEXT ) + || ( eWinType == WindowType::CHECKBOX ) + || ( eWinType == WindowType::RADIOBUTTON ) + || ( eWinType == WindowType::PUSHBUTTON ) + || ( eWinType == WindowType::OKBUTTON ) + || ( eWinType == WindowType::CANCELBUTTON ) + || ( eWinType == WindowType::HELPBUTTON ) + ) + aProp <<= ( GetWindow()->GetStyle() & WB_WORDBREAK ) != 0; + } + break; + case BASEPROPERTY_AUTOMNEMONICS: + { + bool bAutoMnemonics = GetWindow()->GetSettings().GetStyleSettings().GetAutoMnemonic(); + aProp <<= bAutoMnemonics; + } + break; + case BASEPROPERTY_MOUSETRANSPARENT: + { + bool bMouseTransparent = GetWindow()->IsMouseTransparent(); + aProp <<= bMouseTransparent; + } + break; + case BASEPROPERTY_PAINTTRANSPARENT: + { + bool bPaintTransparent = GetWindow()->IsPaintTransparent(); + aProp <<= bPaintTransparent; + } + break; + + case BASEPROPERTY_REPEAT: + aProp <<= ( 0 != ( GetWindow()->GetStyle() & WB_REPEAT ) ); + break; + + case BASEPROPERTY_REPEAT_DELAY: + { + sal_Int32 nButtonRepeat = GetWindow()->GetSettings().GetMouseSettings().GetButtonRepeat(); + aProp <<= nButtonRepeat; + } + break; + + case BASEPROPERTY_SYMBOL_COLOR: + aProp <<= GetWindow()->GetSettings().GetStyleSettings().GetButtonTextColor(); + break; + + case BASEPROPERTY_BORDERCOLOR: + aProp <<= GetWindow()->GetSettings().GetStyleSettings().GetMonoColor(); + break; + } + } + return aProp; +} + + +// css::awt::XLayoutConstrains +css::awt::Size VCLXWindow::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + // Use this method only for those components which can be created through + // css::awt::Toolkit , but do not have an interface + + Size aSz; + if ( GetWindow() ) + { + WindowType nWinType = GetWindow()->GetType(); + switch ( nWinType ) + { + case WindowType::CONTROL: + aSz.setWidth( GetWindow()->GetTextWidth( GetWindow()->GetText() )+2*12 ); + aSz.setHeight( GetWindow()->GetTextHeight()+2*6 ); + break; + + case WindowType::PATTERNBOX: + case WindowType::NUMERICBOX: + case WindowType::METRICBOX: + case WindowType::CURRENCYBOX: + case WindowType::DATEBOX: + case WindowType::TIMEBOX: + case WindowType::LONGCURRENCYBOX: + aSz.setWidth( GetWindow()->GetTextWidth( GetWindow()->GetText() )+2*2 ); + aSz.setHeight( GetWindow()->GetTextHeight()+2*2 ); + break; + case WindowType::SCROLLBARBOX: + return VCLXScrollBar::implGetMinimumSize( GetWindow() ); + default: + aSz = GetWindow()->get_preferred_size(); + } + } + + return css::awt::Size( aSz.Width(), aSz.Height() ); +} + +css::awt::Size VCLXWindow::getPreferredSize( ) +{ + return getMinimumSize(); +} + +css::awt::Size VCLXWindow::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aNewSize( rNewSize ); + css::awt::Size aMinSize = getMinimumSize(); + + if ( aNewSize.Width < aMinSize.Width ) + aNewSize.Width = aMinSize.Width; + if ( aNewSize.Height < aMinSize.Height ) + aNewSize.Height = aMinSize.Height; + + return aNewSize; +} + + +// css::awt::XView +sal_Bool VCLXWindow::setGraphics( const css::uno::Reference< css::awt::XGraphics >& rxDevice ) +{ + SolarMutexGuard aGuard; + + if ( VCLUnoHelper::GetOutputDevice( rxDevice ) ) + mpImpl->mxViewGraphics = rxDevice; + else + mpImpl->mxViewGraphics = nullptr; + + return mpImpl->mxViewGraphics.is(); +} + +css::uno::Reference< css::awt::XGraphics > VCLXWindow::getGraphics( ) +{ + SolarMutexGuard aGuard; + + return mpImpl->mxViewGraphics; +} + +css::awt::Size VCLXWindow::getSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + if ( GetWindow() ) + aSz = GetWindow()->GetSizePixel(); + return css::awt::Size( aSz.Width(), aSz.Height() ); +} + +void VCLXWindow::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( !pWindow ) + return; + + if ( !(isDesignMode() || mpImpl->isEnableVisible()) ) + return; + + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( mpImpl->mxViewGraphics ); + if (!pDev) + pDev = pWindow->GetParent()->GetOutDev(); + TabPage* pTabPage = dynamic_cast< TabPage* >( pWindow.get() ); + if ( pTabPage ) + { + Point aPos( nX, nY ); + aPos = pDev->PixelToLogic( aPos ); + pTabPage->Draw( pDev, aPos, SystemTextColorFlags::NONE ); + return; + } + + Point aPos( nX, nY ); + + if ( pWindow->GetParent() && !pWindow->IsSystemWindow() && ( pWindow->GetParent()->GetOutDev() == pDev ) ) + { + // #i40647# don't draw here if this is a recursive call + // sometimes this is called recursively, because the Update call on the parent + // (strangely) triggers another paint. Prevent a stack overflow here + // Yes, this is only fixing symptoms for the moment... + // #i40647# / 2005-01-18 / frank.schoenheit@sun.com + if ( !mpImpl->getDrawingOntoParent_ref() ) + { + ::comphelper::FlagGuard aDrawingflagGuard( mpImpl->getDrawingOntoParent_ref() ); + + bool bWasVisible = pWindow->IsVisible(); + Point aOldPos( pWindow->GetPosPixel() ); + + if ( bWasVisible && aOldPos == aPos ) + { + pWindow->PaintImmediately(); + return; + } + + pWindow->SetPosPixel( aPos ); + + // Update parent first to avoid painting the parent upon the update + // of this window, as it may otherwise cause the parent + // to hide this window again + if( pWindow->GetParent() ) + pWindow->GetParent()->PaintImmediately(); + + pWindow->Show(); + pWindow->PaintImmediately(); + pWindow->SetParentUpdateMode( false ); + pWindow->Hide(); + pWindow->SetParentUpdateMode( true ); + + pWindow->SetPosPixel( aOldPos ); + if ( bWasVisible ) + pWindow->Show(); + } + } + else if ( pDev ) + { + Point aP = pDev->PixelToLogic( aPos ); + + vcl::PDFExtOutDevData* pPDFExport = dynamic_cast<vcl::PDFExtOutDevData*>(pDev->GetExtOutDevData()); + bool bDrawSimple = ( pDev->GetOutDevType() == OUTDEV_PRINTER ) + || ( pDev->GetOutDevViewType() == OutDevViewType::PrintPreview ) + || ( pPDFExport != nullptr ); + if ( bDrawSimple ) + { + pWindow->Draw( pDev, aP, SystemTextColorFlags::NoControls ); + } + else + { + bool bOldNW =pWindow->IsNativeWidgetEnabled(); + if( bOldNW ) + pWindow->EnableNativeWidget(false); + pWindow->PaintToDevice( pDev, aP ); + if( bOldNW ) + pWindow->EnableNativeWidget(); + } + } +} + +void VCLXWindow::setZoom( float fZoomX, float /*fZoomY*/ ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + { + // Fraction::Fraction takes a double, but we have a float only. + // The implicit conversion from float to double can result in a precision loss, i.e. 1.2 is converted to + // 1.200000000047something. To prevent this, we convert explicitly to double, and round it. + double nZoom( fZoomX ); + Fraction aZoom(::rtl::math::round(nZoom, 4)); + aZoom.ReduceInaccurate(10); // to avoid runovers and BigInt mapping + GetWindow()->SetZoom(aZoom); + } +} + +// css::lang::XEventListener +void SAL_CALL VCLXWindow::disposing( const css::lang::EventObject& _rSource ) +{ + SolarMutexGuard aGuard; + + if (mpImpl->mbDisposing) + return; + + // check if it comes from our AccessibleContext + uno::Reference< uno::XInterface > aAC( mpImpl->mxAccessibleContext, uno::UNO_QUERY ); + uno::Reference< uno::XInterface > xSource( _rSource.Source, uno::UNO_QUERY ); + + if ( aAC.get() == xSource.get() ) + { // yep, it does + mpImpl->mxAccessibleContext.clear(); + } +} + +// css::accessibility::XAccessible +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXWindow::getAccessibleContext( ) +{ + SolarMutexGuard aGuard; + + // already disposed + if (mpImpl->mbDisposing) + return uno::Reference< accessibility::XAccessibleContext >(); + + if ( !mpImpl->mxAccessibleContext.is() && GetWindow() ) + { + mpImpl->mxAccessibleContext = CreateAccessibleContext(); + + // add as event listener to this component + // in case somebody disposes it, we do not want to have a (though weak) reference to a dead + // object + uno::Reference< lang::XComponent > xComp( mpImpl->mxAccessibleContext, uno::UNO_QUERY ); + if ( xComp.is() ) + xComp->addEventListener( this ); + } + + return mpImpl->mxAccessibleContext; +} + +// css::awt::XDockable +void SAL_CALL VCLXWindow::addDockableWindowListener( const css::uno::Reference< css::awt::XDockableWindowListener >& xListener ) +{ + SolarMutexGuard aGuard; + + if (!mpImpl->mbDisposing && xListener.is() ) + mpImpl->getDockableWindowListeners().addInterface( xListener ); + +} + +void SAL_CALL VCLXWindow::removeDockableWindowListener( const css::uno::Reference< css::awt::XDockableWindowListener >& xListener ) +{ + SolarMutexGuard aGuard; + + if (!mpImpl->mbDisposing) + mpImpl->getDockableWindowListeners().removeInterface( xListener ); +} + +void SAL_CALL VCLXWindow::enableDocking( sal_Bool bEnable ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + pWindow->EnableDocking( bEnable ); +} + +sal_Bool SAL_CALL VCLXWindow::isFloating( ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if( pWindow ) + return vcl::Window::GetDockingManager()->IsFloating( pWindow ); + else + return false; +} + +void SAL_CALL VCLXWindow::setFloatingMode( sal_Bool bFloating ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if( pWindow ) + vcl::Window::GetDockingManager()->SetFloatingMode( pWindow, bFloating ); +} + +sal_Bool SAL_CALL VCLXWindow::isLocked( ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if( pWindow ) + return vcl::Window::GetDockingManager()->IsLocked( pWindow ); + else + return false; +} + +void SAL_CALL VCLXWindow::lock( ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if( pWindow && !vcl::Window::GetDockingManager()->IsFloating( pWindow ) ) + vcl::Window::GetDockingManager()->Lock( pWindow ); +} + +void SAL_CALL VCLXWindow::unlock( ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if( pWindow && !vcl::Window::GetDockingManager()->IsFloating( pWindow ) ) + vcl::Window::GetDockingManager()->Unlock( pWindow ); +} + +void SAL_CALL VCLXWindow::startPopupMode( const css::awt::Rectangle& ) +{ + // deprecated +} + +sal_Bool SAL_CALL VCLXWindow::isInPopupMode( ) +{ + // deprecated + return false; +} + + +// css::awt::XWindow2 + +void SAL_CALL VCLXWindow::setOutputSize( const css::awt::Size& aSize ) +{ + SolarMutexGuard aGuard; + if( VclPtr<vcl::Window> pWindow = GetWindow() ) + pWindow->SetOutputSizePixel( VCLSize( aSize ) ); +} + +css::awt::Size SAL_CALL VCLXWindow::getOutputSize( ) +{ + SolarMutexGuard aGuard; + if( VclPtr<vcl::Window> pWindow = GetWindow() ) + return AWTSize( pWindow->GetOutputSizePixel() ); + else + return css::awt::Size(); +} + +sal_Bool SAL_CALL VCLXWindow::isVisible( ) +{ + SolarMutexGuard aGuard; + if( GetWindow() ) + return GetWindow()->IsVisible(); + else + return false; +} + +sal_Bool SAL_CALL VCLXWindow::isActive( ) +{ + SolarMutexGuard aGuard; + if( GetWindow() ) + return GetWindow()->IsActive(); + else + return false; + +} + +sal_Bool SAL_CALL VCLXWindow::isEnabled( ) +{ + SolarMutexGuard aGuard; + if( GetWindow() ) + return GetWindow()->IsEnabled(); + else + return false; +} + +sal_Bool SAL_CALL VCLXWindow::hasFocus( ) +{ + SolarMutexGuard aGuard; + if( GetWindow() ) + return GetWindow()->HasFocus(); + else + return false; +} + +// css::beans::XPropertySetInfo + +UnoPropertyArrayHelper * +VCLXWindow::GetPropHelper() +{ + SolarMutexGuard aGuard; + if ( mpImpl->mpPropHelper == nullptr ) + { + std::vector< sal_uInt16 > aIDs; + GetPropertyIds( aIDs ); + mpImpl->mpPropHelper.reset( new UnoPropertyArrayHelper( aIDs ) ); + } + return mpImpl->mpPropHelper.get(); +} + +css::uno::Sequence< css::beans::Property > SAL_CALL +VCLXWindow::getProperties() +{ + return GetPropHelper()->getProperties(); +} +css::beans::Property SAL_CALL +VCLXWindow::getPropertyByName( const OUString& rName ) +{ + return GetPropHelper()->getPropertyByName( rName ); +} + +sal_Bool SAL_CALL +VCLXWindow::hasPropertyByName( const OUString& rName ) +{ + return GetPropHelper()->hasPropertyByName( rName ); +} + +Reference< XStyleSettings > SAL_CALL VCLXWindow::getStyleSettings() +{ + return mpImpl->getStyleSettings(); +} + +bool VCLXWindow::IsDisposed() const +{ + return mpImpl->mbDisposing; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxwindow1.cxx b/toolkit/source/awt/vclxwindow1.cxx new file mode 100644 index 0000000000..edc78f7d92 --- /dev/null +++ b/toolkit/source/awt/vclxwindow1.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 <toolkit/awt/vclxwindow.hxx> +#include <com/sun/star/beans/NamedValue.hpp> +#include <vcl/wrkwin.hxx> +#include <vcl/window.hxx> + +#ifdef _WIN32 +#include <prewin.h> +#include <postwin.h> +#elif defined(MACOSX) +#include <premac.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> +#endif +#include <vcl/sysdata.hxx> + +/// helper method to set a window handle into a SystemParentData struct +void VCLXWindow::SetSystemParent_Impl(const css::uno::Any& rHandle) +{ + // does only work for WorkWindows + VclPtr<vcl::Window> pWindow = GetWindow(); + if (pWindow->GetType() != WindowType::WORKWINDOW) + { + throw css::uno::RuntimeException("not a work window"); + } + + // use sal_Int64 here to accommodate all int types + // uno::Any shift operator will upcast if necessary + sal_Int64 nHandle = 0; + bool bXEmbed = false; + bool bThrow = false; + if (!(rHandle >>= nHandle)) + { + css::uno::Sequence<css::beans::NamedValue> aProps; + if (rHandle >>= aProps) + { + for (const css::beans::NamedValue& rProp : std::as_const(aProps)) + { + if (rProp.Name == "WINDOW") + rProp.Value >>= nHandle; + else if (rProp.Name == "XEMBED") + rProp.Value >>= bXEmbed; + } + } + else + bThrow = true; + } + if (bThrow) + { + throw css::uno::RuntimeException("incorrect window handle type"); + } + // create system parent data + SystemParentData aSysParentData; + aSysParentData.nSize = sizeof(SystemParentData); +#if defined(_WIN32) + aSysParentData.hWnd = reinterpret_cast<HWND>(nHandle); +#elif defined(MACOSX) + aSysParentData.pView = reinterpret_cast<NSView*>(nHandle); +#elif defined(ANDROID) + // Nothing +#elif defined(IOS) + // Nothing +#elif defined(UNX) + aSysParentData.aWindow = nHandle; + aSysParentData.bXEmbedSupport = bXEmbed; +#endif + + // set system parent + static_cast<WorkWindow*>(pWindow.get())->SetPluginParent(&aSysParentData); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxwindows.cxx b/toolkit/source/awt/vclxwindows.cxx new file mode 100644 index 0000000000..d6ba5e48a6 --- /dev/null +++ b/toolkit/source/awt/vclxwindows.cxx @@ -0,0 +1,7877 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <toolkit/awt/vclxwindows.hxx> +#include <toolkit/helper/accessiblefactory.hxx> +#include <com/sun/star/awt/LineEndFormat.hpp> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <toolkit/helper/vclunohelper.hxx> +#include <helper/property.hxx> +#include <toolkit/helper/convert.hxx> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/resource/XStringResourceResolver.hpp> +#include <com/sun/star/awt/ImageScaleMode.hpp> +#include <com/sun/star/awt/XItemList.hpp> +#include <com/sun/star/awt/TextAlign.hpp> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <sal/log.hxx> + +#include <awt/vclxwindows.hxx> +#include <controls/filectrl.hxx> +#include <controls/svmedit.hxx> +#include <vcl/toolkit/button.hxx> +#include <vcl/toolkit/fmtfield.hxx> +#include <vcl/graph.hxx> +#include <vcl/toolkit/lstbox.hxx> +#include <vcl/toolkit/combobox.hxx> +#include <vcl/toolkit/field.hxx> +#include <vcl/toolkit/fixedhyper.hxx> +#include <vcl/toolkit/imgctrl.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <vcl/toolkit/prgsbar.hxx> +#include <vcl/toolkit/scrbar.hxx> +#include <vcl/svapp.hxx> +#include <vcl/tabpage.hxx> +#include <vcl/tabctrl.hxx> +#include <vcl/settings.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/debug.hxx> + +#include <helper/imagealign.hxx> +#include <helper/msgbox.hxx> +#include <helper/tkresmgr.hxx> +#include "vclxwindows_internal.hxx" +#include <svl/numformat.hxx> + +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::lang::EventObject; +using ::com::sun::star::awt::ItemListEvent; +using ::com::sun::star::awt::XItemList; +using ::com::sun::star::graphic::XGraphic; +using ::com::sun::star::graphic::XGraphicProvider; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt::VisualEffect; +namespace ImageScaleMode = ::com::sun::star::awt::ImageScaleMode; + +static double ImplCalcLongValue( double nValue, sal_uInt16 nDigits ) +{ + double n = nValue; + for ( sal_uInt16 d = 0; d < nDigits; d++ ) + n *= 10; + return n; +} + +static double ImplCalcDoubleValue( double nValue, sal_uInt16 nDigits ) +{ + double n = nValue; + for ( sal_uInt16 d = 0; d < nDigits; d++ ) + n /= 10; + return n; +} + +namespace toolkit +{ + /** sets the "face color" for button like controls (scroll bar, spin button) + */ + void setButtonLikeFaceColor( vcl::Window* _pWindow, const css::uno::Any& _rColorValue ) + { + AllSettings aSettings = _pWindow->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + + if ( !_rColorValue.hasValue() ) + { + const StyleSettings& aAppStyle = Application::GetSettings().GetStyleSettings(); + aStyleSettings.SetFaceColor( aAppStyle.GetFaceColor( ) ); + aStyleSettings.SetCheckedColor( aAppStyle.GetCheckedColor( ) ); + aStyleSettings.SetLightBorderColor( aAppStyle.GetLightBorderColor() ); + aStyleSettings.SetLightColor( aAppStyle.GetLightColor() ); + aStyleSettings.SetShadowColor( aAppStyle.GetShadowColor() ); + aStyleSettings.SetDarkShadowColor( aAppStyle.GetDarkShadowColor() ); + } + else + { + Color nBackgroundColor; + _rColorValue >>= nBackgroundColor; + aStyleSettings.SetFaceColor( nBackgroundColor ); + + // for the real background (everything except the buttons and the thumb), + // use an average between the desired color and "white" + Color aWhite( COL_WHITE ); + Color aCheckedBackground( nBackgroundColor ); + aCheckedBackground.SetRed( ( aCheckedBackground.GetRed() + aWhite.GetRed() ) / 2 ); + aCheckedBackground.SetGreen( ( aCheckedBackground.GetGreen() + aWhite.GetGreen() ) / 2 ); + aCheckedBackground.SetBlue( ( aCheckedBackground.GetBlue() + aWhite.GetBlue() ) / 2 ); + aStyleSettings.SetCheckedColor( aCheckedBackground ); + + sal_Int32 nBackgroundLuminance = nBackgroundColor.GetLuminance(); + sal_Int32 nWhiteLuminance = COL_WHITE.GetLuminance(); + + Color aLightShadow( nBackgroundColor ); + aLightShadow.IncreaseLuminance( static_cast<sal_uInt8>( ( nWhiteLuminance - nBackgroundLuminance ) * 2 / 3 ) ); + aStyleSettings.SetLightBorderColor( aLightShadow ); + + Color aLight( nBackgroundColor ); + aLight.IncreaseLuminance( static_cast<sal_uInt8>( ( nWhiteLuminance - nBackgroundLuminance ) * 1 / 3 ) ); + aStyleSettings.SetLightColor( aLight ); + + Color aShadow( nBackgroundColor ); + aShadow.DecreaseLuminance( static_cast<sal_uInt8>( nBackgroundLuminance * 1 / 3 ) ); + aStyleSettings.SetShadowColor( aShadow ); + + Color aDarkShadow( nBackgroundColor ); + aDarkShadow.DecreaseLuminance( static_cast<sal_uInt8>( nBackgroundLuminance * 2 / 3 ) ); + aStyleSettings.SetDarkShadowColor( aDarkShadow ); + } + + aSettings.SetStyleSettings( aStyleSettings ); + _pWindow->SetSettings( aSettings, true ); + } + + Any getButtonLikeFaceColor( const vcl::Window* _pWindow ) + { + Color nBackgroundColor = _pWindow->GetSettings().GetStyleSettings().GetFaceColor(); + return Any( sal_Int32(nBackgroundColor) ); + } + + static void adjustBooleanWindowStyle( const Any& _rValue, vcl::Window* _pWindow, WinBits _nBits, bool _bInverseSemantics ) + { + WinBits nStyle = _pWindow->GetStyle(); + bool bValue( false ); + OSL_VERIFY( _rValue >>= bValue ); + if ( bValue != _bInverseSemantics ) + nStyle |= _nBits; + else + nStyle &= ~_nBits; + _pWindow->SetStyle( nStyle ); + } + + static void setVisualEffect( const Any& _rValue, vcl::Window* _pWindow ) + { + AllSettings aSettings = _pWindow->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + + sal_Int16 nStyle = LOOK3D; + OSL_VERIFY( _rValue >>= nStyle ); + switch ( nStyle ) + { + case FLAT: + aStyleSettings.SetOptions( aStyleSettings.GetOptions() | StyleSettingsOptions::Mono ); + break; + case LOOK3D: + default: + aStyleSettings.SetOptions( aStyleSettings.GetOptions() & ~StyleSettingsOptions::Mono ); + } + aSettings.SetStyleSettings( aStyleSettings ); + _pWindow->SetSettings( aSettings ); + } + + static Any getVisualEffect( vcl::Window const * _pWindow ) + { + Any aEffect; + + StyleSettings aStyleSettings = _pWindow->GetSettings().GetStyleSettings(); + if ( aStyleSettings.GetOptions() & StyleSettingsOptions::Mono ) + aEffect <<= sal_Int16(FLAT); + else + aEffect <<= sal_Int16(LOOK3D); + return aEffect; + } +} + + + + +void VCLXGraphicControl::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +void VCLXGraphicControl::ImplSetNewImage() +{ + OSL_PRECOND( GetWindow(), "VCLXGraphicControl::ImplSetNewImage: window is required to be not-NULL!" ); + VclPtr< Button > pButton = GetAsDynamic< Button >(); + pButton->SetModeImage( GetImage() ); +} + +void VCLXGraphicControl::setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) +{ + SolarMutexGuard aGuard; + + if ( GetWindow() ) + { + Size aOldSize = GetWindow()->GetSizePixel(); + VCLXWindow::setPosSize( X, Y, Width, Height, Flags ); + if ( ( aOldSize.Width() != Width ) || ( aOldSize.Height() != Height ) ) + ImplSetNewImage(); + } +} + +void VCLXGraphicControl::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !GetWindow() ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_GRAPHIC: + { + Reference< XGraphic > xGraphic; + OSL_VERIFY( Value >>= xGraphic ); + maImage = Image( xGraphic ); + ImplSetNewImage(); + } + break; + + case BASEPROPERTY_IMAGEALIGN: + { + WindowType eType = GetWindow()->GetType(); + if ( ( eType == WindowType::PUSHBUTTON ) + || ( eType == WindowType::RADIOBUTTON ) + || ( eType == WindowType::CHECKBOX ) + ) + { + sal_Int16 nAlignment = sal_Int16(); + if ( Value >>= nAlignment ) + GetAs< Button >()->SetImageAlign( static_cast< ImageAlign >( nAlignment ) ); + } + } + break; + case BASEPROPERTY_IMAGEPOSITION: + { + WindowType eType = GetWindow()->GetType(); + if ( ( eType == WindowType::PUSHBUTTON ) + || ( eType == WindowType::RADIOBUTTON ) + || ( eType == WindowType::CHECKBOX ) + ) + { + sal_Int16 nImagePosition = 2; + OSL_VERIFY( Value >>= nImagePosition ); + GetAs<Button>()->SetImageAlign( ::toolkit::translateImagePosition( nImagePosition ) ); + } + } + break; + default: + VCLXWindow::setProperty( PropertyName, Value ); + break; + } +} + +css::uno::Any VCLXGraphicControl::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + if ( !GetWindow() ) + return aProp; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_GRAPHIC: + aProp <<= Graphic(maImage.GetBitmapEx()).GetXGraphic(); + break; + case BASEPROPERTY_IMAGEALIGN: + { + WindowType eType = GetWindow()->GetType(); + if ( ( eType == WindowType::PUSHBUTTON ) + || ( eType == WindowType::RADIOBUTTON ) + || ( eType == WindowType::CHECKBOX ) + ) + { + aProp <<= ::toolkit::getCompatibleImageAlign( + GetAs<Button>()->GetImageAlign() ); + } + } + break; + case BASEPROPERTY_IMAGEPOSITION: + { + WindowType eType = GetWindow()->GetType(); + if ( ( eType == WindowType::PUSHBUTTON ) + || ( eType == WindowType::RADIOBUTTON ) + || ( eType == WindowType::CHECKBOX ) + ) + { + aProp <<= ::toolkit::translateImagePosition( + GetAs< Button >()->GetImageAlign() ); + } + } + break; + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + break; + } + return aProp; +} + + + + +void VCLXButton::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_DEFAULTBUTTON, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEALIGN, + BASEPROPERTY_IMAGEPOSITION, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_LABEL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_PUSHBUTTONTYPE, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_STATE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TOGGLE, + BASEPROPERTY_FOCUSONCLICK, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_ALIGN, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + 0); + VCLXGraphicControl::ImplGetPropertyIds( rIds ); +} + +VCLXButton::VCLXButton() + :maActionListeners( *this ) + ,maItemListeners( *this ) +{ +} + +VCLXButton::~VCLXButton() +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXButton::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXButton::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maActionListeners.disposeAndClear( aObj ); + maItemListeners.disposeAndClear( aObj ); + VCLXGraphicControl::dispose(); +} + +void VCLXButton::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXButton::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +void VCLXButton::addItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.addInterface( l ); +} + +void VCLXButton::removeItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.removeInterface( l ); +} + +void VCLXButton::setLabel( const OUString& rLabel ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( rLabel ); +} + +void VCLXButton::setActionCommand( const OUString& rCommand ) +{ + SolarMutexGuard aGuard; + + maActionCommand = rCommand; +} + +css::awt::Size VCLXButton::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< PushButton > pButton = GetAs< PushButton >(); + if ( pButton ) + aSz = pButton->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXButton::getPreferredSize( ) +{ + css::awt::Size aSz = getMinimumSize(); + aSz.Width += 16; + aSz.Height += 10; + return aSz; +} + +css::awt::Size VCLXButton::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + Size aSz = VCLSize(rNewSize); + VclPtr< PushButton > pButton = GetAs< PushButton >(); + if ( pButton ) + { + Size aMinSz = pButton->CalcMinimumSize(); + // no text, thus image + if ( pButton->GetText().isEmpty() ) + { + if ( aSz.Width() < aMinSz.Width() ) + aSz.setWidth( aMinSz.Width() ); + if ( aSz.Height() < aMinSz.Height() ) + aSz.setHeight( aMinSz.Height() ); + } + else + { + if ( ( aSz.Width() > aMinSz.Width() ) && ( aSz.Height() < aMinSz.Height() ) ) + aSz.setHeight( aMinSz.Height() ); + else + aSz = aMinSz; + } + } + return AWTSize(aSz); +} + +void VCLXButton::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< Button > pButton = GetAs< Button >(); + if ( !pButton ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_FOCUSONCLICK: + ::toolkit::adjustBooleanWindowStyle( Value, pButton, WB_NOPOINTERFOCUS, true ); + break; + + case BASEPROPERTY_TOGGLE: + ::toolkit::adjustBooleanWindowStyle( Value, pButton, WB_TOGGLE, false ); + break; + + case BASEPROPERTY_DEFAULTBUTTON: + { + WinBits nStyle = pButton->GetStyle() | WB_DEFBUTTON; + bool b = bool(); + if ( ( Value >>= b ) && !b ) + nStyle &= ~WB_DEFBUTTON; + pButton->SetStyle( nStyle ); + } + break; + case BASEPROPERTY_STATE: + { + if ( GetWindow()->GetType() == WindowType::PUSHBUTTON ) + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + static_cast<PushButton*>(pButton.get())->SetState( static_cast<TriState>(n) ); + } + } + break; + default: + { + VCLXGraphicControl::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXButton::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< Button > pButton = GetAs< Button >(); + if ( pButton ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_FOCUSONCLICK: + aProp <<= ( ( pButton->GetStyle() & WB_NOPOINTERFOCUS ) == 0 ); + break; + + case BASEPROPERTY_TOGGLE: + aProp <<= ( ( pButton->GetStyle() & WB_TOGGLE ) != 0 ); + break; + + case BASEPROPERTY_DEFAULTBUTTON: + { + aProp <<= ( pButton->GetStyle() & WB_DEFBUTTON ) != 0; + } + break; + case BASEPROPERTY_STATE: + { + if ( GetWindow()->GetType() == WindowType::PUSHBUTTON ) + { + aProp <<= static_cast<sal_Int16>(static_cast<PushButton*>(pButton.get())->GetState()); + } + } + break; + default: + { + aProp = VCLXGraphicControl::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXButton::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ButtonClick: + { + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + if ( maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.ActionCommand = maActionCommand; + + Callback aCallback = [ this, aEvent ]() + { this->maActionListeners.actionPerformed( aEvent ); }; + + ImplExecuteAsyncWithoutSolarLock( aCallback ); + } + } + break; + + case VclEventId::PushbuttonToggle: + { + PushButton& rButton = dynamic_cast< PushButton& >( *rVclWindowEvent.GetWindow() ); + + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + if ( maItemListeners.getLength() ) + { + css::awt::ItemEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Selected = ( rButton.GetState() == TRISTATE_TRUE ) ? 1 : 0; + maItemListeners.itemStateChanged( aEvent ); + } + } + break; + + default: + VCLXGraphicControl::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + + + + +void VCLXImageControl::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_SCALEIMAGE, + BASEPROPERTY_IMAGE_SCALE_MODE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + 0); + VCLXGraphicControl::ImplGetPropertyIds( rIds ); +} + +VCLXImageControl::VCLXImageControl() +{ +} + +VCLXImageControl::~VCLXImageControl() +{ +} + +void VCLXImageControl::ImplSetNewImage() +{ + OSL_PRECOND( GetWindow(), "VCLXImageControl::ImplSetNewImage: window is required to be not-NULL!" ); + VclPtr<ImageControl> pControl = GetAs< ImageControl >(); + pControl->SetImage( GetImage() ); +} + +css::awt::Size VCLXImageControl::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz = GetImage().GetSizePixel(); + aSz = ImplCalcWindowSize( aSz ); + + return AWTSize(aSz); +} + +css::awt::Size VCLXImageControl::getPreferredSize( ) +{ + return getMinimumSize(); +} + +css::awt::Size VCLXImageControl::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz = rNewSize; + css::awt::Size aMinSz = getMinimumSize(); + if ( aSz.Width < aMinSz.Width ) + aSz.Width = aMinSz.Width; + if ( aSz.Height < aMinSz.Height ) + aSz.Height = aMinSz.Height; + return aSz; +} + +void VCLXImageControl::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< ImageControl > pImageControl = GetAs< ImageControl >(); + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_IMAGE_SCALE_MODE: + { + sal_Int16 nScaleMode( ImageScaleMode::ANISOTROPIC ); + if ( pImageControl && ( Value >>= nScaleMode ) ) + { + pImageControl->SetScaleMode( nScaleMode ); + } + } + break; + + case BASEPROPERTY_SCALEIMAGE: + { + // this is for compatibility only, nowadays, the ImageScaleMode property should be used + bool bScaleImage = false; + if ( pImageControl && ( Value >>= bScaleImage ) ) + { + pImageControl->SetScaleMode( bScaleImage ? ImageScaleMode::ANISOTROPIC : ImageScaleMode::NONE ); + } + } + break; + + default: + VCLXGraphicControl::setProperty( PropertyName, Value ); + break; + } +} + +css::uno::Any VCLXImageControl::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< ImageControl > pImageControl = GetAs< ImageControl >(); + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + + switch ( nPropType ) + { + case BASEPROPERTY_IMAGE_SCALE_MODE: + aProp <<= ( pImageControl ? pImageControl->GetScaleMode() : ImageScaleMode::ANISOTROPIC ); + break; + + case BASEPROPERTY_SCALEIMAGE: + aProp <<= ( pImageControl && pImageControl->GetScaleMode() != ImageScaleMode::NONE ); + break; + + default: + aProp = VCLXGraphicControl::getProperty( PropertyName ); + break; + } + return aProp; +} + + + + +void VCLXCheckBox::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEPOSITION, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_LABEL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_STATE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TRISTATE, + BASEPROPERTY_VISUALEFFECT, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_ALIGN, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + 0); + VCLXGraphicControl::ImplGetPropertyIds( rIds ); +} + +VCLXCheckBox::VCLXCheckBox() : maActionListeners( *this ), maItemListeners( *this ) +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXCheckBox::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXCheckBox::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maItemListeners.disposeAndClear( aObj ); + VCLXGraphicControl::dispose(); +} + +void VCLXCheckBox::addItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.addInterface( l ); +} + +void VCLXCheckBox::removeItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.removeInterface( l ); +} + +void VCLXCheckBox::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXCheckBox::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +void VCLXCheckBox::setActionCommand( const OUString& rCommand ) +{ + SolarMutexGuard aGuard; + maActionCommand = rCommand; +} + +void VCLXCheckBox::setLabel( const OUString& rLabel ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( rLabel ); +} + +void VCLXCheckBox::setState( sal_Int16 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< CheckBox> pCheckBox = GetAs< CheckBox >(); + if ( !pCheckBox) + return; + + TriState eState; + switch ( n ) + { + case 0: eState = TRISTATE_FALSE; break; + case 1: eState = TRISTATE_TRUE; break; + case 2: eState = TRISTATE_INDET; break; + default: eState = TRISTATE_FALSE; + } + pCheckBox->SetState( eState ); + + // #105198# call C++ click listeners (needed for accessibility) + // pCheckBox->GetClickHdl().Call( pCheckBox ); + + // #107218# Call same virtual methods and listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pCheckBox->Toggle(); + pCheckBox->Click(); + SetSynthesizingVCLEvent( false ); +} + +sal_Int16 VCLXCheckBox::getState() +{ + SolarMutexGuard aGuard; + + sal_Int16 nState = -1; + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox ) + { + switch ( pCheckBox->GetState() ) + { + case TRISTATE_FALSE: nState = 0; break; + case TRISTATE_TRUE: nState = 1; break; + case TRISTATE_INDET: nState = 2; break; + default: OSL_FAIL( "VCLXCheckBox::getState(): unknown TriState!" ); + } + } + + return nState; +} + +void VCLXCheckBox::enableTriState( sal_Bool b ) +{ + SolarMutexGuard aGuard; + + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox) + pCheckBox->EnableTriState( b ); +} + +css::awt::Size VCLXCheckBox::getMinimumSize() +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox ) + aSz = pCheckBox->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXCheckBox::getPreferredSize() +{ + return getMinimumSize(); +} + +css::awt::Size VCLXCheckBox::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + Size aSz = VCLSize(rNewSize); + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox ) + { + Size aMinSz = pCheckBox->CalcMinimumSize(rNewSize.Width); + if ( ( aSz.Width() > aMinSz.Width() ) && ( aSz.Height() < aMinSz.Height() ) ) + aSz.setHeight( aMinSz.Height() ); + else + aSz = aMinSz; + } + return AWTSize(aSz); +} + +void VCLXCheckBox::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( !pCheckBox ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VISUALEFFECT: + ::toolkit::setVisualEffect( Value, pCheckBox ); + break; + + case BASEPROPERTY_TRISTATE: + { + bool b = bool(); + if ( Value >>= b ) + pCheckBox->EnableTriState( b ); + } + break; + case BASEPROPERTY_STATE: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + setState( n ); + } + break; + default: + { + VCLXGraphicControl::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXCheckBox::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VISUALEFFECT: + aProp = ::toolkit::getVisualEffect( pCheckBox ); + break; + case BASEPROPERTY_TRISTATE: + aProp <<= pCheckBox->IsTriStateEnabled(); + break; + case BASEPROPERTY_STATE: + aProp <<= static_cast<sal_Int16>(pCheckBox->GetState()); + break; + default: + { + aProp = VCLXGraphicControl::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXCheckBox::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::CheckboxToggle: + { + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // in during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + VclPtr< CheckBox > pCheckBox = GetAs< CheckBox >(); + if ( pCheckBox ) + { + if ( maItemListeners.getLength() ) + { + css::awt::ItemEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Highlighted = 0; + aEvent.Selected = pCheckBox->GetState(); + maItemListeners.itemStateChanged( aEvent ); + } + if ( !IsSynthesizingVCLEvent() && maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.ActionCommand = maActionCommand; + maActionListeners.actionPerformed( aEvent ); + } + } + } + break; + + default: + VCLXGraphicControl::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + + + +void VCLXRadioButton::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEPOSITION, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_LABEL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_STATE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VISUALEFFECT, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_ALIGN, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + BASEPROPERTY_GROUPNAME, + 0); + VCLXGraphicControl::ImplGetPropertyIds( rIds ); +} + + +VCLXRadioButton::VCLXRadioButton() : maItemListeners( *this ), maActionListeners( *this ) +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXRadioButton::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXRadioButton::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maItemListeners.disposeAndClear( aObj ); + VCLXGraphicControl::dispose(); +} + +void VCLXRadioButton::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< RadioButton > pButton = GetAs< RadioButton >(); + if ( !pButton ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VISUALEFFECT: + ::toolkit::setVisualEffect( Value, pButton ); + break; + + case BASEPROPERTY_STATE: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + { + bool b = n != 0; + if ( pButton->IsRadioCheckEnabled() ) + pButton->Check( b ); + else + pButton->SetState( b ); + } + } + break; + case BASEPROPERTY_AUTOTOGGLE: + { + bool b = bool(); + if ( Value >>= b ) + pButton->EnableRadioCheck( b ); + } + break; + default: + { + VCLXGraphicControl::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXRadioButton::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< RadioButton > pButton = GetAs< RadioButton >(); + if ( pButton ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VISUALEFFECT: + aProp = ::toolkit::getVisualEffect( pButton ); + break; + case BASEPROPERTY_STATE: + aProp <<= static_cast<sal_Int16>( pButton->IsChecked() ? 1 : 0 ); + break; + case BASEPROPERTY_AUTOTOGGLE: + aProp <<= pButton->IsRadioCheckEnabled(); + break; + default: + { + aProp = VCLXGraphicControl::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXRadioButton::addItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.addInterface( l ); +} + +void VCLXRadioButton::removeItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.removeInterface( l ); +} + +void VCLXRadioButton::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXRadioButton::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +void VCLXRadioButton::setLabel( const OUString& rLabel ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( rLabel ); +} + +void VCLXRadioButton::setActionCommand( const OUString& rCommand ) +{ + SolarMutexGuard aGuard; + maActionCommand = rCommand; +} + +void VCLXRadioButton::setState( sal_Bool b ) +{ + SolarMutexGuard aGuard; + + VclPtr< RadioButton > pRadioButton = GetAs< RadioButton >(); + if ( pRadioButton) + { + pRadioButton->Check( b ); + // #102717# item listeners are called, but not C++ click listeners in StarOffice code => call click hdl + // But this is needed in old code because Accessibility API uses it. + // pRadioButton->GetClickHdl().Call( pRadioButton ); + + // #107218# Call same virtual methods and listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pRadioButton->Click(); + SetSynthesizingVCLEvent( false ); + } +} + +sal_Bool VCLXRadioButton::getState() +{ + SolarMutexGuard aGuard; + + VclPtr< RadioButton > pRadioButton = GetAs< RadioButton >(); + return pRadioButton && pRadioButton->IsChecked(); +} + +css::awt::Size VCLXRadioButton::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< RadioButton > pRadioButton = GetAs< RadioButton >(); + if ( pRadioButton ) + aSz = pRadioButton->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXRadioButton::getPreferredSize( ) +{ + return getMinimumSize(); +} + +css::awt::Size VCLXRadioButton::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + Size aSz = VCLSize(rNewSize); + VclPtr< RadioButton > pRadioButton = GetAs< RadioButton >(); + if ( pRadioButton ) + { + Size aMinSz = pRadioButton->CalcMinimumSize(rNewSize.Width); + if ( ( aSz.Width() > aMinSz.Width() ) && ( aSz.Height() < aMinSz.Height() ) ) + aSz.setHeight( aMinSz.Height() ); + else + aSz = aMinSz; + } + return AWTSize(aSz); +} + +void VCLXRadioButton::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // in during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ButtonClick: + if ( !IsSynthesizingVCLEvent() && maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.ActionCommand = maActionCommand; + maActionListeners.actionPerformed( aEvent ); + } + ImplClickedOrToggled( false ); + break; + + case VclEventId::RadiobuttonToggle: + ImplClickedOrToggled( true ); + break; + + default: + VCLXGraphicControl::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +void VCLXRadioButton::ImplClickedOrToggled( bool bToggled ) +{ + // In the forms, RadioChecked is not enabled, call itemStateChanged only for click + // In the dialog editor, RadioChecked is enabled, call itemStateChanged only for bToggled + VclPtr< RadioButton > pRadioButton = GetAs< RadioButton >(); + if ( pRadioButton && ( pRadioButton->IsRadioCheckEnabled() == bToggled ) && ( bToggled || pRadioButton->IsStateChanged() ) && maItemListeners.getLength() ) + { + css::awt::ItemEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Highlighted = 0; + aEvent.Selected = pRadioButton->IsChecked() ? 1 : 0; + maItemListeners.itemStateChanged( aEvent ); + } +} + + + +void VCLXSpinField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + 0 ); + VCLXEdit::ImplGetPropertyIds( rIds ); +} + +VCLXSpinField::VCLXSpinField() : maSpinListeners( *this ) +{ +} + +void VCLXSpinField::addSpinListener( const css::uno::Reference< css::awt::XSpinListener > & l ) +{ + SolarMutexGuard aGuard; + maSpinListeners.addInterface( l ); +} + +void VCLXSpinField::removeSpinListener( const css::uno::Reference< css::awt::XSpinListener > & l ) +{ + SolarMutexGuard aGuard; + maSpinListeners.removeInterface( l ); +} + +void VCLXSpinField::up() +{ + SolarMutexGuard aGuard; + + VclPtr< SpinField > pSpinField = GetAs< SpinField >(); + if ( pSpinField ) + pSpinField->Up(); +} + +void VCLXSpinField::down() +{ + SolarMutexGuard aGuard; + + VclPtr< SpinField > pSpinField = GetAs< SpinField >(); + if ( pSpinField ) + pSpinField->Down(); +} + +void VCLXSpinField::first() +{ + SolarMutexGuard aGuard; + + VclPtr< SpinField > pSpinField = GetAs< SpinField >(); + if ( pSpinField ) + pSpinField->First(); +} + +void VCLXSpinField::last() +{ + SolarMutexGuard aGuard; + + VclPtr< SpinField > pSpinField = GetAs< SpinField >(); + if ( pSpinField ) + pSpinField->Last(); +} + +void VCLXSpinField::enableRepeat( sal_Bool bRepeat ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + if ( bRepeat ) + nStyle |= WB_REPEAT; + else + nStyle &= ~WB_REPEAT; + pWindow->SetStyle( nStyle ); + } +} + +void VCLXSpinField::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::SpinfieldUp: + case VclEventId::SpinfieldDown: + case VclEventId::SpinfieldFirst: + case VclEventId::SpinfieldLast: + { + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // in during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + if ( maSpinListeners.getLength() ) + { + css::awt::SpinEvent aEvent; + aEvent.Source = getXWeak(); + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::SpinfieldUp: maSpinListeners.up( aEvent ); + break; + case VclEventId::SpinfieldDown: maSpinListeners.down( aEvent ); + break; + case VclEventId::SpinfieldFirst: maSpinListeners.first( aEvent ); + break; + case VclEventId::SpinfieldLast: maSpinListeners.last( aEvent ); + break; + default: break; + } + + } + } + break; + + default: + VCLXEdit::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + + + +void VCLXListBox::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_DROPDOWN, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LINECOUNT, + BASEPROPERTY_MULTISELECTION, + BASEPROPERTY_MULTISELECTION_SIMPLEMODE, + BASEPROPERTY_ITEM_SEPARATOR_POS, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_SELECTEDITEMS, + BASEPROPERTY_STRINGITEMLIST, + BASEPROPERTY_TYPEDITEMLIST, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_READONLY, + BASEPROPERTY_ALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + + +VCLXListBox::VCLXListBox() + : maActionListeners( *this ), + maItemListeners( *this ) +{ +} + +void VCLXListBox::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maItemListeners.disposeAndClear( aObj ); + maActionListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + +void VCLXListBox::addItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.addInterface( l ); +} + +void VCLXListBox::removeItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.removeInterface( l ); +} + +void VCLXListBox::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXListBox::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +void VCLXListBox::addItem( const OUString& aItem, sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + pBox->InsertEntry( aItem, nPos ); +} + +void VCLXListBox::addItems( const css::uno::Sequence< OUString>& aItems, sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( !pBox ) + return; + + sal_uInt16 nP = nPos; + for ( auto const & item : aItems ) + { + if ( nP == 0xFFFF ) + { + OSL_FAIL( "VCLXListBox::addItems: too many entries!" ); + // skip remaining entries, list cannot hold them, anyway + break; + } + + pBox->InsertEntry( item, nP++ ); + } +} + +void VCLXListBox::removeItems( sal_Int16 nPos, sal_Int16 nCount ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + { + for ( sal_Int16 n = nCount; n; ) + pBox->RemoveEntry( nPos + (--n) ); + } +} + +sal_Int16 VCLXListBox::getItemCount() +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pBox = GetAs< ListBox >(); + return pBox ? pBox->GetEntryCount() : 0; +} + +OUString VCLXListBox::getItem( sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + + OUString aItem; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + aItem = pBox->GetEntry( nPos ); + return aItem; +} + +css::uno::Sequence< OUString> VCLXListBox::getItems() +{ + SolarMutexGuard aGuard; + + css::uno::Sequence< OUString> aSeq; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + { + auto n = pBox->GetEntryCount(); + aSeq = css::uno::Sequence< OUString>( n ); + while (n) + { + --n; + aSeq.getArray()[n] = pBox->GetEntry( n ); + } + } + return aSeq; +} + +sal_Int16 VCLXListBox::getSelectedItemPos() +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + return pBox ? pBox->GetSelectedEntryPos() : 0; +} + +css::uno::Sequence<sal_Int16> VCLXListBox::getSelectedItemsPos() +{ + SolarMutexGuard aGuard; + + css::uno::Sequence<sal_Int16> aSeq; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + { + const sal_Int32 nSelEntries = pBox->GetSelectedEntryCount(); + aSeq = css::uno::Sequence<sal_Int16>( nSelEntries ); + for ( sal_Int32 n = 0; n < nSelEntries; ++n ) + aSeq.getArray()[n] = pBox->GetSelectedEntryPos( n ); + } + return aSeq; +} + +OUString VCLXListBox::getSelectedItem() +{ + SolarMutexGuard aGuard; + + OUString aItem; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + aItem = pBox->GetSelectedEntry(); + return aItem; +} + +css::uno::Sequence< OUString> VCLXListBox::getSelectedItems() +{ + SolarMutexGuard aGuard; + + css::uno::Sequence< OUString> aSeq; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + { + const sal_Int32 nSelEntries = pBox->GetSelectedEntryCount(); + aSeq = css::uno::Sequence< OUString>( nSelEntries ); + for ( sal_Int32 n = 0; n < nSelEntries; ++n ) + aSeq.getArray()[n] = pBox->GetSelectedEntry( n ); + } + return aSeq; +} + +void VCLXListBox::selectItemPos( sal_Int16 nPos, sal_Bool bSelect ) +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox && ( pBox->IsEntryPosSelected( nPos ) != bool(bSelect) ) ) + { + pBox->SelectEntryPos( nPos, bSelect ); + + // VCL doesn't call select handler after API call. + // ImplCallItemListeners(); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pBox->Select(); + SetSynthesizingVCLEvent( false ); + } +} + +void VCLXListBox::selectItemsPos( const css::uno::Sequence<sal_Int16>& aPositions, sal_Bool bSelect ) +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( !pBox ) + return; + + std::vector<sal_Int32> aPositionVec; + aPositionVec.reserve(aPositions.getLength()); + + bool bChanged = false; + for ( auto n = aPositions.getLength(); n; ) + { + const auto nPos = aPositions.getConstArray()[--n]; + if ( pBox->IsEntryPosSelected( nPos ) != bool(bSelect) ) + { + aPositionVec.push_back(nPos); + bChanged = true; + } + } + + if ( !bChanged ) + return; + + bool bOrigUpdateMode = pBox->IsUpdateMode(); + pBox->SetUpdateMode(false); + + pBox->SelectEntriesPos(aPositionVec, bSelect); + + pBox->SetUpdateMode(bOrigUpdateMode); + + // VCL doesn't call select handler after API call. + // ImplCallItemListeners(); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pBox->Select(); + SetSynthesizingVCLEvent( false ); +} + +void VCLXListBox::selectItem( const OUString& rItemText, sal_Bool bSelect ) +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + { + selectItemPos( pBox->GetEntryPos( rItemText ), bSelect ); + } +} + +void VCLXListBox::setDropDownLineCount( sal_Int16 nLines ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + pBox->SetDropDownLineCount( nLines ); +} + +sal_Int16 VCLXListBox::getDropDownLineCount() +{ + SolarMutexGuard aGuard; + + sal_Int16 nLines = 0; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + nLines = pBox->GetDropDownLineCount(); + return nLines; +} + +sal_Bool VCLXListBox::isMutipleMode() +{ + SolarMutexGuard aGuard; + bool bMulti = false; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + bMulti = pBox->IsMultiSelectionEnabled(); + return bMulti; +} + +void VCLXListBox::setMultipleMode( sal_Bool bMulti ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + pBox->EnableMultiSelection( bMulti ); +} + +void VCLXListBox::makeVisible( sal_Int16 nEntry ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pBox = GetAs< ListBox >(); + if ( pBox ) + pBox->SetTopEntry( nEntry ); +} + +void VCLXListBox::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // in during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ListboxSelect: + { + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if( pListBox ) + { + bool bDropDown = ( pListBox->GetStyle() & WB_DROPDOWN ) != 0; + if ( bDropDown && !IsSynthesizingVCLEvent() && maActionListeners.getLength() ) + { + // Call ActionListener on DropDown event + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.ActionCommand = pListBox->GetSelectedEntry(); + maActionListeners.actionPerformed( aEvent ); + } + + if ( maItemListeners.getLength() ) + { + ImplCallItemListeners(); + } + } + } + break; + + case VclEventId::ListboxDoubleClick: + if ( GetWindow() && maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.ActionCommand = GetAs<ListBox>()->GetSelectedEntry(); + maActionListeners.actionPerformed( aEvent ); + } + break; + + default: + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXListBox::CreateAccessibleContext() +{ + SolarMutexGuard aGuard; + + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXListBox::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( !pListBox ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_ITEM_SEPARATOR_POS: + { + sal_Int16 nSeparatorPos(0); + if ( Value >>= nSeparatorPos ) + pListBox->SetSeparatorPos( nSeparatorPos ); + } + break; + case BASEPROPERTY_READONLY: + { + bool b = false; + if ( Value >>= b ) + pListBox->SetReadOnly( b); + } + break; + case BASEPROPERTY_MULTISELECTION: + { + bool b = false; + if ( Value >>= b ) + pListBox->EnableMultiSelection( b ); + } + break; + case BASEPROPERTY_MULTISELECTION_SIMPLEMODE: + ::toolkit::adjustBooleanWindowStyle( Value, pListBox, WB_SIMPLEMODE, false ); + break; + case BASEPROPERTY_LINECOUNT: + { + sal_Int16 n = 0; + if ( Value >>= n ) + pListBox->SetDropDownLineCount( n ); + } + break; + case BASEPROPERTY_STRINGITEMLIST: + { + css::uno::Sequence< OUString> aItems; + if ( Value >>= aItems ) + { + pListBox->Clear(); + addItems( aItems, 0 ); + } + } + break; + case BASEPROPERTY_SELECTEDITEMS: + { + css::uno::Sequence<sal_Int16> aItems; + if ( Value >>= aItems ) + { + for ( auto n = pListBox->GetEntryCount(); n; ) + pListBox->SelectEntryPos( --n, false ); + + if ( aItems.hasElements() ) + selectItemsPos( aItems, true ); + else + pListBox->SetNoSelection(); + + if ( !pListBox->GetSelectedEntryCount() ) + pListBox->SetTopEntry( 0 ); + } + } + break; + case BASEPROPERTY_HIGHLIGHT_COLOR: + { + Color nColor = 0; + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + if (bVoid) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + pListBox->SetHighlightColor(nColor); + } + break; + case BASEPROPERTY_HIGHLIGHT_TEXT_COLOR: + { + Color nColor = 0; + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + if (bVoid) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightTextColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + pListBox->SetHighlightTextColor(nColor); + } + break; + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXListBox::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + css::uno::Any aProp; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_ITEM_SEPARATOR_POS: + aProp <<= sal_Int16( pListBox->GetSeparatorPos() ); + break; + case BASEPROPERTY_READONLY: + { + aProp <<= pListBox->IsReadOnly(); + } + break; + case BASEPROPERTY_MULTISELECTION: + { + aProp <<= pListBox->IsMultiSelectionEnabled(); + } + break; + case BASEPROPERTY_MULTISELECTION_SIMPLEMODE: + { + aProp <<= ( ( pListBox->GetStyle() & WB_SIMPLEMODE ) == 0 ); + } + break; + case BASEPROPERTY_LINECOUNT: + { + aProp <<= static_cast<sal_Int16>(pListBox->GetDropDownLineCount()); + } + break; + case BASEPROPERTY_STRINGITEMLIST: + { + const sal_Int32 nItems = pListBox->GetEntryCount(); + css::uno::Sequence< OUString> aSeq( nItems ); + OUString* pStrings = aSeq.getArray(); + for ( sal_Int32 n = 0; n < nItems; ++n ) + pStrings[n] = pListBox->GetEntry( n ); + aProp <<= aSeq; + + } + break; + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + } + } + return aProp; +} + +css::awt::Size VCLXListBox::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + Size aSz; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + aSz = pListBox->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXListBox::getPreferredSize( ) +{ + SolarMutexGuard aGuard; + Size aSz; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + { + aSz = pListBox->CalcMinimumSize(); + if ( pListBox->GetStyle() & WB_DROPDOWN ) + aSz.AdjustHeight(4 ); + } + return AWTSize(aSz); +} + +css::awt::Size VCLXListBox::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + Size aSz = VCLSize(rNewSize); + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + aSz = pListBox->CalcAdjustedSize( aSz ); + return AWTSize(aSz); +} + +css::awt::Size VCLXListBox::getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + SolarMutexGuard aGuard; + Size aSz; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + aSz = pListBox->CalcBlockSize( nCols, nLines ); + return AWTSize(aSz); +} + +void VCLXListBox::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + SolarMutexGuard aGuard; + nCols = nLines = 0; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox ) + { + sal_uInt16 nC, nL; + pListBox->GetMaxVisColumnsAndLines( nC, nL ); + nCols = nC; + nLines = nL; + } +} + +void VCLXListBox::ImplCallItemListeners() +{ + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + if ( pListBox && maItemListeners.getLength() ) + { + css::awt::ItemEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Highlighted = 0; + + // Set to 0xFFFF on multiple selection, selected entry ID otherwise + aEvent.Selected = (pListBox->GetSelectedEntryCount() == 1 ) ? pListBox->GetSelectedEntryPos() : 0xFFFF; + + maItemListeners.itemStateChanged( aEvent ); + } +} +namespace +{ + Image lcl_getImageFromURL( const OUString& i_rImageURL ) + { + if ( i_rImageURL.isEmpty() ) + return Image(); + + try + { + Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XGraphicProvider > xProvider(graphic::GraphicProvider::create(xContext)); + ::comphelper::NamedValueCollection aMediaProperties; + aMediaProperties.put( "URL", i_rImageURL ); + Reference< XGraphic > xGraphic = xProvider->queryGraphic( aMediaProperties.getPropertyValues() ); + return Image( xGraphic ); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + return Image(); + } +} +void SAL_CALL VCLXListBox::listItemInserted( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + + ENSURE_OR_RETURN_VOID( pListBox, "VCLXListBox::listItemInserted: no ListBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition <= pListBox->GetEntryCount() ), + "VCLXListBox::listItemInserted: illegal (inconsistent) item position!" ); + pListBox->InsertEntry( + i_rEvent.ItemText.IsPresent ? i_rEvent.ItemText.Value : OUString(), + i_rEvent.ItemImageURL.IsPresent ? TkResMgr::getImageFromURL( i_rEvent.ItemImageURL.Value ) : Image(), + i_rEvent.ItemPosition ); +} + +void SAL_CALL VCLXListBox::listItemRemoved( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + + ENSURE_OR_RETURN_VOID( pListBox, "VCLXListBox::listItemRemoved: no ListBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition < pListBox->GetEntryCount() ), + "VCLXListBox::listItemRemoved: illegal (inconsistent) item position!" ); + + pListBox->RemoveEntry( i_rEvent.ItemPosition ); +} + +void SAL_CALL VCLXListBox::listItemModified( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + + ENSURE_OR_RETURN_VOID( pListBox, "VCLXListBox::listItemModified: no ListBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition < pListBox->GetEntryCount() ), + "VCLXListBox::listItemModified: illegal (inconsistent) item position!" ); + + // VCL's ListBox does not support changing an entry's text or image, so remove and re-insert + + const OUString sNewText = i_rEvent.ItemText.IsPresent ? i_rEvent.ItemText.Value : pListBox->GetEntry( i_rEvent.ItemPosition ); + const Image aNewImage( i_rEvent.ItemImageURL.IsPresent ? TkResMgr::getImageFromURL( i_rEvent.ItemImageURL.Value ) : pListBox->GetEntryImage( i_rEvent.ItemPosition ) ); + + pListBox->RemoveEntry( i_rEvent.ItemPosition ); + pListBox->InsertEntry( sNewText, aNewImage, i_rEvent.ItemPosition ); +} + +void SAL_CALL VCLXListBox::allItemsRemoved( const EventObject& ) +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + ENSURE_OR_RETURN_VOID( pListBox, "VCLXListBox::listItemModified: no ListBox?!" ); + + pListBox->Clear(); +} + +void SAL_CALL VCLXListBox::itemListChanged( const EventObject& i_rEvent ) +{ + SolarMutexGuard aGuard; + + VclPtr< ListBox > pListBox = GetAs< ListBox >(); + ENSURE_OR_RETURN_VOID( pListBox, "VCLXListBox::listItemModified: no ListBox?!" ); + + pListBox->Clear(); + + uno::Reference< beans::XPropertySet > xPropSet( i_rEvent.Source, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySetInfo > xPSI( xPropSet->getPropertySetInfo(), uno::UNO_SET_THROW ); + uno::Reference< resource::XStringResourceResolver > xStringResourceResolver; + if ( xPSI->hasPropertyByName("ResourceResolver") ) + { + xStringResourceResolver.set( + xPropSet->getPropertyValue("ResourceResolver"), + uno::UNO_QUERY + ); + } + + + Reference< XItemList > xItemList( i_rEvent.Source, uno::UNO_QUERY_THROW ); + const uno::Sequence< beans::Pair< OUString, OUString > > aItems = xItemList->getAllItems(); + for ( const auto& rItem : aItems ) + { + OUString aLocalizationKey( rItem.First ); + if ( xStringResourceResolver.is() && aLocalizationKey.startsWith("&") ) + { + aLocalizationKey = xStringResourceResolver->resolveString(aLocalizationKey.copy( 1 )); + } + pListBox->InsertEntry( aLocalizationKey, lcl_getImageFromURL( rItem.Second ) ); + } +} + +void SAL_CALL VCLXListBox::disposing( const EventObject& i_rEvent ) +{ + // just disambiguate + VCLXWindow::disposing( i_rEvent ); +} + + + + +void VCLXMessageBox::GetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXTopWindow::ImplGetPropertyIds( rIds ); +} + +VCLXMessageBox::VCLXMessageBox() +{ +} + +VCLXMessageBox::~VCLXMessageBox() +{ +} + +void VCLXMessageBox::setCaptionText( const OUString& rText ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( rText ); +} + +OUString VCLXMessageBox::getCaptionText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + aText = pWindow->GetText(); + return aText; +} + +void VCLXMessageBox::setMessageText( const OUString& rText ) +{ + SolarMutexGuard aGuard; + VclPtr< MessBox > pBox = GetAs< MessBox >(); + if ( pBox ) + pBox->SetMessText( rText ); +} + +OUString VCLXMessageBox::getMessageText() +{ + SolarMutexGuard aGuard; + OUString aText; + VclPtr< MessBox > pBox = GetAs< MessBox >(); + if ( pBox ) + aText = pBox->GetMessText(); + return aText; +} + +sal_Int16 VCLXMessageBox::execute() +{ + SolarMutexGuard aGuard; + VclPtr< MessBox > pBox = GetAs< MessBox >(); + return pBox ? pBox->Execute() : 0; +} + +css::awt::Size SAL_CALL VCLXMessageBox::getMinimumSize() +{ + return css::awt::Size( 250, 100 ); +} + + + +void VCLXDialog::GetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + VCLXTopWindow::ImplGetPropertyIds( rIds ); +} + +VCLXDialog::VCLXDialog() +{ + SAL_INFO("toolkit", "XDialog created"); +} + +VCLXDialog::~VCLXDialog() +{ + SAL_INFO("toolkit", __FUNCTION__); +} + +void SAL_CALL VCLXDialog::endDialog( ::sal_Int32 i_result ) +{ + SolarMutexGuard aGuard; + VclPtr<Dialog> pDialog = GetAsDynamic< Dialog >(); + if ( pDialog ) + pDialog->EndDialog( i_result ); +} + +void SAL_CALL VCLXDialog::setHelpId( const OUString& rId ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetHelpId( rId ); +} + +void VCLXDialog::setTitle( const OUString& Title ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( Title ); +} + +OUString VCLXDialog::getTitle() +{ + SolarMutexGuard aGuard; + + OUString aTitle; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + aTitle = pWindow->GetText(); + return aTitle; +} + +sal_Int16 VCLXDialog::execute() +{ + SolarMutexGuard aGuard; + + sal_Int16 nRet = 0; + if ( GetWindow() ) + { + VclPtr< Dialog > pDlg = GetAs< Dialog >(); + vcl::Window* pParent = pDlg->GetWindow( GetWindowType::ParentOverlap ); + vcl::Window* pOldParent = nullptr; + vcl::Window* pSetParent = nullptr; + if ( pParent && !pParent->IsReallyVisible() ) + { + pOldParent = pDlg->GetParent(); + vcl::Window* pFrame = pDlg->GetWindow( GetWindowType::Frame ); + if( pFrame != pDlg ) + { + pDlg->SetParent( pFrame ); + pSetParent = pFrame; + } + } + + nRet = pDlg->Execute(); + + // set the parent back only in case no new parent was set from outside + // in other words, revert only own changes + if ( pOldParent && pDlg->GetParent() == pSetParent ) + pDlg->SetParent( pOldParent ); + } + return nRet; +} + +void VCLXDialog::endExecute() +{ + endDialog(0); +} + +void SAL_CALL VCLXDialog::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() ); + if ( !pDev ) + pDev = pWindow->GetParent()->GetOutDev(); + + Point aPos = pDev->PixelToLogic( Point( nX, nY ) ); + pWindow->Draw( pDev, aPos, SystemTextColorFlags::NoControls ); + } +} + +css::awt::DeviceInfo VCLXDialog::getInfo() +{ + css::awt::DeviceInfo aInfo = VCLXDevice::getInfo(); + + SolarMutexGuard aGuard; + VclPtr< Dialog > pDlg = GetAs< Dialog >(); + if ( pDlg ) + pDlg->GetDrawWindowBorder( aInfo.LeftInset, aInfo.TopInset, aInfo.RightInset, aInfo.BottomInset ); + + return aInfo; +} + +void SAL_CALL VCLXDialog::setProperty( + const OUString& PropertyName, + const css::uno::Any& Value ) +{ + SolarMutexGuard aGuard; + VclPtr< Dialog > pDialog = GetAs< Dialog >(); + if ( !pDialog ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_GRAPHIC: + { + Reference< XGraphic > xGraphic; + if (( Value >>= xGraphic ) && xGraphic.is() ) + { + Graphic aImage(xGraphic); + + Wallpaper aWallpaper(aImage.GetBitmapEx()); + aWallpaper.SetStyle( WallpaperStyle::Scale ); + pDialog->SetBackground( aWallpaper ); + } + else if ( bVoid || !xGraphic.is() ) + { + Color aColor = pDialog->GetControlBackground(); + if ( aColor == COL_AUTO ) + aColor = pDialog->GetSettings().GetStyleSettings().GetDialogColor(); + + Wallpaper aWallpaper( aColor ); + pDialog->SetBackground( aWallpaper ); + } + } + break; + + default: + { + VCLXContainer::setProperty( PropertyName, Value ); + } + } +} + + + +VCLXMultiPage::VCLXMultiPage() : maTabListeners( *this ), mTabId( 1 ) +{ + SAL_INFO("toolkit", "VCLXMultiPage::VCLXMultiPage()" ); +} + +void VCLXMultiPage::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_MULTIPAGEVALUE, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEALIGN, + BASEPROPERTY_IMAGEPOSITION, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_FOCUSONCLICK, + 0); + VCLXContainer::ImplGetPropertyIds( rIds ); +} + +VCLXMultiPage::~VCLXMultiPage() +{ +} +void SAL_CALL VCLXMultiPage::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maTabListeners.disposeAndClear( aObj ); + VCLXContainer::dispose(); +} +// css::awt::XView +void SAL_CALL VCLXMultiPage::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + + if ( pWindow ) + { + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() ); + if ( !pDev ) + pDev = pWindow->GetParent()->GetOutDev(); + + Point aPos = pDev->PixelToLogic( Point( nX, nY ) ); + pWindow->Draw( pDev, aPos, SystemTextColorFlags::NoControls ); + } +} + +uno::Any SAL_CALL VCLXMultiPage::getProperty( const OUString& PropertyName ) +{ + SAL_INFO("toolkit", " **** VCLXMultiPage::getProperty " << PropertyName ); + SolarMutexGuard aGuard; + css::uno::Any aProp; + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + + case BASEPROPERTY_MULTIPAGEVALUE: + { + aProp <<= getActiveTabID(); + } + break; + default: + aProp = VCLXContainer::getProperty( PropertyName ); + } + return aProp; +} + +void SAL_CALL VCLXMultiPage::setProperty( + const OUString& PropertyName, + const css::uno::Any& Value ) +{ + SAL_INFO("toolkit", " **** VCLXMultiPage::setProperty " << PropertyName ); + SolarMutexGuard aGuard; + + VclPtr< TabControl > pTabControl = GetAs< TabControl >(); + if ( !pTabControl ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_MULTIPAGEVALUE: + { + SAL_INFO("toolkit", "***MULTIPAGE VALUE"); + sal_Int32 nId(0); + Value >>= nId; + // when the multipage is created we attempt to set the activepage + // but no pages created + if ( nId && nId <= getWindows().getLength() ) + activateTab( nId ); + break; + } + case BASEPROPERTY_GRAPHIC: + { + Reference< XGraphic > xGraphic; + if (( Value >>= xGraphic ) && xGraphic.is() ) + { + Graphic aImage(xGraphic); + + Wallpaper aWallpaper(aImage.GetBitmapEx()); + aWallpaper.SetStyle( WallpaperStyle::Scale ); + pTabControl->SetBackground( aWallpaper ); + } + else if ( bVoid || !xGraphic.is() ) + { + Color aColor = pTabControl->GetControlBackground(); + if ( aColor == COL_AUTO ) + aColor = pTabControl->GetSettings().GetStyleSettings().GetDialogColor(); + + Wallpaper aWallpaper( aColor ); + pTabControl->SetBackground( aWallpaper ); + } + } + break; + + default: + { + VCLXContainer::setProperty( PropertyName, Value ); + } + } +} + +TabControl *VCLXMultiPage::getTabControl() const +{ + VclPtr<TabControl> pTabControl = GetAsDynamic< TabControl >(); + if ( pTabControl ) + return pTabControl; + throw uno::RuntimeException(); +} +sal_Int32 SAL_CALL VCLXMultiPage::insertTab() +{ + TabControl *pTabControl = getTabControl(); + VclPtrInstance<TabPage> pTab( pTabControl ); + return static_cast< sal_Int32 >( insertTab( pTab, OUString() ) ); +} + +sal_uInt16 VCLXMultiPage::insertTab( TabPage* pPage, OUString const & sTitle ) +{ + TabControl *pTabControl = getTabControl(); + sal_uInt16 id = sal::static_int_cast< sal_uInt16 >( mTabId++ ); + pTabControl->InsertPage( id, sTitle ); + pTabControl->SetTabPage( id, pPage ); + return id; +} + +void SAL_CALL VCLXMultiPage::removeTab( sal_Int32 ID ) +{ + TabControl *pTabControl = getTabControl(); + if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == nullptr ) + throw lang::IndexOutOfBoundsException(); + pTabControl->RemovePage( sal::static_int_cast< sal_uInt16 >( ID ) ); +} + +void SAL_CALL VCLXMultiPage::activateTab( sal_Int32 ID ) +{ + TabControl *pTabControl = getTabControl(); + SAL_INFO( + "toolkit", + "Attempting to activate tab " << ID << ", active tab is " + << getActiveTabID() << ", numtabs is " << getWindows().getLength()); + if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == nullptr ) + throw lang::IndexOutOfBoundsException(); + pTabControl->SelectTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ); +} + +sal_Int32 SAL_CALL VCLXMultiPage::getActiveTabID() +{ + return getTabControl()->GetCurPageId( ); +} + +void SAL_CALL VCLXMultiPage::addTabListener( const uno::Reference< awt::XTabListener >& xListener ) +{ + SolarMutexGuard aGuard; + maTabListeners.addInterface( xListener ); +} + +void SAL_CALL VCLXMultiPage::removeTabListener( const uno::Reference< awt::XTabListener >& xListener ) +{ + SolarMutexGuard aGuard; + maTabListeners.addInterface( xListener ); +} + +void SAL_CALL VCLXMultiPage::setTabProps( sal_Int32 ID, const uno::Sequence< beans::NamedValue >& Properties ) +{ + SolarMutexGuard aGuard; + TabControl *pTabControl = getTabControl(); + if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == nullptr ) + throw lang::IndexOutOfBoundsException(); + + for (const auto& rProp : Properties) + { + const OUString &name = rProp.Name; + const uno::Any &value = rProp.Value; + + if (name == "Title") + { + OUString title = value.get<OUString>(); + pTabControl->SetPageText( sal::static_int_cast< sal_uInt16 >( ID ), title ); + } + } +} + +uno::Sequence< beans::NamedValue > SAL_CALL VCLXMultiPage::getTabProps( sal_Int32 ID ) +{ + SolarMutexGuard aGuard; + TabControl *pTabControl = getTabControl(); + if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == nullptr ) + throw lang::IndexOutOfBoundsException(); + + uno::Sequence< beans::NamedValue > props + { + { "Title", css::uno::Any(pTabControl->GetPageText( sal::static_int_cast< sal_uInt16 >( ID ) )) }, + { "Position", css::uno::Any(pTabControl->GetPagePos( sal::static_int_cast< sal_uInt16 >( ID ) )) } + }; + return props; +} +void VCLXMultiPage::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::TabpageDeactivate: + { + sal_uLong nPageID = reinterpret_cast<sal_uLong>( rVclWindowEvent.GetData() ); + maTabListeners.deactivated( nPageID ); + break; + + } + case VclEventId::TabpageActivate: + { + sal_uLong nPageID = reinterpret_cast<sal_uLong>( rVclWindowEvent.GetData() ); + maTabListeners.activated( nPageID ); + break; + } + default: + VCLXContainer::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + + + +VCLXTabPage::VCLXTabPage() +{ +} + +void VCLXTabPage::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_IMAGEALIGN, + BASEPROPERTY_IMAGEPOSITION, + BASEPROPERTY_IMAGEURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_FOCUSONCLICK, + 0); + VCLXContainer::ImplGetPropertyIds( rIds ); +} + +VCLXTabPage::~VCLXTabPage() +{ +} + +// css::awt::XView +void SAL_CALL VCLXTabPage::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + + if ( pWindow ) + { + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() ); + if ( !pDev ) + pDev = pWindow->GetParent()->GetOutDev(); + + Point aPos = pDev->PixelToLogic( Point( nX, nY ) ); + pWindow->Draw( pDev, aPos, SystemTextColorFlags::NoControls ); + } +} + +void SAL_CALL VCLXTabPage::setProperty( + const OUString& PropertyName, + const css::uno::Any& Value ) +{ + SolarMutexGuard aGuard; + VclPtr< TabPage > pTabPage = GetAs< TabPage >(); + if ( !pTabPage ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_GRAPHIC: + { + Reference< XGraphic > xGraphic; + if (( Value >>= xGraphic ) && xGraphic.is() ) + { + Graphic aImage(xGraphic); + + Wallpaper aWallpaper(aImage.GetBitmapEx()); + aWallpaper.SetStyle( WallpaperStyle::Scale ); + pTabPage->SetBackground( aWallpaper ); + } + else if ( bVoid || !xGraphic.is() ) + { + Color aColor = pTabPage->GetControlBackground(); + if ( aColor == COL_AUTO ) + aColor = pTabPage->GetSettings().GetStyleSettings().GetDialogColor(); + + Wallpaper aWallpaper( aColor ); + pTabPage->SetBackground( aWallpaper ); + } + } + break; + case BASEPROPERTY_TITLE: + { + OUString sTitle; + if ( Value >>= sTitle ) + { + pTabPage->SetText(sTitle); + } + } + break; + + default: + { + VCLXContainer::setProperty( PropertyName, Value ); + } + } +} + +TabPage *VCLXTabPage::getTabPage() const +{ + VclPtr< TabPage > pTabPage = GetAsDynamic< TabPage >(); + if ( pTabPage ) + return pTabPage; + throw uno::RuntimeException(); +} + + + + +VCLXFixedHyperlink::VCLXFixedHyperlink() : + + maActionListeners( *this ) + +{ +} + +VCLXFixedHyperlink::~VCLXFixedHyperlink() +{ +} + +void VCLXFixedHyperlink::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maActionListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + +void VCLXFixedHyperlink::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ButtonClick: + { + if ( maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); + maActionListeners.actionPerformed( aEvent ); + } + [[fallthrough]]; + } + default: + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXFixedHyperlink::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXFixedHyperlink::setText( const OUString& Text ) +{ + SolarMutexGuard aGuard; + VclPtr< FixedHyperlink > pBase = GetAs< FixedHyperlink >(); + if (pBase) + pBase->SetText(Text); +} + +OUString VCLXFixedHyperlink::getText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + aText = pWindow->GetText(); + return aText; +} + +void VCLXFixedHyperlink::setURL( const OUString& URL ) +{ + SolarMutexGuard aGuard; + VclPtr< FixedHyperlink > pBase = GetAs< FixedHyperlink >(); + if ( pBase ) + pBase->SetURL( URL ); +} + +OUString VCLXFixedHyperlink::getURL( ) +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< FixedHyperlink > pBase = GetAs< FixedHyperlink >(); + if ( pBase ) + aText = pBase->GetURL(); + return aText; +} + +void VCLXFixedHyperlink::setAlignment( sal_Int16 nAlign ) +{ + SolarMutexGuard aGuard; + + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( !pWindow ) + return; + + WinBits nNewBits = 0; + if ( nAlign == css::awt::TextAlign::LEFT ) + nNewBits = WB_LEFT; + else if ( nAlign == css::awt::TextAlign::CENTER ) + nNewBits = WB_CENTER; + else + nNewBits = WB_RIGHT; + + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_LEFT|WB_CENTER|WB_RIGHT); + pWindow->SetStyle( nStyle | nNewBits ); +} + +sal_Int16 VCLXFixedHyperlink::getAlignment() +{ + SolarMutexGuard aGuard; + + sal_Int16 nAlign = 0; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + if ( nStyle & WB_LEFT ) + nAlign = css::awt::TextAlign::LEFT; + else if ( nStyle & WB_CENTER ) + nAlign = css::awt::TextAlign::CENTER; + else + nAlign = css::awt::TextAlign::RIGHT; + } + return nAlign; +} + +void VCLXFixedHyperlink::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXFixedHyperlink::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +css::awt::Size VCLXFixedHyperlink::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + Size aSz; + VclPtr< FixedText > pFixedText = GetAs< FixedText >(); + if ( pFixedText ) + aSz = pFixedText->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXFixedHyperlink::getPreferredSize( ) +{ + return getMinimumSize(); +} + +css::awt::Size VCLXFixedHyperlink::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + Size aSz( VCLUnoHelper::ConvertToVCLSize( rNewSize )); + VclPtr< FixedText > pFixedText = GetAs< FixedText >(); + if (pFixedText) + { + Size aMinSz = pFixedText->CalcMinimumSize(rNewSize.Width); + if ( ( aSz.Width() > aMinSz.Width() ) && ( aSz.Height() < aMinSz.Height() ) ) + aSz.setHeight( aMinSz.Height() ); + else + aSz = aMinSz; + } + + return VCLUnoHelper::ConvertToAWTSize(aSz); +} + +void VCLXFixedHyperlink::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< FixedHyperlink > pBase = GetAs< FixedHyperlink >(); + if ( !pBase ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LABEL: + { + OUString sNewLabel; + if ( Value >>= sNewLabel ) + pBase->SetText(sNewLabel); + break; + } + + case BASEPROPERTY_URL: + { + OUString sNewURL; + if ( Value >>= sNewURL ) + pBase->SetURL( sNewURL ); + break; + } + + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXFixedHyperlink::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< FixedHyperlink > pBase = GetAs< FixedHyperlink >(); + if ( pBase ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_URL: + { + aProp <<= pBase->GetURL(); + break; + } + + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXFixedHyperlink::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LABEL, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_NOLABEL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_URL, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + + + +void VCLXFixedText::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LABEL, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_NOLABEL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXFixedText::VCLXFixedText() +{ +} + +VCLXFixedText::~VCLXFixedText() +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXFixedText::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXFixedText::setText( const OUString& Text ) +{ + SolarMutexGuard aGuard; + + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + pWindow->SetText( Text ); +} + +OUString VCLXFixedText::getText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + aText = pWindow->GetText(); + return aText; +} + +void VCLXFixedText::setAlignment( sal_Int16 nAlign ) +{ + SolarMutexGuard aGuard; + + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( !pWindow ) + return; + + WinBits nNewBits = 0; + if ( nAlign == css::awt::TextAlign::LEFT ) + nNewBits = WB_LEFT; + else if ( nAlign == css::awt::TextAlign::CENTER ) + nNewBits = WB_CENTER; + else + nNewBits = WB_RIGHT; + + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_LEFT|WB_CENTER|WB_RIGHT); + pWindow->SetStyle( nStyle | nNewBits ); +} + +sal_Int16 VCLXFixedText::getAlignment() +{ + SolarMutexGuard aGuard; + + sal_Int16 nAlign = 0; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + if ( nStyle & WB_LEFT ) + nAlign = css::awt::TextAlign::LEFT; + else if ( nStyle & WB_CENTER ) + nAlign = css::awt::TextAlign::CENTER; + else + nAlign = css::awt::TextAlign::RIGHT; + } + return nAlign; +} + +css::awt::Size VCLXFixedText::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< FixedText > pFixedText = GetAs< FixedText >(); + if ( pFixedText ) + aSz = pFixedText->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXFixedText::getPreferredSize( ) +{ + return getMinimumSize(); +} + +css::awt::Size VCLXFixedText::calcAdjustedSize( const css::awt::Size& rMaxSize ) +{ + SolarMutexGuard aGuard; + + Size aAdjustedSize( VCLUnoHelper::ConvertToVCLSize( rMaxSize ) ); + VclPtr< FixedText > pFixedText = GetAs< FixedText >(); + if ( pFixedText ) + aAdjustedSize = pFixedText->CalcMinimumSize( rMaxSize.Width ); + return VCLUnoHelper::ConvertToAWTSize( aAdjustedSize ); +} + + + +void VCLXScrollBar::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BLOCKINCREMENT, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LINEINCREMENT, + BASEPROPERTY_LIVE_SCROLL, + BASEPROPERTY_ORIENTATION, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SCROLLVALUE, + BASEPROPERTY_SCROLLVALUE_MAX, + BASEPROPERTY_SCROLLVALUE_MIN, + BASEPROPERTY_SYMBOL_COLOR, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VISIBLESIZE, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXScrollBar::VCLXScrollBar() : maAdjustmentListeners( *this ) +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXScrollBar::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +// css::lang::XComponent +void VCLXScrollBar::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maAdjustmentListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + +// css::awt::XScrollbar +void VCLXScrollBar::addAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener > & l ) +{ + SolarMutexGuard aGuard; + maAdjustmentListeners.addInterface( l ); +} + +void VCLXScrollBar::removeAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener > & l ) +{ + SolarMutexGuard aGuard; + maAdjustmentListeners.removeInterface( l ); +} + +void VCLXScrollBar::setValue( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->DoScroll( n ); +} + +void VCLXScrollBar::setValues( sal_Int32 nValue, sal_Int32 nVisible, sal_Int32 nMax ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + { + pScrollBar->SetVisibleSize( nVisible ); + pScrollBar->SetRangeMax( nMax ); + pScrollBar->DoScroll( nValue ); + } +} + +sal_Int32 VCLXScrollBar::getValue() +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetThumbPos() : 0; +} + +void VCLXScrollBar::setMaximum( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->SetRangeMax( n ); +} + +sal_Int32 VCLXScrollBar::getMaximum() +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetRangeMax() : 0; +} + +void VCLXScrollBar::setMinimum( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->SetRangeMin( n ); +} + +sal_Int32 VCLXScrollBar::getMinimum() const +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetRangeMin() : 0; +} + +void VCLXScrollBar::setLineIncrement( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->SetLineSize( n ); +} + +sal_Int32 VCLXScrollBar::getLineIncrement() +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetLineSize() : 0; +} + +void VCLXScrollBar::setBlockIncrement( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->SetPageSize( n ); +} + +sal_Int32 VCLXScrollBar::getBlockIncrement() +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetPageSize() : 0; +} + +void VCLXScrollBar::setVisibleSize( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + pScrollBar->SetVisibleSize( n ); +} + +sal_Int32 VCLXScrollBar::getVisibleSize() +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + return pScrollBar ? pScrollBar->GetVisibleSize() : 0; +} + +void VCLXScrollBar::setOrientation( sal_Int32 n ) +{ + SolarMutexGuard aGuard; + + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + nStyle &= ~(WB_HORZ|WB_VERT); + if ( n == css::awt::ScrollBarOrientation::HORIZONTAL ) + nStyle |= WB_HORZ; + else + nStyle |= WB_VERT; + + pWindow->SetStyle( nStyle ); + pWindow->Resize(); + } +} + +sal_Int32 VCLXScrollBar::getOrientation() +{ + SolarMutexGuard aGuard; + + sal_Int32 n = 0; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + WinBits nStyle = pWindow->GetStyle(); + if ( nStyle & WB_HORZ ) + n = css::awt::ScrollBarOrientation::HORIZONTAL; + else + n = css::awt::ScrollBarOrientation::VERTICAL; + } + return n; + +} + +// css::awt::VclWindowPeer +void VCLXScrollBar::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( !pScrollBar ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LIVE_SCROLL: + { + bool bDo = false; + if ( !bVoid ) + { + OSL_VERIFY( Value >>= bDo ); + } + AllSettings aSettings( pScrollBar->GetSettings() ); + StyleSettings aStyle( aSettings.GetStyleSettings() ); + DragFullOptions nDragOptions = aStyle.GetDragFullOptions(); + if ( bDo ) + nDragOptions |= DragFullOptions::Scroll; + else + nDragOptions &= ~DragFullOptions::Scroll; + aStyle.SetDragFullOptions( nDragOptions ); + aSettings.SetStyleSettings( aStyle ); + pScrollBar->SetSettings( aSettings ); + } + break; + + case BASEPROPERTY_SCROLLVALUE: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + setValue( n ); + } + } + break; + case BASEPROPERTY_SCROLLVALUE_MAX: + case BASEPROPERTY_SCROLLVALUE_MIN: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + { + if ( nPropType == BASEPROPERTY_SCROLLVALUE_MAX ) + setMaximum( n ); + else + setMinimum( n ); + } + } + } + break; + case BASEPROPERTY_LINEINCREMENT: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + setLineIncrement( n ); + } + } + break; + case BASEPROPERTY_BLOCKINCREMENT: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + setBlockIncrement( n ); + } + } + break; + case BASEPROPERTY_VISIBLESIZE: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + setVisibleSize( n ); + } + } + break; + case BASEPROPERTY_ORIENTATION: + { + if ( !bVoid ) + { + sal_Int32 n = 0; + if ( Value >>= n ) + setOrientation( n ); + } + } + break; + + case BASEPROPERTY_BACKGROUNDCOLOR: + { + // the default implementation of the base class doesn't work here, since our + // interpretation for this property is slightly different + ::toolkit::setButtonLikeFaceColor( pScrollBar, Value); + } + break; + + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXScrollBar::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + if ( pScrollBar ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + + switch ( nPropType ) + { + case BASEPROPERTY_LIVE_SCROLL: + { + aProp <<= bool( pScrollBar->GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Scroll ); + } + break; + case BASEPROPERTY_SCROLLVALUE: + { + aProp <<= getValue(); + } + break; + case BASEPROPERTY_SCROLLVALUE_MAX: + { + aProp <<= getMaximum(); + } + break; + case BASEPROPERTY_SCROLLVALUE_MIN: + { + aProp <<= getMinimum(); + } + break; + case BASEPROPERTY_LINEINCREMENT: + { + aProp <<= getLineIncrement(); + } + break; + case BASEPROPERTY_BLOCKINCREMENT: + { + aProp <<= getBlockIncrement(); + } + break; + case BASEPROPERTY_VISIBLESIZE: + { + aProp <<= getVisibleSize(); + } + break; + case BASEPROPERTY_ORIENTATION: + { + aProp <<= getOrientation(); + } + break; + case BASEPROPERTY_BACKGROUNDCOLOR: + { + // the default implementation of the base class doesn't work here, since our + // interpretation for this property is slightly different + aProp = ::toolkit::getButtonLikeFaceColor( pScrollBar ); + } + break; + + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXScrollBar::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ScrollbarScroll: + { + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // in during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + if ( maAdjustmentListeners.getLength() ) + { + VclPtr< ScrollBar > pScrollBar = GetAs< ScrollBar >(); + + if( pScrollBar ) + { + css::awt::AdjustmentEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Value = pScrollBar->GetThumbPos(); + + // set adjustment type + ScrollType aType = pScrollBar->GetType(); + if ( aType == ScrollType::LineUp || aType == ScrollType::LineDown ) + { + aEvent.Type = css::awt::AdjustmentType_ADJUST_LINE; + } + else if ( aType == ScrollType::PageUp || aType == ScrollType::PageDown ) + { + aEvent.Type = css::awt::AdjustmentType_ADJUST_PAGE; + } + else if ( aType == ScrollType::Drag ) + { + aEvent.Type = css::awt::AdjustmentType_ADJUST_ABS; + } + + maAdjustmentListeners.adjustmentValueChanged( aEvent ); + } + } + } + break; + + default: + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +css::awt::Size VCLXScrollBar::implGetMinimumSize( vcl::Window const * p ) +{ + tools::Long n = p->GetSettings().GetStyleSettings().GetScrollBarSize(); + return css::awt::Size( n, n ); +} + +css::awt::Size SAL_CALL VCLXScrollBar::getMinimumSize() +{ + SolarMutexGuard aGuard; + return implGetMinimumSize( GetWindow() ); +} + + + + +void VCLXEdit::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ECHOCHAR, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HARDLINEBREAKS, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_HSCROLL, + BASEPROPERTY_LINE_END_FORMAT, + BASEPROPERTY_MAXTEXTLEN, + BASEPROPERTY_MULTILINE, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TEXT, + BASEPROPERTY_VSCROLL, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_PAINTTRANSPARENT, + BASEPROPERTY_AUTOHSCROLL, + BASEPROPERTY_AUTOVSCROLL, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXEdit::VCLXEdit() : maTextListeners( *this ) +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXEdit::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXEdit::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maTextListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + +void VCLXEdit::addTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + SolarMutexGuard aGuard; + GetTextListeners().addInterface( l ); +} + +void VCLXEdit::removeTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + SolarMutexGuard aGuard; + GetTextListeners().removeInterface( l ); +} + +void VCLXEdit::setText( const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + pEdit->SetText( aText ); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pEdit->SetModifyFlag(); + pEdit->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +void VCLXEdit::insertText( const css::awt::Selection& rSel, const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + pEdit->SetSelection( Selection( rSel.Min, rSel.Max ) ); + pEdit->ReplaceSelected( aText ); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pEdit->SetModifyFlag(); + pEdit->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +OUString VCLXEdit::getText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + aText = pWindow->GetText(); + return aText; +} + +OUString VCLXEdit::getSelectedText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit) + aText = pEdit->GetSelected(); + return aText; + +} + +void VCLXEdit::setSelection( const css::awt::Selection& aSelection ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + pEdit->SetSelection( Selection( aSelection.Min, aSelection.Max ) ); +} + +css::awt::Selection VCLXEdit::getSelection() +{ + SolarMutexGuard aGuard; + + Selection aSel; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + aSel = pEdit->GetSelection(); + return css::awt::Selection( aSel.Min(), aSel.Max() ); +} + +sal_Bool VCLXEdit::isEditable() +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + return pEdit && !pEdit->IsReadOnly() && pEdit->IsEnabled(); +} + +void VCLXEdit::setEditable( sal_Bool bEditable ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + pEdit->SetReadOnly( !bEditable ); +} + + +void VCLXEdit::setMaxTextLen( sal_Int16 nLen ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + pEdit->SetMaxTextLen( nLen ); +} + +sal_Int16 VCLXEdit::getMaxTextLen() +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + return pEdit ? pEdit->GetMaxTextLen() : 0; +} + +void VCLXEdit::setEchoChar( sal_Unicode cEcho ) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + pEdit->SetEchoChar( cEcho ); +} + +void VCLXEdit::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( !pEdit ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_HIDEINACTIVESELECTION: + ::toolkit::adjustBooleanWindowStyle( Value, pEdit, WB_NOHIDESELECTION, true ); + if ( pEdit->GetSubEdit() ) + ::toolkit::adjustBooleanWindowStyle( Value, pEdit->GetSubEdit(), WB_NOHIDESELECTION, true ); + break; + + case BASEPROPERTY_READONLY: + { + bool b = bool(); + if ( Value >>= b ) + pEdit->SetReadOnly( b ); + } + break; + case BASEPROPERTY_ECHOCHAR: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + pEdit->SetEchoChar( n ); + } + break; + case BASEPROPERTY_MAXTEXTLEN: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + pEdit->SetMaxTextLen( n ); + } + break; + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXEdit::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_HIDEINACTIVESELECTION: + aProp <<= ( ( pEdit->GetStyle() & WB_NOHIDESELECTION ) == 0 ); + break; + case BASEPROPERTY_READONLY: + aProp <<= pEdit->IsReadOnly(); + break; + case BASEPROPERTY_ECHOCHAR: + aProp <<= static_cast<sal_Int16>(pEdit->GetEchoChar()); + break; + case BASEPROPERTY_MAXTEXTLEN: + aProp <<= static_cast<sal_Int16>(pEdit->GetMaxTextLen()); + break; + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + } + } + return aProp; +} + +css::awt::Size VCLXEdit::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + aSz = pEdit->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXEdit::getPreferredSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + aSz = pEdit->CalcMinimumSize(); + aSz.AdjustHeight(4 ); + } + return AWTSize(aSz); +} + +css::awt::Size VCLXEdit::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz = rNewSize; + css::awt::Size aMinSz = getMinimumSize(); + if ( aSz.Height != aMinSz.Height ) + aSz.Height = aMinSz.Height; + + return aSz; +} + +css::awt::Size VCLXEdit::getMinimumSize( sal_Int16 nCols, sal_Int16 ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + if ( nCols ) + aSz = pEdit->CalcSize( nCols ); + else + aSz = pEdit->CalcMinimumSize(); + } + return AWTSize(aSz); +} + +void VCLXEdit::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + SolarMutexGuard aGuard; + + nLines = 1; + nCols = 0; + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + nCols = pEdit->GetMaxVisChars(); +} + +void VCLXEdit::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::EditModify: + { + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + if ( GetTextListeners().getLength() ) + { + css::awt::TextEvent aEvent; + aEvent.Source = getXWeak(); + GetTextListeners().textChanged( aEvent ); + } + } + break; + + default: + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + + + + +void VCLXComboBox::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_AUTOCOMPLETE, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_DROPDOWN, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LINECOUNT, + BASEPROPERTY_MAXTEXTLEN, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_STRINGITEMLIST, + BASEPROPERTY_TYPEDITEMLIST, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TEXT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_ALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_REFERENCE_DEVICE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + // no, don't call VCLXEdit here - it has properties which we do *not* want to have at combo box + // #i92690# / 2008-08-12 / frank.schoenheit@sun.com + // VCLXEdit::ImplGetPropertyIds( rIds ); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXComboBox::VCLXComboBox() + : maActionListeners( *this ), maItemListeners( *this ) +{ +} + +VCLXComboBox::~VCLXComboBox() +{ + SAL_INFO("toolkit", __FUNCTION__); +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXComboBox::CreateAccessibleContext() +{ + SolarMutexGuard aGuard; + + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXComboBox::dispose() +{ + SolarMutexGuard aGuard; + + css::lang::EventObject aObj; + aObj.Source = getXWeak(); + maItemListeners.disposeAndClear( aObj ); + maActionListeners.disposeAndClear( aObj ); + VCLXEdit::dispose(); +} + + +void VCLXComboBox::addItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.addInterface( l ); +} + +void VCLXComboBox::removeItemListener( const css::uno::Reference< css::awt::XItemListener > & l ) +{ + SolarMutexGuard aGuard; + maItemListeners.removeInterface( l ); +} + +void VCLXComboBox::addActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.addInterface( l ); +} + +void VCLXComboBox::removeActionListener( const css::uno::Reference< css::awt::XActionListener > & l ) +{ + SolarMutexGuard aGuard; + maActionListeners.removeInterface( l ); +} + +void VCLXComboBox::addItem( const OUString& aItem, sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + pBox->InsertEntry( aItem, nPos ); +} + +void VCLXComboBox::addItems( const css::uno::Sequence< OUString>& aItems, sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( !pBox ) + return; + + sal_uInt16 nP = nPos; + for ( const auto& rItem : aItems ) + { + pBox->InsertEntry( rItem, nP ); + if ( nP == 0xFFFF ) + { + OSL_FAIL( "VCLXComboBox::addItems: too many entries!" ); + // skip remaining entries, list cannot hold them, anyway + break; + } + } +} + +void VCLXComboBox::removeItems( sal_Int16 nPos, sal_Int16 nCount ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + { + for ( sal_uInt16 n = nCount; n; ) + pBox->RemoveEntryAt( nPos + (--n) ); + } +} + +sal_Int16 VCLXComboBox::getItemCount() +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + return pBox ? pBox->GetEntryCount() : 0; +} + +OUString VCLXComboBox::getItem( sal_Int16 nPos ) +{ + SolarMutexGuard aGuard; + + OUString aItem; + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + aItem = pBox->GetEntry( nPos ); + return aItem; +} + +css::uno::Sequence< OUString> VCLXComboBox::getItems() +{ + SolarMutexGuard aGuard; + + css::uno::Sequence< OUString> aSeq; + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + { + auto n = pBox->GetEntryCount(); + aSeq = css::uno::Sequence< OUString>( n ); + while ( n ) + { + --n; + aSeq.getArray()[n] = pBox->GetEntry( n ); + } + } + return aSeq; +} + +void VCLXComboBox::setDropDownLineCount( sal_Int16 nLines ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + pBox->SetDropDownLineCount( nLines ); +} + +sal_Int16 VCLXComboBox::getDropDownLineCount() +{ + SolarMutexGuard aGuard; + + sal_Int16 nLines = 0; + VclPtr< ComboBox > pBox = GetAs< ComboBox >(); + if ( pBox ) + nLines = pBox->GetDropDownLineCount(); + return nLines; +} + +void VCLXComboBox::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( !pComboBox ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LINECOUNT: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + pComboBox->SetDropDownLineCount( n ); + } + break; + case BASEPROPERTY_AUTOCOMPLETE: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + pComboBox->EnableAutocomplete( n != 0 ); + else + { + bool b = bool(); + if ( Value >>= b ) + pComboBox->EnableAutocomplete( b ); + } + } + break; + case BASEPROPERTY_STRINGITEMLIST: + { + css::uno::Sequence< OUString> aItems; + if ( Value >>= aItems ) + { + pComboBox->Clear(); + addItems( aItems, 0 ); + } + } + break; + case BASEPROPERTY_HIGHLIGHT_COLOR: + { + Color nColor = 0; + if (bVoid) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + pComboBox->SetHighlightColor(nColor); + } + break; + case BASEPROPERTY_HIGHLIGHT_TEXT_COLOR: + { + Color nColor = 0; + if (bVoid) + { + nColor = Application::GetSettings().GetStyleSettings().GetHighlightTextColor(); + } + else + { + if (!(Value >>= nColor)) + break; + } + pComboBox->SetHighlightTextColor(nColor); + } + break; + default: + { + VCLXEdit::setProperty( PropertyName, Value ); + + // #109385# SetBorderStyle is not virtual + if ( nPropType == BASEPROPERTY_BORDER ) + { + sal_uInt16 nBorder = sal_uInt16(); + if ( (Value >>= nBorder) && nBorder != 0 ) + pComboBox->SetBorderStyle( static_cast<WindowBorderStyle>(nBorder) ); + } + } + } +} + +css::uno::Any VCLXComboBox::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LINECOUNT: + { + aProp <<= static_cast<sal_Int16>(pComboBox->GetDropDownLineCount()); + } + break; + case BASEPROPERTY_AUTOCOMPLETE: + { + aProp <<= pComboBox->IsAutocompleteEnabled(); + } + break; + case BASEPROPERTY_STRINGITEMLIST: + { + const sal_Int32 nItems = pComboBox->GetEntryCount(); + css::uno::Sequence< OUString> aSeq( nItems ); + OUString* pStrings = aSeq.getArray(); + for ( sal_Int32 n = 0; n < nItems; ++n ) + pStrings[n] = pComboBox->GetEntry( n ); + aProp <<= aSeq; + + } + break; + default: + { + aProp = VCLXEdit::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void VCLXComboBox::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + // since we call listeners below, there is a potential that we will be destroyed + // during the listener call. To prevent the resulting crashes, we keep us + // alive as long as we're here + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ComboboxSelect: + if ( maItemListeners.getLength() ) + { + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if( pComboBox ) + { + if ( !pComboBox->IsTravelSelect() ) + { + css::awt::ItemEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Highlighted = 0; + + // Set to 0xFFFF on multiple selection, selected entry ID otherwise + aEvent.Selected = pComboBox->GetEntryPos( pComboBox->GetText() ); + + maItemListeners.itemStateChanged( aEvent ); + } + } + } + break; + + case VclEventId::ComboboxDoubleClick: + if ( maActionListeners.getLength() ) + { + css::awt::ActionEvent aEvent; + aEvent.Source = getXWeak(); +// aEvent.ActionCommand = ...; + maActionListeners.actionPerformed( aEvent ); + } + break; + + default: + VCLXEdit::ProcessWindowEvent( rVclWindowEvent ); + break; + } +} + +css::awt::Size VCLXComboBox::getMinimumSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + aSz = pComboBox->CalcMinimumSize(); + return AWTSize(aSz); +} + +css::awt::Size VCLXComboBox::getPreferredSize( ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + { + aSz = pComboBox->CalcMinimumSize(); + if ( pComboBox->GetStyle() & WB_DROPDOWN ) + aSz.AdjustHeight(4 ); + } + return AWTSize(aSz); +} + +css::awt::Size VCLXComboBox::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + Size aSz = VCLSize(rNewSize); + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + aSz = pComboBox->CalcAdjustedSize( aSz ); + return AWTSize(aSz); +} + +css::awt::Size VCLXComboBox::getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + SolarMutexGuard aGuard; + + Size aSz; + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + aSz = pComboBox->CalcBlockSize( nCols, nLines ); + return AWTSize(aSz); +} + +void VCLXComboBox::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + SolarMutexGuard aGuard; + + nCols = nLines = 0; + VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); + if ( pComboBox ) + { + sal_uInt16 nC, nL; + pComboBox->GetMaxVisColumnsAndLines( nC, nL ); + nCols = nC; + nLines = nL; + } +} +void SAL_CALL VCLXComboBox::listItemInserted( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAsDynamic< ComboBox >(); + + ENSURE_OR_RETURN_VOID( pComboBox, "VCLXComboBox::listItemInserted: no ComboBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition <= pComboBox->GetEntryCount() ), + "VCLXComboBox::listItemInserted: illegal (inconsistent) item position!" ); + pComboBox->InsertEntryWithImage( + i_rEvent.ItemText.IsPresent ? i_rEvent.ItemText.Value : OUString(), + i_rEvent.ItemImageURL.IsPresent ? lcl_getImageFromURL( i_rEvent.ItemImageURL.Value ) : Image(), + i_rEvent.ItemPosition ); +} + +void SAL_CALL VCLXComboBox::listItemRemoved( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAsDynamic< ComboBox >(); + + ENSURE_OR_RETURN_VOID( pComboBox, "VCLXComboBox::listItemRemoved: no ComboBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition < pComboBox->GetEntryCount() ), + "VCLXComboBox::listItemRemoved: illegal (inconsistent) item position!" ); + + pComboBox->RemoveEntryAt( i_rEvent.ItemPosition ); +} + +void SAL_CALL VCLXComboBox::listItemModified( const ItemListEvent& i_rEvent ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAsDynamic< ComboBox >(); + + ENSURE_OR_RETURN_VOID( pComboBox, "VCLXComboBox::listItemModified: no ComboBox?!" ); + ENSURE_OR_RETURN_VOID( ( i_rEvent.ItemPosition >= 0 ) && ( i_rEvent.ItemPosition < pComboBox->GetEntryCount() ), + "VCLXComboBox::listItemModified: illegal (inconsistent) item position!" ); + + // VCL's ComboBox does not support changing an entry's text or image, so remove and re-insert + + const OUString sNewText = i_rEvent.ItemText.IsPresent ? i_rEvent.ItemText.Value : pComboBox->GetEntry( i_rEvent.ItemPosition ); + const Image aNewImage( i_rEvent.ItemImageURL.IsPresent ? lcl_getImageFromURL( i_rEvent.ItemImageURL.Value ) : pComboBox->GetEntryImage( i_rEvent.ItemPosition ) ); + + pComboBox->RemoveEntryAt( i_rEvent.ItemPosition ); + pComboBox->InsertEntryWithImage(sNewText, aNewImage, i_rEvent.ItemPosition); +} + +void SAL_CALL VCLXComboBox::allItemsRemoved( const EventObject& ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAsDynamic< ComboBox >(); + ENSURE_OR_RETURN_VOID( pComboBox, "VCLXComboBox::listItemModified: no ComboBox?!" ); + + pComboBox->Clear(); +} + +void SAL_CALL VCLXComboBox::itemListChanged( const EventObject& i_rEvent ) +{ + SolarMutexGuard aGuard; + + VclPtr< ComboBox > pComboBox = GetAsDynamic< ComboBox >(); + ENSURE_OR_RETURN_VOID( pComboBox, "VCLXComboBox::listItemModified: no ComboBox?!" ); + + pComboBox->Clear(); + + uno::Reference< beans::XPropertySet > xPropSet( i_rEvent.Source, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySetInfo > xPSI( xPropSet->getPropertySetInfo(), uno::UNO_SET_THROW ); + // bool localize = xPSI->hasPropertyByName("ResourceResolver"); + uno::Reference< resource::XStringResourceResolver > xStringResourceResolver; + if ( xPSI->hasPropertyByName("ResourceResolver") ) + { + xStringResourceResolver.set( + xPropSet->getPropertyValue("ResourceResolver"), + uno::UNO_QUERY + ); + } + + + Reference< XItemList > xItemList( i_rEvent.Source, uno::UNO_QUERY_THROW ); + const uno::Sequence< beans::Pair< OUString, OUString > > aItems = xItemList->getAllItems(); + for ( const auto& rItem : aItems ) + { + OUString aLocalizationKey( rItem.First ); + if ( xStringResourceResolver.is() && !aLocalizationKey.isEmpty() && aLocalizationKey[0] == '&' ) + { + aLocalizationKey = xStringResourceResolver->resolveString(aLocalizationKey.copy( 1 )); + } + pComboBox->InsertEntryWithImage(aLocalizationKey, + lcl_getImageFromURL(rItem.Second)); + } +} +void SAL_CALL VCLXComboBox::disposing( const EventObject& i_rEvent ) +{ + // just disambiguate + VCLXEdit::disposing( i_rEvent ); +} + + + +void VCLXFormattedSpinField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + // Interestingly in the UnoControl API this is + // - not derived from XEdit ultimately, (correct ?) - so cut this here ... +// VCLXSpinField::ImplGetPropertyIds( rIds ); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +VCLXFormattedSpinField::VCLXFormattedSpinField() + : mpFormatter(nullptr) +{ +} + +VCLXFormattedSpinField::~VCLXFormattedSpinField() +{ +} + +void VCLXFormattedSpinField::setStrictFormat( bool bStrict ) +{ + SolarMutexGuard aGuard; + + FormatterBase* pFormatter = GetFormatter(); + if ( pFormatter ) + pFormatter->SetStrictFormat( bStrict ); +} + +bool VCLXFormattedSpinField::isStrictFormat() const +{ + FormatterBase* pFormatter = GetFormatter(); + return pFormatter && pFormatter->IsStrictFormat(); +} + + +void VCLXFormattedSpinField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + FormatterBase* pFormatter = GetFormatter(); + if ( !pFormatter ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_SPIN: + { + bool b = bool(); + if ( Value >>= b ) + { + WinBits nStyle = GetWindow()->GetStyle() | WB_SPIN; + if ( !b ) + nStyle &= ~WB_SPIN; + GetWindow()->SetStyle( nStyle ); + } + } + break; + case BASEPROPERTY_STRICTFORMAT: + { + bool b = bool(); + if ( Value >>= b ) + { + pFormatter->SetStrictFormat( b ); + } + } + break; + default: + { + VCLXSpinField::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXFormattedSpinField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + FormatterBase* pFormatter = GetFormatter(); + if ( pFormatter ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_TABSTOP: + { + aProp <<= ( GetWindow()->GetStyle() & WB_SPIN ) != 0; + } + break; + case BASEPROPERTY_STRICTFORMAT: + { + aProp <<= pFormatter->IsStrictFormat(); + } + break; + default: + { + aProp = VCLXSpinField::getProperty( PropertyName ); + } + } + } + return aProp; +} + + + + +void VCLXDateField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DATE, + BASEPROPERTY_DATEMAX, + BASEPROPERTY_DATEMIN, + BASEPROPERTY_DATESHOWCENTURY, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_DROPDOWN, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_EXTDATEFORMAT, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SPIN, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_ENFORCE_FORMAT, + BASEPROPERTY_TEXT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXFormattedSpinField::ImplGetPropertyIds( rIds ); +} + +VCLXDateField::VCLXDateField() +{ +} + +VCLXDateField::~VCLXDateField() +{ +} + +//change the window type here to match the role +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXDateField::CreateAccessibleContext() +{ + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->SetType( WindowType::DATEFIELD ); + } + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXDateField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !(GetWindow()) ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_DATE: + { + if ( bVoid ) + { + GetAs< DateField >()->EnableEmptyFieldValue( true ); + GetAs< DateField >()->SetEmptyFieldValue(); + } + else + { + util::Date d; + if ((Value >>= d) && d.Year != 0) + setDate( d ); + } + } + break; + case BASEPROPERTY_DATEMIN: + { + util::Date d; + if ((Value >>= d) && d.Year != 0) + setMin( d ); + } + break; + case BASEPROPERTY_DATEMAX: + { + util::Date d; + if ((Value >>= d) && d.Year != 0) + setMax( d ); + } + break; + case BASEPROPERTY_EXTDATEFORMAT: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + GetAs< DateField >()->SetExtDateFormat( static_cast<ExtDateFieldFormat>(n) ); + } + break; + case BASEPROPERTY_DATESHOWCENTURY: + { + bool b = bool(); + if ( Value >>= b ) + GetAs< DateField >()->SetShowDateCentury( b ); + } + break; + case BASEPROPERTY_ENFORCE_FORMAT: + { + bool bEnforce( true ); + OSL_VERIFY( Value >>= bEnforce ); + GetAs< DateField >()->EnforceValidValue( bEnforce ); + } + break; + default: + { + VCLXFormattedSpinField::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXDateField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + FormatterBase* pFormatter = GetFormatter(); + if ( pFormatter ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_DATE: + { + aProp <<= getDate(); + } + break; + case BASEPROPERTY_DATEMIN: + { + aProp <<= getMin(); + } + break; + case BASEPROPERTY_DATEMAX: + { + aProp <<= getMax(); + } + break; + case BASEPROPERTY_DATESHOWCENTURY: + { + aProp <<= GetAs< DateField >()->IsShowDateCentury(); + } + break; + case BASEPROPERTY_ENFORCE_FORMAT: + { + aProp <<= GetAs< DateField >()->IsEnforceValidValue( ); + } + break; + default: + { + aProp = VCLXFormattedSpinField::getProperty( PropertyName ); + } + } + } + return aProp; +} + + +void VCLXDateField::setDate( const util::Date& aDate ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + { + pDateField->SetDate( aDate ); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pDateField->SetModifyFlag(); + pDateField->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +util::Date VCLXDateField::getDate() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + return pDateField->GetDate().GetUNODate(); + else + return util::Date(); +} + +void VCLXDateField::setMin( const util::Date& aDate ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + pDateField->SetMin( aDate ); +} + +util::Date VCLXDateField::getMin() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + return pDateField->GetMin().GetUNODate(); + else + return util::Date(); +} + +void VCLXDateField::setMax( const util::Date& aDate ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + pDateField->SetMax( aDate ); +} + +util::Date VCLXDateField::getMax() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + return pDateField->GetMax().GetUNODate(); + else + return util::Date(); +} + +void VCLXDateField::setFirst( const util::Date& aDate ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + pDateField->SetFirst( aDate ); +} + +util::Date VCLXDateField::getFirst() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + return pDateField->GetFirst().GetUNODate(); + else + return util::Date(); +} + +void VCLXDateField::setLast( const util::Date& aDate ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + pDateField->SetLast( aDate ); +} + +util::Date VCLXDateField::getLast() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + return pDateField->GetLast().GetUNODate(); + else + return util::Date(); +} + +void VCLXDateField::setLongFormat( sal_Bool bLong ) +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + pDateField->SetLongFormat( bLong ); +} + +sal_Bool VCLXDateField::isLongFormat() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + return pDateField && pDateField->IsLongFormat(); +} + +void VCLXDateField::setEmpty() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + if ( pDateField ) + { + pDateField->SetEmptyDate(); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pDateField->SetModifyFlag(); + pDateField->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +sal_Bool VCLXDateField::isEmpty() +{ + SolarMutexGuard aGuard; + + VclPtr< DateField > pDateField = GetAs< DateField >(); + return pDateField && pDateField->IsEmptyDate(); +} + +void VCLXDateField::setStrictFormat( sal_Bool bStrict ) +{ + VCLXFormattedSpinField::setStrictFormat( bStrict ); +} + +sal_Bool VCLXDateField::isStrictFormat() +{ + return VCLXFormattedSpinField::isStrictFormat(); +} + + + + +void VCLXTimeField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_EXTTIMEFORMAT, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SPIN, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TIME, + BASEPROPERTY_TIMEMAX, + BASEPROPERTY_TIMEMIN, + BASEPROPERTY_ENFORCE_FORMAT, + BASEPROPERTY_TEXT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXFormattedSpinField::ImplGetPropertyIds( rIds ); +} + +VCLXTimeField::VCLXTimeField() +{ +} + +VCLXTimeField::~VCLXTimeField() +{ +} + +//change the window type here to match the role +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXTimeField::CreateAccessibleContext() +{ + VclPtr< vcl::Window > pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->SetType( WindowType::TIMEFIELD ); + } + return getAccessibleFactory().createAccessibleContext( this ); +} + +void VCLXTimeField::setTime( const util::Time& aTime ) +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + { + pTimeField->SetTime( aTime ); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pTimeField->SetModifyFlag(); + pTimeField->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +util::Time VCLXTimeField::getTime() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + return pTimeField->GetTime().GetUNOTime(); + else + return util::Time(); +} + +void VCLXTimeField::setMin( const util::Time& aTime ) +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + pTimeField->SetMin( aTime ); +} + +util::Time VCLXTimeField::getMin() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + return pTimeField->GetMin().GetUNOTime(); + else + return util::Time(); +} + +void VCLXTimeField::setMax( const util::Time& aTime ) +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + pTimeField->SetMax( aTime ); +} + +util::Time VCLXTimeField::getMax() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + return pTimeField->GetMax().GetUNOTime(); + else + return util::Time(); +} + +void VCLXTimeField::setFirst( const util::Time& aTime ) +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + pTimeField->SetFirst( aTime ); +} + +util::Time VCLXTimeField::getFirst() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + return pTimeField->GetFirst().GetUNOTime(); + else + return util::Time(); +} + +void VCLXTimeField::setLast( const util::Time& aTime ) +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + pTimeField->SetLast( aTime ); +} + +util::Time VCLXTimeField::getLast() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + return pTimeField->GetLast().GetUNOTime(); + else + return util::Time(); +} + +void VCLXTimeField::setEmpty() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + if ( pTimeField ) + pTimeField->SetEmptyTime(); +} + +sal_Bool VCLXTimeField::isEmpty() +{ + SolarMutexGuard aGuard; + + VclPtr< TimeField > pTimeField = GetAs< TimeField >(); + return pTimeField && pTimeField->IsEmptyTime(); +} + +void VCLXTimeField::setStrictFormat( sal_Bool bStrict ) +{ + VCLXFormattedSpinField::setStrictFormat( bStrict ); +} + +sal_Bool VCLXTimeField::isStrictFormat() +{ + return VCLXFormattedSpinField::isStrictFormat(); +} + + +void VCLXTimeField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !(GetWindow()) ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_TIME: + { + if ( bVoid ) + { + GetAs< TimeField >()->EnableEmptyFieldValue( true ); + GetAs< TimeField >()->SetEmptyFieldValue(); + } + else + { + util::Time t; + if ( Value >>= t ) + setTime( t ); + } + } + break; + case BASEPROPERTY_TIMEMIN: + { + util::Time t; + if ( Value >>= t ) + setMin( t ); + } + break; + case BASEPROPERTY_TIMEMAX: + { + util::Time t; + if ( Value >>= t ) + setMax( t ); + } + break; + case BASEPROPERTY_EXTTIMEFORMAT: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + GetAs< TimeField >()->SetExtFormat( static_cast<ExtTimeFieldFormat>(n) ); + } + break; + case BASEPROPERTY_ENFORCE_FORMAT: + { + bool bEnforce( true ); + OSL_VERIFY( Value >>= bEnforce ); + GetAs< TimeField >()->EnforceValidValue( bEnforce ); + } + break; + default: + { + VCLXFormattedSpinField::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXTimeField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + if ( GetWindow() ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_TIME: + { + aProp <<= getTime(); + } + break; + case BASEPROPERTY_TIMEMIN: + { + aProp <<= getMin(); + } + break; + case BASEPROPERTY_TIMEMAX: + { + aProp <<= getMax(); + } + break; + case BASEPROPERTY_ENFORCE_FORMAT: + { + aProp <<= GetAs< TimeField >()->IsEnforceValidValue( ); + } + break; + default: + { + aProp = VCLXFormattedSpinField::getProperty( PropertyName ); + } + } + } + return aProp; +} + + + + +void VCLXNumericField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DECIMALACCURACY, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_NUMSHOWTHOUSANDSEP, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SPIN, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VALUEMAX_DOUBLE, + BASEPROPERTY_VALUEMIN_DOUBLE, + BASEPROPERTY_VALUESTEP_DOUBLE, + BASEPROPERTY_VALUE_DOUBLE, + BASEPROPERTY_ENFORCE_FORMAT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXFormattedSpinField::ImplGetPropertyIds( rIds ); +} + +VCLXNumericField::VCLXNumericField() +{ +} + +VCLXNumericField::~VCLXNumericField() +{ +} + +void VCLXNumericField::setValue( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( !pNumericFormatter ) + return; + + // shift long value using decimal digits + // (e.g., input 105 using 2 digits returns 1,05) + // Thus, to set a value of 1,05, insert 105 and 2 digits + pNumericFormatter->SetValue( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); + + // #107218# Call same listeners like VCL would do after user interaction + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + SetSynthesizingVCLEvent( true ); + pEdit->SetModifyFlag(); + pEdit->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +double VCLXNumericField::getValue() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetValue()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setMin( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + pNumericFormatter->SetMin( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); +} + +double VCLXNumericField::getMin() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetMin()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setMax( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + pNumericFormatter->SetMax( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); +} + +double VCLXNumericField::getMax() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetMax()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setFirst( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + pNumericFormatter->SetFirst( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); +} + +double VCLXNumericField::getFirst() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetFirst()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setLast( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + pNumericFormatter->SetLast( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); +} + +double VCLXNumericField::getLast() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetLast()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setStrictFormat( sal_Bool bStrict ) +{ + VCLXFormattedSpinField::setStrictFormat( bStrict ); +} + +sal_Bool VCLXNumericField::isStrictFormat() +{ + return VCLXFormattedSpinField::isStrictFormat(); +} + +void VCLXNumericField::setSpinSize( double Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + pNumericFormatter->SetSpinSize( + static_cast<tools::Long>(ImplCalcLongValue( Value, pNumericFormatter->GetDecimalDigits() )) ); +} + +double VCLXNumericField::getSpinSize() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter + ? ImplCalcDoubleValue( static_cast<double>(pNumericFormatter->GetSpinSize()), pNumericFormatter->GetDecimalDigits() ) + : 0; +} + +void VCLXNumericField::setDecimalDigits( sal_Int16 Value ) +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if ( pNumericFormatter ) + { + double n = getValue(); + pNumericFormatter->SetDecimalDigits( Value ); + setValue( n ); + } +} + +sal_Int16 VCLXNumericField::getDecimalDigits() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter ? pNumericFormatter->GetDecimalDigits() : 0; +} + +void VCLXNumericField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !(GetWindow()) ) + return; + + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VALUE_DOUBLE: + { + if ( bVoid ) + { + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if (!pNumericFormatter) + return; + pNumericFormatter->EnableEmptyFieldValue( true ); + pNumericFormatter->SetEmptyFieldValue(); + } + else + { + double d = 0; + if ( Value >>= d ) + setValue( d ); + } + } + break; + case BASEPROPERTY_VALUEMIN_DOUBLE: + { + double d = 0; + if ( Value >>= d ) + setMin( d ); + } + break; + case BASEPROPERTY_VALUEMAX_DOUBLE: + { + double d = 0; + if ( Value >>= d ) + setMax( d ); + } + break; + case BASEPROPERTY_VALUESTEP_DOUBLE: + { + double d = 0; + if ( Value >>= d ) + setSpinSize( d ); + } + break; + case BASEPROPERTY_DECIMALACCURACY: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + setDecimalDigits( n ); + } + break; + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + { + bool b = bool(); + if ( Value >>= b ) + { + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if (!pNumericFormatter) + return; + pNumericFormatter->SetUseThousandSep( b ); + } + } + break; + default: + { + VCLXFormattedSpinField::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXNumericField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + FormatterBase* pFormatter = GetFormatter(); + if ( pFormatter ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_VALUE_DOUBLE: + { + aProp <<= getValue(); + } + break; + case BASEPROPERTY_VALUEMIN_DOUBLE: + { + aProp <<= getMin(); + } + break; + case BASEPROPERTY_VALUEMAX_DOUBLE: + { + aProp <<= getMax(); + } + break; + case BASEPROPERTY_VALUESTEP_DOUBLE: + { + aProp <<= getSpinSize(); + } + break; + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + { + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(pFormatter); + aProp <<= pNumericFormatter->IsUseThousandSep(); + } + break; + default: + { + aProp = VCLXFormattedSpinField::getProperty( PropertyName ); + } + } + } + return aProp; +} + + +// ---------------------------------------------------- +// ---------------------------------------------------- + +void VCLXMetricField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DECIMALACCURACY, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_NUMSHOWTHOUSANDSEP, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SPIN, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_ENFORCE_FORMAT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_UNIT, + BASEPROPERTY_CUSTOMUNITTEXT, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + 0); + VCLXFormattedSpinField::ImplGetPropertyIds( rIds ); +} + +VCLXMetricField::VCLXMetricField() +{ +} + +VCLXMetricField::~VCLXMetricField() +{ +} + +MetricFormatter *VCLXMetricField::GetMetricFormatter() +{ + MetricFormatter *pFormatter = static_cast<MetricFormatter *>(GetFormatter()); + if (!pFormatter) + throw css::uno::RuntimeException(); + return pFormatter; +} + +MetricField *VCLXMetricField::GetMetricField() +{ + VclPtr< MetricField > pField = GetAs< MetricField >(); + if (!pField) + throw css::uno::RuntimeException(); + return pField; +} + +// FIXME: later ... +#define MetricUnitUnoToVcl(a) (static_cast<FieldUnit>(a)) + +#define METRIC_MAP_PAIR(method,parent) \ + sal_Int64 VCLXMetricField::get##method( sal_Int16 nUnit ) \ + { \ + SolarMutexGuard aGuard; \ + return GetMetric##parent()->Get##method( MetricUnitUnoToVcl( nUnit ) ); \ + } \ + void VCLXMetricField::set##method( sal_Int64 nValue, sal_Int16 nUnit ) \ + { \ + SolarMutexGuard aGuard; \ + GetMetric##parent()->Set##method( nValue, MetricUnitUnoToVcl( nUnit ) ); \ + } + +METRIC_MAP_PAIR(Min, Formatter) +METRIC_MAP_PAIR(Max, Formatter) +METRIC_MAP_PAIR(First, Field) +METRIC_MAP_PAIR(Last, Field) + +#undef METRIC_MAP_PAIR + +::sal_Int64 VCLXMetricField::getValue( ::sal_Int16 nUnit ) +{ + SolarMutexGuard aGuard; + return GetMetricFormatter()->GetValue( MetricUnitUnoToVcl( nUnit ) ); +} + +::sal_Int64 VCLXMetricField::getCorrectedValue( ::sal_Int16 nUnit ) +{ + SolarMutexGuard aGuard; + return GetMetricFormatter()->GetCorrectedValue( MetricUnitUnoToVcl( nUnit ) ); +} + +// FIXME: acute cut/paste evilness - move this to the parent Edit class ? +void VCLXMetricField::CallListeners() +{ + // #107218# Call same listeners like VCL would do after user interaction + VclPtr< Edit > pEdit = GetAs< Edit >(); + if ( pEdit ) + { + SetSynthesizingVCLEvent( true ); + pEdit->SetModifyFlag(); + pEdit->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +void VCLXMetricField::setValue( ::sal_Int64 Value, ::sal_Int16 Unit ) +{ + SolarMutexGuard aGuard; + GetMetricFormatter()->SetValue( Value, MetricUnitUnoToVcl( Unit ) ); + CallListeners(); +} + +void VCLXMetricField::setUserValue( ::sal_Int64 Value, ::sal_Int16 Unit ) +{ + SolarMutexGuard aGuard; + GetMetricFormatter()->SetUserValue( Value, MetricUnitUnoToVcl( Unit ) ); + CallListeners(); +} + +void VCLXMetricField::setStrictFormat( sal_Bool bStrict ) +{ + VCLXFormattedSpinField::setStrictFormat( bStrict ); +} + +sal_Bool VCLXMetricField::isStrictFormat() +{ + return VCLXFormattedSpinField::isStrictFormat(); +} + +void VCLXMetricField::setSpinSize( sal_Int64 Value ) +{ + SolarMutexGuard aGuard; + GetMetricField()->SetSpinSize( Value ); +} + +sal_Int64 VCLXMetricField::getSpinSize() +{ + SolarMutexGuard aGuard; + return GetMetricField()->GetSpinSize(); +} + +void VCLXMetricField::setDecimalDigits( sal_Int16 Value ) +{ + SolarMutexGuard aGuard; + GetMetricFormatter()->SetDecimalDigits( Value ); +} + +sal_Int16 VCLXMetricField::getDecimalDigits() +{ + SolarMutexGuard aGuard; + + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + return pNumericFormatter ? pNumericFormatter->GetDecimalDigits() : 0; +} + +void VCLXMetricField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !(GetWindow()) ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_DECIMALACCURACY: + { + sal_Int16 n = 0; + if ( Value >>= n ) + setDecimalDigits( n ); + break; + } + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + { + bool b = false; + if ( Value >>= b ) + { + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(GetFormatter()); + if (!pNumericFormatter) + return; + pNumericFormatter->SetUseThousandSep( b ); + } + } + break; + case BASEPROPERTY_UNIT: + { + sal_uInt16 nVal = 0; + if ( Value >>= nVal ) + GetAs< MetricField >()->SetUnit( static_cast<FieldUnit>(nVal) ); + break; + } + case BASEPROPERTY_CUSTOMUNITTEXT: + { + OUString aStr; + if ( Value >>= aStr ) + GetAs< MetricField >()->SetCustomUnitText( aStr ); + break; + } + default: + { + VCLXFormattedSpinField::setProperty( PropertyName, Value ); + break; + } + } +} + +css::uno::Any VCLXMetricField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + FormatterBase* pFormatter = GetFormatter(); + if ( pFormatter ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + { + NumericFormatter* pNumericFormatter = static_cast<NumericFormatter*>(pFormatter); + aProp <<= pNumericFormatter->IsUseThousandSep(); + break; + } + case BASEPROPERTY_UNIT: + aProp <<= static_cast<sal_uInt16>(GetAs< MetricField >()->GetUnit()); + break; + case BASEPROPERTY_CUSTOMUNITTEXT: + aProp <<= GetAs< MetricField >()->GetCustomUnitText(); + break; + default: + { + aProp = VCLXFormattedSpinField::getProperty( PropertyName ); + break; + } + } + } + return aProp; +} + +void VCLXPatternField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_EDITMASK, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_LITERALMASK, + BASEPROPERTY_MAXTEXTLEN, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_TEXT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXFormattedSpinField::ImplGetPropertyIds( rIds ); +} + +VCLXPatternField::VCLXPatternField() +{ +} + +VCLXPatternField::~VCLXPatternField() +{ +} + +void VCLXPatternField::setMasks( const OUString& EditMask, const OUString& LiteralMask ) +{ + SolarMutexGuard aGuard; + + VclPtr< PatternField > pPatternField = GetAs< PatternField >(); + if ( pPatternField ) + { + pPatternField->SetMask( OUStringToOString(EditMask, RTL_TEXTENCODING_ASCII_US), LiteralMask ); + } +} + +void VCLXPatternField::getMasks( OUString& EditMask, OUString& LiteralMask ) +{ + SolarMutexGuard aGuard; + + VclPtr< PatternField > pPatternField = GetAs< PatternField >(); + if ( pPatternField ) + { + EditMask = OStringToOUString(pPatternField->GetEditMask(), RTL_TEXTENCODING_ASCII_US); + LiteralMask = pPatternField->GetLiteralMask(); + } +} + +void VCLXPatternField::setString( const OUString& Str ) +{ + SolarMutexGuard aGuard; + VclPtr< PatternField > pPatternField = GetAs< PatternField >(); + if ( pPatternField ) + pPatternField->SetString( Str ); +} + +OUString VCLXPatternField::getString() +{ + SolarMutexGuard aGuard; + + OUString aString; + VclPtr< PatternField > pPatternField = GetAs< PatternField >(); + if ( pPatternField ) + aString = pPatternField->GetString(); + return aString; +} + +void VCLXPatternField::setStrictFormat( sal_Bool bStrict ) +{ + VCLXFormattedSpinField::setStrictFormat( bStrict ); +} + +sal_Bool VCLXPatternField::isStrictFormat() +{ + return VCLXFormattedSpinField::isStrictFormat(); +} + +void VCLXPatternField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + if ( !(GetWindow()) ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_EDITMASK: + case BASEPROPERTY_LITERALMASK: + { + OUString aString; + if ( Value >>= aString ) + { + OUString aEditMask, aLiteralMask; + getMasks( aEditMask, aLiteralMask ); + if ( nPropType == BASEPROPERTY_EDITMASK ) + aEditMask = aString; + else + aLiteralMask = aString; + setMasks( aEditMask, aLiteralMask ); + } + } + break; + default: + { + VCLXFormattedSpinField::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXPatternField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + if ( GetWindow() ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_EDITMASK: + case BASEPROPERTY_LITERALMASK: + { + OUString aEditMask, aLiteralMask; + getMasks( aEditMask, aLiteralMask ); + if ( nPropType == BASEPROPERTY_EDITMASK ) + aProp <<= aEditMask; + else + aProp <<= aLiteralMask; + } + break; + default: + { + aProp = VCLXFormattedSpinField::getProperty( PropertyName ); + } + } + } + return aProp; +} + + + +VCLXToolBox::VCLXToolBox() +{ +} + +VCLXToolBox::~VCLXToolBox() +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXToolBox::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + +VCLXHeaderBar::VCLXHeaderBar() +{ +} + +VCLXHeaderBar::~VCLXHeaderBar() +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > VCLXHeaderBar::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext( this ); +} + + +VCLXFrame::VCLXFrame() +{ +} + +void VCLXFrame::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_GRAPHIC, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_LABEL, + 0); + VCLXContainer::ImplGetPropertyIds( rIds ); +} + +VCLXFrame::~VCLXFrame() +{ +} + +// css::awt::XView +void SAL_CALL VCLXFrame::draw( sal_Int32 nX, sal_Int32 nY ) +{ + SolarMutexGuard aGuard; + VclPtr< vcl::Window > pWindow = GetWindow(); + + if ( pWindow ) + { + OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() ); + if ( !pDev ) + pDev = pWindow->GetParent()->GetOutDev(); + + Point aPos = pDev->PixelToLogic( Point( nX, nY ) ); + pWindow->Draw( pDev, aPos, SystemTextColorFlags::NoControls ); + } +} + +void SAL_CALL VCLXFrame::setProperty( + const OUString& PropertyName, + const css::uno::Any& Value ) +{ + SolarMutexGuard aGuard; + + VCLXContainer::setProperty( PropertyName, Value ); +} + +void VCLXFrame::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + css::uno::Reference< css::awt::XWindow > xKeepAlive( this ); + VCLXContainer::ProcessWindowEvent( rVclWindowEvent ); +} + +VCLXProgressBar::VCLXProgressBar() + :m_nValue(0) + ,m_nValueMin(0) + ,m_nValueMax(100) +{ +} + +VCLXProgressBar::~VCLXProgressBar() +{ +} + +void VCLXProgressBar::ImplUpdateValue() +{ + VclPtr< ProgressBar > pProgressBar = GetAs< ProgressBar >(); + if ( !pProgressBar ) + return; + + sal_Int32 nVal; + sal_Int32 nValMin; + sal_Int32 nValMax; + + // check min and max + if (m_nValueMin < m_nValueMax) + { + nValMin = m_nValueMin; + nValMax = m_nValueMax; + } + else + { + nValMin = m_nValueMax; + nValMax = m_nValueMin; + } + + // check value + if (m_nValue < nValMin) + { + nVal = nValMin; + } + else if (m_nValue > nValMax) + { + nVal = nValMax; + } + else + { + nVal = m_nValue; + } + + // calculate percent + sal_Int32 nPercent; + if (nValMin != nValMax) + { + nPercent = 100 * (nVal - nValMin) / (nValMax - nValMin); + } + else + { + nPercent = 0; + } + + // set progressbar value + pProgressBar->SetValue( static_cast<sal_uInt16>(nPercent) ); +} + +// css::awt::XProgressBar +void VCLXProgressBar::setForegroundColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->SetControlForeground( Color(ColorTransparency, nColor) ); + } +} + +void VCLXProgressBar::setBackgroundColor( sal_Int32 nColor ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + Color aColor( ColorTransparency, nColor ); + pWindow->SetBackground( aColor ); + pWindow->SetControlBackground( aColor ); + pWindow->Invalidate(); + } +} + +void VCLXProgressBar::setValue( sal_Int32 nValue ) +{ + SolarMutexGuard aGuard; + + m_nValue = nValue; + ImplUpdateValue(); +} + +void VCLXProgressBar::setRange( sal_Int32 nMin, sal_Int32 nMax ) +{ + SolarMutexGuard aGuard; + + if ( nMin < nMax ) + { + // take correct min and max + m_nValueMin = nMin; + m_nValueMax = nMax; + } + else + { + // change min and max + m_nValueMin = nMax; + m_nValueMax = nMin; + } + + ImplUpdateValue(); +} + +sal_Int32 VCLXProgressBar::getValue() +{ + SolarMutexGuard aGuard; + + return m_nValue; +} + +// css::awt::VclWindowPeer +void VCLXProgressBar::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< ProgressBar > pProgressBar = GetAs< ProgressBar >(); + if ( !pProgressBar ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_PROGRESSVALUE: + { + if ( Value >>= m_nValue ) + ImplUpdateValue(); + } + break; + case BASEPROPERTY_PROGRESSVALUE_MIN: + { + if ( Value >>= m_nValueMin ) + ImplUpdateValue(); + } + break; + case BASEPROPERTY_PROGRESSVALUE_MAX: + { + if ( Value >>= m_nValueMax ) + ImplUpdateValue(); + } + break; + case BASEPROPERTY_FILLCOLOR: + { + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + bool bVoid = Value.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + if ( bVoid ) + { + pWindow->SetControlForeground(); + } + else + { + Color nColor; + if ( Value >>= nColor ) + pWindow->SetControlForeground( nColor ); + } + } + } + break; + default: + VCLXWindow::setProperty( PropertyName, Value ); + break; + } +} + +css::uno::Any VCLXProgressBar::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< ProgressBar > pProgressBar = GetAs< ProgressBar >(); + if ( pProgressBar ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_PROGRESSVALUE: + { + aProp <<= m_nValue; + } + break; + case BASEPROPERTY_PROGRESSVALUE_MIN: + { + aProp <<= m_nValueMin; + } + break; + case BASEPROPERTY_PROGRESSVALUE_MAX: + { + aProp <<= m_nValueMax; + } + break; + default: + aProp = VCLXWindow::getProperty( PropertyName ); + break; + } + } + return aProp; +} + +void VCLXProgressBar::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_PROGRESSVALUE, + BASEPROPERTY_PROGRESSVALUE_MIN, + BASEPROPERTY_PROGRESSVALUE_MAX, + BASEPROPERTY_FILLCOLOR, + 0); + VCLXWindow::ImplGetPropertyIds( rIds, true ); +} + +VCLXFileControl::VCLXFileControl() : maTextListeners( *this ) +{ +} + +VCLXFileControl::~VCLXFileControl() +{ + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( pControl ) + pControl->GetEdit().SetModifyHdl( Link<Edit&,void>() ); +} + +namespace +{ + void lcl_setWinBits( vcl::Window* _pWindow, WinBits _nBits, bool _bSet ) + { + WinBits nStyle = _pWindow->GetStyle(); + if ( _bSet ) + nStyle |= _nBits; + else + nStyle &= ~_nBits; + _pWindow->SetStyle( nStyle ); + } +} + +void SAL_CALL VCLXFileControl::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( !pControl ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_HIDEINACTIVESELECTION: + { + bool bValue(false); + OSL_VERIFY( Value >>= bValue ); + + lcl_setWinBits( pControl, WB_NOHIDESELECTION, !bValue ); + lcl_setWinBits( &pControl->GetEdit(), WB_NOHIDESELECTION, !bValue ); + } + break; + + default: + VCLXWindow::setProperty( PropertyName, Value ); + break; + } +} + +void VCLXFileControl::SetWindow( const VclPtr< vcl::Window > &pWindow ) +{ + VclPtr< FileControl > pPrevFileControl = GetAsDynamic< FileControl >(); + if ( pPrevFileControl ) + pPrevFileControl->SetEditModifyHdl( Link<Edit&,void>() ); + + FileControl* pNewFileControl = dynamic_cast<FileControl*>( pWindow.get() ); + if ( pNewFileControl ) + pNewFileControl->SetEditModifyHdl( LINK( this, VCLXFileControl, ModifyHdl ) ); + + VCLXWindow::SetWindow( pWindow ); +} + +void VCLXFileControl::addTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + maTextListeners.addInterface( l ); +} + +void VCLXFileControl::removeTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + maTextListeners.removeInterface( l ); +} + +void VCLXFileControl::setText( const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->SetText( aText ); + + // also in Java a textChanged is triggered, not in VCL. + // css::awt::Toolkit should be JAVA-compliant... + ModifyHdl(); + } +} + +void VCLXFileControl::insertText( const css::awt::Selection& rSel, const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl ) + { + pFileControl->GetEdit().SetSelection( Selection( rSel.Min, rSel.Max ) ); + pFileControl->GetEdit().ReplaceSelected( aText ); + } +} + +OUString VCLXFileControl::getText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + aText = pWindow->GetText(); + return aText; +} + +OUString VCLXFileControl::getSelectedText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl) + aText = pFileControl->GetEdit().GetSelected(); + return aText; + +} + +void VCLXFileControl::setSelection( const css::awt::Selection& aSelection ) +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl ) + pFileControl->GetEdit().SetSelection( Selection( aSelection.Min, aSelection.Max ) ); +} + +css::awt::Selection VCLXFileControl::getSelection() +{ + SolarMutexGuard aGuard; + + css::awt::Selection aSel; + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl ) + { + aSel.Min = pFileControl->GetEdit().GetSelection().Min(); + aSel.Max = pFileControl->GetEdit().GetSelection().Max(); + } + return aSel; +} + +sal_Bool VCLXFileControl::isEditable() +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + return pFileControl && !pFileControl->GetEdit().IsReadOnly() && pFileControl->GetEdit().IsEnabled(); +} + +void VCLXFileControl::setEditable( sal_Bool bEditable ) +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl ) + pFileControl->GetEdit().SetReadOnly( !bEditable ); +} + +void VCLXFileControl::setMaxTextLen( sal_Int16 nLen ) +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + if ( pFileControl ) + pFileControl->GetEdit().SetMaxTextLen( nLen ); +} + +sal_Int16 VCLXFileControl::getMaxTextLen() +{ + SolarMutexGuard aGuard; + + VclPtr< FileControl > pFileControl = GetAs< FileControl >(); + return pFileControl ? pFileControl->GetEdit().GetMaxTextLen() : 0; +} + + +IMPL_LINK_NOARG(VCLXFileControl, ModifyHdl, Edit&, void) +{ + ModifyHdl(); +} + +void VCLXFileControl::ModifyHdl() +{ + css::awt::TextEvent aEvent; + aEvent.Source = getXWeak(); + maTextListeners.textChanged( aEvent ); +} + +css::awt::Size VCLXFileControl::getMinimumSize() +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz; + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( pControl ) + { + Size aTmpSize = pControl->GetEdit().CalcMinimumSize(); + aTmpSize.AdjustWidth(pControl->GetButton().CalcMinimumSize().Width() ); + aSz = AWTSize(pControl->CalcWindowSize( aTmpSize )); + } + return aSz; +} + +css::awt::Size VCLXFileControl::getPreferredSize() +{ + css::awt::Size aSz = getMinimumSize(); + aSz.Height += 4; + return aSz; +} + +css::awt::Size VCLXFileControl::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz =rNewSize; + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( pControl ) + { + css::awt::Size aMinSz = getMinimumSize(); + if ( aSz.Height != aMinSz.Height ) + aSz.Height = aMinSz.Height; + } + return aSz; +} + +css::awt::Size VCLXFileControl::getMinimumSize( sal_Int16 nCols, sal_Int16 ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz; + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( pControl ) + { + aSz = AWTSize(pControl->GetEdit().CalcSize( nCols )); + aSz.Width += pControl->GetButton().CalcMinimumSize().Width(); + } + return aSz; +} + +void VCLXFileControl::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + SolarMutexGuard aGuard; + + nCols = 0; + nLines = 1; + VclPtr< FileControl > pControl = GetAs< FileControl >(); + if ( pControl ) + nCols = pControl->GetEdit().GetMaxVisChars(); +} + +void VCLXFileControl::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + // FIXME: elide duplication ? + BASEPROPERTY_HIDEINACTIVESELECTION, + 0); + VCLXWindow::ImplGetPropertyIds( rIds, true ); +} + +SVTXFormattedField::SVTXFormattedField() + :bIsStandardSupplier(true) + ,nKeyToSetDelayed(-1) +{ +} + +SVTXFormattedField::~SVTXFormattedField() +{ +} + +void SVTXFormattedField::SetWindow( const VclPtr< vcl::Window > &_pWindow ) +{ + VCLXSpinField::SetWindow(_pWindow); + if (GetAs< FormattedField >()) + GetAs< FormattedField >()->GetFormatter().SetAutoColor(true); +} + +void SVTXFormattedField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + { + Formatter& rFormatter = pField->GetFormatter(); + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_ENFORCE_FORMAT: + { + bool bEnable( true ); + if ( Value >>= bEnable ) + rFormatter.EnableNotANumber( !bEnable ); + } + break; + + case BASEPROPERTY_EFFECTIVE_MIN: + case BASEPROPERTY_VALUEMIN_DOUBLE: + SetMinValue(Value); + break; + + case BASEPROPERTY_EFFECTIVE_MAX: + case BASEPROPERTY_VALUEMAX_DOUBLE: + SetMaxValue(Value); + break; + + case BASEPROPERTY_EFFECTIVE_DEFAULT: + SetDefaultValue(Value); + break; + + case BASEPROPERTY_TREATASNUMBER: + { + bool b; + if ( Value >>= b ) + SetTreatAsNumber(b); + } + break; + + case BASEPROPERTY_FORMATSSUPPLIER: + if (!Value.hasValue()) + setFormatsSupplier(css::uno::Reference< css::util::XNumberFormatsSupplier > (nullptr)); + else + { + css::uno::Reference< css::util::XNumberFormatsSupplier > xNFS; + if ( Value >>= xNFS ) + setFormatsSupplier(xNFS); + } + break; + case BASEPROPERTY_FORMATKEY: + if (!Value.hasValue()) + setFormatKey(0); + else + { + sal_Int32 n = 0; + if ( Value >>= n ) + setFormatKey(n); + } + break; + + case BASEPROPERTY_EFFECTIVE_VALUE: + case BASEPROPERTY_VALUE_DOUBLE: + { + const css::uno::TypeClass rTC = Value.getValueType().getTypeClass(); + if (rTC != css::uno::TypeClass_STRING) + // no string + if (rTC != css::uno::TypeClass_DOUBLE) + // no double + if (Value.hasValue()) + { // but a value + // try if it is something convertible + sal_Int32 nValue = 0; + if (!(Value >>= nValue)) + throw css::lang::IllegalArgumentException(); + SetValue(css::uno::Any(static_cast<double>(nValue))); + break; + } + + SetValue(Value); + } + break; + case BASEPROPERTY_VALUESTEP_DOUBLE: + { + double d = 0.0; + if ( Value >>= d ) + rFormatter.SetSpinSize( d ); + else + { + sal_Int32 n = 0; + if ( Value >>= n ) + rFormatter.SetSpinSize( n ); + } + } + break; + case BASEPROPERTY_DECIMALACCURACY: + { + sal_Int32 n = 0; + if ( Value >>= n ) + rFormatter.SetDecimalDigits( static_cast<sal_uInt16>(n) ); + } + break; + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + { + bool b; + if ( Value >>= b ) + rFormatter.SetThousandsSep( b ); + } + break; + + default: + VCLXSpinField::setProperty( PropertyName, Value ); + } + + if (BASEPROPERTY_TEXTCOLOR == nPropType) + { // after setting a new text color, think again about the AutoColor flag of the control + // 17.05.2001 - 86859 - frank.schoenheit@germany.sun.com + rFormatter.SetAutoColor(!Value.hasValue()); + } + } + else + VCLXSpinField::setProperty( PropertyName, Value ); +} + +css::uno::Any SVTXFormattedField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aReturn; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + { + Formatter& rFormatter = pField->GetFormatter(); + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_EFFECTIVE_MIN: + case BASEPROPERTY_VALUEMIN_DOUBLE: + aReturn = GetMinValue(); + break; + + case BASEPROPERTY_EFFECTIVE_MAX: + case BASEPROPERTY_VALUEMAX_DOUBLE: + aReturn = GetMaxValue(); + break; + + case BASEPROPERTY_EFFECTIVE_DEFAULT: + aReturn = GetDefaultValue(); + break; + + case BASEPROPERTY_TREATASNUMBER: + aReturn <<= GetTreatAsNumber(); + break; + + case BASEPROPERTY_EFFECTIVE_VALUE: + case BASEPROPERTY_VALUE_DOUBLE: + aReturn = GetValue(); + break; + + case BASEPROPERTY_VALUESTEP_DOUBLE: + aReturn <<= rFormatter.GetSpinSize(); + break; + + case BASEPROPERTY_DECIMALACCURACY: + aReturn <<= rFormatter.GetDecimalDigits(); + break; + + case BASEPROPERTY_FORMATSSUPPLIER: + { + if (!bIsStandardSupplier) + { // ansonsten void + css::uno::Reference< css::util::XNumberFormatsSupplier > xSupplier = m_xCurrentSupplier; + aReturn <<= xSupplier; + } + } + break; + + case BASEPROPERTY_FORMATKEY: + { + if (!bIsStandardSupplier) + aReturn <<= getFormatKey(); + } + break; + + default: + aReturn = VCLXSpinField::getProperty(PropertyName); + } + } + return aReturn; +} + +css::uno::Any SVTXFormattedField::convertEffectiveValue(const css::uno::Any& rValue) const +{ + css::uno::Any aReturn; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return aReturn; + + Formatter& rFieldFormatter = pField->GetFormatter(); + switch (rValue.getValueType().getTypeClass()) + { + case css::uno::TypeClass_DOUBLE: + if (rFieldFormatter.TreatingAsNumber()) + { + double d = 0.0; + rValue >>= d; + aReturn <<= d; + } + else + { + SvNumberFormatter* pFormatter = rFieldFormatter.GetFormatter(); + if (!pFormatter) + pFormatter = rFieldFormatter.StandardFormatter(); + // should never fail + + const Color* pDum; + double d = 0.0; + rValue >>= d; + OUString sConverted; + pFormatter->GetOutputString(d, 0, sConverted, &pDum); + aReturn <<= sConverted; + } + break; + case css::uno::TypeClass_STRING: + { + OUString aStr; + rValue >>= aStr; + if (rFieldFormatter.TreatingAsNumber()) + { + SvNumberFormatter* pFormatter = rFieldFormatter.GetFormatter(); + if (!pFormatter) + pFormatter = rFieldFormatter.StandardFormatter(); + + double dVal; + sal_uInt32 nTestFormat(0); + if (!pFormatter->IsNumberFormat(aStr, nTestFormat, dVal)) + aReturn.clear(); + aReturn <<= dVal; + } + else + aReturn <<= aStr; + } + break; + default: + aReturn.clear(); + break; + } + return aReturn; +} + +void SVTXFormattedField::SetMinValue(const css::uno::Any& rValue) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return; + + Formatter& rFormatter = pField->GetFormatter(); + switch (rValue.getValueType().getTypeClass()) + + { + case css::uno::TypeClass_DOUBLE: + { + double d = 0.0; + rValue >>= d; + rFormatter.SetMinValue(d); + break; + } + default: + DBG_ASSERT(rValue.getValueType().getTypeClass() == css::uno::TypeClass_VOID, "SVTXFormattedField::SetMinValue : invalid argument (an exception will be thrown) !"); + if ( rValue.getValueType().getTypeClass() != css::uno::TypeClass_VOID ) + + { + throw css::lang::IllegalArgumentException(); + } + rFormatter.ClearMinValue(); + break; + } +} + +css::uno::Any SVTXFormattedField::GetMinValue() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return css::uno::Any(); + Formatter& rFormatter = pField->GetFormatter(); + if (!rFormatter.HasMinValue()) + return css::uno::Any(); + + css::uno::Any aReturn; + aReturn <<= rFormatter.GetMinValue(); + return aReturn; +} + +void SVTXFormattedField::SetMaxValue(const css::uno::Any& rValue) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return; + + Formatter& rFormatter = pField->GetFormatter(); + switch (rValue.getValueType().getTypeClass()) + { + case css::uno::TypeClass_DOUBLE: + { + double d = 0.0; + rValue >>= d; + rFormatter.SetMaxValue(d); + break; + } + default: + if (rValue.getValueType().getTypeClass() != css::uno::TypeClass_VOID) + + { + throw css::lang::IllegalArgumentException(); + } + rFormatter.ClearMaxValue(); + break; + } +} + +css::uno::Any SVTXFormattedField::GetMaxValue() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return css::uno::Any(); + Formatter& rFormatter = pField->GetFormatter(); + if (!rFormatter.HasMaxValue()) + return css::uno::Any(); + + css::uno::Any aReturn; + aReturn <<= rFormatter.GetMaxValue(); + return aReturn; +} + +void SVTXFormattedField::SetDefaultValue(const css::uno::Any& rValue) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return; + + css::uno::Any aConverted = convertEffectiveValue(rValue); + + Formatter& rFormatter = pField->GetFormatter(); + switch (aConverted.getValueType().getTypeClass()) + { + case css::uno::TypeClass_DOUBLE: + { + double d = 0.0; + aConverted >>= d; + rFormatter.SetDefaultValue(d); + } + break; + case css::uno::TypeClass_STRING: + { + OUString aStr; + aConverted >>= aStr; + rFormatter.SetDefaultText( aStr ); + } + break; + default: + rFormatter.EnableEmptyField(true); + // only void accepted + break; + } +} + +css::uno::Any SVTXFormattedField::GetDefaultValue() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return css::uno::Any(); + Formatter& rFormatter = pField->GetFormatter(); + if (rFormatter.IsEmptyFieldEnabled()) + return css::uno::Any(); + + css::uno::Any aReturn; + if (rFormatter.TreatingAsNumber()) + aReturn <<= rFormatter.GetDefaultValue(); + else + aReturn <<= rFormatter.GetDefaultText(); + return aReturn; +} + +bool SVTXFormattedField::GetTreatAsNumber() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (pField) + return pField->GetFormatter().TreatingAsNumber(); + + return true; +} + +void SVTXFormattedField::SetTreatAsNumber(bool bSet) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (pField) + pField->GetFormatter().TreatAsNumber(bSet); +} + +css::uno::Any SVTXFormattedField::GetValue() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return css::uno::Any(); + + Formatter& rFormatter = pField->GetFormatter(); + css::uno::Any aReturn; + if (!rFormatter.TreatingAsNumber()) + { + OUString sText = rFormatter.GetTextValue(); + aReturn <<= sText; + } + else + { + if (!pField->GetText().isEmpty()) // empty is returned as void by default + aReturn <<= rFormatter.GetValue(); + } + + return aReturn; +} + +void SVTXFormattedField::SetValue(const css::uno::Any& rValue) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return; + + if (!rValue.hasValue()) + { + pField->SetText(""); + } + else + { + Formatter& rFormatter = pField->GetFormatter(); + if (rValue.getValueType().getTypeClass() == css::uno::TypeClass_DOUBLE ) + { + double d = 0.0; + rValue >>= d; + rFormatter.SetValue(d); + } + else + { + DBG_ASSERT(rValue.getValueType().getTypeClass() == css::uno::TypeClass_STRING, "SVTXFormattedField::SetValue : invalid argument !"); + + OUString sText; + rValue >>= sText; + if (!rFormatter.TreatingAsNumber()) + rFormatter.SetTextFormatted(sText); + else + rFormatter.SetTextValue(sText); + } + } +// NotifyTextListeners(); +} + +void SVTXFormattedField::setFormatsSupplier(const css::uno::Reference< css::util::XNumberFormatsSupplier > & xSupplier) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + + rtl::Reference<SvNumberFormatsSupplierObj> pNew; + if (!xSupplier.is()) + { + if (pField) + { + Formatter& rFormatter = pField->GetFormatter(); + pNew = new SvNumberFormatsSupplierObj(rFormatter.StandardFormatter()); + bIsStandardSupplier = true; + } + } + else + { + pNew = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>(xSupplier); + bIsStandardSupplier = false; + } + + if (!pNew) + return; // TODO : how to process ? + + m_xCurrentSupplier = pNew; + if (!pField) + return; + + // save the actual value + css::uno::Any aCurrent = GetValue(); + Formatter& rFormatter = pField->GetFormatter(); + rFormatter.SetFormatter(m_xCurrentSupplier->GetNumberFormatter(), false); + if (nKeyToSetDelayed != -1) + { + rFormatter.SetFormatKey(nKeyToSetDelayed); + nKeyToSetDelayed = -1; + } + SetValue(aCurrent); + NotifyTextListeners(); +} + +sal_Int32 SVTXFormattedField::getFormatKey() const +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetFormatKey() : 0; +} + +void SVTXFormattedField::setFormatKey(sal_Int32 nKey) +{ + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if (!pField) + return; + + Formatter& rFormatter = pField->GetFormatter(); + if (rFormatter.GetFormatter()) + rFormatter.SetFormatKey(nKey); + else + { + // probably I am in a block, in which first the key and next the formatter will be set, + // initially this happens quite certain, as the properties are set in alphabetic sequence, + // and the FormatsSupplier is processed before the FormatKey + nKeyToSetDelayed = nKey; + } + NotifyTextListeners(); +} + +void SVTXFormattedField::NotifyTextListeners() +{ + if ( GetTextListeners().getLength() ) + { + css::awt::TextEvent aEvent; + aEvent.Source = getXWeak(); + GetTextListeners().textChanged( aEvent ); + } +} + +void SVTXFormattedField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + // FIXME: elide duplication ? + BASEPROPERTY_EFFECTIVE_MIN, + BASEPROPERTY_VALUEMIN_DOUBLE, + BASEPROPERTY_EFFECTIVE_MAX, + BASEPROPERTY_VALUEMAX_DOUBLE, + BASEPROPERTY_EFFECTIVE_DEFAULT, + BASEPROPERTY_TREATASNUMBER, + BASEPROPERTY_EFFECTIVE_VALUE, + BASEPROPERTY_VALUE_DOUBLE, + BASEPROPERTY_VALUESTEP_DOUBLE, + BASEPROPERTY_DECIMALACCURACY, + BASEPROPERTY_FORMATSSUPPLIER, + BASEPROPERTY_NUMSHOWTHOUSANDSEP, + BASEPROPERTY_FORMATKEY, + BASEPROPERTY_TREATASNUMBER, + BASEPROPERTY_ENFORCE_FORMAT, + 0); + VCLXWindow::ImplGetPropertyIds( rIds, true ); + VCLXSpinField::ImplGetPropertyIds( rIds ); +} + +SVTXCurrencyField::SVTXCurrencyField() +{ +} + +SVTXCurrencyField::~SVTXCurrencyField() +{ +} + +void SVTXCurrencyField::setValue( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetValue( Value ); +} + +double SVTXCurrencyField::getValue() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetValue() : 0; +} + +void SVTXCurrencyField::setMin( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetMinValue( Value ); +} + +double SVTXCurrencyField::getMin() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetMinValue() : 0; +} + +void SVTXCurrencyField::setMax( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetMaxValue( Value ); +} + +double SVTXCurrencyField::getMax() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetMaxValue() : 0; +} + +void SVTXCurrencyField::setFirst( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinFirst( Value ); +} + +double SVTXCurrencyField::getFirst() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinFirst() : 0; +} + +void SVTXCurrencyField::setLast( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinLast( Value ); +} + +double SVTXCurrencyField::getLast() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinLast() : 0; +} + +void SVTXCurrencyField::setSpinSize( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinSize( Value ); +} + +double SVTXCurrencyField::getSpinSize() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinSize() : 0; +} + +void SVTXCurrencyField::setDecimalDigits( sal_Int16 Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetDecimalDigits( Value ); +} + +sal_Int16 SVTXCurrencyField::getDecimalDigits() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetDecimalDigits() : 0; +} + +void SVTXCurrencyField::setStrictFormat( sal_Bool bStrict ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetStrictFormat( bStrict ); +} + +sal_Bool SVTXCurrencyField::isStrictFormat() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField && pField->GetFormatter().IsStrictFormat(); +} + +void SVTXCurrencyField::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< DoubleCurrencyField > pField = GetAs< DoubleCurrencyField >(); + if ( pField ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_CURRENCYSYMBOL: + { + OUString aStr; + Value >>= aStr; + pField->setCurrencySymbol( aStr ); + } + break; + case BASEPROPERTY_CURSYM_POSITION: + { + bool b = false; + Value >>= b; + pField->setPrependCurrSym(b); + } + break; + + default: + SVTXFormattedField::setProperty(PropertyName, Value); + } + } + else + SVTXFormattedField::setProperty(PropertyName, Value); +} + +css::uno::Any SVTXCurrencyField::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aReturn; + + VclPtr< DoubleCurrencyField > pField = GetAs< DoubleCurrencyField >(); + if ( pField ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch (nPropType) + { + case BASEPROPERTY_CURRENCYSYMBOL: + { + aReturn <<= pField->getCurrencySymbol(); + } + break; + case BASEPROPERTY_CURSYM_POSITION: + { + aReturn <<= pField->getPrependCurrSym(); + } + break; + default: + return SVTXFormattedField::getProperty(PropertyName); + } + } + return SVTXFormattedField::getProperty(PropertyName); +} + +void SVTXCurrencyField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_ALIGN, + BASEPROPERTY_BACKGROUNDCOLOR, + BASEPROPERTY_BORDER, + BASEPROPERTY_BORDERCOLOR, + BASEPROPERTY_CURRENCYSYMBOL, + BASEPROPERTY_CURSYM_POSITION, + BASEPROPERTY_DECIMALACCURACY, + BASEPROPERTY_DEFAULTCONTROL, + BASEPROPERTY_ENABLED, + BASEPROPERTY_ENABLEVISIBLE, + BASEPROPERTY_FONTDESCRIPTOR, + BASEPROPERTY_HELPTEXT, + BASEPROPERTY_HELPURL, + BASEPROPERTY_NUMSHOWTHOUSANDSEP, + BASEPROPERTY_PRINTABLE, + BASEPROPERTY_READONLY, + BASEPROPERTY_REPEAT, + BASEPROPERTY_REPEAT_DELAY, + BASEPROPERTY_SPIN, + BASEPROPERTY_STRICTFORMAT, + BASEPROPERTY_TABSTOP, + BASEPROPERTY_VALUEMAX_DOUBLE, + BASEPROPERTY_VALUEMIN_DOUBLE, + BASEPROPERTY_VALUESTEP_DOUBLE, + BASEPROPERTY_VALUE_DOUBLE, + BASEPROPERTY_ENFORCE_FORMAT, + BASEPROPERTY_HIDEINACTIVESELECTION, + BASEPROPERTY_VERTICALALIGN, + BASEPROPERTY_WRITING_MODE, + BASEPROPERTY_CONTEXT_WRITING_MODE, + BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR, + BASEPROPERTY_HIGHLIGHT_COLOR, + BASEPROPERTY_HIGHLIGHT_TEXT_COLOR, + 0); + VCLXWindow::ImplGetPropertyIds( rIds ); +} + +SVTXNumericField::SVTXNumericField() +{ +} + +SVTXNumericField::~SVTXNumericField() +{ +} + + +css::uno::Reference<accessibility::XAccessibleContext> SVTXNumericField::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext(this); +} + + +void SVTXNumericField::setValue( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetValue( Value ); +} + +double SVTXNumericField::getValue() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetValue() : 0; +} + +void SVTXNumericField::setMin( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetMinValue( Value ); +} + +double SVTXNumericField::getMin() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetMinValue() : 0; +} + +void SVTXNumericField::setMax( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetMaxValue( Value ); +} + +double SVTXNumericField::getMax() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetMaxValue() : 0; +} + +void SVTXNumericField::setFirst( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinFirst( Value ); +} + +double SVTXNumericField::getFirst() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinFirst() : 0; +} + +void SVTXNumericField::setLast( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinLast( Value ); +} + +double SVTXNumericField::getLast() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinLast() : 0; +} + +void SVTXNumericField::setSpinSize( double Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetSpinSize( Value ); +} + +double SVTXNumericField::getSpinSize() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetSpinSize() : 0; +} + +void SVTXNumericField::setDecimalDigits( sal_Int16 Value ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetDecimalDigits( Value ); +} + +sal_Int16 SVTXNumericField::getDecimalDigits() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField ? pField->GetFormatter().GetDecimalDigits() : 0; +} + +void SVTXNumericField::setStrictFormat( sal_Bool bStrict ) +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + if ( pField ) + pField->GetFormatter().SetStrictFormat( bStrict ); +} + +sal_Bool SVTXNumericField::isStrictFormat() +{ + SolarMutexGuard aGuard; + + VclPtr<FormattedField> pField = GetAs< FormattedField >(); + return pField && pField->GetFormatter().IsStrictFormat(); +} + +void SVTXNumericField::GetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + SVTXFormattedField::ImplGetPropertyIds( rIds ); +} + +SVTXDateField::SVTXDateField() +{ +} + +SVTXDateField::~SVTXDateField() +{ +} + +void SAL_CALL SVTXDateField::setProperty( const OUString& PropertyName, const css::uno::Any& Value ) +{ + VCLXDateField::setProperty( PropertyName, Value ); + + // some properties need to be forwarded to the sub edit, too + SolarMutexGuard g; + VclPtr< Edit > pSubEdit = GetWindow() ? GetAs<Edit>()->GetSubEdit() : nullptr; + if ( !pSubEdit ) + return; + + switch ( GetPropertyId( PropertyName ) ) + { + case BASEPROPERTY_TEXTLINECOLOR: + if ( !Value.hasValue() ) + pSubEdit->SetTextLineColor(); + else + { + Color nColor; + if ( Value >>= nColor ) + pSubEdit->SetTextLineColor( nColor ); + } + break; + } +} + +void SVTXDateField::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + BASEPROPERTY_TEXTLINECOLOR, + 0); + VCLXDateField::ImplGetPropertyIds( rIds ); +} + +VCLXMultiLineEdit::VCLXMultiLineEdit() + :maTextListeners( *this ) + ,meLineEndType( LINEEND_LF ) // default behavior before introducing this property: LF (unix-like) +{ +} + +VCLXMultiLineEdit::~VCLXMultiLineEdit() +{ +} + +void VCLXMultiLineEdit::addTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + maTextListeners.addInterface( l ); +} + +void VCLXMultiLineEdit::removeTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) +{ + maTextListeners.removeInterface( l ); +} + +void VCLXMultiLineEdit::setText( const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + { + pEdit->SetText( aText ); + + // #107218# Call same listeners like VCL would do after user interaction + SetSynthesizingVCLEvent( true ); + pEdit->SetModifyFlag(); + pEdit->Modify(); + SetSynthesizingVCLEvent( false ); + } +} + +void VCLXMultiLineEdit::insertText( const css::awt::Selection& rSel, const OUString& aText ) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + { + setSelection( rSel ); + pEdit->ReplaceSelected( aText ); + } +} + +OUString VCLXMultiLineEdit::getText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + aText = pEdit->GetText( meLineEndType ); + return aText; +} + +OUString VCLXMultiLineEdit::getSelectedText() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit) + aText = pMultiLineEdit->GetSelected( meLineEndType ); + return aText; + +} + +void VCLXMultiLineEdit::setSelection( const css::awt::Selection& aSelection ) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit ) + { + pMultiLineEdit->SetSelection( Selection( aSelection.Min, aSelection.Max ) ); + } +} + +css::awt::Selection VCLXMultiLineEdit::getSelection() +{ + SolarMutexGuard aGuard; + + css::awt::Selection aSel; + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit ) + { + aSel.Min = pMultiLineEdit->GetSelection().Min(); + aSel.Max = pMultiLineEdit->GetSelection().Max(); + } + return aSel; +} + +sal_Bool VCLXMultiLineEdit::isEditable() +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + return pMultiLineEdit && !pMultiLineEdit->IsReadOnly() && pMultiLineEdit->IsEnabled(); +} + +void VCLXMultiLineEdit::setEditable( sal_Bool bEditable ) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit ) + pMultiLineEdit->SetReadOnly( !bEditable ); +} + +void VCLXMultiLineEdit::setMaxTextLen( sal_Int16 nLen ) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit ) + pMultiLineEdit->SetMaxTextLen( nLen ); +} + +sal_Int16 VCLXMultiLineEdit::getMaxTextLen() +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + return pMultiLineEdit ? static_cast<sal_Int16>(pMultiLineEdit->GetMaxTextLen()) : sal_Int16(0); +} + +OUString VCLXMultiLineEdit::getTextLines() +{ + SolarMutexGuard aGuard; + + OUString aText; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + aText = pEdit->GetTextLines( meLineEndType ); + return aText; +} + +css::awt::Size VCLXMultiLineEdit::getMinimumSize() +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + aSz = AWTSize(pEdit->CalcMinimumSize()); + return aSz; +} + +css::awt::Size VCLXMultiLineEdit::getPreferredSize() +{ + return getMinimumSize(); +} + +css::awt::Size VCLXMultiLineEdit::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz = rNewSize; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + aSz = AWTSize(pEdit->CalcAdjustedSize( VCLSize(rNewSize ))); + return aSz; +} + +css::awt::Size VCLXMultiLineEdit::getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + aSz = AWTSize(pEdit->CalcBlockSize( nCols, nLines )); + return aSz; +} + +void VCLXMultiLineEdit::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + SolarMutexGuard aGuard; + + nCols = nLines = 0; + VclPtr< MultiLineEdit > pEdit = GetAs< MultiLineEdit >(); + if ( pEdit ) + { + sal_uInt16 nC, nL; + pEdit->GetMaxVisColumnsAndLines( nC, nL ); + nCols = nC; + nLines = nL; + } +} + +void VCLXMultiLineEdit::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::EditModify: + { + if ( maTextListeners.getLength() ) + { + css::awt::TextEvent aEvent; + aEvent.Source = getXWeak(); + maTextListeners.textChanged( aEvent ); + } + } + break; + default: + { + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); + } + break; + } +} + +void VCLXMultiLineEdit::setProperty( const OUString& PropertyName, const css::uno::Any& Value) +{ + SolarMutexGuard aGuard; + + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( !pMultiLineEdit ) + return; + + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LINE_END_FORMAT: + { + sal_Int16 nLineEndType = css::awt::LineEndFormat::LINE_FEED; + OSL_VERIFY( Value >>= nLineEndType ); + switch ( nLineEndType ) + { + case css::awt::LineEndFormat::CARRIAGE_RETURN: meLineEndType = LINEEND_CR; break; + case css::awt::LineEndFormat::LINE_FEED: meLineEndType = LINEEND_LF; break; + case css::awt::LineEndFormat::CARRIAGE_RETURN_LINE_FEED: meLineEndType = LINEEND_CRLF; break; + default: OSL_FAIL( "VCLXMultiLineEdit::setProperty: invalid line end value!" ); break; + } + } + break; + + case BASEPROPERTY_READONLY: + { + bool b; + if ( Value >>= b ) + pMultiLineEdit->SetReadOnly( b ); + } + break; + case BASEPROPERTY_MAXTEXTLEN: + { + sal_Int16 n = sal_Int16(); + if ( Value >>= n ) + pMultiLineEdit->SetMaxTextLen( n ); + } + break; + case BASEPROPERTY_HIDEINACTIVESELECTION: + { + bool b; + if ( Value >>= b ) + { + pMultiLineEdit->EnableFocusSelectionHide( b ); + lcl_setWinBits( pMultiLineEdit, WB_NOHIDESELECTION, !b ); + } + } + break; + default: + { + VCLXWindow::setProperty( PropertyName, Value ); + } + } +} + +css::uno::Any VCLXMultiLineEdit::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + css::uno::Any aProp; + VclPtr< MultiLineEdit > pMultiLineEdit = GetAs< MultiLineEdit >(); + if ( pMultiLineEdit ) + { + sal_uInt16 nPropType = GetPropertyId( PropertyName ); + switch ( nPropType ) + { + case BASEPROPERTY_LINE_END_FORMAT: + { + sal_Int16 nLineEndType = css::awt::LineEndFormat::LINE_FEED; + switch ( meLineEndType ) + { + case LINEEND_CR: nLineEndType = css::awt::LineEndFormat::CARRIAGE_RETURN; break; + case LINEEND_LF: nLineEndType = css::awt::LineEndFormat::LINE_FEED; break; + case LINEEND_CRLF: nLineEndType = css::awt::LineEndFormat::CARRIAGE_RETURN_LINE_FEED; break; + default: OSL_FAIL( "VCLXMultiLineEdit::getProperty: invalid line end value!" ); break; + } + aProp <<= nLineEndType; + } + break; + + case BASEPROPERTY_READONLY: + { + aProp <<= pMultiLineEdit->IsReadOnly(); + } + break; + case BASEPROPERTY_MAXTEXTLEN: + { + aProp <<= static_cast<sal_Int16>(pMultiLineEdit->GetMaxTextLen()); + } + break; + default: + { + aProp = VCLXWindow::getProperty( PropertyName ); + } + } + } + return aProp; +} + +void SAL_CALL VCLXMultiLineEdit::setFocus( ) +{ + SolarMutexGuard aGuard; + + // don't grab the focus if we already have it. Reason is that the only thing which the edit + // does is forwarding the focus to its text window. This text window then does a "select all". + // So if the text window already has the focus, and we give the focus to the multi line + // edit, then all which happens is that everything is selected. + // #i27072# + if ( GetWindow() && !GetWindow()->HasChildPathFocus() ) + GetWindow()->GrabFocus(); +} + +void VCLXMultiLineEdit::ImplGetPropertyIds( std::vector< sal_uInt16 > &rIds ) +{ + PushPropertyIds( rIds, + // FIXME: elide duplication ? + BASEPROPERTY_LINE_END_FORMAT, + BASEPROPERTY_READONLY, + BASEPROPERTY_MAXTEXTLEN, + BASEPROPERTY_HIDEINACTIVESELECTION, + 0); + VCLXWindow::ImplGetPropertyIds( rIds, true ); +} + +css::uno::Reference<css::accessibility::XAccessibleContext> VCLXMultiLineEdit::CreateAccessibleContext() +{ + return getAccessibleFactory().createAccessibleContext(this); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/awt/vclxwindows_internal.hxx b/toolkit/source/awt/vclxwindows_internal.hxx new file mode 100644 index 0000000000..0425225865 --- /dev/null +++ b/toolkit/source/awt/vclxwindows_internal.hxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_SOURCE_AWT_VCLXWINDOWS_INTERNAL_HXX +#define INCLUDED_TOOLKIT_SOURCE_AWT_VCLXWINDOWS_INTERNAL_HXX + +#include <vcl/window.hxx> + +namespace toolkit +{ +void setButtonLikeFaceColor(vcl::Window* _pWindow, const css::uno::Any& _rColorValue); +css::uno::Any getButtonLikeFaceColor(const vcl::Window* _pWindow); +} + +#endif // INCLUDED_TOOLKIT_SOURCE_AWT_VCLXWINDOWS_INTERNAL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/accessiblecontrolcontext.cxx b/toolkit/source/controls/accessiblecontrolcontext.cxx new file mode 100644 index 0000000000..761821bce6 --- /dev/null +++ b/toolkit/source/controls/accessiblecontrolcontext.cxx @@ -0,0 +1,343 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/accessiblecontrolcontext.hxx> +#include <com/sun/star/awt/XControl.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <vcl/svapp.hxx> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/accessiblecontexthelper.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/window.hxx> + + +namespace toolkit +{ + + + using ::comphelper::OContextEntryGuard; + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::accessibility; + + + //= OAccessibleControlContext + + + OAccessibleControlContext::OAccessibleControlContext() + { + // nothing to do here, we have a late ctor + } + + + OAccessibleControlContext::~OAccessibleControlContext() + { + ensureDisposed(); + } + + + void OAccessibleControlContext::Init( const Reference< XAccessible >& _rxCreator ) + { + OContextEntryGuard aGuard( this ); + + // retrieve the model of the control + OSL_ENSURE( !m_xControlModel.is(), "OAccessibleControlContext::Init: already know a control model...!???" ); + + Reference< awt::XControl > xControl( _rxCreator, UNO_QUERY ); + if ( xControl.is() ) + m_xControlModel.set(xControl->getModel(), css::uno::UNO_QUERY); + OSL_ENSURE( m_xControlModel.is(), "OAccessibleControlContext::Init: invalid creator (no control, or control without model!" ); + if ( !m_xControlModel.is() ) + throw DisposedException(); // caught by the caller (the create method) + + // start listening at the model + startModelListening(); + + // announce the XAccessible to our base class + OAccessibleControlContext_Base::lateInit( _rxCreator ); + } + + + rtl::Reference<OAccessibleControlContext> OAccessibleControlContext::create( const Reference< XAccessible >& _rxCreator ) + { + rtl::Reference<OAccessibleControlContext> pNew; + try + { + pNew = new OAccessibleControlContext; + pNew->Init( _rxCreator ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "OAccessibleControlContext::create: caught an exception from the late ctor!" ); + } + return pNew; + } + + + void OAccessibleControlContext::startModelListening( ) + { + Reference< XComponent > xModelComp( m_xControlModel, UNO_QUERY ); + OSL_ENSURE( xModelComp.is(), "OAccessibleControlContext::startModelListening: invalid model!" ); + if ( xModelComp.is() ) + xModelComp->addEventListener( this ); + } + + + void OAccessibleControlContext::stopModelListening( ) + { + Reference< XComponent > xModelComp( m_xControlModel, UNO_QUERY ); + OSL_ENSURE( xModelComp.is(), "OAccessibleControlContext::stopModelListening: invalid model!" ); + if ( xModelComp.is() ) + xModelComp->removeEventListener( this ); + } + + + sal_Int64 SAL_CALL OAccessibleControlContext::getAccessibleChildCount( ) + { + // we do not have children + return 0; + } + + + Reference< XAccessible > SAL_CALL OAccessibleControlContext::getAccessibleChild( sal_Int64 ) + { + // we do not have children + throw IndexOutOfBoundsException(); + } + + + Reference< XAccessible > SAL_CALL OAccessibleControlContext::getAccessibleParent( ) + { + return Reference< XAccessible >(); + } + + + sal_Int16 SAL_CALL OAccessibleControlContext::getAccessibleRole( ) + { + return AccessibleRole::SHAPE; + } + + + OUString SAL_CALL OAccessibleControlContext::getAccessibleDescription( ) + { + OContextEntryGuard aGuard( this ); + return getModelStringProperty( "HelpText" ); + } + + + OUString SAL_CALL OAccessibleControlContext::getAccessibleName( ) + { + OContextEntryGuard aGuard( this ); + return getModelStringProperty( "Name" ); + } + + + Reference< XAccessibleRelationSet > SAL_CALL OAccessibleControlContext::getAccessibleRelationSet( ) + { + return nullptr; + } + + + sal_Int64 SAL_CALL OAccessibleControlContext::getAccessibleStateSet( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + // no OContextEntryGuard here, as we do not want to throw an exception in case we're not alive anymore + + sal_Int64 nStateSet = 0; + if ( isAlive() ) + { + // no own states, only the ones which are foreign controlled + } + else + { // only the DEFUNC state if we're already disposed + nStateSet |= AccessibleStateType::DEFUNC; + } + return nStateSet; + } + + + void SAL_CALL OAccessibleControlContext::disposing( const EventObject& _rSource ) + { + OSL_ENSURE( Reference< XPropertySet >( _rSource.Source, UNO_QUERY ).get() == m_xControlModel.get(), + "OAccessibleControlContext::disposing: where did this come from?" ); + + stopModelListening( ); + m_xControlModel.clear(); + m_xModelPropsInfo.clear(); + + OAccessibleControlContext_Base::disposing(); + } + + + OUString OAccessibleControlContext::getModelStringProperty( const char* _pPropertyName ) + { + OUString sReturn; + try + { + if ( !m_xModelPropsInfo.is() && m_xControlModel.is() ) + m_xModelPropsInfo = m_xControlModel->getPropertySetInfo(); + + OUString sPropertyName( OUString::createFromAscii( _pPropertyName ) ); + if ( m_xModelPropsInfo.is() && m_xModelPropsInfo->hasPropertyByName( sPropertyName ) ) + m_xControlModel->getPropertyValue( sPropertyName ) >>= sReturn; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "OAccessibleControlContext::getModelStringProperty" ); + } + return sReturn; + } + + + vcl::Window* OAccessibleControlContext::implGetWindow( Reference< awt::XWindow >* _pxUNOWindow ) const + { + Reference< awt::XControl > xControl( getAccessibleCreator(), UNO_QUERY ); + Reference< awt::XWindow > xWindow; + if ( xControl.is() ) + xWindow.set(xControl->getPeer(), css::uno::UNO_QUERY); + + vcl::Window* pWindow = xWindow.is() ? VCLUnoHelper::GetWindow( xWindow ) : nullptr; + + if ( _pxUNOWindow ) + *_pxUNOWindow = xWindow; + + return pWindow; + } + + + awt::Rectangle OAccessibleControlContext::implGetBounds( ) + { + SolarMutexGuard aSolarGuard; + // want to do some VCL stuff here ... + OContextEntryGuard aGuard( this ); + + OSL_FAIL( "OAccessibleControlContext::implGetBounds: performance issue: forced to calc the size myself!" ); + // In design mode (and this is what this class is for), the surrounding shape (if any) should handle this call + // The problem is that in design mode, our size may not be correct (in the drawing layer, controls are + // positioned/sized for painting only), and that calculation of our position is expensive + + // what we know (or can obtain from somewhere): + // * the PosSize of our peer, relative to its parent window + // * the parent window which the PosSize is relative to + // * our foreign controlled accessible parent + // from this info, we can determine the position of our peer relative to the foreign parent + + // our control + Reference< awt::XWindow > xWindow; + VclPtr< vcl::Window > pVCLWindow = implGetWindow( &xWindow ); + + awt::Rectangle aBounds( 0, 0, 0, 0 ); + if ( xWindow.is() ) + { + // ugly, but... though the XWindow has a getPosSize, it is impossible to determine the + // parent which this position/size is relative to. This means we must tunnel UNO and ask the + // implementation + vcl::Window* pVCLParent = pVCLWindow ? pVCLWindow->GetParent() : nullptr; + + // the relative location of the window + ::Point aWindowRelativePos( 0, 0); + if ( pVCLWindow ) + aWindowRelativePos = pVCLWindow->GetPosPixel(); + + // the screen position of the "window parent" of the control + ::Point aVCLParentScreenPos( 0, 0 ); + if ( pVCLParent ) + aVCLParentScreenPos = pVCLParent->GetPosPixel(); + + // now the size of the control + aBounds = xWindow->getPosSize(); + + // correct the pos + aBounds.X = aWindowRelativePos.X() + aVCLParentScreenPos.X(); + aBounds.Y = aWindowRelativePos.Y() + aVCLParentScreenPos.Y(); + } + + return aBounds; + } + + + Reference< XAccessible > SAL_CALL OAccessibleControlContext::getAccessibleAtPoint( const awt::Point& /* _rPoint */ ) + { + // no children at all + return nullptr; + } + + + void SAL_CALL OAccessibleControlContext::grabFocus( ) + { + OSL_FAIL( "OAccessibleControlContext::grabFocus: !isFocusTraversable, but grabFocus!" ); + } + + + sal_Int32 SAL_CALL OAccessibleControlContext::getForeground( ) + { + SolarMutexGuard aSolarGuard; + // want to do some VCL stuff here ... + OContextEntryGuard aGuard( this ); + + VclPtr< vcl::Window > pWindow = implGetWindow(); + Color nColor; + if ( pWindow ) + { + if ( pWindow->IsControlForeground() ) + nColor = pWindow->GetControlForeground(); + else + { + vcl::Font aFont; + if ( pWindow->IsControlFont() ) + aFont = pWindow->GetControlFont(); + else + aFont = pWindow->GetFont(); + nColor = aFont.GetColor(); + } + } + return sal_Int32(nColor); + } + + + sal_Int32 SAL_CALL OAccessibleControlContext::getBackground( ) + { + SolarMutexGuard aSolarGuard; + // want to do some VCL stuff here ... + OContextEntryGuard aGuard( this ); + + VclPtr< vcl::Window > pWindow = implGetWindow(); + Color nColor; + if ( pWindow ) + { + if ( pWindow->IsControlBackground() ) + nColor = pWindow->GetControlBackground(); + else + nColor = pWindow->GetBackground().GetColor(); + } + + return sal_Int32(nColor); + } + + +} //namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/animatedimages.cxx b/toolkit/source/controls/animatedimages.cxx new file mode 100644 index 0000000000..dec4a8c533 --- /dev/null +++ b/toolkit/source/controls/animatedimages.cxx @@ -0,0 +1,491 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/animatedimages.hxx> +#include <helper/property.hxx> + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/awt/ImageScaleMode.hpp> +#include <com/sun/star/awt/XAnimation.hpp> +#include <com/sun/star/awt/XAnimatedImages.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <o3tl/safeint.hxx> +#include <toolkit/controls/unocontrolbase.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> + +#include <cppuhelper/implbase2.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace css::awt; +using namespace css::container; +using namespace css::lang; +using namespace css::uno; + +namespace { + +typedef ::cppu::AggImplInheritanceHelper2 < UnoControlBase + , css::awt::XAnimation + , css::container::XContainerListener + > AnimatedImagesControl_Base; + +class AnimatedImagesControl : public AnimatedImagesControl_Base +{ +public: + AnimatedImagesControl(); + OUString GetComponentServiceName() const override; + + // XAnimation + virtual void SAL_CALL startAnimation( ) override; + virtual void SAL_CALL stopAnimation( ) override; + virtual sal_Bool SAL_CALL isAnimationRunning( ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XControl + sal_Bool SAL_CALL setModel( const css::uno::Reference< css::awt::XControlModel >& i_rModel ) override; + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& i_toolkit, const css::uno::Reference< css::awt::XWindowPeer >& i_parentPeer ) override; + + + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& i_event ) override; +}; + + AnimatedImagesControl::AnimatedImagesControl() + { + } + + + OUString AnimatedImagesControl::GetComponentServiceName() const + { + return "AnimatedImages"; + } + + + void SAL_CALL AnimatedImagesControl::startAnimation( ) + { + Reference< XAnimation > xAnimation( getPeer(), UNO_QUERY ); + if ( xAnimation.is() ) + xAnimation->startAnimation(); + } + + + void SAL_CALL AnimatedImagesControl::stopAnimation( ) + { + Reference< XAnimation > xAnimation( getPeer(), UNO_QUERY ); + if ( xAnimation.is() ) + xAnimation->stopAnimation(); + } + + + sal_Bool SAL_CALL AnimatedImagesControl::isAnimationRunning( ) + { + Reference< XAnimation > xAnimation( getPeer(), UNO_QUERY ); + if ( xAnimation.is() ) + return xAnimation->isAnimationRunning(); + return false; + } + + + OUString SAL_CALL AnimatedImagesControl::getImplementationName( ) + { + return "org.openoffice.comp.toolkit.AnimatedImagesControl"; + } + + + Sequence< OUString > SAL_CALL AnimatedImagesControl::getSupportedServiceNames() + { + Sequence< OUString > aServices( AnimatedImagesControl_Base::getSupportedServiceNames() ); + aServices.realloc( aServices.getLength() + 1 ); + aServices.getArray()[ aServices.getLength() - 1 ] = "com.sun.star.awt.AnimatedImagesControl"; + return aServices; + } + + void lcl_updatePeer( Reference< XWindowPeer > const& i_peer, Reference< XControlModel > const& i_model ) + { + const Reference< css::util::XModifyListener > xPeerModify( i_peer, UNO_QUERY ); + if ( xPeerModify.is() ) + { + EventObject aEvent; + aEvent.Source = i_model; + xPeerModify->modified( aEvent ); + } + } + + sal_Bool SAL_CALL AnimatedImagesControl::setModel( const Reference< XControlModel >& i_rModel ) + { + const Reference< XAnimatedImages > xOldContainer( getModel(), UNO_QUERY ); + const Reference< XAnimatedImages > xNewContainer( i_rModel, UNO_QUERY ); + + if ( !AnimatedImagesControl_Base::setModel( i_rModel ) ) + return false; + + if ( xOldContainer.is() ) + xOldContainer->removeContainerListener( this ); + + if ( xNewContainer.is() ) + xNewContainer->addContainerListener( this ); + + lcl_updatePeer( getPeer(), getModel() ); + + return true; + } + + + void SAL_CALL AnimatedImagesControl::createPeer( const Reference< XToolkit >& i_toolkit, const Reference< XWindowPeer >& i_parentPeer ) + { + AnimatedImagesControl_Base::createPeer( i_toolkit, i_parentPeer ); + + lcl_updatePeer( getPeer(), getModel() ); + } + + + void SAL_CALL AnimatedImagesControl::elementInserted( const ContainerEvent& i_event ) + { + const Reference< XContainerListener > xPeerListener( getPeer(), UNO_QUERY ); + if ( xPeerListener.is() ) + xPeerListener->elementInserted( i_event ); + } + + + void SAL_CALL AnimatedImagesControl::elementRemoved( const ContainerEvent& i_event ) + { + const Reference< XContainerListener > xPeerListener( getPeer(), UNO_QUERY ); + if ( xPeerListener.is() ) + xPeerListener->elementRemoved( i_event ); + } + + + void SAL_CALL AnimatedImagesControl::elementReplaced( const ContainerEvent& i_event ) + { + const Reference< XContainerListener > xPeerListener( getPeer(), UNO_QUERY ); + if ( xPeerListener.is() ) + xPeerListener->elementReplaced( i_event ); + } + + + void SAL_CALL AnimatedImagesControl::disposing( const EventObject& i_event ) + { + UnoControlBase::disposing( i_event ); + } + +} + +namespace toolkit { + + namespace + { + void lcl_checkIndex( const std::vector< css::uno::Sequence< OUString > > & rImageSets, const sal_Int32 i_index, const Reference< XInterface >& i_context, + const bool i_forInsert = false ) + { + if ( ( i_index < 0 ) || ( o3tl::make_unsigned( i_index ) > rImageSets.size() + ( i_forInsert ? 1 : 0 ) ) ) + throw IndexOutOfBoundsException( OUString(), i_context ); + } + + void lcl_notify( std::unique_lock<std::mutex>& i_guard, comphelper::OInterfaceContainerHelper4<XContainerListener>& rContainer, + void ( SAL_CALL XContainerListener::*i_notificationMethod )( const ContainerEvent& ), + const sal_Int32 i_accessor, const Sequence< OUString >& i_imageURLs, const Reference< XInterface >& i_context ) + { + if ( !rContainer.getLength(i_guard) ) + return; + + ContainerEvent aEvent; + aEvent.Source = i_context; + aEvent.Accessor <<= i_accessor; + aEvent.Element <<= i_imageURLs; + + rContainer.notifyEach( i_guard, i_notificationMethod, aEvent ); + } + } + + + AnimatedImagesControlModel::AnimatedImagesControlModel( Reference< css::uno::XComponentContext > const & i_factory ) + :AnimatedImagesControlModel_Base( i_factory ) + { + ImplRegisterProperty( BASEPROPERTY_AUTO_REPEAT ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_IMAGE_SCALE_MODE ); + ImplRegisterProperty( BASEPROPERTY_STEP_TIME ); + } + + + AnimatedImagesControlModel::AnimatedImagesControlModel( const AnimatedImagesControlModel& i_copySource ) + :AnimatedImagesControlModel_Base( i_copySource ) + ,maImageSets( i_copySource.maImageSets ) + { + } + + + AnimatedImagesControlModel::~AnimatedImagesControlModel() + { + } + + + rtl::Reference<UnoControlModel> AnimatedImagesControlModel::Clone() const + { + return new AnimatedImagesControlModel( *this ); + } + + + Reference< css::beans::XPropertySetInfo > SAL_CALL AnimatedImagesControlModel::getPropertySetInfo( ) + { + static Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + OUString SAL_CALL AnimatedImagesControlModel::getServiceName() + { + return "com.sun.star.awt.AnimatedImagesControlModel"; + } + + + OUString SAL_CALL AnimatedImagesControlModel::getImplementationName( ) + { + return "org.openoffice.comp.toolkit.AnimatedImagesControlModel"; + } + + + Sequence< OUString > SAL_CALL AnimatedImagesControlModel::getSupportedServiceNames() + { + return { "com.sun.star.awt.AnimatedImagesControlModel", "com.sun.star.awt.UnoControlModel" }; + } + + + void AnimatedImagesControlModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 i_handle, const Any& i_value ) + { + switch ( i_handle ) + { + case BASEPROPERTY_IMAGE_SCALE_MODE: + { + sal_Int16 nImageScaleMode( ImageScaleMode::ANISOTROPIC ); + OSL_VERIFY( i_value >>= nImageScaleMode ); // convertFastPropertyValue ensures that this has the proper type + if ( ( nImageScaleMode != ImageScaleMode::NONE ) + && ( nImageScaleMode != ImageScaleMode::ISOTROPIC ) + && ( nImageScaleMode != ImageScaleMode::ANISOTROPIC ) + ) + throw IllegalArgumentException( OUString(), *this, 1 ); + } + break; + } + + AnimatedImagesControlModel_Base::setFastPropertyValue_NoBroadcast( rGuard, i_handle, i_value ); + } + + + Any AnimatedImagesControlModel::ImplGetDefaultValue( sal_uInt16 i_propertyId ) const + { + switch ( i_propertyId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return Any( OUString("com.sun.star.awt.AnimatedImagesControl") ); + + case BASEPROPERTY_BORDER: + return Any( css::awt::VisualEffect::NONE ); + + case BASEPROPERTY_STEP_TIME: + return Any( sal_Int32(100) ); + + case BASEPROPERTY_AUTO_REPEAT: + return Any( true ); + + case BASEPROPERTY_IMAGE_SCALE_MODE: + return Any( ImageScaleMode::NONE ); + + default: + return UnoControlModel::ImplGetDefaultValue( i_propertyId ); + } + } + + + ::cppu::IPropertyArrayHelper& AnimatedImagesControlModel::getInfoHelper() + { + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; + } + + + ::sal_Int32 SAL_CALL AnimatedImagesControlModel::getStepTime() + { + sal_Int32 nStepTime( 100 ); + OSL_VERIFY( getPropertyValue( GetPropertyName( BASEPROPERTY_STEP_TIME ) ) >>= nStepTime ); + return nStepTime; + } + + + void SAL_CALL AnimatedImagesControlModel::setStepTime( ::sal_Int32 i_stepTime ) + { + setPropertyValue( GetPropertyName( BASEPROPERTY_STEP_TIME ), Any( i_stepTime ) ); + } + + + sal_Bool SAL_CALL AnimatedImagesControlModel::getAutoRepeat() + { + bool bAutoRepeat( true ); + OSL_VERIFY( getPropertyValue( GetPropertyName( BASEPROPERTY_AUTO_REPEAT ) ) >>= bAutoRepeat ); + return bAutoRepeat; + } + + + void SAL_CALL AnimatedImagesControlModel::setAutoRepeat( sal_Bool i_autoRepeat ) + { + setPropertyValue( GetPropertyName( BASEPROPERTY_AUTO_REPEAT ), Any( i_autoRepeat ) ); + } + + + ::sal_Int16 SAL_CALL AnimatedImagesControlModel::getScaleMode() + { + sal_Int16 nImageScaleMode( ImageScaleMode::ANISOTROPIC ); + OSL_VERIFY( getPropertyValue( GetPropertyName( BASEPROPERTY_IMAGE_SCALE_MODE ) ) >>= nImageScaleMode ); + return nImageScaleMode; + } + + + void SAL_CALL AnimatedImagesControlModel::setScaleMode( ::sal_Int16 i_scaleMode ) + { + setPropertyValue( GetPropertyName( BASEPROPERTY_IMAGE_SCALE_MODE ), Any( i_scaleMode ) ); + } + + + ::sal_Int32 SAL_CALL AnimatedImagesControlModel::getImageSetCount( ) + { + std::unique_lock aGuard( m_aMutex ); + if ( m_bDisposed ) + throw DisposedException(); + + return maImageSets.size(); + } + + + Sequence< OUString > SAL_CALL AnimatedImagesControlModel::getImageSet( ::sal_Int32 i_index ) + { + std::unique_lock aGuard( m_aMutex ); + if ( m_bDisposed ) + throw DisposedException(); + + lcl_checkIndex( maImageSets, i_index, *this ); + + return maImageSets[ i_index ]; + } + + + void SAL_CALL AnimatedImagesControlModel::insertImageSet( ::sal_Int32 i_index, const Sequence< OUString >& i_imageURLs ) + { + std::unique_lock aGuard( m_aMutex ); + // sanity checks + if ( m_bDisposed ) + throw DisposedException(); + + lcl_checkIndex( maImageSets, i_index, *this, true ); + + // actual insertion + maImageSets.insert( maImageSets.begin() + i_index, i_imageURLs ); + + // listener notification + lcl_notify( aGuard, maContainerListeners, &XContainerListener::elementInserted, i_index, i_imageURLs, *this ); + } + + + void SAL_CALL AnimatedImagesControlModel::replaceImageSet( ::sal_Int32 i_index, const Sequence< OUString >& i_imageURLs ) + { + std::unique_lock aGuard( m_aMutex ); + // sanity checks + if ( m_bDisposed ) + throw DisposedException(); + + lcl_checkIndex( maImageSets, i_index, *this ); + + // actual insertion + maImageSets[ i_index ] = i_imageURLs; + + // listener notification + lcl_notify( aGuard, maContainerListeners, &XContainerListener::elementReplaced, i_index, i_imageURLs, *this ); + } + + + void SAL_CALL AnimatedImagesControlModel::removeImageSet( ::sal_Int32 i_index ) + { + std::unique_lock aGuard( m_aMutex ); + // sanity checks + if ( m_bDisposed ) + throw DisposedException(); + + lcl_checkIndex( maImageSets, i_index, *this ); + + // actual removal + ::std::vector< Sequence< OUString > >::iterator removalPos = maImageSets.begin() + i_index; + Sequence< OUString > aRemovedElement( *removalPos ); + maImageSets.erase( removalPos ); + + // listener notification + lcl_notify( aGuard, maContainerListeners, &XContainerListener::elementRemoved, i_index, aRemovedElement, *this ); + } + + + void SAL_CALL AnimatedImagesControlModel::addContainerListener( const Reference< XContainerListener >& i_listener ) + { + std::unique_lock aGuard( m_aMutex ); + maContainerListeners.addInterface( aGuard, i_listener ); + } + + + void SAL_CALL AnimatedImagesControlModel::removeContainerListener( const Reference< XContainerListener >& i_listener ) + { + std::unique_lock aGuard( m_aMutex ); + maContainerListeners.removeInterface( aGuard, i_listener ); + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_openoffice_comp_toolkit_AnimatedImagesControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new AnimatedImagesControl()); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_openoffice_comp_toolkit_AnimatedImagesControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::AnimatedImagesControlModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/controlmodelcontainerbase.cxx b/toolkit/source/controls/controlmodelcontainerbase.cxx new file mode 100644 index 0000000000..8c9bba0890 --- /dev/null +++ b/toolkit/source/controls/controlmodelcontainerbase.cxx @@ -0,0 +1,1789 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/controlmodelcontainerbase.hxx> +#include <vcl/svapp.hxx> +#include <o3tl/safeint.hxx> +#include <osl/mutex.hxx> +#include <helper/property.hxx> +#include <helper/servicenames.hxx> +#include <controls/geometrycontrolmodel.hxx> +#include <toolkit/controls/unocontrols.hxx> +#include <controls/formattedcontrol.hxx> +#include <controls/roadmapcontrol.hxx> +#include <controls/tkscrollbar.hxx> +#include <controls/tabpagemodel.hxx> +#include <controls/stdtabcontroller.hxx> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/resource/XStringResourceResolver.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/weakagg.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/outdev.hxx> +#include <comphelper/types.hxx> + +#include "tree/treecontrol.hxx" +#include "grid/gridcontrol.hxx" +#include <controls/tabpagecontainer.hxx> + +#include <map> +#include <algorithm> +#include <tools/urlobj.hxx> +#include <osl/file.hxx> +#include <sal/log.hxx> +#include <controls/dialogcontrol.hxx> + +#include <helper/unopropertyarrayhelper.hxx> +#include "controlmodelcontainerbase_internal.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace toolkit; + +constexpr OUString PROPERTY_RESOURCERESOLVER = u"ResourceResolver"_ustr; + + +namespace +{ + const Sequence< OUString >& lcl_getLanguageDependentProperties() + { + // note: properties must be sorted + static Sequence<OUString> s_aLanguageDependentProperties{ "HelpText", "Title" }; + return s_aLanguageDependentProperties; + } + +// functor for disposing a control model +struct DisposeControlModel +{ + void operator()( Reference< XControlModel >& _rxModel ) + { + try + { + ::comphelper::disposeComponent( _rxModel ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("toolkit", "caught an exception while disposing a component" ); + } + } +}; + +} + + +// functor for cloning a control model, and insertion into a target list +struct CloneControlModel +{ +private: + ControlModelContainerBase::UnoControlModelHolderVector& m_rTargetVector; + +public: + explicit CloneControlModel( ControlModelContainerBase::UnoControlModelHolderVector& _rTargetVector ) + :m_rTargetVector( _rTargetVector ) + { + } + + void operator()( const ControlModelContainerBase::UnoControlModelHolder& _rSource ) + { + // clone the source object + Reference< XCloneable > xCloneSource( _rSource.first, UNO_QUERY ); + Reference< XControlModel > xClone( xCloneSource->createClone(), UNO_QUERY ); + // add to target list + m_rTargetVector.emplace_back( xClone, _rSource.second ); + } +}; + + +// functor for comparing a XControlModel with a given reference +struct CompareControlModel +{ +private: + Reference< XControlModel > m_xReference; +public: + explicit CompareControlModel( const Reference< XControlModel >& _rxReference ) : m_xReference( _rxReference ) { } + + bool operator()( const ControlModelContainerBase::UnoControlModelHolder& _rCompare ) + { + return _rCompare.first.get() == m_xReference.get(); + } +}; + +constexpr OUString aTabIndexPropertyNameStr( u"TabIndex"_ustr ); + +ControlModelContainerBase::ControlModelContainerBase( const Reference< XComponentContext >& rxContext ) + :ControlModelContainer_IBase( rxContext ) + ,maContainerListeners( *this ) + ,mbGroupsUpToDate( false ) + ,m_nTabPageId(0) +{ + ImplRegisterProperty(BASEPROPERTY_ENABLED); +} + +ControlModelContainerBase::ControlModelContainerBase( const ControlModelContainerBase& rModel ) + : ControlModelContainer_IBase( rModel ) + , maContainerListeners( *this ) + , mbGroupsUpToDate( false ) + , m_nTabPageId( rModel.m_nTabPageId ) +{ +} + +ControlModelContainerBase::~ControlModelContainerBase() +{ + maModels.clear(); + mbGroupsUpToDate = false; +} + +Any ControlModelContainerBase::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + Any aAny; + + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + aAny <<= OUString::createFromAscii( szServiceName_UnoControlDialog ); + break; + default: + aAny = UnoControlModel::ImplGetDefaultValue( nPropId ); + } + + return aAny; +} + +::cppu::IPropertyArrayHelper& ControlModelContainerBase::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +void SAL_CALL ControlModelContainerBase::dispose( ) +{ + + // tell our listeners + { + std::unique_lock aGuard( m_aMutex ); + + EventObject aDisposeEvent; + aDisposeEvent.Source = static_cast< XAggregation* >( static_cast< ::cppu::OWeakAggObject* >( this ) ); + + maContainerListeners.disposeAndClear( aGuard, aDisposeEvent ); + maChangeListeners.disposeAndClear( aGuard, aDisposeEvent ); + } + + + // call the base class + UnoControlModel::dispose(); + + + // dispose our child models + // for this, collect the models (we collect them from maModels, and this is modified when disposing children) + ::std::vector< Reference< XControlModel > > aChildModels( maModels.size() ); + + ::std::transform( + maModels.begin(), maModels.end(), // source range + aChildModels.begin(), // target location + []( const UnoControlModelHolder& rUnoControlModelHolder ) + { return rUnoControlModelHolder.first; } // operation to apply -> select the XControlModel part + ); + + // now dispose + ::std::for_each( aChildModels.begin(), aChildModels.end(), DisposeControlModel() ); + aChildModels.clear(); + + mbGroupsUpToDate = false; +} + +// XMultiPropertySet +Reference< XPropertySetInfo > ControlModelContainerBase::getPropertySetInfo( ) +{ + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +void ControlModelContainerBase::Clone_Impl(ControlModelContainerBase& _rClone) const +{ + // clone all children + ::std::for_each( + maModels.begin(), maModels.end(), + CloneControlModel( _rClone.maModels ) + ); +} +rtl::Reference<UnoControlModel> ControlModelContainerBase::Clone() const +{ + // clone the container itself + rtl::Reference<ControlModelContainerBase> pClone = new ControlModelContainerBase( *this ); + Clone_Impl(*pClone); + + return pClone; +} + +ControlModelContainerBase::UnoControlModelHolderVector::iterator ControlModelContainerBase::ImplFindElement( std::u16string_view rName ) +{ + return ::std::find_if( maModels.begin(), maModels.end(), [&](const UnoControlModelHolder& elem) { return elem.second == rName; }); +} + +// ::XMultiServiceFactory +Reference< XInterface > ControlModelContainerBase::createInstance( const OUString& aServiceSpecifier ) +{ + SolarMutexGuard aGuard; + + rtl::Reference<OGeometryControlModel_Base> pNewModel; + + if ( aServiceSpecifier == "com.sun.star.awt.UnoControlEditModel" ) + pNewModel = new OGeometryControlModel< UnoControlEditModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFormattedFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlFormattedFieldModel >( m_xContext); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFileControlModel" ) + pNewModel = new OGeometryControlModel< UnoControlFileControlModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlButtonModel" ) + pNewModel = new OGeometryControlModel< UnoControlButtonModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlImageControlModel" ) + pNewModel = new OGeometryControlModel< UnoControlImageControlModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlRadioButtonModel" ) + pNewModel = new OGeometryControlModel< UnoControlRadioButtonModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlCheckBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlCheckBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFixedHyperlinkModel" ) + pNewModel = new OGeometryControlModel< UnoControlFixedHyperlinkModel >( m_xContext ); + else if ( aServiceSpecifier == "stardiv.vcl.controlmodel.FixedText" ) + pNewModel = new OGeometryControlModel< UnoControlFixedTextModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlGroupBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlGroupBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlListBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlListBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlComboBoxModel" ) + pNewModel = new OGeometryControlModel< UnoControlComboBoxModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlDateFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlDateFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlTimeFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlTimeFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlNumericFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlNumericFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlCurrencyFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlCurrencyFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlPatternFieldModel" ) + pNewModel = new OGeometryControlModel< UnoControlPatternFieldModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlProgressBarModel" ) + pNewModel = new OGeometryControlModel< UnoControlProgressBarModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlScrollBarModel" ) + pNewModel = new OGeometryControlModel< UnoControlScrollBarModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlFixedLineModel" ) + pNewModel = new OGeometryControlModel< UnoControlFixedLineModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoControlRoadmapModel" ) + pNewModel = new OGeometryControlModel< UnoControlRoadmapModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.tree.TreeControlModel" ) + pNewModel = new OGeometryControlModel< UnoTreeModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.grid.UnoControlGridModel" ) + pNewModel = new OGeometryControlModel< UnoGridModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.tab.UnoControlTabPageContainerModel" ) + pNewModel = new OGeometryControlModel< UnoControlTabPageContainerModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoMultiPageModel" ) + pNewModel = new OGeometryControlModel< UnoMultiPageModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.tab.UnoControlTabPageModel" ) + pNewModel = new OGeometryControlModel< UnoControlTabPageModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoPageModel" ) + pNewModel = new OGeometryControlModel< UnoPageModel >( m_xContext ); + else if ( aServiceSpecifier == "com.sun.star.awt.UnoFrameModel" ) + pNewModel = new OGeometryControlModel< UnoFrameModel >( m_xContext ); + + if ( !pNewModel ) + { + Reference< XInterface > xObject = m_xContext->getServiceManager()->createInstanceWithContext(aServiceSpecifier, m_xContext); + Reference< XServiceInfo > xSI( xObject, UNO_QUERY ); + Reference< XCloneable > xCloneAccess( xSI, UNO_QUERY ); + Reference< XAggregation > xAgg( xCloneAccess, UNO_QUERY ); + if ( xAgg.is() ) + { + if ( xSI->supportsService("com.sun.star.awt.UnoControlModel") ) + { + // release 3 of the 4 references we have to the object + xAgg.clear(); + xSI.clear(); + xObject.clear(); + + pNewModel = new OCommonGeometryControlModel( xCloneAccess, aServiceSpecifier ); + } + } + } + + return cppu::getXWeak(pNewModel.get()); +} + +Reference< XInterface > ControlModelContainerBase::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& i_arguments ) +{ + const Reference< XInterface > xInstance( createInstance( ServiceSpecifier ) ); + const Reference< XInitialization > xInstanceInit( xInstance, UNO_QUERY ); + ENSURE_OR_RETURN( xInstanceInit.is(), "ControlModelContainerBase::createInstanceWithArguments: can't pass the arguments!", xInstance ); + xInstanceInit->initialize( i_arguments ); + return xInstance; +} + +Sequence< OUString > ControlModelContainerBase::getAvailableServiceNames() +{ + return { "com.sun.star.awt.UnoControlEditModel", + "com.sun.star.awt.UnoControlFormattedFieldModel", + "com.sun.star.awt.UnoControlFileControlModel", + "com.sun.star.awt.UnoControlButtonModel", + "com.sun.star.awt.UnoControlImageControlModel", + "com.sun.star.awt.UnoControlRadioButtonModel", + "com.sun.star.awt.UnoControlCheckBoxModel", + "com.sun.star.awt.UnoControlFixedTextModel", + "com.sun.star.awt.UnoControlGroupBoxModel", + "com.sun.star.awt.UnoControlListBoxModel", + "com.sun.star.awt.UnoControlComboBoxModel", + "com.sun.star.awt.UnoControlDateFieldModel", + "com.sun.star.awt.UnoControlTimeFieldModel", + "com.sun.star.awt.UnoControlNumericFieldModel", + "com.sun.star.awt.UnoControlCurrencyFieldModel", + "com.sun.star.awt.UnoControlPatternFieldModel", + "com.sun.star.awt.UnoControlProgressBarModel", + "com.sun.star.awt.UnoControlScrollBarModel", + "com.sun.star.awt.UnoControlFixedLineModel", + "com.sun.star.awt.UnoControlRoadmapModel", + "com.sun.star.awt.tree.TreeControlModel", + "com.sun.star.awt.grid.UnoControlGridModel", + "com.sun.star.awt.UnoControlFixedHyperlinkModel", + "com.sun.star.awt.tab.UnoControlTabPageContainerModel", + "com.sun.star.awt.tab.UnoControlTabPageModel", + "com.sun.star.awt.UnoMultiPageModel", + "com.sun.star.awt.UnoFrameModel" + }; +} + +// XContainer +void ControlModelContainerBase::addContainerListener( const Reference< XContainerListener >& l ) +{ + maContainerListeners.addInterface( l ); +} + +void ControlModelContainerBase::removeContainerListener( const Reference< XContainerListener >& l ) +{ + maContainerListeners.removeInterface( l ); +} + +// XElementAccess +Type ControlModelContainerBase::getElementType() +{ + Type aType = cppu::UnoType<XControlModel>::get(); + return aType; +} + +sal_Bool ControlModelContainerBase::hasElements() +{ + return !maModels.empty(); +} + +// XNameContainer, XNameReplace, XNameAccess +void ControlModelContainerBase::replaceByName( const OUString& aName, const Any& aElement ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xNewModel; + aElement >>= xNewModel; + if ( !xNewModel.is() ) + throw IllegalArgumentException(); + + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() == aElementPos ) + throw NoSuchElementException(); + // Dialog behaviour is to have all containee names unique (MSO Userform is the same) + // With container controls you could have constructed an existing hierarchy and are now + // add this to an existing container, in this case a name nested in the containment + // hierarchy of the added control could contain a name clash, if we have access to the + // list of global names then recursively check for previously existing names (we need + // to do this obviously before the 'this' objects container is updated) + Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + if ( xAllChildren.is() ) + { + // remove old control (and children) from global list of containers + updateUserFormChildren( xAllChildren, aName, Remove, uno::Reference< XControlModel >() ); + // Add new control (and containers if they exist) + updateUserFormChildren( xAllChildren, aName, Insert, xNewModel ); + } + // stop listening at the old model + stopControlListening( aElementPos->first ); + Reference< XControlModel > xReplaced( aElementPos->first ); + // remember the new model, and start listening + aElementPos->first = xNewModel; + startControlListening( xNewModel ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.ReplacedElement <<= xReplaced; + aEvent.Accessor <<= aName; + + // notify the container listener + maContainerListeners.elementReplaced( aEvent ); + + // our "tab controller model" has potentially changed -> notify this + implNotifyTabModelChange( aName ); +} + +Any ControlModelContainerBase::getByName( const OUString& aName ) +{ + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() == aElementPos ) + throw NoSuchElementException(); + + return Any( aElementPos->first ); +} + +Sequence< OUString > ControlModelContainerBase::getElementNames() +{ + Sequence< OUString > aNames( maModels.size() ); + + ::std::transform( + maModels.begin(), maModels.end(), // source range + aNames.getArray(), // target range + []( const UnoControlModelHolder& rUnoControlModelHolder ) + { return rUnoControlModelHolder.second; } // operator to apply: select the second element (the name) + ); + + return aNames; +} + +sal_Bool ControlModelContainerBase::hasByName( const OUString& aName ) +{ + return maModels.end() != ImplFindElement( aName ); +} + +void ControlModelContainerBase::insertByName( const OUString& aName, const Any& aElement ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xM; + aElement >>= xM; + + if ( xM.is() ) + { + Reference< beans::XPropertySet > xProps( xM, UNO_QUERY ); + if ( xProps.is() ) + { + + Reference< beans::XPropertySetInfo > xPropInfo = xProps->getPropertySetInfo(); + + const OUString& sImageSourceProperty = GetPropertyName( BASEPROPERTY_IMAGEURL ); + if ( xPropInfo->hasPropertyByName( sImageSourceProperty ) && ImplHasProperty(BASEPROPERTY_DIALOGSOURCEURL) ) + { + Any aUrl = xProps->getPropertyValue( sImageSourceProperty ); + + OUString absoluteUrl = + getPhysicalLocation( getPropertyValue( GetPropertyName( BASEPROPERTY_DIALOGSOURCEURL ) ), aUrl ); + + aUrl <<= absoluteUrl; + + xProps->setPropertyValue( sImageSourceProperty , aUrl ); + } + } + } + + + if ( aName.isEmpty() || !xM.is() ) + throw IllegalArgumentException(); + + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() != aElementPos ) + throw ElementExistException(); + + // Dialog behaviour is to have all containee names unique (MSO Userform is the same) + // With container controls you could have constructed an existing hierarchy and are now + // add this to an existing container, in this case a name nested in the containment + // hierarchy of the added control could contain a name clash, if we have access to the + // list of global names then we need to recursively check for previously existing + // names (we need to do this obviously before the 'this' objects container is updated) + // remove old control (and children) from global list of containers + Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + + if ( xAllChildren.is() ) + updateUserFormChildren( xAllChildren, aName, Insert, xM ); + maModels.emplace_back( xM, aName ); + mbGroupsUpToDate = false; + startControlListening( xM ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.Accessor <<= aName; + maContainerListeners.elementInserted( aEvent ); + + // our "tab controller model" has potentially changed -> notify this + implNotifyTabModelChange( aName ); +} + +void ControlModelContainerBase::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + UnoControlModelHolderVector::iterator aElementPos = ImplFindElement( aName ); + if ( maModels.end() == aElementPos ) + throw NoSuchElementException(); + + // Dialog behaviour is to have all containee names unique (MSO Userform is the same) + // With container controls you could have constructed an existing hierarchy and are now + // removing this control from an existing container, in this case all nested names in + // the containment hierarchy of the control to be removed need to be removed from the global + // names cache (we need to do this obviously before the 'this' objects container is updated) + Reference< XNameContainer > xAllChildren( getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + if ( xAllChildren.is() ) + updateUserFormChildren( xAllChildren, aName, Remove, uno::Reference< XControlModel >() ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element <<= aElementPos->first; + aEvent.Accessor <<= aName; + maContainerListeners.elementRemoved( aEvent ); + + stopControlListening( aElementPos->first ); + Reference< XPropertySet > xPS( aElementPos->first, UNO_QUERY ); + maModels.erase( aElementPos ); + mbGroupsUpToDate = false; + + if ( xPS.is() ) + { + try + { + xPS->setPropertyValue( PROPERTY_RESOURCERESOLVER, Any( Reference< resource::XStringResourceResolver >() ) ); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + + // our "tab controller model" has potentially changed -> notify this + implNotifyTabModelChange( aName ); +} + + +sal_Bool SAL_CALL ControlModelContainerBase::getGroupControl( ) +{ + return true; +} + + +void SAL_CALL ControlModelContainerBase::setGroupControl( sal_Bool ) +{ + SAL_WARN("toolkit", "explicit grouping not supported" ); +} + + +void SAL_CALL ControlModelContainerBase::setControlModels( const Sequence< Reference< XControlModel > >& _rControls ) +{ + SolarMutexGuard aGuard; + + // set the tab indexes according to the order of models in the sequence + + sal_Int16 nTabIndex = 1; + + for ( auto const & control : _rControls ) + { + // look up the control in our own structure. This is to prevent invalid arguments + UnoControlModelHolderVector::const_iterator aPos = + ::std::find_if( + maModels.begin(), maModels.end(), + CompareControlModel( control ) + ); + if ( maModels.end() != aPos ) + { + // okay, this is an existent model + // now set the TabIndex property (if applicable) + Reference< XPropertySet > xProps( aPos->first, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xProps.is() ) + xPSI = xProps->getPropertySetInfo(); + if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) ) + xProps->setPropertyValue( aTabIndexPropertyNameStr, Any( nTabIndex++ ) ); + } + mbGroupsUpToDate = false; + } +} + + +typedef ::std::multimap< sal_Int32, Reference< XControlModel > > MapIndexToModel; + + +Sequence< Reference< XControlModel > > SAL_CALL ControlModelContainerBase::getControlModels( ) +{ + SolarMutexGuard aGuard; + + MapIndexToModel aSortedModels; + // will be the sorted container of all models which have a tab index property + ::std::vector< Reference< XControlModel > > aUnindexedModels; + // will be the container of all models which do not have a tab index property + + for ( const auto& rModel : maModels ) + { + Reference< XControlModel > xModel( rModel.first ); + + // see if the model has a TabIndex property + Reference< XPropertySet > xControlProps( xModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xControlProps.is() ) + xPSI = xControlProps->getPropertySetInfo( ); + DBG_ASSERT( xPSI.is(), "ControlModelContainerBase::getControlModels: invalid child model!" ); + + // has it? + if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) ) + { // yes + sal_Int32 nTabIndex = -1; + xControlProps->getPropertyValue( aTabIndexPropertyNameStr ) >>= nTabIndex; + + aSortedModels.emplace( nTabIndex, xModel ); + } + else if ( xModel.is() ) + // no, it hasn't, but we have to include it, anyway + aUnindexedModels.push_back( xModel ); + } + + // okay, here we have a container of all our models, sorted by tab index, + // plus a container of "unindexed" models + // -> merge them + Sequence< Reference< XControlModel > > aReturn( aUnindexedModels.size() + aSortedModels.size() ); + ::std::transform( + aSortedModels.begin(), aSortedModels.end(), + ::std::copy( aUnindexedModels.begin(), aUnindexedModels.end(), aReturn.getArray() ), + [] ( const MapIndexToModel::value_type& entryIndexToModel ) + { return entryIndexToModel.second; } + ); + + return aReturn; +} + + +void SAL_CALL ControlModelContainerBase::setGroup( const Sequence< Reference< XControlModel > >&, const OUString& ) +{ + // not supported. We have only implicit grouping: + // We only have a sequence of control models, and we _know_ (yes, that's a HACK relying on + // implementation details) that VCL does grouping according to the order of controls automatically + // At least VCL does this for all we're interested in: Radio buttons. + SAL_WARN("toolkit", "grouping not supported" ); +} + +////----- XInitialization ------------------------------------------------------------------- +void SAL_CALL ControlModelContainerBase::initialize (const Sequence<Any>& rArguments) +{ + if ( rArguments.getLength() == 1 ) + { + sal_Int16 nPageId = -1; + if ( !( rArguments[ 0 ] >>= nPageId )) + throw lang::IllegalArgumentException(); + m_nTabPageId = nPageId; + } + else + m_nTabPageId = -1; +} +::sal_Int16 SAL_CALL ControlModelContainerBase::getTabPageID() +{ + return m_nTabPageId; +} +sal_Bool SAL_CALL ControlModelContainerBase::getEnabled() +{ + SolarMutexGuard aGuard; + bool bEnabled = false; + getPropertyValue(GetPropertyName(BASEPROPERTY_ENABLED)) >>= bEnabled; + return bEnabled; +} +void SAL_CALL ControlModelContainerBase::setEnabled( sal_Bool _enabled ) +{ + SolarMutexGuard aGuard; + setPropertyValue(GetPropertyName(BASEPROPERTY_ENABLED), Any(_enabled)); +} +OUString SAL_CALL ControlModelContainerBase::getTitle() +{ + SolarMutexGuard aGuard; + OUString sTitle; + getPropertyValue(GetPropertyName(BASEPROPERTY_TITLE)) >>= sTitle; + return sTitle; +} +void SAL_CALL ControlModelContainerBase::setTitle( const OUString& _title ) +{ + SolarMutexGuard aGuard; + setPropertyValue(GetPropertyName(BASEPROPERTY_TITLE),Any(_title)); +} +OUString SAL_CALL ControlModelContainerBase::getImageURL() +{ + return m_sImageURL; +} +void SAL_CALL ControlModelContainerBase::setImageURL( const OUString& _imageurl ) +{ + m_sImageURL = _imageurl; + SolarMutexGuard aGuard; + setPropertyValue(GetPropertyName(BASEPROPERTY_IMAGEURL), Any(_imageurl)); +} +OUString SAL_CALL ControlModelContainerBase::getToolTip() +{ + return m_sTooltip; +} +void SAL_CALL ControlModelContainerBase::setToolTip( const OUString& _tooltip ) +{ + m_sTooltip = _tooltip; +} + + +namespace +{ + enum GroupingMachineState + { + eLookingForGroup, + eExpandingGroup + }; + + + sal_Int32 lcl_getDialogStep( const Reference< XControlModel >& _rxModel ) + { + sal_Int32 nStep = 0; + try + { + Reference< XPropertySet > xModelProps( _rxModel, UNO_QUERY ); + xModelProps->getPropertyValue( u"Step"_ustr ) >>= nStep; + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("toolkit", "caught an exception while determining the dialog page" ); + } + return nStep; + } +} + + +sal_Int32 SAL_CALL ControlModelContainerBase::getGroupCount( ) +{ + SolarMutexGuard aGuard; + + implUpdateGroupStructure(); + + return maGroups.size(); +} + + +void SAL_CALL ControlModelContainerBase::getGroup( sal_Int32 _nGroup, Sequence< Reference< XControlModel > >& _rGroup, OUString& _rName ) +{ + SolarMutexGuard aGuard; + + implUpdateGroupStructure(); + + if ( ( _nGroup < 0 ) || ( o3tl::make_unsigned(_nGroup) >= maGroups.size() ) ) + { + SAL_WARN("toolkit", "invalid argument and I am not allowed to throw exception!" ); + _rGroup.realloc( 0 ); + _rName.clear(); + } + else + { + AllGroups::const_iterator aGroupPos = maGroups.begin() + _nGroup; + _rGroup.realloc( aGroupPos->size() ); + // copy the models + ::std::copy( aGroupPos->begin(), aGroupPos->end(), _rGroup.getArray() ); + // give the group a name + _rName = OUString::number( _nGroup ); + } +} + + +void SAL_CALL ControlModelContainerBase::getGroupByName( const OUString& _rName, Sequence< Reference< XControlModel > >& _rGroup ) +{ + SolarMutexGuard aGuard; + + OUString sDummyName; + getGroup( _rName.toInt32( ), _rGroup, sDummyName ); +} + + +void SAL_CALL ControlModelContainerBase::addChangesListener( const Reference< XChangesListener >& _rxListener ) +{ + std::unique_lock g(m_aMutex); + maChangeListeners.addInterface( g, _rxListener ); +} + + +void SAL_CALL ControlModelContainerBase::removeChangesListener( const Reference< XChangesListener >& _rxListener ) +{ + std::unique_lock g(m_aMutex); + maChangeListeners.removeInterface( g, _rxListener ); +} + + +void ControlModelContainerBase::implNotifyTabModelChange( const OUString& _rAccessor ) +{ + // multiplex to our change listeners: + // the changes event + ChangesEvent aEvent; + aEvent.Source = *this; + aEvent.Base <<= aEvent.Source; // the "base of the changes root" is also ourself + aEvent.Changes.realloc( 1 ); // exactly one change + aEvent.Changes.getArray()[ 0 ].Accessor <<= _rAccessor; + + + std::unique_lock g(m_aMutex); + std::vector< Reference< css::util::XChangesListener > > aChangeListeners( maChangeListeners.getElements(g) ); + g.unlock(); + for ( const auto& rListener : aChangeListeners ) + rListener->changesOccurred( aEvent ); +} + + +void ControlModelContainerBase::implUpdateGroupStructure() +{ + if ( mbGroupsUpToDate ) + // nothing to do + return; + + // conditions for a group: + // * all elements of the group are radio buttons + // * all elements of the group are on the same dialog page + // * in the overall control order (determined by the tab index), all elements are subsequent + + maGroups.clear(); + + const Sequence< Reference< XControlModel > > aControlModels = getControlModels(); + + // in extreme we have as much groups as controls + maGroups.reserve( aControlModels.getLength() ); + + GroupingMachineState eState = eLookingForGroup; // the current state of our machine + Reference< XServiceInfo > xModelSI; // for checking for a radio button + AllGroups::iterator aCurrentGroup = maGroups.end(); // the group which we're currently building + sal_Int32 nCurrentGroupStep = -1; // the step which all controls of the current group belong to + + + for ( const Reference< XControlModel >& rControlModel : aControlModels ) + { + // we'll need this in every state + xModelSI.set(rControlModel, css::uno::UNO_QUERY); + // is it a radio button? + bool bIsRadioButton = xModelSI.is() && xModelSI->supportsService( "com.sun.star.awt.UnoControlRadioButtonModel" ); + + switch ( eState ) + { + case eLookingForGroup: + { + if ( !bIsRadioButton ) + // this is no radio button -> still looking for the beginning of a group + continue; + // the current model is a radio button + // -> we found the beginning of a new group + // create the place for this group + size_t nGroups = maGroups.size(); + maGroups.resize( nGroups + 1 ); + aCurrentGroup = maGroups.begin() + nGroups; + // and add the (only, til now) member + aCurrentGroup->push_back( rControlModel ); + + // get the step which all controls of this group now have to belong to + nCurrentGroupStep = lcl_getDialogStep( rControlModel ); + // new state: looking for further members + eState = eExpandingGroup; + + } + break; + + case eExpandingGroup: + { + if ( !bIsRadioButton ) + { // no radio button -> the group is done + aCurrentGroup = maGroups.end(); + eState = eLookingForGroup; + continue; + } + + // it is a radio button - is it on the proper page? + const sal_Int32 nThisModelStep = lcl_getDialogStep( rControlModel ); + if ( ( nThisModelStep == nCurrentGroupStep ) // the current button is on the same dialog page + || ( 0 == nThisModelStep ) // the current button appears on all pages + ) + { + // -> it belongs to the same group + aCurrentGroup->push_back( rControlModel ); + // state still is eExpandingGroup - we're looking for further elements + eState = eExpandingGroup; + + continue; + } + + // it's a radio button, but on a different page + // -> we open a new group for it + + + // open a new group + size_t nGroups = maGroups.size(); + maGroups.resize( nGroups + 1 ); + aCurrentGroup = maGroups.begin() + nGroups; + // and add the (only, til now) member + aCurrentGroup->push_back( rControlModel ); + + nCurrentGroupStep = nThisModelStep; + + // state is the same: we still are looking for further elements of the current group + eState = eExpandingGroup; + } + break; + } + } + + mbGroupsUpToDate = true; +} + + +void SAL_CALL ControlModelContainerBase::propertyChange( const PropertyChangeEvent& _rEvent ) +{ + SolarMutexGuard aGuard; + + DBG_ASSERT( _rEvent.PropertyName == "TabIndex", + "ControlModelContainerBase::propertyChange: not listening for this property!" ); + + // the accessor for the changed element + OUString sAccessor; + UnoControlModelHolderVector::const_iterator aPos = + ::std::find_if( + maModels.begin(), maModels.end(), + CompareControlModel( Reference< XControlModel >( _rEvent.Source, UNO_QUERY ) ) + ); + OSL_ENSURE( maModels.end() != aPos, "ControlModelContainerBase::propertyChange: don't know this model!" ); + if ( maModels.end() != aPos ) + sAccessor = aPos->second; + + // our groups are not up-to-date + mbGroupsUpToDate = false; + + // notify + implNotifyTabModelChange( sAccessor ); +} + + +void SAL_CALL ControlModelContainerBase::disposing( const EventObject& /*rEvent*/ ) +{ +} + + +void ControlModelContainerBase::startControlListening( const Reference< XControlModel >& _rxChildModel ) +{ + SolarMutexGuard aGuard; + + Reference< XPropertySet > xModelProps( _rxChildModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xModelProps.is() ) + xPSI = xModelProps->getPropertySetInfo(); + + if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) ) + xModelProps->addPropertyChangeListener( aTabIndexPropertyNameStr, this ); +} + + +void ControlModelContainerBase::stopControlListening( const Reference< XControlModel >& _rxChildModel ) +{ + SolarMutexGuard aGuard; + + Reference< XPropertySet > xModelProps( _rxChildModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI; + if ( xModelProps.is() ) + xPSI = xModelProps->getPropertySetInfo(); + + if ( xPSI.is() && xPSI->hasPropertyByName( aTabIndexPropertyNameStr ) ) + xModelProps->removePropertyChangeListener( aTabIndexPropertyNameStr, this ); +} + + +// = class ResourceListener + + +ResourceListener::ResourceListener( + const Reference< util::XModifyListener >& rListener ) : + m_xListener( rListener ), + m_bListening( false ) +{ +} + +ResourceListener::~ResourceListener() +{ +} + +// XInterface +Any SAL_CALL ResourceListener::queryInterface( const Type& rType ) +{ + Any a = ::cppu::queryInterface( + rType , + static_cast< XModifyListener* >( this ), + static_cast< XEventListener* >( this )); + + if ( a.hasValue() ) + return a; + + return OWeakObject::queryInterface( rType ); +} + +void SAL_CALL ResourceListener::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ResourceListener::release() noexcept +{ + OWeakObject::release(); +} + +void ResourceListener::startListening( + const Reference< resource::XStringResourceResolver >& rResource ) +{ + { + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + bool bListening( m_bListening ); + bool bResourceSet( m_xResource.is() ); + aGuard.unlock(); + // --- SAFE --- + + if ( bListening && bResourceSet ) + stopListening(); + + // --- SAFE --- + aGuard.lock(); + m_xResource = rResource; + aGuard.unlock(); + // --- SAFE --- + } + + if ( !rResource.is() ) + return; + + try + { + rResource->addModifyListener( this ); + + // --- SAFE --- + std::scoped_lock aGuard( m_aMutex ); + m_bListening = true; + // --- SAFE --- + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } +} + +void ResourceListener::stopListening() +{ + Reference< util::XModifyBroadcaster > xModifyBroadcaster; + + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + if ( m_bListening && m_xResource.is() ) + xModifyBroadcaster = m_xResource; + aGuard.unlock(); + // --- SAFE --- + + if ( !xModifyBroadcaster.is() ) + return; + + try + { + // --- SAFE --- + aGuard.lock(); + m_bListening = false; + m_xResource.clear(); + aGuard.unlock(); + // --- SAFE --- + + xModifyBroadcaster->removeModifyListener( this ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } +} + +// XModifyListener +void SAL_CALL ResourceListener::modified( + const lang::EventObject& aEvent ) +{ + Reference< util::XModifyListener > xListener; + + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + xListener = m_xListener; + aGuard.unlock(); + // --- SAFE --- + + if ( !xListener.is() ) + return; + + try + { + xListener->modified( aEvent ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } +} + +// XEventListener +void SAL_CALL ResourceListener::disposing( + const EventObject& Source ) +{ + Reference< lang::XEventListener > xListener; + Reference< resource::XStringResourceResolver > xResource; + + // --- SAFE --- + std::unique_lock aGuard( m_aMutex ); + Reference< XInterface > xIfacRes( m_xResource, UNO_QUERY ); + Reference< XInterface > xIfacList( m_xListener, UNO_QUERY ); + aGuard.unlock(); + // --- SAFE --- + + if ( Source.Source == xIfacRes ) + { + // --- SAFE --- + aGuard.lock(); + m_bListening = false; + xResource = m_xResource; + xListener = m_xListener; + m_xResource.clear(); + aGuard.unlock(); + // --- SAFE --- + + if ( xListener.is() ) + { + try + { + xListener->disposing( Source ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + } + else if ( Source.Source == xIfacList ) + { + // --- SAFE --- + aGuard.lock(); + m_bListening = false; + xListener = m_xListener; + xResource = m_xResource; + m_xResource.clear(); + m_xListener.clear(); + aGuard.unlock(); + // --- SAFE --- + + // Remove ourself as listener from resource resolver + if ( xResource.is() ) + { + try + { + xResource->removeModifyListener( this ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + } +} + + + +ControlContainerBase::ControlContainerBase( const Reference< XComponentContext >& rxContext ) + :m_xContext(rxContext) + ,mbSizeModified(false) + ,mbPosModified(false) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; + mxListener = new ResourceListener( Reference< util::XModifyListener >(this) ); +} + +ControlContainerBase::~ControlContainerBase() +{ +} + +void ControlContainerBase::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) +{ + SolarMutexGuard aGuard; + UnoControlContainer::createPeer( rxToolkit, rParentPeer ); +} + +void ControlContainerBase::ImplInsertControl( Reference< XControlModel > const & rxModel, const OUString& rName ) +{ + Reference< XPropertySet > xP( rxModel, UNO_QUERY ); + + OUString aDefCtrl; + xP->getPropertyValue( GetPropertyName( BASEPROPERTY_DEFAULTCONTROL ) ) >>= aDefCtrl; + Reference < XControl > xCtrl( m_xContext->getServiceManager()->createInstanceWithContext(aDefCtrl, m_xContext), UNO_QUERY ); + + DBG_ASSERT( xCtrl.is(), "ControlContainerBase::ImplInsertControl: could not create the control!" ); + if ( xCtrl.is() ) + { + xCtrl->setModel( rxModel ); + addControl( rName, xCtrl ); + // will implicitly call addingControl, where we can add the PropertiesChangeListener to the model + // (which we formerly did herein) + // 08.01.2001 - 96008 - fs@openoffice.org + + ImplSetPosSize( xCtrl ); + } +} + +void ControlContainerBase::ImplRemoveControl( Reference< XControlModel > const & rxModel ) +{ + Sequence< Reference< XControl > > aControls = getControls(); + Reference< XControl > xCtrl = StdTabController::FindControl( aControls, rxModel ); + if ( xCtrl.is() ) + { + removeControl( xCtrl ); + try + { + xCtrl->dispose(); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } +} + +void ControlContainerBase::ImplSetPosSize( Reference< XControl >& rxCtrl ) +{ + Reference< XPropertySet > xP( rxCtrl->getModel(), UNO_QUERY ); + + sal_Int32 nX = 0, nY = 0, nWidth = 0, nHeight = 0; + xP->getPropertyValue("PositionX") >>= nX; + xP->getPropertyValue("PositionY") >>= nY; + xP->getPropertyValue("Width") >>= nWidth; + xP->getPropertyValue("Height") >>= nHeight; + MapMode aMode( MapUnit::MapAppFont ); + OutputDevice*pOutDev = Application::GetDefaultDevice(); + if ( pOutDev ) + { + ::Size aTmp( nX, nY ); + aTmp = pOutDev->LogicToPixel( aTmp, aMode ); + nX = aTmp.Width(); + nY = aTmp.Height(); + aTmp = ::Size( nWidth, nHeight ); + aTmp = pOutDev->LogicToPixel( aTmp, aMode ); + nWidth = aTmp.Width(); + nHeight = aTmp.Height(); + } + else + { + Reference< XWindowPeer > xPeer = ImplGetCompatiblePeer(); + Reference< XDevice > xD( xPeer, UNO_QUERY ); + + SimpleFontMetric aFM; + FontDescriptor aFD; + Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_FONTDESCRIPTOR ) ); + aVal >>= aFD; + if ( !aFD.StyleName.isEmpty() ) + { + Reference< XFont > xFont = xD->getFont( aFD ); + aFM = xFont->getFontMetric(); + } + else + { + Reference< XGraphics > xG = xD->createGraphics(); + aFM = xG->getFontMetric(); + } + + sal_Int16 nH = aFM.Ascent + aFM.Descent; + sal_Int16 nW = nH/2; // calculate average width?! + + nX *= nW; + nX /= 4; + nWidth *= nW; + nWidth /= 4; + nY *= nH; + nY /= 8; + nHeight *= nH; + nHeight /= 8; + } + Reference < XWindow > xW( rxCtrl, UNO_QUERY ); + xW->setPosSize( nX, nY, nWidth, nHeight, PosSize::POSSIZE ); +} + +void ControlContainerBase::dispose() +{ + EventObject aEvt; + aEvt.Source = getXWeak(); + // Notify our listener helper about dispose + // --- SAFE --- + + SolarMutexClearableGuard aGuard; + Reference< XEventListener > xListener = mxListener; + mxListener.clear(); + aGuard.clear(); + // --- SAFE --- + + if ( xListener.is() ) + xListener->disposing( aEvt ); + UnoControlContainer::dispose(); +} + +void SAL_CALL ControlContainerBase::disposing( + const EventObject& Source ) +{ + UnoControlContainer::disposing( Source ); +} + +sal_Bool ControlContainerBase::setModel( const Reference< XControlModel >& rxModel ) +{ + SolarMutexGuard aGuard; + + // destroy the old tab controller, if existent + if ( mxTabController.is() ) + { + mxTabController->setModel( nullptr ); // just to be sure, should not be necessary + removeTabController( mxTabController ); + ::comphelper::disposeComponent( mxTabController ); // just to be sure, should not be necessary + mxTabController.clear(); + } + + if ( getModel().is() ) + { + const Sequence< Reference< XControl > > aControls = getControls(); + + for ( const Reference< XControl >& rCtrl : aControls ) + removeControl( rCtrl ); + // will implicitly call removingControl, which will remove the PropertyChangeListener + // (which we formerly did herein) + // 08.01.2001 - 96008 - fs@openoffice.org + + Reference< XContainer > xC( getModel(), UNO_QUERY ); + if ( xC.is() ) + xC->removeContainerListener( this ); + + Reference< XChangesNotifier > xChangeNotifier( getModel(), UNO_QUERY ); + if ( xChangeNotifier.is() ) + xChangeNotifier->removeChangesListener( this ); + } + + bool bRet = UnoControl::setModel( rxModel ); + + if ( getModel().is() ) + { + Reference< XNameAccess > xNA( getModel(), UNO_QUERY ); + if ( xNA.is() ) + { + const Sequence< OUString > aNames = xNA->getElementNames(); + + Reference< XControlModel > xCtrlModel; + for( const OUString& rName : aNames ) + { + xNA->getByName( rName ) >>= xCtrlModel; + ImplInsertControl( xCtrlModel, rName ); + } + } + + Reference< XContainer > xC( getModel(), UNO_QUERY ); + if ( xC.is() ) + xC->addContainerListener( this ); + + Reference< XChangesNotifier > xChangeNotifier( getModel(), UNO_QUERY ); + if ( xChangeNotifier.is() ) + xChangeNotifier->addChangesListener( this ); + } + + Reference< XTabControllerModel > xTabbing( getModel(), UNO_QUERY ); + if ( xTabbing.is() ) + { + mxTabController = new StdTabController; + mxTabController->setModel( xTabbing ); + addTabController( mxTabController ); + } + ImplStartListingForResourceEvents(); + + return bRet; +} +void ControlContainerBase::setDesignMode( sal_Bool bOn ) +{ + SolarMutexGuard aGuard; + + UnoControl::setDesignMode( bOn ); + + Sequence< Reference< XControl > > xCtrls = getControls(); + for ( Reference< XControl >& rControl : asNonConstRange(xCtrls) ) + rControl->setDesignMode( bOn ); + + // #109067# in design mode the tab controller is not notified about + // tab index changes, therefore the tab order must be activated + // when switching from design mode to live mode + if ( mxTabController.is() && !bOn ) + mxTabController->activateTabOrder(); +} + +void ControlContainerBase::elementInserted( const ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xModel; + OUString aName; + + Event.Accessor >>= aName; + Event.Element >>= xModel; + ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementInserted: illegal element!" ); + try + { + ImplInsertControl( xModel, aName ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } +} + +void ControlContainerBase::elementRemoved( const ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xModel; + Event.Element >>= xModel; + ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementRemoved: illegal element!" ); + try + { + ImplRemoveControl( xModel ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } +} + +void ControlContainerBase::elementReplaced( const ContainerEvent& Event ) +{ + SolarMutexGuard aGuard; + + Reference< XControlModel > xModel; + Event.ReplacedElement >>= xModel; + try + { + OSL_ENSURE( xModel.is(), "ControlContainerBase::elementReplaced: invalid ReplacedElement!" ); + if ( xModel.is() ) + ImplRemoveControl( xModel ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + + OUString aName; + Event.Accessor >>= aName; + Event.Element >>= xModel; + ENSURE_OR_RETURN_VOID( xModel.is(), "ControlContainerBase::elementReplaced: invalid new element!" ); + try + { + ImplInsertControl( xModel, aName ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } +} + +// XPropertiesChangeListener +void ControlContainerBase::ImplModelPropertiesChanged( const Sequence< PropertyChangeEvent >& rEvents ) +{ + if( !isDesignMode() && !mbCreatingCompatiblePeer ) + { + auto pEvt = std::find_if(rEvents.begin(), rEvents.end(), + [](const PropertyChangeEvent& rEvt) { + return rEvt.PropertyName == "PositionX" + || rEvt.PropertyName == "PositionY" + || rEvt.PropertyName == "Width" + || rEvt.PropertyName == "Height"; + }); + if (pEvt != rEvents.end()) + { + Reference< XControlModel > xModel( pEvt->Source, UNO_QUERY ); + bool bOwnModel = xModel.get() == getModel().get(); + if ( bOwnModel ) + { + if ( !mbPosModified && !mbSizeModified ) + { + // Don't set new pos/size if we get new values from window listener + Reference< XControl > xThis(this); + ImplSetPosSize( xThis ); + } + } + else + { + Sequence<Reference<XControl> > aControlSequence(getControls()); + Reference<XControl> aControlRef( StdTabController::FindControl( aControlSequence, xModel ) ); + ImplSetPosSize( aControlRef ); + } + } + } + + UnoControlContainer::ImplModelPropertiesChanged( rEvents ); +} + +void ControlContainerBase::addingControl( const Reference< XControl >& _rxControl ) +{ + SolarMutexGuard aGuard; + UnoControlContainer::addingControl( _rxControl ); + + if ( !_rxControl.is() ) + return; + + Reference< XMultiPropertySet > xProps( _rxControl->getModel(), UNO_QUERY ); + if ( xProps.is() ) + { + const Sequence< OUString > aNames { + "PositionX", + "PositionY", + "Width", + "Height" + }; + + xProps->addPropertiesChangeListener( aNames, this ); + } +} + +void ControlContainerBase::removingControl( const Reference< XControl >& _rxControl ) +{ + SolarMutexGuard aGuard; + UnoControlContainer::removingControl( _rxControl ); + + if ( _rxControl.is() ) + { + Reference< XMultiPropertySet > xProps( _rxControl->getModel(), UNO_QUERY ); + if ( xProps.is() ) + xProps->removePropertiesChangeListener( this ); + } + +} + +void SAL_CALL ControlContainerBase::changesOccurred( const ChangesEvent& ) +{ + SolarMutexGuard aGuard; + // a tab controller model may have changed + + // #109067# in design mode don't notify the tab controller + // about tab index changes + if ( mxTabController.is() && !mbDesignMode ) + mxTabController->activateTabOrder(); +} +static void lcl_ApplyResolverToNestedContainees( const Reference< resource::XStringResourceResolver >& xStringResourceResolver, const Reference< XControlContainer >& xContainer ) +{ + OUString aPropName( PROPERTY_RESOURCERESOLVER ); + + Any aNewStringResourceResolver; + aNewStringResourceResolver <<= xStringResourceResolver; + + Sequence< OUString > aPropNames { aPropName }; + + const Sequence< Reference< awt::XControl > > aSeq = xContainer->getControls(); + for ( const Reference< XControl >& xControl : aSeq ) + { + Reference< XPropertySet > xPropertySet; + + if ( xControl.is() ) + xPropertySet.set( xControl->getModel(), UNO_QUERY ); + + if ( !xPropertySet.is() ) + continue; + + try + { + Reference< resource::XStringResourceResolver > xCurrStringResourceResolver; + Any aOldValue = xPropertySet->getPropertyValue( aPropName ); + if ( ( aOldValue >>= xCurrStringResourceResolver ) + && ( xStringResourceResolver == xCurrStringResourceResolver ) + ) + { + Reference< XMultiPropertySet > xMultiPropSet( xPropertySet, UNO_QUERY ); + Reference< XPropertiesChangeListener > xListener( xPropertySet, UNO_QUERY ); + xMultiPropSet->firePropertiesChangeEvent( aPropNames, xListener ); + } + else + xPropertySet->setPropertyValue( aPropName, aNewStringResourceResolver ); + } + catch (const Exception&) + { + } + + uno::Reference< XControlContainer > xNestedContainer( xControl, uno::UNO_QUERY ); + if ( xNestedContainer.is() ) + lcl_ApplyResolverToNestedContainees( xStringResourceResolver, xNestedContainer ); + + } + +} +void ControlContainerBase::ImplStartListingForResourceEvents() +{ + Reference< resource::XStringResourceResolver > xStringResourceResolver; + + if ( !ImplHasProperty(PROPERTY_RESOURCERESOLVER) ) + return; + + ImplGetPropertyValue( PROPERTY_RESOURCERESOLVER ) >>= xStringResourceResolver; + + // Add our helper as listener to retrieve notifications about changes + Reference< util::XModifyListener > rListener( mxListener ); + ResourceListener* pResourceListener = static_cast< ResourceListener* >( rListener.get() ); + + // resource listener will stop listening if resolver reference is empty + if ( pResourceListener ) + pResourceListener->startListening( xStringResourceResolver ); + ImplUpdateResourceResolver(); +} + +void ControlContainerBase::ImplUpdateResourceResolver() +{ + Reference< resource::XStringResourceResolver > xStringResourceResolver; + + if ( !ImplHasProperty(PROPERTY_RESOURCERESOLVER) ) + return; + + ImplGetPropertyValue(PROPERTY_RESOURCERESOLVER) >>= xStringResourceResolver; + + if ( !xStringResourceResolver.is() ) + return; + + lcl_ApplyResolverToNestedContainees( xStringResourceResolver, this ); + + // propagate resource resolver changes to language dependent props of the dialog + Reference< XPropertySet > xPropertySet( getModel(), UNO_QUERY ); + if ( xPropertySet.is() ) + { + Reference< XMultiPropertySet > xMultiPropSet( xPropertySet, UNO_QUERY ); + Reference< XPropertiesChangeListener > xListener( xPropertySet, UNO_QUERY ); + xMultiPropSet->firePropertiesChangeEvent( lcl_getLanguageDependentProperties(), xListener ); + } +} + +//// ---------------------------------------------------- +//// Helper Method to convert relative url to physical location +//// ---------------------------------------------------- + +OUString getPhysicalLocation( const css::uno::Any& rbase, const css::uno::Any& rUrl ) +{ + + OUString baseLocation; + OUString url; + + rbase >>= baseLocation; + rUrl >>= url; + + OUString absoluteURL( url ); + if ( !url.isEmpty() ) + { + INetURLObject urlObj(baseLocation); + urlObj.removeSegment(); + baseLocation = urlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + const INetURLObject protocolCheck( url ); + const INetProtocol protocol = protocolCheck.GetProtocol(); + if ( protocol == INetProtocol::NotValid ) + { + OUString testAbsoluteURL; + if ( ::osl::FileBase::E_None == ::osl::FileBase::getAbsoluteFileURL( baseLocation, url, testAbsoluteURL ) ) + absoluteURL = testAbsoluteURL; + } + } + + return absoluteURL; +} + +void +ControlModelContainerBase::updateUserFormChildren( const Reference< XNameContainer >& xAllChildren, const OUString& aName, ChildOperation Operation, const css::uno::Reference< css::awt::XControlModel >& xTarget ) +{ + if ( Operation < Insert || Operation > Remove ) + throw IllegalArgumentException(); + + if ( !xAllChildren.is() ) + throw IllegalArgumentException(); + + if ( Operation == Remove ) + { + Reference< XControlModel > xOldModel( xAllChildren->getByName( aName ), UNO_QUERY ); + xAllChildren->removeByName( aName ); + + Reference< XNameContainer > xChildContainer( xOldModel, UNO_QUERY ); + if ( xChildContainer.is() ) + { + Reference< XPropertySet > xProps( xChildContainer, UNO_QUERY ); + // container control is being removed from this container, reset the + // global list of containers + if ( xProps.is() ) + xProps->setPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ), uno::Any( uno::Reference< XNameContainer >() ) ); + const Sequence< OUString > aChildNames = xChildContainer->getElementNames(); + for ( const auto& rName : aChildNames ) + updateUserFormChildren( xAllChildren, rName, Operation, Reference< XControlModel > () ); + } + } + else if ( Operation == Insert ) + { + xAllChildren->insertByName( aName, uno::Any( xTarget ) ); + Reference< XNameContainer > xChildContainer( xTarget, UNO_QUERY ); + if ( xChildContainer.is() ) + { + // container control is being added from this container, reset the + // global list of containers to point to the correct global list + Reference< XPropertySet > xProps( xChildContainer, UNO_QUERY ); + if ( xProps.is() ) + xProps->setPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ), uno::Any( xAllChildren ) ); + const Sequence< OUString > aChildNames = xChildContainer->getElementNames(); + for ( const auto& rName : aChildNames ) + { + Reference< XControlModel > xChildTarget( xChildContainer->getByName( rName ), UNO_QUERY ); + updateUserFormChildren( xAllChildren, rName, Operation, xChildTarget ); + } + } + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/controlmodelcontainerbase_internal.hxx b/toolkit/source/controls/controlmodelcontainerbase_internal.hxx new file mode 100644 index 0000000000..c65cabc8ad --- /dev/null +++ b/toolkit/source/controls/controlmodelcontainerbase_internal.hxx @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_SOURCE_CONTROLS_CONTROLMODELCONTAINERBASE_INTERNAL_HXX +#define INCLUDED_TOOLKIT_SOURCE_CONTROLS_CONTROLMODELCONTAINERBASE_INTERNAL_HXX + +#include <com/sun/star/uno/Any.hxx> + +////HELPER +OUString getPhysicalLocation(const css::uno::Any& rbase, const css::uno::Any& rUrl); + +#endif // INCLUDED_TOOLKIT_SOURCE_CONTROLS_CONTROLMODELCONTAINERBASE_INTERNAL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/dialogcontrol.cxx b/toolkit/source/controls/dialogcontrol.cxx new file mode 100644 index 0000000000..ba954a1541 --- /dev/null +++ b/toolkit/source/controls/dialogcontrol.cxx @@ -0,0 +1,1245 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <algorithm> + +#include <sal/types.h> +#include <vcl/svapp.hxx> +#include <controls/dialogcontrol.hxx> +#include <controls/geometrycontrolmodel.hxx> +#include <helper/property.hxx> +#include <helper/servicenames.hxx> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/weak.hxx> +#include <tools/debug.hxx> +#include <comphelper/sequence.hxx> +#include <vcl/outdev.hxx> + +#include <vcl/image.hxx> +#include <cppuhelper/implbase.hxx> +#include <unordered_map> + +#include <vcl/tabctrl.hxx> +#include <toolkit/controls/unocontrols.hxx> + +#include <awt/vclxwindows.hxx> +#include <helper/unopropertyarrayhelper.hxx> +#include "controlmodelcontainerbase_internal.hxx" +#include <mutex> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; + +constexpr OUStringLiteral PROPERTY_DIALOGSOURCEURL = u"DialogSourceURL"; +constexpr OUStringLiteral PROPERTY_IMAGEURL = u"ImageURL"; +constexpr OUStringLiteral PROPERTY_GRAPHIC = u"Graphic"; + + +// we probably will need both a hash of control models and hash of controls +// => use some template magic + +namespace { + +template< typename T > +class SimpleNamedThingContainer : public ::cppu::WeakImplHelper< container::XNameContainer > +{ + std::unordered_map< OUString, Reference< T > > things; + std::mutex m_aMutex; +public: + // css::container::XNameContainer, XNameReplace, XNameAccess + virtual void SAL_CALL replaceByName( const OUString& aName, const Any& aElement ) override + { + std::scoped_lock aGuard( m_aMutex ); + auto it = things.find( aName ); + if ( it == things.end() ) + throw NoSuchElementException(); + Reference< T > xElement; + if ( ! ( aElement >>= xElement ) ) + throw IllegalArgumentException(); + it->second = xElement; + } + virtual Any SAL_CALL getByName( const OUString& aName ) override + { + std::scoped_lock aGuard( m_aMutex ); + auto it = things.find( aName ); + if ( it == things.end() ) + throw NoSuchElementException(); + return uno::Any( it->second ); + } + virtual Sequence< OUString > SAL_CALL getElementNames( ) override + { + std::scoped_lock aGuard( m_aMutex ); + return comphelper::mapKeysToSequence( things ); + } + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { + std::scoped_lock aGuard( m_aMutex ); + return ( things.find( aName ) != things.end() ); + } + virtual void SAL_CALL insertByName( const OUString& aName, const Any& aElement ) override + { + std::scoped_lock aGuard( m_aMutex ); + auto it = things.find( aName ); + if ( it != things.end() ) + throw ElementExistException(); + Reference< T > xElement; + if ( ! ( aElement >>= xElement ) ) + throw IllegalArgumentException(); + things[ aName ] = xElement; + } + virtual void SAL_CALL removeByName( const OUString& aName ) override + { + std::scoped_lock aGuard( m_aMutex ); + if ( things.erase( aName ) == 0 ) + throw NoSuchElementException(); + } + virtual Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<T>::get(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + std::scoped_lock aGuard( m_aMutex ); + return !things.empty(); + } +}; + +class UnoControlDialogModel : public ControlModelContainerBase +{ +protected: + css::uno::Reference< css::graphic::XGraphicObject > mxGrfObj; + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + // ::comphelper::OPropertySetHelper + void setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const css::uno::Any& rValue ) override; +public: + explicit UnoControlDialogModel( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + UnoControlDialogModel( const UnoControlDialogModel& rModel ); + + rtl::Reference<UnoControlModel> Clone() const override; + // css::beans::XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "stardiv.Toolkit.UnoControlDialogModel"; } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + auto s(ControlModelContainerBase::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlDialogModel"; + ps[s.getLength() - 1] = "stardiv.vcl.controlmodel.Dialog"; + return s; + } +}; + +UnoControlDialogModel::UnoControlDialogModel( const Reference< XComponentContext >& rxContext ) + :ControlModelContainerBase( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); +// ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); +// ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_TITLE ); + ImplRegisterProperty( BASEPROPERTY_SIZEABLE ); + ImplRegisterProperty( BASEPROPERTY_DESKTOP_AS_PARENT ); + ImplRegisterProperty( BASEPROPERTY_DECORATION ); + ImplRegisterProperty( BASEPROPERTY_DIALOGSOURCEURL ); + ImplRegisterProperty( BASEPROPERTY_GRAPHIC ); + ImplRegisterProperty( BASEPROPERTY_IMAGEURL ); + ImplRegisterProperty( BASEPROPERTY_HSCROLL ); + ImplRegisterProperty( BASEPROPERTY_VSCROLL ); + ImplRegisterProperty( BASEPROPERTY_SCROLLWIDTH ); + ImplRegisterProperty( BASEPROPERTY_SCROLLHEIGHT ); + ImplRegisterProperty( BASEPROPERTY_SCROLLTOP ); + ImplRegisterProperty( BASEPROPERTY_SCROLLLEFT ); + + Any aBool; + aBool <<= true; + ImplRegisterProperty( BASEPROPERTY_MOVEABLE, aBool ); + ImplRegisterProperty( BASEPROPERTY_CLOSEABLE, aBool ); + // #TODO separate class for 'UserForm' ( instead of re-using Dialog ? ) + uno::Reference< XNameContainer > xNameCont = new SimpleNamedThingContainer< XControlModel >; + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES, uno::Any( xNameCont ) ); +} + +UnoControlDialogModel::UnoControlDialogModel( const UnoControlDialogModel& rModel ) + : ControlModelContainerBase( rModel ) +{ + // need to clone BASEPROPERTY_USERFORMCONTAINEES too + Reference< XNameContainer > xSrcNameCont( const_cast< UnoControlDialogModel& >(rModel).getPropertyValue( GetPropertyName( BASEPROPERTY_USERFORMCONTAINEES ) ), UNO_QUERY ); + Reference<XNameContainer > xNameCont( new SimpleNamedThingContainer< XControlModel > ); + + const uno::Sequence< OUString > sNames = xSrcNameCont->getElementNames(); + for ( OUString const & name : sNames ) + { + if ( xSrcNameCont->hasByName( name ) ) + xNameCont->insertByName( name, xSrcNameCont->getByName( name ) ); + } + std::unique_lock aGuard(m_aMutex); + setFastPropertyValue_NoBroadcast( aGuard, BASEPROPERTY_USERFORMCONTAINEES, Any( xNameCont ) ); +} + +rtl::Reference<UnoControlModel> UnoControlDialogModel::Clone() const +{ + // clone the container itself + rtl::Reference<UnoControlDialogModel> pClone = new UnoControlDialogModel( *this ); + + Clone_Impl(*pClone); + + return pClone; +} + + +OUString UnoControlDialogModel::getServiceName( ) +{ + return "stardiv.vcl.controlmodel.Dialog"; +} + +Any UnoControlDialogModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + Any aAny; + + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + aAny <<= OUString::createFromAscii( szServiceName_UnoControlDialog ); + break; + case BASEPROPERTY_SCROLLWIDTH: + case BASEPROPERTY_SCROLLHEIGHT: + case BASEPROPERTY_SCROLLTOP: + case BASEPROPERTY_SCROLLLEFT: + aAny <<= sal_Int32(0); + break; + default: + aAny = UnoControlModel::ImplGetDefaultValue( nPropId ); + } + + return aAny; +} + +::cppu::IPropertyArrayHelper& UnoControlDialogModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// XMultiPropertySet +Reference< XPropertySetInfo > UnoControlDialogModel::getPropertySetInfo( ) +{ + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +void UnoControlDialogModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const css::uno::Any& rValue ) +{ + ControlModelContainerBase::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + try + { + if ( nHandle == BASEPROPERTY_IMAGEURL && ImplHasProperty( BASEPROPERTY_GRAPHIC ) ) + { + OUString sImageURL; + uno::Reference<graphic::XGraphic> xGraphic; + if (rValue >>= sImageURL) + { + setFastPropertyValueImpl(rGuard, + BASEPROPERTY_GRAPHIC, + uno::Any(ImageHelper::getGraphicAndGraphicObjectFromURL_nothrow( + mxGrfObj, sImageURL))); + } + else if (rValue >>= xGraphic) + { + setFastPropertyValueImpl(rGuard, BASEPROPERTY_GRAPHIC, uno::Any(xGraphic)); + } + } + } + catch( const css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "caught an exception while setting ImageURL properties" ); + } +} + +} + + +// = class UnoDialogControl + + +UnoDialogControl::UnoDialogControl( const uno::Reference< uno::XComponentContext >& rxContext ) + :UnoDialogControl_Base( rxContext ) + ,maTopWindowListeners( *this ) + ,mbWindowListener(false) +{ + maComponentInfos.nWidth = 300; + maComponentInfos.nHeight = 450; + } + +UnoDialogControl::~UnoDialogControl() +{ +} + +OUString UnoDialogControl::GetComponentServiceName() const +{ + + bool bDecoration( true ); + ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_DECORATION )) >>= bDecoration; + if ( bDecoration ) + return "Dialog"; + else + return "TabPage"; +} + +void UnoDialogControl::dispose() +{ + SolarMutexGuard aGuard; + + EventObject aEvt; + aEvt.Source = getXWeak(); + maTopWindowListeners.disposeAndClear( aEvt ); + ControlContainerBase::dispose(); +} + +void SAL_CALL UnoDialogControl::disposing( + const EventObject& Source ) +{ + ControlContainerBase::disposing( Source ); +} + +sal_Bool UnoDialogControl::setModel( const Reference< XControlModel >& rxModel ) +{ + // #Can we move all the Resource stuff to the ControlContainerBase ? + SolarMutexGuard aGuard; + bool bRet = ControlContainerBase::setModel( rxModel ); + ImplStartListingForResourceEvents(); + return bRet; +} + +void UnoDialogControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) +{ + SolarMutexGuard aGuard; + + UnoControlContainer::createPeer( rxToolkit, rParentPeer ); + + Reference < XTopWindow > xTW( getPeer(), UNO_QUERY ); + if ( !xTW.is() ) + return; + + xTW->setMenuBar( mxMenuBar ); + + if ( !mbWindowListener ) + { + Reference< XWindowListener > xWL(this); + addWindowListener( xWL ); + mbWindowListener = true; + } + + if ( maTopWindowListeners.getLength() ) + xTW->addTopWindowListener( &maTopWindowListeners ); + // there must be a better way than doing this, we can't + // process the scrolltop & scrollleft in XDialog because + // the children haven't been added when those props are applied + ImplSetPeerProperty( GetPropertyName( BASEPROPERTY_SCROLLTOP ), ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLTOP ) ) ); + ImplSetPeerProperty( GetPropertyName( BASEPROPERTY_SCROLLLEFT ), ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLLEFT ) ) ); +} + +OUString UnoDialogControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoDialogControl"; +} + +sal_Bool UnoDialogControl::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> UnoDialogControl::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.UnoControlDialog", + "stardiv.vcl.control.Dialog"}; +} + +void UnoDialogControl::PrepareWindowDescriptor( css::awt::WindowDescriptor& rDesc ) +{ + UnoControlContainer::PrepareWindowDescriptor( rDesc ); + bool bDecoration( true ); + ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_DECORATION )) >>= bDecoration; + if ( !bDecoration ) + { + // Now we have to manipulate the WindowDescriptor + rDesc.WindowAttributes = rDesc.WindowAttributes | css::awt::WindowAttribute::NODECORATION; + } + + // We have to set the graphic property before the peer + // will be created. Otherwise the properties will be copied + // into the peer via propertiesChangeEvents. As the order of + // can lead to overwrites we have to set the graphic property + // before the propertiesChangeEvents are sent! + OUString aImageURL; + Reference< graphic::XGraphic > xGraphic; + if (( ImplGetPropertyValue( PROPERTY_IMAGEURL ) >>= aImageURL ) && + ( !aImageURL.isEmpty() )) + { + OUString absoluteUrl = getPhysicalLocation(ImplGetPropertyValue(PROPERTY_DIALOGSOURCEURL), uno::Any(aImageURL)); + xGraphic = ImageHelper::getGraphicFromURL_nothrow( absoluteUrl ); + ImplSetPropertyValue( PROPERTY_GRAPHIC, uno::Any( xGraphic ), true ); + } +} + +void UnoDialogControl::addTopWindowListener( const Reference< XTopWindowListener >& rxListener ) +{ + maTopWindowListeners.addInterface( rxListener ); + if( getPeer().is() && maTopWindowListeners.getLength() == 1 ) + { + Reference < XTopWindow > xTW( getPeer(), UNO_QUERY ); + xTW->addTopWindowListener( &maTopWindowListeners ); + } +} + +void UnoDialogControl::removeTopWindowListener( const Reference< XTopWindowListener >& rxListener ) +{ + if( getPeer().is() && maTopWindowListeners.getLength() == 1 ) + { + Reference < XTopWindow > xTW( getPeer(), UNO_QUERY ); + xTW->removeTopWindowListener( &maTopWindowListeners ); + } + maTopWindowListeners.removeInterface( rxListener ); +} + +void UnoDialogControl::toFront( ) +{ + SolarMutexGuard aGuard; + if ( getPeer().is() ) + { + Reference< XTopWindow > xTW( getPeer(), UNO_QUERY ); + if( xTW.is() ) + xTW->toFront(); + } +} + +void UnoDialogControl::toBack( ) +{ + SolarMutexGuard aGuard; + if ( getPeer().is() ) + { + Reference< XTopWindow > xTW( getPeer(), UNO_QUERY ); + if( xTW.is() ) + xTW->toBack(); + } +} + +void UnoDialogControl::setMenuBar( const Reference< XMenuBar >& rxMenuBar ) +{ + SolarMutexGuard aGuard; + mxMenuBar = rxMenuBar; + if ( getPeer().is() ) + { + Reference< XTopWindow > xTW( getPeer(), UNO_QUERY ); + if( xTW.is() ) + xTW->setMenuBar( mxMenuBar ); + } +} +static ::Size ImplMapPixelToAppFont( OutputDevice const * pOutDev, const ::Size& aSize ) +{ + ::Size aTmp = pOutDev->PixelToLogic(aSize, MapMode(MapUnit::MapAppFont)); + return aTmp; +} +// css::awt::XWindowListener +void SAL_CALL UnoDialogControl::windowResized( const css::awt::WindowEvent& e ) +{ + OutputDevice*pOutDev = Application::GetDefaultDevice(); + DBG_ASSERT( pOutDev, "Missing Default Device!" ); + if ( !pOutDev || mbSizeModified ) + return; + + // Currently we are simply using MapUnit::MapAppFont + ::Size aAppFontSize( e.Width, e.Height ); + + Reference< XControl > xDialogControl( *this, UNO_QUERY_THROW ); + Reference< XDevice > xDialogDevice( xDialogControl->getPeer(), UNO_QUERY ); + OSL_ENSURE( xDialogDevice.is(), "UnoDialogControl::windowResized: no peer, but a windowResized event?" ); + + // #i87592 In design mode the drawing layer works with sizes with decoration. + // Therefore we have to subtract them before writing back to the properties (model). + if ( xDialogDevice.is() && mbDesignMode ) + { + DeviceInfo aDeviceInfo( xDialogDevice->getInfo() ); + aAppFontSize.AdjustWidth( -(aDeviceInfo.LeftInset + aDeviceInfo.RightInset) ); + aAppFontSize.AdjustHeight( -(aDeviceInfo.TopInset + aDeviceInfo.BottomInset) ); + } + + aAppFontSize = ImplMapPixelToAppFont( pOutDev, aAppFontSize ); + + // Remember that changes have been done by listener. No need to + // update the position because of property change event. + mbSizeModified = true; + // Properties in a sequence must be sorted! + Sequence< OUString > aProps{ "Height", "Width" }; + Sequence< Any > aValues{ + Any(sal_Int32( + std::clamp(aAppFontSize.Height(), tools::Long(SAL_MIN_INT32), tools::Long(SAL_MAX_INT32)))), + Any(sal_Int32( + std::clamp(aAppFontSize.Width(), tools::Long(SAL_MIN_INT32), tools::Long(SAL_MAX_INT32)))) + }; + + ImplSetPropertyValues( aProps, aValues, true ); + mbSizeModified = false; + +} + +void SAL_CALL UnoDialogControl::windowMoved( const css::awt::WindowEvent& e ) +{ + OutputDevice*pOutDev = Application::GetDefaultDevice(); + DBG_ASSERT( pOutDev, "Missing Default Device!" ); + if ( !pOutDev || mbPosModified ) + return; + + // Currently we are simply using MapUnit::MapAppFont + ::Size aTmp( e.X, e.Y ); + aTmp = ImplMapPixelToAppFont( pOutDev, aTmp ); + + // Remember that changes have been done by listener. No need to + // update the position because of property change event. + mbPosModified = true; + Sequence< OUString > aProps{ "PositionX", "PositionY" }; + Sequence< Any > aValues{ + Any(sal_Int32( + std::clamp(aTmp.Width(), tools::Long(SAL_MIN_INT32), tools::Long(SAL_MAX_INT32)))), + Any(sal_Int32( + std::clamp(aTmp.Height(), tools::Long(SAL_MIN_INT32), tools::Long(SAL_MAX_INT32)))) + }; + + ImplSetPropertyValues( aProps, aValues, true ); + mbPosModified = false; + +} + +void SAL_CALL UnoDialogControl::windowShown( const EventObject& ) {} + +void SAL_CALL UnoDialogControl::windowHidden( const EventObject& ) {} + +void SAL_CALL UnoDialogControl::endDialog( ::sal_Int32 i_result ) +{ + Reference< XDialog2 > xPeerDialog( getPeer(), UNO_QUERY ); + if ( xPeerDialog.is() ) + xPeerDialog->endDialog( i_result ); +} + +void SAL_CALL UnoDialogControl::setHelpId( const OUString& i_id ) +{ + Reference< XDialog2 > xPeerDialog( getPeer(), UNO_QUERY ); + if ( xPeerDialog.is() ) + xPeerDialog->setHelpId( i_id ); +} + +void UnoDialogControl::setTitle( const OUString& Title ) +{ + SolarMutexGuard aGuard; + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TITLE ), uno::Any(Title), true ); +} + +OUString UnoDialogControl::getTitle() +{ + SolarMutexGuard aGuard; + return ImplGetPropertyValue_UString( BASEPROPERTY_TITLE ); +} + +sal_Int16 UnoDialogControl::execute() +{ + SolarMutexGuard aGuard; + sal_Int16 nDone = -1; + if ( getPeer().is() ) + { + Reference< XDialog > xDlg( getPeer(), UNO_QUERY ); + if( xDlg.is() ) + { + GetComponentInfos().bVisible = true; + nDone = xDlg->execute(); + GetComponentInfos().bVisible = false; + } + } + return nDone; +} + +void UnoDialogControl::endExecute() +{ + SolarMutexGuard aGuard; + if ( getPeer().is() ) + { + Reference< XDialog > xDlg( getPeer(), UNO_QUERY ); + if( xDlg.is() ) + { + xDlg->endExecute(); + GetComponentInfos().bVisible = false; + } + } +} + +// XModifyListener +void SAL_CALL UnoDialogControl::modified( + const lang::EventObject& /*rEvent*/ ) +{ + ImplUpdateResourceResolver(); +} + +void UnoDialogControl::ImplModelPropertiesChanged( const Sequence< PropertyChangeEvent >& rEvents ) +{ + for( const PropertyChangeEvent& rEvt : rEvents ) + { + Reference< XControlModel > xModel( rEvt.Source, UNO_QUERY ); + bool bOwnModel = xModel.get() == getModel().get(); + if (bOwnModel && rEvt.PropertyName == "ImageURL" && !ImplHasProperty(BASEPROPERTY_GRAPHIC)) + { + OUString aImageURL; + Reference< graphic::XGraphic > xGraphic; + if (( ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_IMAGEURL ) ) >>= aImageURL ) && + ( !aImageURL.isEmpty() )) + { + OUString absoluteUrl = getPhysicalLocation(ImplGetPropertyValue(GetPropertyName(BASEPROPERTY_DIALOGSOURCEURL)), uno::Any(aImageURL)); + xGraphic = ImageHelper::getGraphicFromURL_nothrow( absoluteUrl ); + } + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_GRAPHIC), uno::Any( xGraphic ), true ); + break; + } + else if (bOwnModel && rEvt.PropertyName == "Graphic") + { + uno::Reference<graphic::XGraphic> xGraphic; + if (ImplGetPropertyValue("Graphic") >>= xGraphic) + { + ImplSetPropertyValue("Graphic", uno::Any(xGraphic), true); + } + break; + } + } + ControlContainerBase::ImplModelPropertiesChanged(rEvents); +} + + + +UnoMultiPageControl::UnoMultiPageControl( const uno::Reference< uno::XComponentContext >& rxContext ) : ControlContainerBase(rxContext), maTabListeners( *this ) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; +} + +UnoMultiPageControl::~UnoMultiPageControl() +{ +} +// XTabListener + +void SAL_CALL UnoMultiPageControl::inserted( SAL_UNUSED_PARAMETER ::sal_Int32 ) +{ +} +void SAL_CALL UnoMultiPageControl::removed( SAL_UNUSED_PARAMETER ::sal_Int32 ) +{ +} +void SAL_CALL UnoMultiPageControl::changed( SAL_UNUSED_PARAMETER ::sal_Int32, + SAL_UNUSED_PARAMETER const Sequence< NamedValue >& ) +{ +} +void SAL_CALL UnoMultiPageControl::activated( ::sal_Int32 ID ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_MULTIPAGEVALUE ), uno::Any( ID ), false ); + +} +void SAL_CALL UnoMultiPageControl::deactivated( SAL_UNUSED_PARAMETER ::sal_Int32 ) +{ +} +void SAL_CALL UnoMultiPageControl::disposing(const EventObject&) +{ +} + +void SAL_CALL UnoMultiPageControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maTabListeners.disposeAndClear( aEvt ); + ControlContainerBase::dispose(); +} + +// css::awt::XSimpleTabController +::sal_Int32 SAL_CALL UnoMultiPageControl::insertTab() +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + return xMultiPage->insertTab(); +} + +void SAL_CALL UnoMultiPageControl::removeTab( ::sal_Int32 ID ) +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + xMultiPage->removeTab( ID ); +} + +void SAL_CALL UnoMultiPageControl::setTabProps( ::sal_Int32 ID, const Sequence< NamedValue >& Properties ) +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + xMultiPage->setTabProps( ID, Properties ); +} + +Sequence< NamedValue > SAL_CALL UnoMultiPageControl::getTabProps( ::sal_Int32 ID ) +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + return xMultiPage->getTabProps( ID ); +} + +void SAL_CALL UnoMultiPageControl::activateTab( ::sal_Int32 ID ) +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + xMultiPage->activateTab( ID ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_MULTIPAGEVALUE ), uno::Any( ID ), true ); + +} + +::sal_Int32 SAL_CALL UnoMultiPageControl::getActiveTabID() +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY_THROW ); + return xMultiPage->getActiveTabID(); +} + +void SAL_CALL UnoMultiPageControl::addTabListener( const Reference< XTabListener >& Listener ) +{ + maTabListeners.addInterface( Listener ); + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY ); + if ( xMultiPage.is() && maTabListeners.getLength() == 1 ) + xMultiPage->addTabListener( &maTabListeners ); +} + +void SAL_CALL UnoMultiPageControl::removeTabListener( const Reference< XTabListener >& Listener ) +{ + Reference< XSimpleTabController > xMultiPage( getPeer(), UNO_QUERY ); + if ( xMultiPage.is() && maTabListeners.getLength() == 1 ) + xMultiPage->removeTabListener( &maTabListeners ); + maTabListeners.removeInterface( Listener ); +} + +IMPL_IMPLEMENTATION_ID( UnoMultiPageControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoMultiPageControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XSimpleTabController>::get(), + cppu::UnoType<awt::XTabListener>::get(), + ControlContainerBase::getTypes() + ); + return aTypeList.getTypes(); +} + +// uno::XInterface +uno::Any UnoMultiPageControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XTabListener* >(this), + static_cast< awt::XSimpleTabController* >(this) ); + return (aRet.hasValue() ? aRet : ControlContainerBase::queryAggregation( rType )); +} + +OUString UnoMultiPageControl::GetComponentServiceName() const +{ + bool bDecoration( true ); + ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_DECORATION )) >>= bDecoration; + if ( bDecoration ) + return "tabcontrol"; + // Hopefully we can tweak the tabcontrol to display without tabs + return "tabcontrolnotabs"; +} + +void UnoMultiPageControl::bindPage( const uno::Reference< awt::XControl >& _rxControl ) +{ + uno::Reference< awt::XWindowPeer > xPage( _rxControl->getPeer() ); + uno::Reference< awt::XSimpleTabController > xTabCntrl( getPeer(), uno::UNO_QUERY ); + uno::Reference< beans::XPropertySet > xProps( _rxControl->getModel(), uno::UNO_QUERY ); + + VCLXTabPage* pXPage = dynamic_cast< VCLXTabPage* >( xPage.get() ); + TabPage* pPage = pXPage ? pXPage->getTabPage() : nullptr; + if ( xTabCntrl.is() && pPage ) + { + VCLXMultiPage* pXTab = dynamic_cast< VCLXMultiPage* >( xTabCntrl.get() ); + if ( pXTab ) + { + OUString sTitle; + xProps->getPropertyValue( GetPropertyName( BASEPROPERTY_TITLE ) ) >>= sTitle; + pXTab->insertTab( pPage, sTitle); + } + } + +} + +void UnoMultiPageControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) +{ + SolarMutexGuard aSolarGuard; + + UnoControlContainer::createPeer( rxToolkit, rParentPeer ); + + const uno::Sequence< uno::Reference< awt::XControl > > aCtrls = getControls(); + for( const auto& rCtrl : aCtrls ) + bindPage( rCtrl ); + sal_Int32 nActiveTab(0); + Reference< XPropertySet > xMultiProps( getModel(), UNO_QUERY ); + xMultiProps->getPropertyValue( GetPropertyName( BASEPROPERTY_MULTIPAGEVALUE ) ) >>= nActiveTab; + + uno::Reference< awt::XSimpleTabController > xTabCntrl( getPeer(), uno::UNO_QUERY ); + if ( xTabCntrl.is() ) + { + xTabCntrl->addTabListener( this ); + if ( nActiveTab && aCtrls.hasElements() ) // Ensure peer is initialise with correct activated tab + { + xTabCntrl->activateTab( nActiveTab ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_MULTIPAGEVALUE ), uno::Any( nActiveTab ), true ); + } + } +} + +void UnoMultiPageControl::impl_createControlPeerIfNecessary( const uno::Reference< awt::XControl >& _rxControl) +{ + OSL_PRECOND( _rxControl.is(), "UnoMultiPageControl::impl_createControlPeerIfNecessary: invalid control, this will crash!" ); + + // if the container already has a peer, then also create a peer for the control + uno::Reference< awt::XWindowPeer > xMyPeer( getPeer() ); + + if( xMyPeer.is() ) + { + _rxControl->createPeer( nullptr, xMyPeer ); + bindPage( _rxControl ); + ImplActivateTabControllers(); + } + +} + +// ------------- UnoMultiPageModel ----------------- + +UnoMultiPageModel::UnoMultiPageModel( const Reference< XComponentContext >& rxContext ) : ControlModelContainerBase( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_SIZEABLE ); + //ImplRegisterProperty( BASEPROPERTY_DIALOGSOURCEURL ); + ImplRegisterProperty( BASEPROPERTY_MULTIPAGEVALUE ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES ); + + Any aBool; + aBool <<= true; + ImplRegisterProperty( BASEPROPERTY_MOVEABLE, aBool ); + ImplRegisterProperty( BASEPROPERTY_CLOSEABLE, aBool ); + ImplRegisterProperty( BASEPROPERTY_DECORATION, aBool ); + // MultiPage Control has the tab stop property. And the default value is True. + ImplRegisterProperty( BASEPROPERTY_TABSTOP, aBool ); + + uno::Reference< XNameContainer > xNameCont = new SimpleNamedThingContainer< XControlModel >; + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES, uno::Any( xNameCont ) ); +} + +UnoMultiPageModel::~UnoMultiPageModel() +{ +} + +rtl::Reference<UnoControlModel> UnoMultiPageModel::Clone() const +{ + // clone the container itself + rtl::Reference<UnoMultiPageModel> pClone = new UnoMultiPageModel( *this ); + Clone_Impl( *pClone ); + return pClone; +} + +OUString UnoMultiPageModel::getServiceName() +{ + return "com.sun.star.awt.UnoMultiPageModel"; +} + +uno::Any UnoMultiPageModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "com.sun.star.awt.UnoControlMultiPage" ) ); + } + return ControlModelContainerBase::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoMultiPageModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoMultiPageModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +void UnoMultiPageModel::insertByName( const OUString& aName, const Any& aElement ) +{ + Reference< XServiceInfo > xInfo; + aElement >>= xInfo; + + if ( !xInfo.is() ) + throw IllegalArgumentException(); + + // Only a Page model can be inserted into the multipage + if ( !xInfo->supportsService( "com.sun.star.awt.UnoPageModel" ) ) + throw IllegalArgumentException(); + + return ControlModelContainerBase::insertByName( aName, aElement ); +} + + +sal_Bool SAL_CALL UnoMultiPageModel::getGroupControl( ) +{ + return true; +} + + + +UnoPageControl::UnoPageControl( const uno::Reference< uno::XComponentContext >& rxContext ) : ControlContainerBase(rxContext) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; +} + +UnoPageControl::~UnoPageControl() +{ +} + +OUString UnoPageControl::GetComponentServiceName() const +{ + return "tabpage"; +} + + +// ------------- UnoPageModel ----------------- + +UnoPageModel::UnoPageModel( const Reference< XComponentContext >& rxContext ) : ControlModelContainerBase( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_TITLE ); + ImplRegisterProperty( BASEPROPERTY_SIZEABLE ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES ); +// ImplRegisterProperty( BASEPROPERTY_DIALOGSOURCEURL ); + + Any aBool; + aBool <<= true; + ImplRegisterProperty( BASEPROPERTY_MOVEABLE, aBool ); + ImplRegisterProperty( BASEPROPERTY_CLOSEABLE, aBool ); + //ImplRegisterProperty( BASEPROPERTY_TABSTOP, aBool ); + + uno::Reference< XNameContainer > xNameCont = new SimpleNamedThingContainer< XControlModel >; + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES, uno::Any( xNameCont ) ); +} + +UnoPageModel::~UnoPageModel() +{ +} + +rtl::Reference<UnoControlModel> UnoPageModel::Clone() const +{ + // clone the container itself + rtl::Reference<UnoPageModel> pClone = new UnoPageModel( *this ); + Clone_Impl( *pClone ); + return pClone; +} + +OUString UnoPageModel::getServiceName() +{ + return "com.sun.star.awt.UnoPageModel"; +} + +uno::Any UnoPageModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "com.sun.star.awt.UnoControlPage" ) ); + } + return ControlModelContainerBase::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoPageModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoPageModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + + +sal_Bool SAL_CALL UnoPageModel::getGroupControl( ) +{ + return false; +} + +// Frame control + + + +UnoFrameControl::UnoFrameControl( const uno::Reference< uno::XComponentContext >& rxContext ) : ControlContainerBase(rxContext) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; +} + +UnoFrameControl::~UnoFrameControl() +{ +} + +OUString UnoFrameControl::GetComponentServiceName() const +{ + return "frame"; +} + +void UnoFrameControl::ImplSetPosSize( Reference< XControl >& rxCtrl ) +{ + bool bOwnCtrl = false; + OUString sTitle; + if ( rxCtrl.get() == Reference<XControl>( this ).get() ) + bOwnCtrl = true; + Reference< XPropertySet > xProps( getModel(), UNO_QUERY ); + //xProps->getPropertyValue( GetPropertyName( BASEPROPERTY_TITLE ) ) >>= sTitle; + xProps->getPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ) ) >>= sTitle; + + ControlContainerBase::ImplSetPosSize( rxCtrl ); + Reference < XWindow > xW( rxCtrl, UNO_QUERY ); + if ( bOwnCtrl || !xW.is() || sTitle.isEmpty() ) + return; + + awt::Rectangle aSizePos = xW->getPosSize(); + + sal_Int32 nX = aSizePos.X, nY = aSizePos.Y, nWidth = aSizePos.Width, nHeight = aSizePos.Height; + // Retrieve the values set by the base class + OutputDevice*pOutDev = Application::GetDefaultDevice(); + if ( pOutDev ) + { + // Adjust Y based on height of Title + ::tools::Rectangle aRect = pOutDev->GetTextRect( {}, sTitle ); + nY = nY + ( aRect.GetHeight() / 2 ); + } + else + { + Reference< XWindowPeer > xPeer = ImplGetCompatiblePeer(); + Reference< XDevice > xD( xPeer, UNO_QUERY ); + + SimpleFontMetric aFM; + FontDescriptor aFD; + Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_FONTDESCRIPTOR ) ); + + aVal >>= aFD; + if ( !aFD.StyleName.isEmpty() ) + { + Reference< XFont > xFont = xD->getFont( aFD ); + aFM = xFont->getFontMetric(); + } + else + { + Reference< XGraphics > xG = xD->createGraphics(); + aFM = xG->getFontMetric(); + } + + sal_Int16 nH = aFM.Ascent + aFM.Descent; + // offset y based on height of font ( not sure if my guess at the correct calculation is correct here ) + nY = nY + ( nH / 8); // how do I test this + } + xW->setPosSize( nX, nY, nWidth, nHeight, PosSize::POSSIZE ); +} + +// ------------- UnoFrameModel ----------------- + +UnoFrameModel::UnoFrameModel( const Reference< XComponentContext >& rxContext ) : ControlModelContainerBase( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_LABEL ); + ImplRegisterProperty( BASEPROPERTY_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_CONTEXT_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES ); + ImplRegisterProperty( BASEPROPERTY_HSCROLL ); + ImplRegisterProperty( BASEPROPERTY_VSCROLL ); + ImplRegisterProperty( BASEPROPERTY_SCROLLWIDTH ); + ImplRegisterProperty( BASEPROPERTY_SCROLLHEIGHT ); + ImplRegisterProperty( BASEPROPERTY_SCROLLTOP ); + ImplRegisterProperty( BASEPROPERTY_SCROLLLEFT ); + + + uno::Reference< XNameContainer > xNameCont = new SimpleNamedThingContainer< XControlModel >; + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES, uno::Any( xNameCont ) ); +} + +UnoFrameModel::~UnoFrameModel() +{ +} + +rtl::Reference<UnoControlModel> UnoFrameModel::Clone() const +{ + // clone the container itself + rtl::Reference<UnoFrameModel> pClone = new UnoFrameModel( *this ); + Clone_Impl( *pClone ); + return pClone; +} + +OUString UnoFrameModel::getServiceName() +{ + return "com.sun.star.awt.UnoFrameModel"; +} + +uno::Any UnoFrameModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + { + return uno::Any( OUString( "com.sun.star.awt.UnoControlFrame" ) ); + } + case BASEPROPERTY_SCROLLWIDTH: + case BASEPROPERTY_SCROLLHEIGHT: + case BASEPROPERTY_SCROLLTOP: + case BASEPROPERTY_SCROLLLEFT: + return uno::Any( sal_Int32(0) ); + case BASEPROPERTY_USERFORMCONTAINEES: + { + uno::Reference< XNameContainer > xNameCont = new SimpleNamedThingContainer< XControlModel >; + return Any( xNameCont ); + } + } + return ControlModelContainerBase::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoFrameModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoFrameModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlDialogModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OGeometryControlModel<UnoControlDialogModel>(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoDialogControl_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoDialogControl(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoMultiPageControl_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoMultiPageControl(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoMultiPageModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoMultiPageModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoPageControl_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoPageControl(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoPageModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoPageModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFrameControl_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFrameControl(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFrameModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFrameModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/eventcontainer.cxx b/toolkit/source/controls/eventcontainer.cxx new file mode 100644 index 0000000000..1b57e984d7 --- /dev/null +++ b/toolkit/source/controls/eventcontainer.cxx @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <cppuhelper/factory.hxx> + +#include <controls/eventcontainer.hxx> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> + + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::registry; +using namespace com::sun::star::script; +using namespace cppu; + +namespace toolkit +{ + +// Methods XElementAccess +Type ScriptEventContainer::getElementType() +{ + return mType; +} + +sal_Bool ScriptEventContainer::hasElements() +{ + return !mHashMap.empty(); +} + +// Methods XNameAccess +Any ScriptEventContainer::getByName( const OUString& aName ) +{ + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + if( aIt == mHashMap.end() ) + { + throw NoSuchElementException(); + } + sal_Int32 iHashResult = (*aIt).second; + Any aRetAny = mValues[ iHashResult ]; + return aRetAny; +} + +Sequence< OUString > ScriptEventContainer::getElementNames() +{ + return mNames; +} + +sal_Bool ScriptEventContainer::hasByName( const OUString& aName ) +{ + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + bool bRet = ( aIt != mHashMap.end() ); + return bRet; +} + + +// Methods XNameReplace +void ScriptEventContainer::replaceByName( const OUString& aName, const Any& aElement ) +{ + const Type& aAnyType = aElement.getValueType(); + if( mType != aAnyType ) + throw IllegalArgumentException(); + + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + if( aIt == mHashMap.end() ) + { + throw NoSuchElementException(); + } + sal_Int32 iHashResult = (*aIt).second; + Any aOldElement = mValues[ iHashResult ]; + mValues[ iHashResult ] = aElement; + + // Fire event + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.ReplacedElement = aOldElement; + aEvent.Accessor <<= aName; + maContainerListeners.elementReplaced( aEvent ); +} + + +// Methods XNameContainer +void ScriptEventContainer::insertByName( const OUString& aName, const Any& aElement ) +{ + const Type& aAnyType = aElement.getValueType(); + if( mType != aAnyType ) + throw IllegalArgumentException(); + + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + if( aIt != mHashMap.end() ) + { + throw ElementExistException(); + } + + sal_Int32 nCount = mNames.getLength(); + mNames.realloc( nCount + 1 ); + mValues.resize( nCount + 1 ); + mNames.getArray()[ nCount ] = aName; + mValues[ nCount ] = aElement; + mHashMap[ aName ] = nCount; + + // Fire event + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.Accessor <<= aName; + maContainerListeners.elementInserted( aEvent ); +} + +void ScriptEventContainer::removeByName( const OUString& Name ) +{ + NameContainerNameMap::iterator aIt = mHashMap.find( Name ); + if( aIt == mHashMap.end() ) + { + throw NoSuchElementException(); + } + + sal_Int32 iHashResult = (*aIt).second; + Any aOldElement = mValues[ iHashResult ]; + + // Fire event + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aOldElement; + aEvent.Accessor <<= Name; + maContainerListeners.elementRemoved( aEvent ); + + mHashMap.erase( aIt ); + sal_Int32 iLast = mNames.getLength() - 1; + if( iLast != iHashResult ) + { + OUString* pNames = mNames.getArray(); + pNames[ iHashResult ] = pNames[ iLast ]; + mValues[ iHashResult ] = mValues[ iLast ]; + mHashMap[ pNames[ iHashResult ] ] = iHashResult; + } + mNames.realloc( iLast ); + mValues.resize( iLast ); +} + +// Methods XContainer +void ScriptEventContainer::addContainerListener( const css::uno::Reference< css::container::XContainerListener >& l ) +{ + maContainerListeners.addInterface( l ); +} + +void ScriptEventContainer::removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& l ) +{ + maContainerListeners.removeInterface( l ); +} + + +ScriptEventContainer::ScriptEventContainer() + : mType( cppu::UnoType<ScriptEventDescriptor>::get() ), + maContainerListeners( *this ) +{ +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/filectrl.cxx b/toolkit/source/controls/filectrl.cxx new file mode 100644 index 0000000000..f1b476220b --- /dev/null +++ b/toolkit/source/controls/filectrl.cxx @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/filectrl.hxx> + +#include <com/sun/star/ui/dialogs/FilePicker.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <comphelper/processfactory.hxx> +#include <osl/file.h> +#include <svl/svlresid.hxx> +#include <svl/svl.hrc> +#include <comphelper/diagnose_ex.hxx> +#include <tools/urlobj.hxx> +#include <vcl/toolkit/edit.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui; + + +FileControl::FileControl( vcl::Window* pParent, WinBits nStyle ) : + Window( pParent, nStyle|WB_DIALOGCONTROL ), + maEdit( VclPtr<Edit>::Create(this, (nStyle&(~WB_BORDER))|WB_NOTABSTOP) ), + maButton( VclPtr<PushButton>::Create( this, (nStyle&(~WB_BORDER))|WB_NOLIGHTBORDER|WB_NOPOINTERFOCUS|WB_NOTABSTOP ) ), + maButtonText( SvlResId(STR_FILECTRL_BUTTONTEXT) ), + mnInternalFlags( FileControlMode_Internal::ORIGINALBUTTONTEXT ) +{ + maButton->SetClickHdl( LINK( this, FileControl, ButtonHdl ) ); + + maButton->Show(); + maEdit->Show(); + + SetCompoundControl( true ); + + SetStyle( ImplInitStyle( GetStyle() ) ); +} + + +WinBits FileControl::ImplInitStyle( WinBits nStyle ) +{ + if ( !( nStyle & WB_NOTABSTOP ) ) + { + maEdit->SetStyle( (maEdit->GetStyle()|WB_TABSTOP)&(~WB_NOTABSTOP) ); + maButton->SetStyle( (maButton->GetStyle()|WB_TABSTOP)&(~WB_NOTABSTOP) ); + } + else + { + maEdit->SetStyle( (maEdit->GetStyle()|WB_NOTABSTOP)&(~WB_TABSTOP) ); + maButton->SetStyle( (maButton->GetStyle()|WB_NOTABSTOP)&(~WB_TABSTOP) ); + } + + const WinBits nAlignmentStyle = ( WB_TOP | WB_VCENTER | WB_BOTTOM ); + maEdit->SetStyle( ( maEdit->GetStyle() & ~nAlignmentStyle ) | ( nStyle & nAlignmentStyle ) ); + + if ( !(nStyle & WB_NOGROUP) ) + nStyle |= WB_GROUP; + + if ( !(nStyle & WB_NOBORDER ) ) + nStyle |= WB_BORDER; + + nStyle &= ~WB_TABSTOP; + + return nStyle; +} + + +FileControl::~FileControl() +{ + disposeOnce(); +} + +void FileControl::dispose() +{ + maEdit.disposeAndClear(); + maButton.disposeAndClear(); + Window::dispose(); +} + +void FileControl::SetText( const OUString& rStr ) +{ + maEdit->SetText( rStr ); +} + + +OUString FileControl::GetText() const +{ + return maEdit->GetText(); +} + + +void FileControl::StateChanged( StateChangedType nType ) +{ + if ( nType == StateChangedType::Enable ) + { + maEdit->Enable( IsEnabled() ); + maButton->Enable( IsEnabled() ); + } + else if ( nType == StateChangedType::Zoom ) + { + GetEdit().SetZoom( GetZoom() ); + GetButton().SetZoom( GetZoom() ); + } + else if ( nType == StateChangedType::Style ) + { + SetStyle( ImplInitStyle( GetStyle() ) ); + } + else if ( nType == StateChangedType::ControlFont ) + { + GetEdit().SetControlFont( GetControlFont() ); + // Only use height of the button, as in HTML + // always Courier is used + vcl::Font aFont = GetButton().GetControlFont(); + aFont.SetFontSize( GetControlFont().GetFontSize() ); + GetButton().SetControlFont( aFont ); + } + else if ( nType == StateChangedType::ControlForeground ) + { + GetEdit().SetControlForeground( GetControlForeground() ); + GetButton().SetControlForeground( GetControlForeground() ); + } + else if ( nType == StateChangedType::ControlBackground ) + { + GetEdit().SetControlBackground( GetControlBackground() ); + GetButton().SetControlBackground( GetControlBackground() ); + } + Window::StateChanged( nType ); +} + + +void FileControl::Resize() +{ + static const tools::Long ButtonBorder = 10; + + if( mnInternalFlags & FileControlMode_Internal::INRESIZE ) + return; + mnInternalFlags |= FileControlMode_Internal::INRESIZE;//InResize = sal_True + + Size aOutSz = GetOutputSizePixel(); + tools::Long nButtonTextWidth = maButton->GetTextWidth( maButtonText ); + if ( !(mnInternalFlags & FileControlMode_Internal::ORIGINALBUTTONTEXT) || + ( nButtonTextWidth < aOutSz.Width()/3 ) ) + { + maButton->SetText( maButtonText ); + } + else + { + OUString aSmallText( "..." ); + maButton->SetText( aSmallText ); + nButtonTextWidth = maButton->GetTextWidth( aSmallText ); + } + + tools::Long nButtonWidth = nButtonTextWidth+ButtonBorder; + maEdit->setPosSizePixel( 0, 0, aOutSz.Width()-nButtonWidth, aOutSz.Height() ); + maButton->setPosSizePixel( aOutSz.Width()-nButtonWidth, 0, nButtonWidth, aOutSz.Height() ); + + mnInternalFlags &= ~FileControlMode_Internal::INRESIZE; //InResize = sal_False +} + + +void FileControl::GetFocus() +{ + if (!maEdit || maEdit->isDisposed()) + return; + maEdit->GrabFocus(); +} + +void FileControl::SetEditModifyHdl( const Link<Edit&,void>& rLink ) +{ + if (!maEdit || maEdit->isDisposed()) + return; + maEdit->SetModifyHdl(rLink); +} + +void FileControl::Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags nFlags ) +{ + WinBits nOldEditStyle = GetEdit().GetStyle(); + if ( GetStyle() & WB_BORDER ) + GetEdit().SetStyle( nOldEditStyle|WB_BORDER ); + Size aOrigSize(GetEdit().GetSizePixel()); + GetEdit().SetSizePixel(GetSizePixel()); + GetEdit().Draw( pDev, rPos, nFlags ); + GetEdit().SetSizePixel(aOrigSize); + if ( GetStyle() & WB_BORDER ) + GetEdit().SetStyle( nOldEditStyle ); +} + +IMPL_LINK_NOARG(FileControl, ButtonHdl, Button*, void) +{ + try + { + Reference< XComponentContext > xContext = comphelper::getProcessComponentContext(); + Reference < dialogs::XFilePicker3 > xFilePicker = dialogs::FilePicker::createWithMode( xContext, dialogs::TemplateDescription::FILEOPEN_SIMPLE ); + // transform the system notation text into a file URL + OUString sSystemNotation = GetText(), sFileURL; + oslFileError nError = osl_getFileURLFromSystemPath( sSystemNotation.pData, &sFileURL.pData ); + if ( nError == osl_File_E_INVAL ) + sFileURL = GetText(); // #97709# Maybe URL is already a file URL... + + //#90430# Check if URL is really a file URL + OUString aTmp; + if ( osl_getSystemPathFromFileURL( sFileURL.pData, &aTmp.pData ) == osl_File_E_None ) + { + // initially set this directory + xFilePicker->setDisplayDirectory( sFileURL ); + } + + if ( xFilePicker->execute() ) + { + Sequence < OUString > aPathSeq = xFilePicker->getSelectedFiles(); + + if ( aPathSeq.hasElements() ) + { + OUString aNewText = aPathSeq[0]; + INetURLObject aObj( aNewText ); + if ( aObj.GetProtocol() == INetProtocol::File ) + aNewText = aObj.PathToFileName(); + SetText( aNewText ); + maEdit->GetModifyHdl().Call( *maEdit ); + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "FileControl::ImplBrowseFile: caught an exception while executing the file picker!" ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/formattedcontrol.cxx b/toolkit/source/controls/formattedcontrol.cxx new file mode 100644 index 0000000000..cf3094c358 --- /dev/null +++ b/toolkit/source/controls/formattedcontrol.cxx @@ -0,0 +1,478 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/formattedcontrol.hxx> +#include <helper/property.hxx> + +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/NumberFormatter.hpp> +#include <com/sun/star/util/NumberFormatsSupplier.hpp> + +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/processfactory.hxx> +#include <osl/diagnose.h> + +#include <helper/unopropertyarrayhelper.hxx> +#include <mutex> + +namespace toolkit +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::util; + + + namespace + { + + std::mutex& getDefaultFormatsMutex() + { + static std::mutex s_aDefaultFormatsMutex; + return s_aDefaultFormatsMutex; + } + + Reference< XNumberFormatsSupplier > s_xDefaultFormats; + bool s_bTriedCreation = false; + oslInterlockedCount s_refCount(0); + + const Reference< XNumberFormatsSupplier >& lcl_getDefaultFormats_throw() + { + std::scoped_lock aGuard( getDefaultFormatsMutex() ); + + if ( !s_xDefaultFormats.is() && !s_bTriedCreation ) + { + s_bTriedCreation = true; + s_xDefaultFormats = NumberFormatsSupplier::createWithDefaultLocale( ::comphelper::getProcessComponentContext() ); + } + if ( !s_xDefaultFormats.is() ) + throw RuntimeException(); + + return s_xDefaultFormats; + } + + void lcl_registerDefaultFormatsClient() + { + osl_atomic_increment( &s_refCount ); + } + + void lcl_revokeDefaultFormatsClient() + { + Reference< XNumberFormatsSupplier > xReleasePotentialLastReference; + { + std::scoped_lock aGuard( getDefaultFormatsMutex() ); + if ( 0 != osl_atomic_decrement( &s_refCount ) ) + return; + + xReleasePotentialLastReference = std::move(s_xDefaultFormats); + s_bTriedCreation = false; + } + xReleasePotentialLastReference.clear(); + } + } + + + // = UnoControlFormattedFieldModel + + + UnoControlFormattedFieldModel::UnoControlFormattedFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) + ,m_bRevokedAsClient( false ) + ,m_bSettingValueAndText( false ) + { + ImplRegisterProperty( BASEPROPERTY_ALIGN ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_EFFECTIVE_DEFAULT ); + ImplRegisterProperty( BASEPROPERTY_EFFECTIVE_VALUE ); + ImplRegisterProperty( BASEPROPERTY_EFFECTIVE_MAX ); + ImplRegisterProperty( BASEPROPERTY_EFFECTIVE_MIN ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_FORMATKEY ); + ImplRegisterProperty( BASEPROPERTY_FORMATSSUPPLIER ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_MAXTEXTLEN ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_REPEAT ); + ImplRegisterProperty( BASEPROPERTY_REPEAT_DELAY ); + ImplRegisterProperty( BASEPROPERTY_READONLY ); + ImplRegisterProperty( BASEPROPERTY_SPIN ); + ImplRegisterProperty( BASEPROPERTY_STRICTFORMAT ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_TEXT ); + ImplRegisterProperty( BASEPROPERTY_TEXTCOLOR ); + ImplRegisterProperty( BASEPROPERTY_HIDEINACTIVESELECTION ); + ImplRegisterProperty( BASEPROPERTY_ENFORCE_FORMAT ); + ImplRegisterProperty( BASEPROPERTY_VERTICALALIGN ); + ImplRegisterProperty( BASEPROPERTY_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_CONTEXT_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR ); + ImplRegisterProperty( BASEPROPERTY_HIGHLIGHT_COLOR ); + ImplRegisterProperty( BASEPROPERTY_HIGHLIGHT_TEXT_COLOR ); + + Any aTreatAsNumber; + aTreatAsNumber <<= true; + ImplRegisterProperty( BASEPROPERTY_TREATASNUMBER, aTreatAsNumber ); + + lcl_registerDefaultFormatsClient(); + } + + + UnoControlFormattedFieldModel::~UnoControlFormattedFieldModel() + { + } + + + OUString UnoControlFormattedFieldModel::getServiceName() + { + return "stardiv.vcl.controlmodel.FormattedField"; + } + + + void UnoControlFormattedFieldModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const Any& rValue ) + { + UnoControlModel::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + + switch ( nHandle ) + { + case BASEPROPERTY_EFFECTIVE_VALUE: + if ( !m_bSettingValueAndText ) + impl_updateTextFromValue_nothrow(rGuard); + break; + case BASEPROPERTY_FORMATSSUPPLIER: + impl_updateCachedFormatter_nothrow(rGuard); + impl_updateTextFromValue_nothrow(rGuard); + break; + case BASEPROPERTY_FORMATKEY: + impl_updateCachedFormatKey_nothrow(rGuard); + impl_updateTextFromValue_nothrow(rGuard); + break; + } + } + + + void UnoControlFormattedFieldModel::impl_updateTextFromValue_nothrow( std::unique_lock<std::mutex>& rGuard) + { + if ( !m_xCachedFormatter.is() ) + impl_updateCachedFormatter_nothrow(rGuard); + if ( !m_xCachedFormatter.is() ) + return; + + try + { + Any aEffectiveValue; + getFastPropertyValue( rGuard, aEffectiveValue, BASEPROPERTY_EFFECTIVE_VALUE ); + + OUString sStringValue; + if ( !( aEffectiveValue >>= sStringValue ) ) + { + double nDoubleValue(0); + if ( aEffectiveValue >>= nDoubleValue ) + { + sal_Int32 nFormatKey( 0 ); + if ( m_aCachedFormat.hasValue() ) + m_aCachedFormat >>= nFormatKey; + sStringValue = m_xCachedFormatter->convertNumberToString( nFormatKey, nDoubleValue ); + } + } + + setFastPropertyValueImpl( rGuard, BASEPROPERTY_TEXT, Any( sStringValue ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + + + void UnoControlFormattedFieldModel::impl_updateCachedFormatter_nothrow(std::unique_lock<std::mutex>& rGuard) + { + Any aFormatsSupplier; + getFastPropertyValue( rGuard, aFormatsSupplier, BASEPROPERTY_FORMATSSUPPLIER ); + try + { + Reference< XNumberFormatsSupplier > xSupplier( aFormatsSupplier, UNO_QUERY ); + if ( !xSupplier.is() ) + xSupplier = lcl_getDefaultFormats_throw(); + + if ( !m_xCachedFormatter.is() ) + { + m_xCachedFormatter.set( + NumberFormatter::create(::comphelper::getProcessComponentContext()), + UNO_QUERY_THROW + ); + } + m_xCachedFormatter->attachNumberFormatsSupplier( xSupplier ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + + + void UnoControlFormattedFieldModel::impl_updateCachedFormatKey_nothrow(std::unique_lock<std::mutex>& rGuard) + { + Any aFormatKey; + getFastPropertyValue( rGuard, aFormatKey, BASEPROPERTY_FORMATKEY ); + m_aCachedFormat = aFormatKey; + } + + + void UnoControlFormattedFieldModel::dispose( ) + { + UnoControlModel::dispose(); + + std::unique_lock aGuard( m_aMutex ); + if ( !m_bRevokedAsClient ) + { + lcl_revokeDefaultFormatsClient(); + m_bRevokedAsClient = true; + } + } + + + void UnoControlFormattedFieldModel::ImplNormalizePropertySequence( const sal_Int32 _nCount, sal_Int32* _pHandles, + Any* _pValues, sal_Int32* _pValidHandles ) const + { + ImplEnsureHandleOrder( _nCount, _pHandles, _pValues, BASEPROPERTY_EFFECTIVE_VALUE, BASEPROPERTY_TEXT ); + + UnoControlModel::ImplNormalizePropertySequence( _nCount, _pHandles, _pValues, _pValidHandles ); + } + + + namespace + { + class ResetFlagOnExit + { + private: + bool& m_rFlag; + + public: + explicit ResetFlagOnExit( bool& _rFlag ) + :m_rFlag( _rFlag ) + { + } + ~ResetFlagOnExit() + { + m_rFlag = false; + } + }; + } + + + void SAL_CALL UnoControlFormattedFieldModel::setPropertyValues( const Sequence< OUString >& _rPropertyNames, const Sequence< Any >& _rValues ) + { + bool bSettingValue = false; + bool bSettingText = false; + for ( auto const & propertyName : _rPropertyNames ) + { + if ( BASEPROPERTY_EFFECTIVE_VALUE == GetPropertyId( propertyName ) ) + bSettingValue = true; + + if ( BASEPROPERTY_TEXT == GetPropertyId( propertyName ) ) + bSettingText = true; + } + + m_bSettingValueAndText = ( bSettingValue && bSettingText ); + ResetFlagOnExit aResetFlag( m_bSettingValueAndText ); + UnoControlModel::setPropertyValues( _rPropertyNames, _rValues ); + } + + + bool UnoControlFormattedFieldModel::convertFastPropertyValue( + std::unique_lock<std::mutex>& rGuard, + Any& rConvertedValue, Any& rOldValue, sal_Int32 nPropId, + const Any& rValue ) + { + if ( BASEPROPERTY_EFFECTIVE_DEFAULT == nPropId && rValue.hasValue() ) + { + double dVal = 0; + bool bStreamed = (rValue >>= dVal); + if ( bStreamed ) + { + rConvertedValue <<= dVal; + } + else + { + sal_Int32 nVal = 0; + bStreamed = (rValue >>= nVal); + if ( bStreamed ) + { + rConvertedValue <<= static_cast<double>(nVal); + } + else + { + OUString sVal; + bStreamed = (rValue >>= sVal); + if ( bStreamed ) + { + rConvertedValue <<= sVal; + } + } + } + + if ( bStreamed ) + { + getFastPropertyValue( rGuard, rOldValue, nPropId ); + return !CompareProperties( rConvertedValue, rOldValue ); + } + + throw IllegalArgumentException( + ("Unable to convert the given value for the property " + + GetPropertyName(static_cast<sal_uInt16>(nPropId)) + + " (double, integer, or string expected)."), + static_cast< XPropertySet* >(this), + 1); + } + + return UnoControlModel::convertFastPropertyValue( rGuard, rConvertedValue, rOldValue, nPropId, rValue ); + } + + + Any UnoControlFormattedFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const + { + Any aReturn; + switch (nPropId) + { + case BASEPROPERTY_DEFAULTCONTROL: aReturn <<= OUString("stardiv.vcl.control.FormattedField"); break; + + case BASEPROPERTY_TREATASNUMBER: aReturn <<= true; break; + + case BASEPROPERTY_EFFECTIVE_DEFAULT: + case BASEPROPERTY_EFFECTIVE_VALUE: + case BASEPROPERTY_EFFECTIVE_MAX: + case BASEPROPERTY_EFFECTIVE_MIN: + case BASEPROPERTY_FORMATKEY: + case BASEPROPERTY_FORMATSSUPPLIER: + // (void) + break; + + default : aReturn = UnoControlModel::ImplGetDefaultValue( nPropId ); break; + } + + return aReturn; + } + + + ::cppu::IPropertyArrayHelper& UnoControlFormattedFieldModel::getInfoHelper() + { + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; + } + + // beans::XMultiPropertySet + + Reference< XPropertySetInfo > UnoControlFormattedFieldModel::getPropertySetInfo( ) + { + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + OUString UnoControlFormattedFieldModel::getImplementationName() + { + return "stardiv.Toolkit.UnoControlFormattedFieldModel"; + } + + css::uno::Sequence<OUString> + UnoControlFormattedFieldModel::getSupportedServiceNames() + { + auto s(UnoControlModel::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlFormattedFieldModel"; + ps[s.getLength() - 1] = "stardiv.vcl.controlmodel.FormattedField"; + return s; + } + + // = UnoFormattedFieldControl + + + UnoFormattedFieldControl::UnoFormattedFieldControl() + { + } + + + OUString UnoFormattedFieldControl::GetComponentServiceName() const + { + return "FormattedField"; + } + + + void UnoFormattedFieldControl::textChanged(const TextEvent& e) + { + Reference< XVclWindowPeer > xPeer(getPeer(), UNO_QUERY); + OSL_ENSURE(xPeer.is(), "UnoFormattedFieldControl::textChanged : what kind of peer do I have ?"); + + Sequence< OUString > aNames{ GetPropertyName( BASEPROPERTY_EFFECTIVE_VALUE ), + GetPropertyName( BASEPROPERTY_TEXT ) }; + + Sequence< Any > aValues{ xPeer->getProperty( aNames[0] ), + xPeer->getProperty( aNames[1] ) }; + + ImplSetPropertyValues( aNames, aValues, false ); + + if ( GetTextListeners().getLength() ) + GetTextListeners().textChanged( e ); + } + + OUString UnoFormattedFieldControl::getImplementationName() + { + return "stardiv.Toolkit.UnoFormattedFieldControl"; + } + + css::uno::Sequence<OUString> + UnoFormattedFieldControl::getSupportedServiceNames() + { + auto s(UnoEditControl::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlFormattedField"; + ps[s.getLength() - 1] = "stardiv.vcl.control.FormattedField"; + return s; + } +} // namespace toolkit + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlFormattedFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoControlFormattedFieldModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFormattedFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoFormattedFieldControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/geometrycontrolmodel.cxx b/toolkit/source/controls/geometrycontrolmodel.cxx new file mode 100644 index 0000000000..70e8817da2 --- /dev/null +++ b/toolkit/source/controls/geometrycontrolmodel.cxx @@ -0,0 +1,609 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/geometrycontrolmodel.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/resource/XStringResourceResolver.hpp> +#include <osl/diagnose.h> +#include <comphelper/sequence.hxx> +#include <controls/eventcontainer.hxx> +#include <helper/property.hxx> +#include <algorithm> +#include <functional> +#include <utility> + + +#define GCM_PROPERTY_ID_POS_X 1 +#define GCM_PROPERTY_ID_POS_Y 2 +#define GCM_PROPERTY_ID_WIDTH 3 +#define GCM_PROPERTY_ID_HEIGHT 4 +#define GCM_PROPERTY_ID_NAME 5 +#define GCM_PROPERTY_ID_TABINDEX 6 +#define GCM_PROPERTY_ID_STEP 7 +#define GCM_PROPERTY_ID_TAG 8 +#define GCM_PROPERTY_ID_RESOURCERESOLVER 9 + +constexpr OUStringLiteral GCM_PROPERTY_POS_X = u"PositionX"; +constexpr OUStringLiteral GCM_PROPERTY_POS_Y = u"PositionY"; +constexpr OUStringLiteral GCM_PROPERTY_WIDTH = u"Width"; +constexpr OUStringLiteral GCM_PROPERTY_HEIGHT = u"Height"; +constexpr OUStringLiteral GCM_PROPERTY_NAME = u"Name"; +constexpr OUStringLiteral GCM_PROPERTY_TABINDEX = u"TabIndex"; +constexpr OUStringLiteral GCM_PROPERTY_STEP = u"Step"; +constexpr OUStringLiteral GCM_PROPERTY_TAG = u"Tag"; +constexpr OUStringLiteral GCM_PROPERTY_RESOURCERESOLVER = u"ResourceResolver"; + +#define DEFAULT_ATTRIBS() PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT + + +// namespace toolkit +// { + + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::container; + using namespace ::comphelper; + + + //= OGeometryControlModel_Base + + + OGeometryControlModel_Base::OGeometryControlModel_Base(css::uno::XAggregation* _pAggregateInstance) + :OPropertySetAggregationHelper( m_aBHelper ) + ,OPropertyContainer( m_aBHelper ) + ,OGCM_Base( m_aMutex ) + ,m_nPosX(0) + ,m_nPosY(0) + ,m_nWidth(0) + ,m_nHeight(0) + ,m_nTabIndex(-1) + ,m_nStep(0) + ,m_bCloneable(false) + { + OSL_ENSURE(nullptr != _pAggregateInstance, "OGeometryControlModel_Base::OGeometryControlModel_Base: invalid aggregate!"); + + osl_atomic_increment(&m_refCount); + { + m_xAggregate = _pAggregateInstance; + + { // check if the aggregate is clonable + Reference< XCloneable > xCloneAccess(m_xAggregate, UNO_QUERY); + m_bCloneable = xCloneAccess.is(); + } + + setAggregation(m_xAggregate); + m_xAggregate->setDelegator(getXWeak()); + } + osl_atomic_decrement(&m_refCount); + + registerProperties(); + } + + + OGeometryControlModel_Base::OGeometryControlModel_Base(Reference< XCloneable >& _rxAggregateInstance) + :OPropertySetAggregationHelper( m_aBHelper ) + ,OPropertyContainer( m_aBHelper ) + ,OGCM_Base( m_aMutex ) + ,m_nPosX(0) + ,m_nPosY(0) + ,m_nWidth(0) + ,m_nHeight(0) + ,m_nTabIndex(-1) + ,m_nStep(0) + ,m_bCloneable(_rxAggregateInstance.is()) + { + osl_atomic_increment(&m_refCount); + { + { + // ensure that the temporary gets destructed NOW + m_xAggregate.set(_rxAggregateInstance, UNO_QUERY); + } + OSL_ENSURE(m_xAggregate.is(), "OGeometryControlModel_Base::OGeometryControlModel_Base: invalid object given!"); + + // now the aggregate has a ref count of 2, but before setting the delegator it must be 1 + _rxAggregateInstance.clear(); + // now it should be the 1 we need here ... + + setAggregation(m_xAggregate); + m_xAggregate->setDelegator(getXWeak()); + } + osl_atomic_decrement(&m_refCount); + + registerProperties(); + } + + + Sequence< Type > SAL_CALL OGeometryControlModel_Base::getTypes( ) + { + // our own types + Sequence< Type > aTypes = ::comphelper::concatSequences( + OPropertySetAggregationHelper::getTypes(), + getBaseTypes(), + OGCM_Base::getTypes() + ); + + if ( m_xAggregate.is() ) + { + // retrieve the types of the aggregate + Reference< XTypeProvider > xAggregateTypeProv; + m_xAggregate->queryAggregation( cppu::UnoType<decltype(xAggregateTypeProv)>::get() ) >>= xAggregateTypeProv; + OSL_ENSURE( xAggregateTypeProv.is(), "OGeometryControlModel_Base::getTypes: aggregate should be a type provider!" ); + Sequence< Type > aAggTypes; + if ( xAggregateTypeProv.is() ) + aAggTypes = xAggregateTypeProv->getTypes(); + + // concat the sequences + sal_Int32 nOldSize = aTypes.getLength(); + aTypes.realloc( nOldSize + aAggTypes.getLength() ); + ::std::copy( + std::cbegin(aAggTypes), + std::cend(aAggTypes), + aTypes.getArray() + nOldSize + ); + } + + return aTypes; + } + + + void OGeometryControlModel_Base::registerProperties() + { + // register our members for the property handling of the OPropertyContainer + registerProperty(GCM_PROPERTY_POS_X, GCM_PROPERTY_ID_POS_X, DEFAULT_ATTRIBS(), &m_nPosX, cppu::UnoType<decltype(m_nPosX)>::get()); + registerProperty(GCM_PROPERTY_POS_Y, GCM_PROPERTY_ID_POS_Y, DEFAULT_ATTRIBS(), &m_nPosY, cppu::UnoType<decltype(m_nPosY)>::get()); + registerProperty(GCM_PROPERTY_WIDTH, GCM_PROPERTY_ID_WIDTH, DEFAULT_ATTRIBS(), &m_nWidth, cppu::UnoType<decltype(m_nWidth)>::get()); + registerProperty(GCM_PROPERTY_HEIGHT, GCM_PROPERTY_ID_HEIGHT, DEFAULT_ATTRIBS(), &m_nHeight, cppu::UnoType<decltype(m_nHeight)>::get()); + registerProperty(GCM_PROPERTY_NAME, GCM_PROPERTY_ID_NAME, DEFAULT_ATTRIBS(), &m_aName, cppu::UnoType<decltype(m_aName)>::get()); + registerProperty(GCM_PROPERTY_TABINDEX, GCM_PROPERTY_ID_TABINDEX, DEFAULT_ATTRIBS(), &m_nTabIndex, cppu::UnoType<decltype(m_nTabIndex)>::get()); + registerProperty(GCM_PROPERTY_STEP, GCM_PROPERTY_ID_STEP, DEFAULT_ATTRIBS(), &m_nStep, cppu::UnoType<decltype(m_nStep)>::get()); + registerProperty(GCM_PROPERTY_TAG, GCM_PROPERTY_ID_TAG, DEFAULT_ATTRIBS(), &m_aTag, cppu::UnoType<decltype(m_aTag)>::get()); + registerProperty(GCM_PROPERTY_RESOURCERESOLVER, GCM_PROPERTY_ID_RESOURCERESOLVER, DEFAULT_ATTRIBS(), &m_xStrResolver, cppu::UnoType<decltype(m_xStrResolver)>::get()); + } + + + css::uno::Any OGeometryControlModel_Base::ImplGetDefaultValueByHandle(sal_Int32 nHandle) + { + css::uno::Any aDefault; + + switch ( nHandle ) + { + case GCM_PROPERTY_ID_POS_X: aDefault <<= sal_Int32(0); break; + case GCM_PROPERTY_ID_POS_Y: aDefault <<= sal_Int32(0); break; + case GCM_PROPERTY_ID_WIDTH: aDefault <<= sal_Int32(0); break; + case GCM_PROPERTY_ID_HEIGHT: aDefault <<= sal_Int32(0); break; + case GCM_PROPERTY_ID_NAME: aDefault <<= OUString(); break; + case GCM_PROPERTY_ID_TABINDEX: aDefault <<= sal_Int16(-1); break; + case GCM_PROPERTY_ID_STEP: aDefault <<= sal_Int32(0); break; + case GCM_PROPERTY_ID_TAG: aDefault <<= OUString(); break; + case GCM_PROPERTY_ID_RESOURCERESOLVER: aDefault <<= Reference< resource::XStringResourceResolver >(); break; + default: OSL_FAIL( "ImplGetDefaultValueByHandle - unknown Property" ); + } + + return aDefault; + } + + + css::uno::Any OGeometryControlModel_Base::ImplGetPropertyValueByHandle(sal_Int32 nHandle) const + { + css::uno::Any aValue; + + switch ( nHandle ) + { + case GCM_PROPERTY_ID_POS_X: aValue <<= m_nPosX; break; + case GCM_PROPERTY_ID_POS_Y: aValue <<= m_nPosY; break; + case GCM_PROPERTY_ID_WIDTH: aValue <<= m_nWidth; break; + case GCM_PROPERTY_ID_HEIGHT: aValue <<= m_nHeight; break; + case GCM_PROPERTY_ID_NAME: aValue <<= m_aName; break; + case GCM_PROPERTY_ID_TABINDEX: aValue <<= m_nTabIndex; break; + case GCM_PROPERTY_ID_STEP: aValue <<= m_nStep; break; + case GCM_PROPERTY_ID_TAG: aValue <<= m_aTag; break; + case GCM_PROPERTY_ID_RESOURCERESOLVER: aValue <<= m_xStrResolver; break; + default: OSL_FAIL( "ImplGetPropertyValueByHandle - unknown Property" ); + } + + return aValue; + } + + + void OGeometryControlModel_Base::ImplSetPropertyValueByHandle(sal_Int32 nHandle, const css::uno::Any& aValue) + { + switch ( nHandle ) + { + case GCM_PROPERTY_ID_POS_X: aValue >>= m_nPosX; break; + case GCM_PROPERTY_ID_POS_Y: aValue >>= m_nPosY; break; + case GCM_PROPERTY_ID_WIDTH: aValue >>= m_nWidth; break; + case GCM_PROPERTY_ID_HEIGHT: aValue >>= m_nHeight; break; + case GCM_PROPERTY_ID_NAME: aValue >>= m_aName; break; + case GCM_PROPERTY_ID_TABINDEX: aValue >>= m_nTabIndex; break; + case GCM_PROPERTY_ID_STEP: aValue >>= m_nStep; break; + case GCM_PROPERTY_ID_TAG: aValue >>= m_aTag; break; + case GCM_PROPERTY_ID_RESOURCERESOLVER: aValue >>= m_xStrResolver; break; + default: OSL_FAIL( "ImplSetPropertyValueByHandle - unknown Property" ); + } + } + + + Any SAL_CALL OGeometryControlModel_Base::queryAggregation( const Type& _rType ) + { + Any aReturn; + if (_rType.equals(cppu::UnoType<XCloneable>::get()) && !m_bCloneable) + // somebody is asking for the XCloneable interface, but our aggregate does not support it + // -> outta here + // (need this extra check, cause OGCM_Base::queryAggregation would return this interface + // in every case) + return aReturn; + + aReturn = OGCM_Base::queryAggregation(_rType); + // the basic interfaces (XInterface, XAggregation etc) + + if (!aReturn.hasValue()) + aReturn = OPropertySetAggregationHelper::queryInterface(_rType); + // the property set related interfaces + + if (!aReturn.hasValue() && m_xAggregate.is()) + aReturn = m_xAggregate->queryAggregation(_rType); + // the interfaces our aggregate can provide + + return aReturn; + } + + + Any SAL_CALL OGeometryControlModel_Base::queryInterface( const Type& _rType ) + { + return OGCM_Base::queryInterface(_rType); + } + + + void SAL_CALL OGeometryControlModel_Base::acquire( ) noexcept + { + OGCM_Base::acquire(); + } + + + void SAL_CALL OGeometryControlModel_Base::release( ) noexcept + { + OGCM_Base::release(); + } + + + void OGeometryControlModel_Base::releaseAggregation() + { + // release the aggregate (_before_ clearing m_xAggregate) + if (m_xAggregate.is()) + m_xAggregate->setDelegator(nullptr); + setAggregation(nullptr); + } + + + OGeometryControlModel_Base::~OGeometryControlModel_Base() + { + releaseAggregation(); + } + + + sal_Bool SAL_CALL OGeometryControlModel_Base::convertFastPropertyValue(Any& _rConvertedValue, Any& _rOldValue, + sal_Int32 _nHandle, const Any& _rValue) + { + return OPropertyContainer::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue); + } + + + void SAL_CALL OGeometryControlModel_Base::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue) + { + OPropertyContainer::setFastPropertyValue_NoBroadcast(_nHandle, _rValue); + } + + + void SAL_CALL OGeometryControlModel_Base::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const + { + OPropertyArrayAggregationHelper& rPH = static_cast<OPropertyArrayAggregationHelper&>(const_cast<OGeometryControlModel_Base*>(this)->getInfoHelper()); + OUString sPropName; + sal_Int32 nOriginalHandle = -1; + + if (rPH.fillAggregatePropertyInfoByHandle(&sPropName, &nOriginalHandle, _nHandle)) + OPropertySetAggregationHelper::getFastPropertyValue(_rValue, _nHandle); + else + OPropertyContainer::getFastPropertyValue(_rValue, _nHandle); + } + + + css::beans::PropertyState OGeometryControlModel_Base::getPropertyStateByHandle(sal_Int32 nHandle) + { + css::uno::Any aValue = ImplGetPropertyValueByHandle( nHandle ); + css::uno::Any aDefault = ImplGetDefaultValueByHandle( nHandle ); + + return CompareProperties( aValue, aDefault ) ? css::beans::PropertyState_DEFAULT_VALUE : css::beans::PropertyState_DIRECT_VALUE; + } + + + void OGeometryControlModel_Base::setPropertyToDefaultByHandle(sal_Int32 nHandle) + { + ImplSetPropertyValueByHandle( nHandle , ImplGetDefaultValueByHandle( nHandle ) ); + } + + + css::uno::Any OGeometryControlModel_Base::getPropertyDefaultByHandle( sal_Int32 nHandle ) const + { + return ImplGetDefaultValueByHandle( nHandle ); + } + + + Reference< XPropertySetInfo> SAL_CALL OGeometryControlModel_Base::getPropertySetInfo() + { + return OPropertySetAggregationHelper::createPropertySetInfo(getInfoHelper()); + } + + + Reference< XCloneable > SAL_CALL OGeometryControlModel_Base::createClone( ) + { + OSL_ENSURE(m_bCloneable, "OGeometryControlModel_Base::createClone: invalid call!"); + if (!m_bCloneable) + return Reference< XCloneable >(); + + // let the aggregate create its own clone + // the interface + Reference< XCloneable > xCloneAccess; + m_xAggregate->queryAggregation(cppu::UnoType<decltype(xCloneAccess)>::get()) >>= xCloneAccess; + OSL_ENSURE(xCloneAccess.is(), "OGeometryControlModel_Base::createClone: suspicious aggregate!"); + if (!xCloneAccess.is()) + return Reference< XCloneable >(); + // the aggregate's clone + Reference< XCloneable > xAggregateClone = xCloneAccess->createClone(); + OSL_ENSURE(xAggregateClone.is(), "OGeometryControlModel_Base::createClone: suspicious return of the aggregate!"); + + // create a new wrapper aggregating this return value + rtl::Reference<OGeometryControlModel_Base> pOwnClone = createClone_Impl(xAggregateClone); + OSL_ENSURE(pOwnClone, "OGeometryControlModel_Base::createClone: invalid derivee behaviour!"); + OSL_ENSURE(!xAggregateClone.is(), "OGeometryControlModel_Base::createClone: invalid ctor behaviour!"); + // should have been reset + + // set properties + pOwnClone->m_nPosX = m_nPosX; + pOwnClone->m_nPosY = m_nPosY; + pOwnClone->m_nWidth = m_nWidth; + pOwnClone->m_nHeight = m_nHeight; + pOwnClone->m_aName = m_aName; + pOwnClone->m_nTabIndex = m_nTabIndex; + pOwnClone->m_nStep = m_nStep; + pOwnClone->m_aTag = m_aTag; + + + // Clone event container + Reference< css::script::XScriptEventsSupplier > xEventsSupplier = + static_cast< css::script::XScriptEventsSupplier* >( this ); + + if( xEventsSupplier.is() ) + { + Reference< XNameContainer > xEventCont = xEventsSupplier->getEvents(); + Reference< XNameContainer > xCloneEventCont = pOwnClone->getEvents(); + + const css::uno::Sequence< OUString > aNames = + xEventCont->getElementNames(); + + for( const OUString& aName : aNames ) + { + css::uno::Any aElement = xEventCont->getByName( aName ); + xCloneEventCont->insertByName( aName, aElement ); + } + } + + return pOwnClone; + } + + + Reference< XNameContainer > SAL_CALL OGeometryControlModel_Base::getEvents() + { + if( !mxEventContainer.is() ) + mxEventContainer = new toolkit::ScriptEventContainer(); + return mxEventContainer; + } + + + void SAL_CALL OGeometryControlModel_Base::disposing() + { + OGCM_Base::disposing(); + OPropertySetAggregationHelper::disposing(); + + Reference<XComponent> xComp; + if ( query_aggregation( m_xAggregate, xComp ) ) + xComp->dispose(); + } + + + //= OCommonGeometryControlModel + + + typedef std::unordered_map< OUString, sal_Int32 > HashMapString2Int; + typedef std::vector< css::uno::Sequence< css::beans::Property > > PropSeqArray; + typedef std::vector< ::std::vector< sal_Int32 > > IntArrayArray; + + // for creating class-unique PropertySetInfo's, we need some info: + namespace { HashMapString2Int gServiceSpecifierMap; } + // this one maps from a String, which is the service specifier for our + // aggregate, to a unique id + + namespace { PropSeqArray gAggregateProperties; } + // this one contains the properties which belong to all the unique ids + // in ServiceSpecifierMap + + namespace { IntArrayArray gAmbiguousPropertyIds; } + // the ids of the properties which we as well as our aggregate supply + // For such props, we let our base class handle them, and whenever such + // a prop is set, we forward this to our aggregate. + + // With this, we can ensure that two instances of this class share the + // same PropertySetInfo if and only if both aggregates have the same + // service specifier. + + + OCommonGeometryControlModel::OCommonGeometryControlModel( Reference< XCloneable >& _rxAgg, OUString _aServiceSpecifier ) + :OGeometryControlModel_Base( _rxAgg ) + ,m_sServiceSpecifier(std::move( _aServiceSpecifier )) + ,m_nPropertyMapId( 0 ) + { + Reference< XPropertySetInfo > xPI; + if ( m_xAggregateSet.is() ) + xPI = m_xAggregateSet->getPropertySetInfo(); + if ( !xPI.is() ) + { + releaseAggregation(); + throw IllegalArgumentException(); + } + + HashMapString2Int::iterator aPropMapIdPos = gServiceSpecifierMap.find( m_sServiceSpecifier ); + if ( gServiceSpecifierMap.end() == aPropMapIdPos ) + { + m_nPropertyMapId = gAggregateProperties.size(); + gAggregateProperties.push_back( xPI->getProperties() ); + gAmbiguousPropertyIds.emplace_back( ); + + gServiceSpecifierMap[ m_sServiceSpecifier ] = m_nPropertyMapId; + } + else + m_nPropertyMapId = aPropMapIdPos->second; + } + + namespace { + + struct PropertyNameLess + { + bool operator()( const Property& _rLHS, const Property& _rRHS ) + { + return _rLHS.Name < _rRHS.Name; + } + }; + + + struct PropertyNameEqual + { + const OUString& m_rCompare; + explicit PropertyNameEqual( const OUString& _rCompare ) : m_rCompare( _rCompare ) { } + + bool operator()( const Property& _rLHS ) + { + return _rLHS.Name == m_rCompare; + } + }; + + } + + ::cppu::IPropertyArrayHelper* OCommonGeometryControlModel::createArrayHelper( sal_Int32 _nId ) const + { + OSL_ENSURE( _nId == m_nPropertyMapId, "OCommonGeometryControlModel::createArrayHelper: invalid argument!" ); + OSL_ENSURE( _nId < static_cast<sal_Int32>(gAggregateProperties.size()), "OCommonGeometryControlModel::createArrayHelper: invalid status info (1)!" ); + OSL_ENSURE( _nId < static_cast<sal_Int32>(gAmbiguousPropertyIds.size()), "OCommonGeometryControlModel::createArrayHelper: invalid status info (2)!" ); + + // our own properties + Sequence< Property > aProps; + OPropertyContainer::describeProperties( aProps ); + + // the aggregate properties + Sequence< Property > aAggregateProps = gAggregateProperties[ _nId ]; + + // look for duplicates, and remember them + IntArrayArray::value_type& rDuplicateIds = gAmbiguousPropertyIds[ _nId ]; + // for this, sort the aggregate properties + auto [begin, end] = asNonConstRange(aAggregateProps); + ::std::sort( + begin, + end, + PropertyNameLess() + ); + + // now loop through our own props + for ( const Property& rProp : std::as_const(aProps) ) + { + // look for the current property in the properties of our aggregate + const Property* pAggPropPos = ::std::find_if( std::cbegin(aAggregateProps), std::cend(aAggregateProps), PropertyNameEqual( rProp.Name ) ); + if ( pAggPropPos != std::cend(aAggregateProps) ) + { // found a duplicate + // -> remove from the aggregate property sequence + ::comphelper::removeElementAt( aAggregateProps, pAggPropPos - std::cbegin(aAggregateProps) ); + + // and additionally, remember the id of this property + rDuplicateIds.push_back( rProp.Handle ); + } + } + + // now, finally, sort the duplicates + ::std::sort( rDuplicateIds.begin(), rDuplicateIds.end(), ::std::less< sal_Int32 >() ); + + return new OPropertyArrayAggregationHelper(aProps, aAggregateProps); + } + + + ::cppu::IPropertyArrayHelper& SAL_CALL OCommonGeometryControlModel::getInfoHelper() + { + return *getArrayHelper( m_nPropertyMapId ); + } + + + rtl::Reference<OGeometryControlModel_Base> OCommonGeometryControlModel::createClone_Impl( Reference< XCloneable >& _rxAggregateInstance ) + { + return new OCommonGeometryControlModel( _rxAggregateInstance, m_sServiceSpecifier ); + } + + Sequence< sal_Int8 > SAL_CALL OCommonGeometryControlModel::getImplementationId( ) + { + return css::uno::Sequence<sal_Int8>(); + } + + namespace { + + struct Int32Equal + { + sal_Int32 m_nCompare; + explicit Int32Equal( sal_Int32 _nCompare ) : m_nCompare( _nCompare ) { } + + bool operator()( sal_Int32 _nLHS ) + { + return _nLHS == m_nCompare; + } + }; + + } + + void SAL_CALL OCommonGeometryControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue ) + { + OGeometryControlModel_Base::setFastPropertyValue_NoBroadcast( _nHandle, _rValue ); + + // look if this id is one we recognized as duplicate + IntArrayArray::value_type& rDuplicateIds = gAmbiguousPropertyIds[ m_nPropertyMapId ]; + + if ( std::any_of(rDuplicateIds.begin(), rDuplicateIds.end(), Int32Equal( _nHandle )) ) + { + // yes, it is such a property + OUString sPropName; + sal_Int16 nAttributes(0); + static_cast< OPropertyArrayAggregationHelper* >( getArrayHelper( m_nPropertyMapId ) )->fillPropertyMembersByHandle( &sPropName, &nAttributes, _nHandle ); + + if ( m_xAggregateSet.is() && !sPropName.isEmpty() ) + m_xAggregateSet->setPropertyValue( sPropName, _rValue ); + } + } + + +// } // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/defaultgridcolumnmodel.cxx b/toolkit/source/controls/grid/defaultgridcolumnmodel.cxx new file mode 100644 index 0000000000..5e1a085ba0 --- /dev/null +++ b/toolkit/source/controls/grid/defaultgridcolumnmodel.cxx @@ -0,0 +1,375 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "gridcolumn.hxx" + +#include <com/sun/star/awt/grid/XGridColumnModel.hpp> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/componentguard.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <comphelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <vector> + +using namespace css::awt; +using namespace css::awt::grid; +using namespace css::container; +using namespace css::lang; +using namespace css::uno; +using namespace toolkit; + +namespace { + +typedef ::comphelper::WeakComponentImplHelper < css::awt::grid::XGridColumnModel + , css::lang::XServiceInfo + > DefaultGridColumnModel_Base; + +class DefaultGridColumnModel : public DefaultGridColumnModel_Base +{ +public: + DefaultGridColumnModel(); + DefaultGridColumnModel( DefaultGridColumnModel const & i_copySource ); + + // XGridColumnModel + virtual ::sal_Int32 SAL_CALL getColumnCount() override; + virtual css::uno::Reference< css::awt::grid::XGridColumn > SAL_CALL createColumn( ) override; + virtual ::sal_Int32 SAL_CALL addColumn(const css::uno::Reference< css::awt::grid::XGridColumn > & column) override; + virtual void SAL_CALL removeColumn( ::sal_Int32 i_columnIndex ) override; + virtual css::uno::Sequence< css::uno::Reference< css::awt::grid::XGridColumn > > SAL_CALL getColumns() override; + virtual css::uno::Reference< css::awt::grid::XGridColumn > SAL_CALL getColumn(::sal_Int32 index) override; + virtual void SAL_CALL setDefaultColumns(sal_Int32 rowElements) 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; + + // XContainer + virtual void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + virtual void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + + // XCloneable + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + + // OComponentHelper + virtual void disposing( std::unique_lock<std::mutex>& ) override; + +private: + typedef ::std::vector< rtl::Reference< GridColumn > > Columns; + + ::comphelper::OInterfaceContainerHelper4<XContainerListener> m_aContainerListeners; + Columns m_aColumns; +}; + + DefaultGridColumnModel::DefaultGridColumnModel() + { + } + + DefaultGridColumnModel::DefaultGridColumnModel( DefaultGridColumnModel const & i_copySource ) + { + Columns aColumns; + aColumns.reserve( i_copySource.m_aColumns.size() ); + try + { + for ( Columns::const_iterator col = i_copySource.m_aColumns.begin(); + col != i_copySource.m_aColumns.end(); + ++col + ) + { + rtl::Reference< GridColumn > const xClone( new GridColumn(**col) ); + + xClone->setIndex( col - i_copySource.m_aColumns.begin() ); + + aColumns.push_back( xClone ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + if ( aColumns.size() == i_copySource.m_aColumns.size() ) + m_aColumns.swap( aColumns ); + } + + ::sal_Int32 SAL_CALL DefaultGridColumnModel::getColumnCount() + { + return m_aColumns.size(); + } + + + Reference< XGridColumn > SAL_CALL DefaultGridColumnModel::createColumn( ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return new GridColumn(); + } + + + ::sal_Int32 SAL_CALL DefaultGridColumnModel::addColumn( const Reference< XGridColumn > & i_column ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + GridColumn* const pGridColumn = dynamic_cast<GridColumn*>( i_column.get() ); + if ( pGridColumn == nullptr ) + throw css::lang::IllegalArgumentException( "invalid column implementation", *this, 1 ); + + m_aColumns.push_back( pGridColumn ); + sal_Int32 index = m_aColumns.size() - 1; + pGridColumn->setIndex( index ); + + // fire insertion notifications + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= index; + aEvent.Element <<= i_column; + + m_aContainerListeners.notifyEach( aGuard, &XContainerListener::elementInserted, aEvent ); + + return index; + } + + + void SAL_CALL DefaultGridColumnModel::removeColumn( ::sal_Int32 i_columnIndex ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_columnIndex < 0 ) || ( o3tl::make_unsigned( i_columnIndex ) >= m_aColumns.size() ) ) + throw css::lang::IndexOutOfBoundsException( OUString(), *this ); + + Columns::iterator const pos = m_aColumns.begin() + i_columnIndex; + Reference< XGridColumn > const xColumn( *pos ); + m_aColumns.erase( pos ); + + // update indexes of all subsequent columns + sal_Int32 columnIndex( i_columnIndex ); + for ( Columns::iterator updatePos = m_aColumns.begin() + columnIndex; + updatePos != m_aColumns.end(); + ++updatePos, ++columnIndex + ) + { + GridColumn* pColumnImpl = updatePos->get(); + pColumnImpl->setIndex( columnIndex ); + } + + // fire removal notifications + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= i_columnIndex; + aEvent.Element <<= xColumn; + + m_aContainerListeners.notifyEach( aGuard, &XContainerListener::elementRemoved, aEvent ); + + aGuard.unlock(); + + // dispose the removed column + try + { + xColumn->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + + + Sequence< Reference< XGridColumn > > SAL_CALL DefaultGridColumnModel::getColumns() + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return ::comphelper::containerToSequence<Reference<XGridColumn>>( m_aColumns ); + } + + + Reference< XGridColumn > SAL_CALL DefaultGridColumnModel::getColumn(::sal_Int32 index) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( index >=0 && o3tl::make_unsigned(index) < m_aColumns.size()) + return m_aColumns[index]; + + throw css::lang::IndexOutOfBoundsException(); + } + + + void SAL_CALL DefaultGridColumnModel::setDefaultColumns(sal_Int32 rowElements) + { + ::std::vector< ContainerEvent > aRemovedColumns; + ::std::vector< ContainerEvent > aInsertedColumns; + + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + // remove existing columns + while ( !m_aColumns.empty() ) + { + const size_t lastColIndex = m_aColumns.size() - 1; + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= sal_Int32( lastColIndex ); + aEvent.Element <<= Reference<XGridColumn>(m_aColumns[ lastColIndex ]); + aRemovedColumns.push_back( aEvent ); + + m_aColumns.erase( m_aColumns.begin() + lastColIndex ); + } + + // add new columns + for ( sal_Int32 i=0; i<rowElements; ++i ) + { + ::rtl::Reference< GridColumn > const pGridColumn = new GridColumn(); + OUString colTitle = "Column " + OUString::number( i + 1 ); + pGridColumn->setTitle( colTitle ); + pGridColumn->setColumnWidth( 80 /* APPFONT */ ); + pGridColumn->setFlexibility( 1 ); + pGridColumn->setResizeable( true ); + pGridColumn->setDataColumnIndex( i ); + + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= i; + aEvent.Element <<= Reference<XGridColumn>(pGridColumn); + aInsertedColumns.push_back( aEvent ); + + m_aColumns.push_back( pGridColumn ); + pGridColumn->setIndex( i ); + } + + // fire removal notifications + for (const auto& rEvent : aRemovedColumns) + { + m_aContainerListeners.notifyEach( aGuard, &XContainerListener::elementRemoved, rEvent ); + } + + // fire insertion notifications + for (const auto& rEvent : aInsertedColumns) + { + m_aContainerListeners.notifyEach( aGuard, &XContainerListener::elementInserted, rEvent ); + } + + aGuard.unlock(); + + // dispose removed columns + for (const auto& rEvent : aRemovedColumns) + { + try + { + const Reference< XComponent > xColComp( rEvent.Element, UNO_QUERY ); + if (xColComp) + xColComp->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + } + + + OUString SAL_CALL DefaultGridColumnModel::getImplementationName( ) + { + return "stardiv.Toolkit.DefaultGridColumnModel"; + } + + sal_Bool SAL_CALL DefaultGridColumnModel::supportsService( const OUString& i_serviceName ) + { + return cppu::supportsService(this, i_serviceName); + } + + Sequence< OUString > SAL_CALL DefaultGridColumnModel::getSupportedServiceNames( ) + { + return { "com.sun.star.awt.grid.DefaultGridColumnModel" }; + } + + + void SAL_CALL DefaultGridColumnModel::addContainerListener( const Reference< XContainerListener >& i_listener ) + { + std::unique_lock aGuard(m_aMutex); + if ( i_listener.is() ) + m_aContainerListeners.addInterface( aGuard, i_listener ); + } + + + void SAL_CALL DefaultGridColumnModel::removeContainerListener( const Reference< XContainerListener >& i_listener ) + { + std::unique_lock aGuard(m_aMutex); + if ( i_listener.is() ) + m_aContainerListeners.removeInterface( aGuard, i_listener ); + } + + + void DefaultGridColumnModel::disposing( std::unique_lock<std::mutex>& rGuard ) + { + DefaultGridColumnModel_Base::disposing(rGuard); + + EventObject aEvent( *this ); + m_aContainerListeners.disposeAndClear( rGuard, aEvent ); + + // remove, dispose and clear columns + while ( !m_aColumns.empty() ) + { + try + { + m_aColumns[ 0 ]->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + + m_aColumns.erase( m_aColumns.begin() ); + } + + Columns().swap(m_aColumns); + } + + + Reference< css::util::XCloneable > SAL_CALL DefaultGridColumnModel::createClone( ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return new DefaultGridColumnModel( *this ); + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_DefaultGridColumnModel_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new DefaultGridColumnModel()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/defaultgriddatamodel.cxx b/toolkit/source/controls/grid/defaultgriddatamodel.cxx new file mode 100644 index 0000000000..3b803d4a2e --- /dev/null +++ b/toolkit/source/controls/grid/defaultgriddatamodel.cxx @@ -0,0 +1,512 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/grid/XMutableGridDataModel.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <comphelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> + +#include <algorithm> +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::awt::grid; +using namespace ::com::sun::star::lang; + +namespace { + +typedef ::comphelper::WeakComponentImplHelper < XMutableGridDataModel + , XServiceInfo + > DefaultGridDataModel_Base; + +class DefaultGridDataModel: public DefaultGridDataModel_Base +{ +public: + DefaultGridDataModel(); + DefaultGridDataModel( DefaultGridDataModel const & i_copySource ); + + // XMutableGridDataModel + virtual void SAL_CALL addRow( const Any& i_heading, const css::uno::Sequence< css::uno::Any >& Data ) override; + virtual void SAL_CALL addRows( const css::uno::Sequence< css::uno::Any>& Headings, const css::uno::Sequence< css::uno::Sequence< css::uno::Any > >& Data ) override; + virtual void SAL_CALL insertRow( ::sal_Int32 i_index, const css::uno::Any& i_heading, const css::uno::Sequence< css::uno::Any >& Data ) override; + virtual void SAL_CALL insertRows( ::sal_Int32 i_index, const css::uno::Sequence< css::uno::Any>& Headings, const css::uno::Sequence< css::uno::Sequence< css::uno::Any > >& Data ) override; + virtual void SAL_CALL removeRow( ::sal_Int32 RowIndex ) override; + virtual void SAL_CALL removeAllRows( ) override; + virtual void SAL_CALL updateCellData( ::sal_Int32 ColumnIndex, ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL updateRowData( const css::uno::Sequence< ::sal_Int32 >& ColumnIndexes, ::sal_Int32 RowIndex, const css::uno::Sequence< css::uno::Any >& Values ) override; + virtual void SAL_CALL updateRowHeading( ::sal_Int32 RowIndex, const css::uno::Any& Heading ) override; + virtual void SAL_CALL updateCellToolTip( ::sal_Int32 ColumnIndex, ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL updateRowToolTip( ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL addGridDataListener( const css::uno::Reference< css::awt::grid::XGridDataListener >& Listener ) override; + virtual void SAL_CALL removeGridDataListener( const css::uno::Reference< css::awt::grid::XGridDataListener >& Listener ) override; + + // XGridDataModel + virtual ::sal_Int32 SAL_CALL getRowCount() override; + virtual ::sal_Int32 SAL_CALL getColumnCount() override; + virtual css::uno::Any SAL_CALL getCellData( ::sal_Int32 Column, ::sal_Int32 Row ) override; + virtual css::uno::Any SAL_CALL getCellToolTip( ::sal_Int32 Column, ::sal_Int32 Row ) override; + virtual css::uno::Any SAL_CALL getRowHeading( ::sal_Int32 RowIndex ) override; + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getRowData( ::sal_Int32 RowIndex ) override; + + // OComponentHelper + virtual void disposing( std::unique_lock<std::mutex>& ) override; + + // XCloneable + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) 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; + +private: + typedef ::std::pair< Any, Any > CellData; + typedef ::std::vector< CellData > RowData; + typedef ::std::vector< RowData > GridData; + + void broadcast( + GridDataEvent const & i_event, + void ( SAL_CALL css::awt::grid::XGridDataListener::*i_listenerMethod )( css::awt::grid::GridDataEvent const & ), + std::unique_lock<std::mutex>& i_instanceLock + ); + + void impl_insertRow( std::unique_lock<std::mutex>& rGuard, sal_Int32 const i_position, Any const & i_heading, Sequence< Any > const & i_rowData, sal_Int32 const i_assumedColCount = -1 ); + + ::sal_Int32 impl_getRowCount(std::unique_lock<std::mutex>&) const { return sal_Int32( m_aData.size() ); } + + CellData const & impl_getCellData_throw( std::unique_lock<std::mutex>& rGuard, sal_Int32 const i_columnIndex, sal_Int32 const i_rowIndex ) const; + CellData& impl_getCellDataAccess_throw( std::unique_lock<std::mutex>& rGuard, sal_Int32 const i_columnIndex, sal_Int32 const i_rowIndex ); + RowData& impl_getRowDataAccess_throw( std::unique_lock<std::mutex>& rGuard, sal_Int32 const i_rowIndex, size_t const i_requiredColumnCount ); + + GridData m_aData; + ::std::vector< css::uno::Any > m_aRowHeaders; + sal_Int32 m_nColumnCount; + comphelper::OInterfaceContainerHelper4<XGridDataListener> maGridDataListeners; +}; + + DefaultGridDataModel::DefaultGridDataModel() + :m_nColumnCount(0) + { + } + + + DefaultGridDataModel::DefaultGridDataModel( DefaultGridDataModel const & i_copySource ) + :m_aData( i_copySource.m_aData ) + ,m_aRowHeaders( i_copySource.m_aRowHeaders ) + ,m_nColumnCount( i_copySource.m_nColumnCount ) + { + } + + void DefaultGridDataModel::broadcast( GridDataEvent const & i_event, + void ( SAL_CALL XGridDataListener::*i_listenerMethod )( GridDataEvent const & ), std::unique_lock<std::mutex>& i_instanceLock ) + { + maGridDataListeners.notifyEach( i_instanceLock, i_listenerMethod, i_event ); + } + + + ::sal_Int32 SAL_CALL DefaultGridDataModel::getRowCount() + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return impl_getRowCount(aGuard); + } + + + ::sal_Int32 SAL_CALL DefaultGridDataModel::getColumnCount() + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return m_nColumnCount; + } + + + DefaultGridDataModel::CellData const & DefaultGridDataModel::impl_getCellData_throw( std::unique_lock<std::mutex>& /*rGuard*/, sal_Int32 const i_column, sal_Int32 const i_row ) const + { + if ( ( i_row < 0 ) || ( o3tl::make_unsigned( i_row ) > m_aData.size() ) + || ( i_column < 0 ) || ( i_column > m_nColumnCount ) + ) + throw IndexOutOfBoundsException( OUString(), *const_cast< DefaultGridDataModel* >( this ) ); + + RowData const & rRow( m_aData[ i_row ] ); + if ( o3tl::make_unsigned( i_column ) < rRow.size() ) + return rRow[ i_column ]; + + static CellData s_aEmpty; + return s_aEmpty; + } + + + DefaultGridDataModel::RowData& DefaultGridDataModel::impl_getRowDataAccess_throw( std::unique_lock<std::mutex>& /*rGuard*/, sal_Int32 const i_rowIndex, size_t const i_requiredColumnCount ) + { + OSL_ENSURE( i_requiredColumnCount <= o3tl::make_unsigned( m_nColumnCount ), "DefaultGridDataModel::impl_getRowDataAccess_throw: invalid column count!" ); + if ( ( i_rowIndex < 0 ) || ( o3tl::make_unsigned( i_rowIndex ) >= m_aData.size() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + RowData& rRowData( m_aData[ i_rowIndex ] ); + if ( rRowData.size() < i_requiredColumnCount ) + rRowData.resize( i_requiredColumnCount ); + return rRowData; + } + + + DefaultGridDataModel::CellData& DefaultGridDataModel::impl_getCellDataAccess_throw( std::unique_lock<std::mutex>& rGuard, sal_Int32 const i_columnIndex, sal_Int32 const i_rowIndex ) + { + if ( ( i_columnIndex < 0 ) || ( i_columnIndex >= m_nColumnCount ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + RowData& rRowData( impl_getRowDataAccess_throw( rGuard, i_rowIndex, size_t( i_columnIndex + 1 ) ) ); + return rRowData[ i_columnIndex ]; + } + + + Any SAL_CALL DefaultGridDataModel::getCellData( ::sal_Int32 i_column, ::sal_Int32 i_row ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return impl_getCellData_throw( aGuard, i_column, i_row ).first; + } + + + Any SAL_CALL DefaultGridDataModel::getCellToolTip( ::sal_Int32 i_column, ::sal_Int32 i_row ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + return impl_getCellData_throw( aGuard, i_column, i_row ).second; + } + + + Any SAL_CALL DefaultGridDataModel::getRowHeading( ::sal_Int32 i_row ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_row < 0 ) || ( o3tl::make_unsigned( i_row ) >= m_aRowHeaders.size() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + return m_aRowHeaders[ i_row ]; + } + + + Sequence< Any > SAL_CALL DefaultGridDataModel::getRowData( ::sal_Int32 i_rowIndex ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + Sequence< Any > resultData( m_nColumnCount ); + RowData& rRowData = impl_getRowDataAccess_throw( aGuard, i_rowIndex, m_nColumnCount ); + + ::std::transform( rRowData.begin(), rRowData.end(), resultData.getArray(), + [] ( const CellData& rCellData ) + { return rCellData.first; }); + return resultData; + } + + + void DefaultGridDataModel::impl_insertRow( std::unique_lock<std::mutex>& /*rGuard*/, sal_Int32 const i_position, Any const & i_heading, Sequence< Any > const & i_rowData, sal_Int32 const i_assumedColCount ) + { + OSL_PRECOND( ( i_assumedColCount <= 0 ) || ( i_assumedColCount >= i_rowData.getLength() ), + "DefaultGridDataModel::impl_insertRow: invalid column count!" ); + + // insert heading + m_aRowHeaders.insert( m_aRowHeaders.begin() + i_position, i_heading ); + + // create new data row + RowData newRow( i_assumedColCount > 0 ? i_assumedColCount : i_rowData.getLength() ); + RowData::iterator cellData = newRow.begin(); + for ( const Any& rData : i_rowData ) + { + cellData->first = rData; + ++cellData; + } + + // insert data row + m_aData.insert( m_aData.begin() + i_position, newRow ); + } + + + void SAL_CALL DefaultGridDataModel::addRow( const Any& i_heading, const Sequence< Any >& i_data ) + { + insertRow( getRowCount(), i_heading, i_data ); + } + + + void SAL_CALL DefaultGridDataModel::addRows( const Sequence< Any >& i_headings, const Sequence< Sequence< Any > >& i_data ) + { + insertRows( getRowCount(), i_headings, i_data ); + } + + + void SAL_CALL DefaultGridDataModel::insertRow( ::sal_Int32 i_index, const Any& i_heading, const Sequence< Any >& i_data ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_index < 0 ) || ( i_index > impl_getRowCount(aGuard) ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + // actually insert the row + impl_insertRow( aGuard, i_index, i_heading, i_data ); + + // update column count + sal_Int32 const columnCount = i_data.getLength(); + if ( columnCount > m_nColumnCount ) + m_nColumnCount = columnCount; + + broadcast( + GridDataEvent( *this, -1, -1, i_index, i_index ), + &XGridDataListener::rowsInserted, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::insertRows( ::sal_Int32 i_index, const Sequence< Any>& i_headings, const Sequence< Sequence< Any > >& i_data ) + { + if ( i_headings.getLength() != i_data.getLength() ) + throw IllegalArgumentException( OUString(), *this, -1 ); + + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_index < 0 ) || ( i_index > impl_getRowCount(aGuard) ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + sal_Int32 const rowCount = i_headings.getLength(); + if ( rowCount == 0 ) + return; + + // determine max col count in the new data + auto pData = std::max_element(i_data.begin(), i_data.end(), + [](const Sequence< Any >& a, const Sequence< Any >& b) { return a.getLength() < b.getLength(); }); + sal_Int32 maxColCount = pData->getLength(); + + if ( maxColCount < m_nColumnCount ) + maxColCount = m_nColumnCount; + + for ( sal_Int32 row=0; row<rowCount; ++row ) + { + impl_insertRow( aGuard, i_index + row, i_headings[row], i_data[row], maxColCount ); + } + + if ( maxColCount > m_nColumnCount ) + m_nColumnCount = maxColCount; + + broadcast( + GridDataEvent( *this, -1, -1, i_index, i_index + rowCount - 1 ), + &XGridDataListener::rowsInserted, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::removeRow( ::sal_Int32 i_rowIndex ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_rowIndex < 0 ) || ( o3tl::make_unsigned( i_rowIndex ) >= m_aData.size() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + m_aRowHeaders.erase( m_aRowHeaders.begin() + i_rowIndex ); + m_aData.erase( m_aData.begin() + i_rowIndex ); + + broadcast( + GridDataEvent( *this, -1, -1, i_rowIndex, i_rowIndex ), + &XGridDataListener::rowsRemoved, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::removeAllRows( ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + m_aRowHeaders.clear(); + m_aData.clear(); + + broadcast( + GridDataEvent( *this, -1, -1, -1, -1 ), + &XGridDataListener::rowsRemoved, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::updateCellData( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + impl_getCellDataAccess_throw( aGuard, i_columnIndex, i_rowIndex ).first = i_value; + + broadcast( + GridDataEvent( *this, i_columnIndex, i_columnIndex, i_rowIndex, i_rowIndex ), + &XGridDataListener::dataChanged, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::updateRowData( const Sequence< ::sal_Int32 >& i_columnIndexes, ::sal_Int32 i_rowIndex, const Sequence< Any >& i_values ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_rowIndex < 0 ) || ( o3tl::make_unsigned( i_rowIndex ) >= m_aData.size() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + if ( i_columnIndexes.getLength() != i_values.getLength() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + sal_Int32 const columnCount = i_columnIndexes.getLength(); + if ( columnCount == 0 ) + return; + + for ( sal_Int32 const columnIndex : i_columnIndexes ) + { + if ( ( columnIndex < 0 ) || ( columnIndex > m_nColumnCount ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + } + + RowData& rDataRow = m_aData[ i_rowIndex ]; + for ( sal_Int32 col = 0; col < columnCount; ++col ) + { + sal_Int32 const columnIndex = i_columnIndexes[ col ]; + if ( o3tl::make_unsigned( columnIndex ) >= rDataRow.size() ) + rDataRow.resize( columnIndex + 1 ); + + rDataRow[ columnIndex ].first = i_values[ col ]; + } + + auto aPair = ::std::minmax_element( i_columnIndexes.begin(), i_columnIndexes.end() ); + sal_Int32 const firstAffectedColumn = *aPair.first; + sal_Int32 const lastAffectedColumn = *aPair.second; + broadcast( + GridDataEvent( *this, firstAffectedColumn, lastAffectedColumn, i_rowIndex, i_rowIndex ), + &XGridDataListener::dataChanged, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::updateRowHeading( ::sal_Int32 i_rowIndex, const Any& i_heading ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + if ( ( i_rowIndex < 0 ) || ( o3tl::make_unsigned( i_rowIndex ) >= m_aRowHeaders.size() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + m_aRowHeaders[ i_rowIndex ] = i_heading; + + broadcast( + GridDataEvent( *this, -1, -1, i_rowIndex, i_rowIndex ), + &XGridDataListener::rowHeadingChanged, + aGuard + ); + } + + + void SAL_CALL DefaultGridDataModel::updateCellToolTip( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + impl_getCellDataAccess_throw( aGuard, i_columnIndex, i_rowIndex ).second = i_value; + } + + + void SAL_CALL DefaultGridDataModel::updateRowToolTip( ::sal_Int32 i_rowIndex, const Any& i_value ) + { + std::unique_lock aGuard(m_aMutex); + throwIfDisposed(aGuard); + + RowData& rRowData = impl_getRowDataAccess_throw( aGuard, i_rowIndex, m_nColumnCount ); + for ( auto& rCell : rRowData ) + rCell.second = i_value; + } + + + void SAL_CALL DefaultGridDataModel::addGridDataListener( const Reference< grid::XGridDataListener >& i_listener ) + { + std::unique_lock aGuard(m_aMutex); + maGridDataListeners.addInterface( aGuard, i_listener ); + } + + + void SAL_CALL DefaultGridDataModel::removeGridDataListener( const Reference< grid::XGridDataListener >& i_listener ) + { + std::unique_lock aGuard(m_aMutex); + maGridDataListeners.removeInterface( aGuard, i_listener ); + } + + + void DefaultGridDataModel::disposing(std::unique_lock<std::mutex>& rGuard) + { + css::lang::EventObject aEvent; + aEvent.Source.set( *this ); + maGridDataListeners.disposeAndClear(rGuard, aEvent); + + GridData().swap(m_aData); + std::vector<Any>().swap(m_aRowHeaders); + m_nColumnCount = 0; + } + + + OUString SAL_CALL DefaultGridDataModel::getImplementationName( ) + { + return "stardiv.Toolkit.DefaultGridDataModel"; + } + + sal_Bool SAL_CALL DefaultGridDataModel::supportsService( const OUString& ServiceName ) + { + return cppu::supportsService(this, ServiceName); + } + + Sequence< OUString > SAL_CALL DefaultGridDataModel::getSupportedServiceNames( ) + { + return { "com.sun.star.awt.grid.DefaultGridDataModel" }; + } + + + Reference< css::util::XCloneable > SAL_CALL DefaultGridDataModel::createClone( ) + { + return new DefaultGridDataModel( *this ); + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_DefaultGridDataModel_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new DefaultGridDataModel()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/gridcolumn.cxx b/toolkit/source/controls/grid/gridcolumn.cxx new file mode 100644 index 0000000000..92d28ce9c8 --- /dev/null +++ b/toolkit/source/controls/grid/gridcolumn.cxx @@ -0,0 +1,287 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "gridcolumn.hxx" + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> + +namespace toolkit +{ + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::awt::grid; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::style; + + + //= DefaultGridColumnModel + + + GridColumn::GridColumn() + :m_nIndex(-1) + ,m_nDataColumnIndex(-1) + ,m_nColumnWidth(4) + ,m_nMaxWidth(0) + ,m_nMinWidth(0) + ,m_nFlexibility(1) + ,m_bResizeable(true) + ,m_eHorizontalAlign( HorizontalAlignment_LEFT ) + { + } + + + GridColumn::GridColumn( GridColumn const & i_copySource ) + :m_aIdentifier( i_copySource.m_aIdentifier ) + ,m_nIndex( -1 ) + ,m_nDataColumnIndex( i_copySource.m_nDataColumnIndex ) + ,m_nColumnWidth( i_copySource.m_nColumnWidth ) + ,m_nMaxWidth( i_copySource.m_nMaxWidth ) + ,m_nMinWidth( i_copySource.m_nMinWidth ) + ,m_nFlexibility( i_copySource.m_nFlexibility ) + ,m_bResizeable( i_copySource.m_bResizeable ) + ,m_sTitle( i_copySource.m_sTitle ) + ,m_sHelpText( i_copySource.m_sHelpText ) + ,m_eHorizontalAlign( i_copySource.m_eHorizontalAlign ) + { + } + + + GridColumn::~GridColumn() + { + } + + + void GridColumn::broadcast_changed( char const * const i_asciiAttributeName, const Any& i_oldValue, const Any& i_newValue, + std::unique_lock<std::mutex>& i_Guard ) + { + Reference< XInterface > const xSource( getXWeak() ); + GridColumnEvent const aEvent( + xSource, OUString::createFromAscii( i_asciiAttributeName ), + i_oldValue, i_newValue, m_nIndex + ); + + maGridColumnListeners.notifyEach( i_Guard, &XGridColumnListener::columnChanged, aEvent ); + } + + + css::uno::Any SAL_CALL GridColumn::getIdentifier() + { + std::unique_lock aGuard( m_aMutex ); + return m_aIdentifier; + } + + + void SAL_CALL GridColumn::setIdentifier(const css::uno::Any & value) + { + std::unique_lock aGuard( m_aMutex ); + m_aIdentifier = value; + } + + + ::sal_Int32 SAL_CALL GridColumn::getColumnWidth() + { + std::unique_lock aGuard( m_aMutex ); + return m_nColumnWidth; + } + + + void SAL_CALL GridColumn::setColumnWidth(::sal_Int32 value) + { + impl_set( m_nColumnWidth, value, "ColumnWidth" ); + } + + + ::sal_Int32 SAL_CALL GridColumn::getMaxWidth() + { + std::unique_lock aGuard( m_aMutex ); + return m_nMaxWidth; + } + + + void SAL_CALL GridColumn::setMaxWidth(::sal_Int32 value) + { + impl_set( m_nMaxWidth, value, "MaxWidth" ); + } + + + ::sal_Int32 SAL_CALL GridColumn::getMinWidth() + { + std::unique_lock aGuard( m_aMutex ); + return m_nMinWidth; + } + + + void SAL_CALL GridColumn::setMinWidth(::sal_Int32 value) + { + impl_set( m_nMinWidth, value, "MinWidth" ); + } + + + OUString SAL_CALL GridColumn::getTitle() + { + std::unique_lock aGuard( m_aMutex ); + return m_sTitle; + } + + + void SAL_CALL GridColumn::setTitle(const OUString & value) + { + impl_set( m_sTitle, value, "Title" ); + } + + + OUString SAL_CALL GridColumn::getHelpText() + { + std::unique_lock aGuard( m_aMutex ); + return m_sHelpText; + } + + + void SAL_CALL GridColumn::setHelpText( const OUString & value ) + { + impl_set( m_sHelpText, value, "HelpText" ); + } + + + sal_Bool SAL_CALL GridColumn::getResizeable() + { + std::unique_lock aGuard( m_aMutex ); + return m_bResizeable; + } + + + void SAL_CALL GridColumn::setResizeable(sal_Bool value) + { + impl_set( m_bResizeable, bool(value), "Resizeable" ); + } + + + ::sal_Int32 SAL_CALL GridColumn::getFlexibility() + { + std::unique_lock aGuard( m_aMutex ); + return m_nFlexibility; + } + + + void SAL_CALL GridColumn::setFlexibility( ::sal_Int32 i_value ) + { + if ( i_value < 0 ) + throw IllegalArgumentException( OUString(), *this, 1 ); + impl_set( m_nFlexibility, i_value, "Flexibility" ); + } + + + HorizontalAlignment SAL_CALL GridColumn::getHorizontalAlign() + { + std::unique_lock aGuard( m_aMutex ); + return m_eHorizontalAlign; + } + + + void SAL_CALL GridColumn::setHorizontalAlign(HorizontalAlignment align) + { + impl_set( m_eHorizontalAlign, align, "HorizontalAlign" ); + } + + + void SAL_CALL GridColumn::addGridColumnListener( const Reference< XGridColumnListener >& xListener ) + { + std::unique_lock aGuard( m_aMutex ); + maGridColumnListeners.addInterface( aGuard, xListener ); + } + + + void SAL_CALL GridColumn::removeGridColumnListener( const Reference< XGridColumnListener >& xListener ) + { + std::unique_lock aGuard( m_aMutex ); + maGridColumnListeners.removeInterface( aGuard, xListener ); + } + + + void GridColumn::disposing(std::unique_lock<std::mutex>&) + { + m_aIdentifier.clear(); + m_sTitle.clear(); + m_sHelpText.clear(); + } + + + ::sal_Int32 SAL_CALL GridColumn::getIndex() + { + std::unique_lock aGuard( m_aMutex ); + return m_nIndex; + } + + + void GridColumn::setIndex( sal_Int32 const i_index ) + { + std::unique_lock aGuard( m_aMutex ); + m_nIndex = i_index; + } + + + ::sal_Int32 SAL_CALL GridColumn::getDataColumnIndex() + { + std::unique_lock aGuard( m_aMutex ); + return m_nDataColumnIndex; + } + + + void SAL_CALL GridColumn::setDataColumnIndex( ::sal_Int32 i_dataColumnIndex ) + { + impl_set( m_nDataColumnIndex, i_dataColumnIndex, "DataColumnIndex" ); + } + + + OUString SAL_CALL GridColumn::getImplementationName( ) + { + return "org.openoffice.comp.toolkit.GridColumn"; + } + + sal_Bool SAL_CALL GridColumn::supportsService( const OUString& i_serviceName ) + { + return cppu::supportsService(this, i_serviceName); + } + + css::uno::Sequence< OUString > SAL_CALL GridColumn::getSupportedServiceNames( ) + { + return { "com.sun.star.awt.grid.GridColumn" }; + } + + + Reference< XCloneable > SAL_CALL GridColumn::createClone( ) + { + return new GridColumn( *this ); + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_openoffice_comp_toolkit_GridColumn_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::GridColumn()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/gridcolumn.hxx b/toolkit/source/controls/grid/gridcolumn.hxx new file mode 100644 index 0000000000..13c8792271 --- /dev/null +++ b/toolkit/source/controls/grid/gridcolumn.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 . + */ + +#ifndef INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDCOLUMN_HXX +#define INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDCOLUMN_HXX + +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/style/HorizontalAlignment.hpp> + +#include <comphelper/compbase.hxx> +#include <comphelper/interfacecontainer4.hxx> + +namespace toolkit +{ + +typedef comphelper::WeakComponentImplHelper < css::awt::grid::XGridColumn + , css::lang::XServiceInfo + > GridColumn_Base; +class GridColumn final : public GridColumn_Base +{ +public: + GridColumn(); + GridColumn( GridColumn const & i_copySource ); + virtual ~GridColumn() override; + + // css::awt::grid::XGridColumn + virtual css::uno::Any SAL_CALL getIdentifier() override; + virtual void SAL_CALL setIdentifier(const css::uno::Any & value) override; + virtual ::sal_Int32 SAL_CALL getColumnWidth() override; + virtual void SAL_CALL setColumnWidth(::sal_Int32 the_value) override; + virtual ::sal_Int32 SAL_CALL getMaxWidth() override; + virtual void SAL_CALL setMaxWidth(::sal_Int32 the_value) override; + virtual ::sal_Int32 SAL_CALL getMinWidth() override; + virtual void SAL_CALL setMinWidth(::sal_Int32 the_value) override; + virtual sal_Bool SAL_CALL getResizeable() override; + virtual void SAL_CALL setResizeable(sal_Bool the_value) override; + virtual ::sal_Int32 SAL_CALL getFlexibility() override; + virtual void SAL_CALL setFlexibility( ::sal_Int32 _flexibility ) override; + virtual OUString SAL_CALL getTitle() override; + virtual void SAL_CALL setTitle(const OUString & value) override; + virtual OUString SAL_CALL getHelpText() override; + virtual void SAL_CALL setHelpText(const OUString & value) override; + virtual ::sal_Int32 SAL_CALL getIndex() override; + virtual ::sal_Int32 SAL_CALL getDataColumnIndex() override; + virtual void SAL_CALL setDataColumnIndex( ::sal_Int32 i_dataColumnIndex ) override; + virtual css::style::HorizontalAlignment SAL_CALL getHorizontalAlign() override; + virtual void SAL_CALL setHorizontalAlign(css::style::HorizontalAlignment align) override; + virtual void SAL_CALL addGridColumnListener( const css::uno::Reference< css::awt::grid::XGridColumnListener >& xListener ) override; + virtual void SAL_CALL removeGridColumnListener( const css::uno::Reference< css::awt::grid::XGridColumnListener >& xListener ) override; + + // OComponentHelper + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XCloneable (base of XGridColumn) + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) 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; + + // attribute access + void setIndex( sal_Int32 const i_index ); + +private: + void broadcast_changed( + char const * const i_asciiAttributeName, + const css::uno::Any& i_oldValue, + const css::uno::Any& i_newValue, + std::unique_lock<std::mutex>& i_Guard + ); + + template< class TYPE > + void impl_set( TYPE & io_attribute, TYPE const & i_newValue, char const * i_attributeName ) + { + std::unique_lock aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( OUString(), getXWeak() ); + if ( io_attribute == i_newValue ) + return; + + TYPE const aOldValue( io_attribute ); + io_attribute = i_newValue; + broadcast_changed( i_attributeName, css::uno::Any( aOldValue ), css::uno::Any( io_attribute ), aGuard ); + } + + css::uno::Any m_aIdentifier; + sal_Int32 m_nIndex; + sal_Int32 m_nDataColumnIndex; + sal_Int32 m_nColumnWidth; + sal_Int32 m_nMaxWidth; + sal_Int32 m_nMinWidth; + sal_Int32 m_nFlexibility; + bool m_bResizeable; + OUString m_sTitle; + OUString m_sHelpText; + css::style::HorizontalAlignment m_eHorizontalAlign; + comphelper::OInterfaceContainerHelper4<css::awt::grid::XGridColumnListener> maGridColumnListeners; +}; + +} + +#endif // INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDCOLUMN_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/gridcontrol.cxx b/toolkit/source/controls/grid/gridcontrol.cxx new file mode 100644 index 0000000000..39f4abf531 --- /dev/null +++ b/toolkit/source/controls/grid/gridcontrol.cxx @@ -0,0 +1,462 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "gridcontrol.hxx" +#include "grideventforwarder.hxx" + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/view/SelectionType.hpp> +#include <com/sun/star/awt/grid/XGridControl.hpp> +#include <com/sun/star/awt/grid/XGridDataModel.hpp> +#include <com/sun/star/awt/grid/XGridRowSelection.hpp> +#include <com/sun/star/awt/grid/XMutableGridDataModel.hpp> +#include <com/sun/star/awt/grid/DefaultGridDataModel.hpp> +#include <com/sun/star/awt/grid/SortableGridDataModel.hpp> +#include <com/sun/star/awt/grid/DefaultGridColumnModel.hpp> +#include <helper/property.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <toolkit/controls/unocontrolbase.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> + +#include <memory> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::awt::grid; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::util; + +namespace toolkit { + +namespace +{ + Reference< XGridDataModel > lcl_getDefaultDataModel_throw( const Reference<XComponentContext> & i_context ) + { + Reference< XMutableGridDataModel > const xDelegatorModel( DefaultGridDataModel::create( i_context ), UNO_SET_THROW ); + Reference< XGridDataModel > const xDataModel( SortableGridDataModel::create( i_context, xDelegatorModel ), UNO_QUERY_THROW ); + return xDataModel; + } + + Reference< XGridColumnModel > lcl_getDefaultColumnModel_throw( const Reference<XComponentContext> & i_context ) + { + Reference< XGridColumnModel > const xColumnModel = DefaultGridColumnModel::create( i_context ); + return xColumnModel; + } +} + + +UnoGridModel::UnoGridModel( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_FILLCOLOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_SIZEABLE ); // resizable + ImplRegisterProperty( BASEPROPERTY_HSCROLL ); + ImplRegisterProperty( BASEPROPERTY_VSCROLL ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_GRID_SHOWROWHEADER ); + ImplRegisterProperty( BASEPROPERTY_ROW_HEADER_WIDTH ); + ImplRegisterProperty( BASEPROPERTY_GRID_SHOWCOLUMNHEADER ); + ImplRegisterProperty( BASEPROPERTY_COLUMN_HEADER_HEIGHT ); + ImplRegisterProperty( BASEPROPERTY_ROW_HEIGHT ); + ImplRegisterProperty( BASEPROPERTY_GRID_DATAMODEL, Any( lcl_getDefaultDataModel_throw( m_xContext ) ) ); + ImplRegisterProperty( BASEPROPERTY_GRID_COLUMNMODEL, Any( lcl_getDefaultColumnModel_throw( m_xContext ) ) ); + ImplRegisterProperty( BASEPROPERTY_GRID_SELECTIONMODE ); + ImplRegisterProperty( BASEPROPERTY_FONTRELIEF ); + ImplRegisterProperty( BASEPROPERTY_FONTEMPHASISMARK ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_TEXTCOLOR ); + ImplRegisterProperty( BASEPROPERTY_TEXTLINECOLOR ); + ImplRegisterProperty( BASEPROPERTY_USE_GRID_LINES ); + ImplRegisterProperty( BASEPROPERTY_GRID_LINE_COLOR ); + ImplRegisterProperty( BASEPROPERTY_GRID_HEADER_BACKGROUND ); + ImplRegisterProperty( BASEPROPERTY_GRID_HEADER_TEXT_COLOR ); + ImplRegisterProperty( BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS ); + ImplRegisterProperty( BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR ); + ImplRegisterProperty( BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR ); + ImplRegisterProperty( BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR ); + ImplRegisterProperty( BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR ); + ImplRegisterProperty( BASEPROPERTY_VERTICALALIGN ); +} + + +UnoGridModel::UnoGridModel( const UnoGridModel& rModel ) + :UnoControlModel( rModel ) +{ + osl_atomic_increment( &m_refCount ); + { + Reference< XGridDataModel > xDataModel; + // clone the data model + const Reference< XFastPropertySet > xCloneSource( &const_cast< UnoGridModel& >( rModel ) ); + try + { + const Reference< XCloneable > xCloneable( xCloneSource->getFastPropertyValue( BASEPROPERTY_GRID_DATAMODEL ), UNO_QUERY_THROW ); + xDataModel.set( xCloneable->createClone(), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + if ( !xDataModel.is() ) + xDataModel = lcl_getDefaultDataModel_throw( m_xContext ); + std::unique_lock aGuard(m_aMutex); + UnoControlModel::setFastPropertyValue_NoBroadcast( aGuard, BASEPROPERTY_GRID_DATAMODEL, Any( xDataModel ) ); + // do *not* use setFastPropertyValue here: The UnoControlModel ctor made a simple copy of all property values, + // so before this call here, we share our data model with the own of the clone source. setFastPropertyValue, + // then, disposes the old data model - which means the data model which in fact belongs to the clone source. + // so, call the UnoControlModel's impl-method for setting the value. + + // clone the column model + Reference< XGridColumnModel > xColumnModel; + try + { + const Reference< XCloneable > xCloneable( xCloneSource->getFastPropertyValue( BASEPROPERTY_GRID_COLUMNMODEL ), UNO_QUERY_THROW ); + xColumnModel.set( xCloneable->createClone(), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + if ( !xColumnModel.is() ) + xColumnModel = lcl_getDefaultColumnModel_throw( m_xContext ); + UnoControlModel::setFastPropertyValue_NoBroadcast( aGuard, BASEPROPERTY_GRID_COLUMNMODEL, Any( xColumnModel ) ); + // same comment as above: do not use our own setPropertyValue here. + } + osl_atomic_decrement( &m_refCount ); +} + + +rtl::Reference<UnoControlModel> UnoGridModel::Clone() const +{ + return new UnoGridModel( *this ); +} + + +namespace +{ + void lcl_dispose_nothrow( const Any& i_component ) + { + try + { + const Reference< XComponent > xComponent( i_component, UNO_QUERY ); + if (xComponent) + xComponent->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } +} + + +void SAL_CALL UnoGridModel::dispose( ) +{ + lcl_dispose_nothrow( getFastPropertyValue( BASEPROPERTY_GRID_COLUMNMODEL ) ); + lcl_dispose_nothrow( getFastPropertyValue( BASEPROPERTY_GRID_DATAMODEL ) ); + + UnoControlModel::dispose(); +} + + +void UnoGridModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const Any& rValue ) +{ + Any aOldSubModel; + if ( ( nHandle == BASEPROPERTY_GRID_COLUMNMODEL ) || ( nHandle == BASEPROPERTY_GRID_DATAMODEL ) ) + { + getFastPropertyValue( rGuard, aOldSubModel, nHandle ); + if ( aOldSubModel == rValue ) + { + OSL_ENSURE( false, "UnoGridModel::setFastPropertyValue_NoBroadcast: setting the same value, again!" ); + // shouldn't this have been caught by convertFastPropertyValue? + aOldSubModel.clear(); + } + } + + UnoControlModel::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + + if ( aOldSubModel.hasValue() ) + lcl_dispose_nothrow( aOldSubModel ); +} + + +OUString UnoGridModel::getServiceName() +{ + return "com.sun.star.awt.grid.UnoControlGridModel"; +} + + +Any UnoGridModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString("com.sun.star.awt.grid.UnoControlGrid") ); + case BASEPROPERTY_GRID_SELECTIONMODE: + return uno::Any( SelectionType(1) ); + case BASEPROPERTY_GRID_SHOWROWHEADER: + case BASEPROPERTY_USE_GRID_LINES: + return uno::Any( false ); + case BASEPROPERTY_ROW_HEADER_WIDTH: + return uno::Any( sal_Int32( 10 ) ); + case BASEPROPERTY_GRID_SHOWCOLUMNHEADER: + return uno::Any( true ); + case BASEPROPERTY_COLUMN_HEADER_HEIGHT: + case BASEPROPERTY_ROW_HEIGHT: + case BASEPROPERTY_GRID_HEADER_BACKGROUND: + case BASEPROPERTY_GRID_HEADER_TEXT_COLOR: + case BASEPROPERTY_GRID_LINE_COLOR: + case BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS: + case BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR: + case BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR: + case BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR: + case BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR: + return Any(); + default: + return UnoControlModel::ImplGetDefaultValue( nPropId ); + } + +} + + +::cppu::IPropertyArrayHelper& UnoGridModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + + +// XMultiPropertySet +Reference< XPropertySetInfo > UnoGridModel::getPropertySetInfo( ) +{ + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + + +//= UnoGridControl + +UnoGridControl::UnoGridControl() + :m_aSelectionListeners( *this ) + ,m_pEventForwarder( new toolkit::GridEventForwarder( *this ) ) +{ +} + + +UnoGridControl::~UnoGridControl() +{ +} + + +OUString UnoGridControl::GetComponentServiceName() const +{ + return "Grid"; +} + + +void SAL_CALL UnoGridControl::dispose( ) +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + m_aSelectionListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + + +void SAL_CALL UnoGridControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + const Reference< XGridRowSelection > xGrid( getPeer(), UNO_QUERY_THROW ); + xGrid->addSelectionListener( &m_aSelectionListeners ); +} + + +namespace +{ + void lcl_setEventForwarding( const Reference< XControlModel >& i_gridControlModel, const std::unique_ptr< toolkit::GridEventForwarder >& i_listener, + bool const i_add ) + { + const Reference< XPropertySet > xModelProps( i_gridControlModel, UNO_QUERY ); + if ( !xModelProps.is() ) + return; + + try + { + Reference< XContainer > const xColModel( + xModelProps->getPropertyValue("ColumnModel"), + UNO_QUERY_THROW ); + if ( i_add ) + xColModel->addContainerListener( i_listener.get() ); + else + xColModel->removeContainerListener( i_listener.get() ); + + Reference< XGridDataModel > const xDataModel( + xModelProps->getPropertyValue("GridDataModel"), + UNO_QUERY_THROW + ); + Reference< XMutableGridDataModel > const xMutableDataModel( xDataModel, UNO_QUERY ); + if ( xMutableDataModel.is() ) + { + if ( i_add ) + xMutableDataModel->addGridDataListener( i_listener.get() ); + else + xMutableDataModel->removeGridDataListener( i_listener.get() ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } +} + + +sal_Bool SAL_CALL UnoGridControl::setModel( const Reference< XControlModel >& i_model ) +{ + lcl_setEventForwarding( getModel(), m_pEventForwarder, false ); + if ( !UnoGridControl_Base::setModel( i_model ) ) + return false; + lcl_setEventForwarding( getModel(), m_pEventForwarder, true ); + return true; +} + + +::sal_Int32 UnoGridControl::getRowAtPoint(::sal_Int32 x, ::sal_Int32 y) +{ + Reference< XGridControl > const xGrid ( getPeer(), UNO_QUERY_THROW ); + return xGrid->getRowAtPoint( x, y ); +} + + +::sal_Int32 UnoGridControl::getColumnAtPoint(::sal_Int32 x, ::sal_Int32 y) +{ + Reference< XGridControl > const xGrid ( getPeer(), UNO_QUERY_THROW ); + return xGrid->getColumnAtPoint( x, y ); +} + + +::sal_Int32 SAL_CALL UnoGridControl::getCurrentColumn( ) +{ + Reference< XGridControl > const xGrid ( getPeer(), UNO_QUERY_THROW ); + return xGrid->getCurrentColumn(); +} + + +::sal_Int32 SAL_CALL UnoGridControl::getCurrentRow( ) +{ + Reference< XGridControl > const xGrid ( getPeer(), UNO_QUERY_THROW ); + return xGrid->getCurrentRow(); +} + + +void SAL_CALL UnoGridControl::goToCell( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) +{ + Reference< XGridControl > const xGrid ( getPeer(), UNO_QUERY_THROW ); + xGrid->goToCell( i_columnIndex, i_rowIndex ); +} + + +void SAL_CALL UnoGridControl::selectRow( ::sal_Int32 i_rowIndex ) +{ + Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->selectRow( i_rowIndex ); +} + + +void SAL_CALL UnoGridControl::selectAllRows() +{ + Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->selectAllRows(); +} + + +void SAL_CALL UnoGridControl::deselectRow( ::sal_Int32 i_rowIndex ) +{ + Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->deselectRow( i_rowIndex ); +} + + +void SAL_CALL UnoGridControl::deselectAllRows() +{ + Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->deselectAllRows(); +} + + +css::uno::Sequence< ::sal_Int32 > SAL_CALL UnoGridControl::getSelectedRows() +{ + return Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->getSelectedRows(); +} + + +sal_Bool SAL_CALL UnoGridControl::hasSelectedRows() +{ + return Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->hasSelectedRows(); +} + + +sal_Bool SAL_CALL UnoGridControl::isRowSelected(::sal_Int32 index) +{ + return Reference< XGridRowSelection >( getPeer(), UNO_QUERY_THROW )->isRowSelected( index ); +} + + +void SAL_CALL UnoGridControl::addSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) +{ + m_aSelectionListeners.addInterface( listener ); +} + + +void SAL_CALL UnoGridControl::removeSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) +{ + m_aSelectionListeners.removeInterface( listener ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_GridControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoGridControl()); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_GridControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoGridModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/gridcontrol.hxx b/toolkit/source/controls/grid/gridcontrol.hxx new file mode 100644 index 0000000000..9b7eae0eaa --- /dev/null +++ b/toolkit/source/controls/grid/gridcontrol.hxx @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDCONTROL_HXX +#define INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDCONTROL_HXX + +#include <com/sun/star/awt/grid/XGridControl.hpp> +#include <com/sun/star/awt/grid/XGridRowSelection.hpp> + +#include <toolkit/controls/unocontrolbase.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <cppuhelper/implbase2.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> + +#include <memory> + +namespace toolkit +{ + +class GridEventForwarder; + + +// = UnoGridModel + +class UnoGridModel : public UnoControlModel +{ +protected: + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + +public: + explicit UnoGridModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ); + UnoGridModel( const UnoGridModel& rModel ); + + rtl::Reference<UnoControlModel> Clone() const override; + + // css::lang::XComponent + void SAL_CALL dispose( ) override; + + // css::beans::XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // OPropertySetHelper + void setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const css::uno::Any& rValue ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "stardiv.Toolkit.GridControlModel"; } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + auto s(UnoControlModel::getSupportedServiceNames()); + s.realloc(s.getLength() + 1); + s.getArray()[s.getLength() - 1] = "com.sun.star.awt.grid.UnoControlGridModel"; + return s; + } +}; + + +// = UnoGridControl + +typedef ::cppu::AggImplInheritanceHelper2 < UnoControlBase + , css::awt::grid::XGridControl + , css::awt::grid::XGridRowSelection + > UnoGridControl_Base; +class UnoGridControl : public UnoGridControl_Base +{ +public: + UnoGridControl(); + OUString GetComponentServiceName() const override; + + // css::lang::XComponent + void SAL_CALL dispose( ) override; + + // css::awt::XControl + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + sal_Bool SAL_CALL setModel( const css::uno::Reference< css::awt::XControlModel >& rxModel ) override; + + // css::awt::grid::XGridControl + virtual ::sal_Int32 SAL_CALL getColumnAtPoint(::sal_Int32 x, ::sal_Int32 y) override; + virtual ::sal_Int32 SAL_CALL getRowAtPoint(::sal_Int32 x, ::sal_Int32 y) override; + virtual ::sal_Int32 SAL_CALL getCurrentColumn( ) override; + virtual ::sal_Int32 SAL_CALL getCurrentRow( ) override; + virtual void SAL_CALL goToCell( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) override; + + // css::awt::grid::XGridRowSelection + virtual void SAL_CALL selectRow( ::sal_Int32 i_rowIndex ) override; + virtual void SAL_CALL selectAllRows() override; + virtual void SAL_CALL deselectRow( ::sal_Int32 i_rowIndex ) override; + virtual void SAL_CALL deselectAllRows() override; + virtual css::uno::Sequence< ::sal_Int32 > SAL_CALL getSelectedRows() override; + virtual sal_Bool SAL_CALL hasSelectedRows() override; + virtual sal_Bool SAL_CALL isRowSelected(::sal_Int32 index) override; + virtual void SAL_CALL addSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) override; + virtual void SAL_CALL removeSelectionListener(const css::uno::Reference< css::awt::grid::XGridSelectionListener > & listener) override; + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "stardiv.Toolkit.GridControl"; } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + auto s(UnoControlBase::getSupportedServiceNames()); + s.realloc(s.getLength() + 1); + s.getArray()[s.getLength() - 1] = "com.sun.star.awt.grid.UnoControlGrid"; + return s; + } + + using UnoControl::getPeer; + +protected: + virtual ~UnoGridControl() override; + +private: + SelectionListenerMultiplexer m_aSelectionListeners; + std::unique_ptr< GridEventForwarder > m_pEventForwarder; +}; + +} // toolkit + +#endif // _TOOLKIT_TREE_CONTROL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/grideventforwarder.cxx b/toolkit/source/controls/grid/grideventforwarder.cxx new file mode 100644 index 0000000000..5baf5fdda3 --- /dev/null +++ b/toolkit/source/controls/grid/grideventforwarder.cxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "grideventforwarder.hxx" +#include "gridcontrol.hxx" + + +namespace toolkit +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::awt::grid::GridDataEvent; + using ::com::sun::star::container::ContainerEvent; + using ::com::sun::star::lang::EventObject; + + + //= GridEventForwarder + + + GridEventForwarder::GridEventForwarder( UnoGridControl& i_parent ) + :m_parent( i_parent ) + { + } + + + GridEventForwarder::~GridEventForwarder() + { + } + + + void SAL_CALL GridEventForwarder::acquire() noexcept + { + m_parent.acquire(); + } + + + void SAL_CALL GridEventForwarder::release() noexcept + { + m_parent.release(); + } + + + void SAL_CALL GridEventForwarder::rowsInserted( const GridDataEvent& i_event ) + { + Reference< XGridDataListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->rowsInserted( i_event ); + } + + + void SAL_CALL GridEventForwarder::rowsRemoved( const GridDataEvent& i_event ) + { + Reference< XGridDataListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->rowsRemoved( i_event ); + } + + + void SAL_CALL GridEventForwarder::dataChanged( const GridDataEvent& i_event ) + { + Reference< XGridDataListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->dataChanged( i_event ); + } + + + void SAL_CALL GridEventForwarder::rowHeadingChanged( const GridDataEvent& i_event ) + { + Reference< XGridDataListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->rowHeadingChanged( i_event ); + } + + + void SAL_CALL GridEventForwarder::elementInserted( const ContainerEvent& i_event ) + { + Reference< XContainerListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->elementInserted( i_event ); + } + + + void SAL_CALL GridEventForwarder::elementRemoved( const ContainerEvent& i_event ) + { + Reference< XContainerListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->elementRemoved( i_event ); + } + + + void SAL_CALL GridEventForwarder::elementReplaced( const ContainerEvent& i_event ) + { + Reference< XContainerListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->elementReplaced( i_event ); + } + + + void SAL_CALL GridEventForwarder::disposing( const EventObject& i_event ) + { + Reference< XEventListener > xPeer( m_parent.getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->disposing( i_event ); + } + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/grideventforwarder.hxx b/toolkit/source/controls/grid/grideventforwarder.hxx new file mode 100644 index 0000000000..9578e62ee0 --- /dev/null +++ b/toolkit/source/controls/grid/grideventforwarder.hxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDEVENTFORWARDER_HXX +#define INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDEVENTFORWARDER_HXX + +#include <com/sun/star/awt/grid/XGridDataListener.hpp> +#include <com/sun/star/container/XContainerListener.hpp> + +#include <cppuhelper/implbase2.hxx> + + +namespace toolkit +{ + + + class UnoGridControl; + + + //= GridEventForwarder + + typedef ::cppu::ImplHelper2 < css::awt::grid::XGridDataListener + , css::container::XContainerListener + > GridEventForwarder_Base; + + class GridEventForwarder : public GridEventForwarder_Base + { + public: + explicit GridEventForwarder( UnoGridControl& i_parent ); + virtual ~GridEventForwarder(); + + public: + // XInterface + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XGridDataListener + virtual void SAL_CALL rowsInserted( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowsRemoved( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL dataChanged( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowHeadingChanged( const css::awt::grid::GridDataEvent& Event ) override; + + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& i_event ) override; + + private: + UnoGridControl& m_parent; + }; + + +} // namespace toolkit + + +#endif // INCLUDED_TOOLKIT_SOURCE_CONTROLS_GRID_GRIDEVENTFORWARDER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/grid/sortablegriddatamodel.cxx b/toolkit/source/controls/grid/sortablegriddatamodel.cxx new file mode 100644 index 0000000000..5eac49f475 --- /dev/null +++ b/toolkit/source/controls/grid/sortablegriddatamodel.cxx @@ -0,0 +1,928 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <com/sun/star/i18n/Collator.hpp> +#include <com/sun/star/i18n/XCollator.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/NotInitializedException.hpp> +#include <com/sun/star/ucb/AlreadyInitializedException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/awt/grid/XGridDataListener.hpp> +#include <com/sun/star/awt/grid/XSortableMutableGridDataModel.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/implbase1.hxx> +#include <comphelper/anycompare.hxx> +#include <comphelper/componentguard.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <o3tl/safeint.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +using namespace css::awt; +using namespace css::awt::grid; +using namespace css::i18n; +using namespace css::lang; +using namespace css::ucb; +using namespace css::uno; + +namespace { + +class SortableGridDataModel; +class MethodGuard; + +typedef ::cppu::WeakComponentImplHelper < css::awt::grid::XSortableMutableGridDataModel + , css::lang::XServiceInfo + , css::lang::XInitialization + > SortableGridDataModel_Base; +typedef ::cppu::ImplHelper1 < css::awt::grid::XGridDataListener + > SortableGridDataModel_PrivateBase; +class SortableGridDataModel :public ::cppu::BaseMutex + ,public SortableGridDataModel_Base + ,public SortableGridDataModel_PrivateBase +{ +public: + explicit SortableGridDataModel( const css::uno::Reference< css::uno::XComponentContext > & rxContext ); + SortableGridDataModel( SortableGridDataModel const & i_copySource ); + + bool isInitialized() const { return m_isInitialized; } + +protected: + virtual ~SortableGridDataModel() override; + +public: + // XSortableGridData + virtual void SAL_CALL sortByColumn( ::sal_Int32 ColumnIndex, sal_Bool SortAscending ) override; + virtual void SAL_CALL removeColumnSort( ) override; + virtual css::beans::Pair< ::sal_Int32, sal_Bool > SAL_CALL getCurrentSortOrder( ) override; + + // XMutableGridDataModel + virtual void SAL_CALL addRow( const css::uno::Any& Heading, const css::uno::Sequence< css::uno::Any >& Data ) override; + virtual void SAL_CALL addRows( const css::uno::Sequence< css::uno::Any >& Headings, const css::uno::Sequence< css::uno::Sequence< css::uno::Any > >& Data ) override; + virtual void SAL_CALL insertRow( ::sal_Int32 i_index, const css::uno::Any& i_heading, const css::uno::Sequence< css::uno::Any >& Data ) override; + virtual void SAL_CALL insertRows( ::sal_Int32 i_index, const css::uno::Sequence< css::uno::Any>& Headings, const css::uno::Sequence< css::uno::Sequence< css::uno::Any > >& Data ) override; + virtual void SAL_CALL removeRow( ::sal_Int32 RowIndex ) override; + virtual void SAL_CALL removeAllRows( ) override; + virtual void SAL_CALL updateCellData( ::sal_Int32 ColumnIndex, ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL updateRowData( const css::uno::Sequence< ::sal_Int32 >& ColumnIndexes, ::sal_Int32 RowIndex, const css::uno::Sequence< css::uno::Any >& Values ) override; + virtual void SAL_CALL updateRowHeading( ::sal_Int32 RowIndex, const css::uno::Any& Heading ) override; + virtual void SAL_CALL updateCellToolTip( ::sal_Int32 ColumnIndex, ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL updateRowToolTip( ::sal_Int32 RowIndex, const css::uno::Any& Value ) override; + virtual void SAL_CALL addGridDataListener( const css::uno::Reference< css::awt::grid::XGridDataListener >& Listener ) override; + virtual void SAL_CALL removeGridDataListener( const css::uno::Reference< css::awt::grid::XGridDataListener >& Listener ) override; + + // XGridDataModel + virtual ::sal_Int32 SAL_CALL getRowCount() override; + virtual ::sal_Int32 SAL_CALL getColumnCount() override; + virtual css::uno::Any SAL_CALL getCellData( ::sal_Int32 Column, ::sal_Int32 RowIndex ) override; + virtual css::uno::Any SAL_CALL getCellToolTip( ::sal_Int32 Column, ::sal_Int32 RowIndex ) override; + virtual css::uno::Any SAL_CALL getRowHeading( ::sal_Int32 RowIndex ) override; + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getRowData( ::sal_Int32 RowIndex ) override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XCloneable + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) 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; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XGridDataListener + virtual void SAL_CALL rowsInserted( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowsRemoved( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL dataChanged( const css::awt::grid::GridDataEvent& Event ) override; + virtual void SAL_CALL rowHeadingChanged( const css::awt::grid::GridDataEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& i_event ) override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire( ) noexcept final override; + virtual void SAL_CALL release( ) noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL getImplementationId( ) override; + +private: + /** translates the given public index into one to be passed to our delegator + @throws css::lang::IndexOutOfBoundsException + if the given index does not denote a valid row + */ + ::sal_Int32 impl_getPrivateRowIndex_throw( ::sal_Int32 const i_publicRowIndex ) const; + + /** translates the given private row index to a public one + */ + ::sal_Int32 impl_getPublicRowIndex_nothrow( ::sal_Int32 const i_privateRowIndex ) const; + + bool impl_isSorted_nothrow() const + { + return m_currentSortColumn >= 0; + } + + /** rebuilds the index translation structure. + + Neither <member>m_currentSortColumn</member> nor <member>m_sortAscending</member> are touched by this method. + Also, the given column index is not checked, this is the responsibility of the caller. + */ + bool impl_reIndex_nothrow( ::sal_Int32 const i_columnIndex, bool const i_sortAscending ); + + /** translates the given event, obtained from our delegator, to a version which can be broadcasted to our own + clients. + */ + css::awt::grid::GridDataEvent + impl_createPublicEvent( css::awt::grid::GridDataEvent const & i_originalEvent ) const; + + /** broadcasts the given event to our registered XGridDataListeners + */ + void impl_broadcast( + void ( SAL_CALL css::awt::grid::XGridDataListener::*i_listenerMethod )( const css::awt::grid::GridDataEvent & ), + css::awt::grid::GridDataEvent const & i_publicEvent, + MethodGuard& i_instanceLock + ); + + /** rebuilds our indexes, notifying row removal and row addition events + + First, a rowsRemoved event is notified to our registered listeners. Then, the index translation tables are + rebuilt, and a rowsInserted event is notified. + + Only to be called when we're sorted. + */ + void impl_rebuildIndexesAndNotify( MethodGuard& i_instanceLock ); + + /** removes the current sorting, and notifies a change of all data + */ + void impl_removeColumnSort( MethodGuard& i_instanceLock ); + + /** removes the current sorting, without any broadcast + */ + void impl_removeColumnSort_noBroadcast(); + +private: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + bool m_isInitialized; + css::uno::Reference< css::awt::grid::XMutableGridDataModel > m_delegator; + css::uno::Reference< css::i18n::XCollator > m_collator; + ::sal_Int32 m_currentSortColumn; + bool m_sortAscending; + ::std::vector< ::sal_Int32 > m_publicToPrivateRowIndex; + ::std::vector< ::sal_Int32 > m_privateToPublicRowIndex; +}; + +class MethodGuard : public ::comphelper::ComponentGuard +{ +public: + MethodGuard( SortableGridDataModel& i_component, ::cppu::OBroadcastHelper & i_broadcastHelper ) + :comphelper::ComponentGuard( i_component, i_broadcastHelper ) + { + if ( !i_component.isInitialized() ) + throw css::lang::NotInitializedException( OUString(), i_component ); + } +}; + +template< class STLCONTAINER > +void lcl_clear( STLCONTAINER& i_container ) +{ + STLCONTAINER().swap(i_container); +} + + SortableGridDataModel::SortableGridDataModel( Reference< XComponentContext > const & rxContext ) + :SortableGridDataModel_Base( m_aMutex ) + ,SortableGridDataModel_PrivateBase() + ,m_xContext( rxContext ) + ,m_isInitialized( false ) + ,m_delegator() + ,m_collator() + ,m_currentSortColumn( -1 ) + ,m_sortAscending( true ) + ,m_publicToPrivateRowIndex() + ,m_privateToPublicRowIndex() + { + } + + + SortableGridDataModel::SortableGridDataModel( SortableGridDataModel const & i_copySource ) + :cppu::BaseMutex() + ,SortableGridDataModel_Base( m_aMutex ) + ,SortableGridDataModel_PrivateBase() + ,m_xContext( i_copySource.m_xContext ) + ,m_isInitialized( true ) + ,m_delegator() + ,m_collator( i_copySource.m_collator ) + ,m_currentSortColumn( i_copySource.m_currentSortColumn ) + ,m_sortAscending( i_copySource.m_sortAscending ) + ,m_publicToPrivateRowIndex( i_copySource.m_publicToPrivateRowIndex ) + ,m_privateToPublicRowIndex( i_copySource.m_privateToPublicRowIndex ) + { + ENSURE_OR_THROW( i_copySource.m_delegator.is(), + "not expected to be called for a disposed copy source!" ); + m_delegator.set( i_copySource.m_delegator->createClone(), UNO_QUERY_THROW ); + } + + + SortableGridDataModel::~SortableGridDataModel() + { + if ( !rBHelper.bDisposed ) + { + acquire(); + dispose(); + } + } + + + Any SAL_CALL SortableGridDataModel::queryInterface( const Type& aType ) + { + Any aReturn( SortableGridDataModel_Base::queryInterface( aType ) ); + if ( !aReturn.hasValue() ) + aReturn = SortableGridDataModel_PrivateBase::queryInterface( aType ); + return aReturn; + } + + + void SAL_CALL SortableGridDataModel::acquire( ) noexcept + { + SortableGridDataModel_Base::acquire(); + } + + + void SAL_CALL SortableGridDataModel::release( ) noexcept + { + SortableGridDataModel_Base::release(); + } + + + Sequence< Type > SAL_CALL SortableGridDataModel::getTypes( ) + { + return SortableGridDataModel_Base::getTypes(); + // don't expose the types got via SortableGridDataModel_PrivateBase - they're private, after all + } + + + Sequence< ::sal_Int8 > SAL_CALL SortableGridDataModel::getImplementationId( ) + { + return css::uno::Sequence<sal_Int8>(); + } + + Reference< XCollator > lcl_loadDefaultCollator_throw( const Reference<XComponentContext> & rxContext ) + { + Reference< XCollator > const xCollator = Collator::create( rxContext ); + xCollator->loadDefaultCollator( Application::GetSettings().GetLanguageTag().getLocale(), 0 ); + return xCollator; + } + + void SAL_CALL SortableGridDataModel::initialize( const Sequence< Any >& i_arguments ) + { + ::comphelper::ComponentGuard aGuard( *this, rBHelper ); + + if ( m_delegator.is() ) + throw AlreadyInitializedException( OUString(), *this ); + + Reference< XMutableGridDataModel > xDelegator; + Reference< XCollator > xCollator; + switch ( i_arguments.getLength() ) + { + case 1: // SortableGridDataModel.create( XMutableGridDataModel ) + xDelegator.set( i_arguments[0], UNO_QUERY ); + xCollator = lcl_loadDefaultCollator_throw( m_xContext ); + break; + + case 2: // SortableGridDataModel.createWithCollator( XMutableGridDataModel, XCollator ) + xDelegator.set( i_arguments[0], UNO_QUERY ); + xCollator.set( i_arguments[1], UNO_QUERY ); + if ( !xCollator.is() ) + throw IllegalArgumentException( OUString(), *this, 2 ); + break; + } + if ( !xDelegator.is() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + m_delegator = xDelegator; + m_collator = xCollator; + + m_delegator->addGridDataListener( this ); + + m_isInitialized = true; + } + + + GridDataEvent SortableGridDataModel::impl_createPublicEvent( GridDataEvent const & i_originalEvent ) const + { + GridDataEvent aEvent( i_originalEvent ); + aEvent.Source = *const_cast< SortableGridDataModel* >( this ); + aEvent.FirstRow = impl_getPublicRowIndex_nothrow( aEvent.FirstRow ); + aEvent.LastRow = impl_getPublicRowIndex_nothrow( aEvent.LastRow ); + return aEvent; + } + + + void SortableGridDataModel::impl_broadcast( void ( SAL_CALL XGridDataListener::*i_listenerMethod )( const GridDataEvent & ), + GridDataEvent const & i_publicEvent, MethodGuard& i_instanceLock ) + { + ::cppu::OInterfaceContainerHelper* pListeners = rBHelper.getContainer( cppu::UnoType<XGridDataListener>::get() ); + if ( pListeners == nullptr ) + return; + + i_instanceLock.clear(); + pListeners->notifyEach( i_listenerMethod, i_publicEvent ); + } + + + void SAL_CALL SortableGridDataModel::rowsInserted( const GridDataEvent& i_event ) + { + MethodGuard aGuard( *this, rBHelper ); + + if ( impl_isSorted_nothrow() ) + { + // no infrastructure is in place currently to sort the new row to its proper location, + // so we remove the sorting here. + impl_removeColumnSort( aGuard ); + aGuard.reset(); + } + + GridDataEvent const aEvent( impl_createPublicEvent( i_event ) ); + impl_broadcast( &XGridDataListener::rowsInserted, aEvent, aGuard ); + } + + void lcl_decrementValuesGreaterThan( ::std::vector< ::sal_Int32 > & io_indexMap, sal_Int32 const i_threshold ) + { + for ( auto& rIndex : io_indexMap ) + { + if ( rIndex >= i_threshold ) + --rIndex; + } + } + + void SortableGridDataModel::impl_rebuildIndexesAndNotify( MethodGuard& i_instanceLock ) + { + OSL_PRECOND( impl_isSorted_nothrow(), "SortableGridDataModel::impl_rebuildIndexesAndNotify: illegal call!" ); + + // clear the indexes + lcl_clear( m_publicToPrivateRowIndex ); + lcl_clear( m_privateToPublicRowIndex ); + + // rebuild the index + if ( !impl_reIndex_nothrow( m_currentSortColumn, m_sortAscending ) ) + { + impl_removeColumnSort( i_instanceLock ); + return; + } + + // broadcast an artificial event, saying that all rows have been removed + GridDataEvent const aRemovalEvent( *this, -1, -1, -1, -1 ); + impl_broadcast( &XGridDataListener::rowsRemoved, aRemovalEvent, i_instanceLock ); + i_instanceLock.reset(); + + // broadcast an artificial event, saying that n rows have been added + GridDataEvent const aAdditionEvent( *this, -1, -1, 0, m_delegator->getRowCount() - 1 ); + impl_broadcast( &XGridDataListener::rowsInserted, aAdditionEvent, i_instanceLock ); + } + + + void SAL_CALL SortableGridDataModel::rowsRemoved( const GridDataEvent& i_event ) + { + MethodGuard aGuard( *this, rBHelper ); + + // if the data is not sorted, broadcast the event unchanged + if ( !impl_isSorted_nothrow() ) + { + GridDataEvent const aEvent( impl_createPublicEvent( i_event ) ); + impl_broadcast( &XGridDataListener::rowsRemoved, aEvent, aGuard ); + return; + } + + // if all rows have been removed, also simply multiplex to own listeners + if ( i_event.FirstRow < 0 ) + { + lcl_clear( m_publicToPrivateRowIndex ); + lcl_clear( m_privateToPublicRowIndex ); + GridDataEvent aEvent( i_event ); + aEvent.Source = *this; + impl_broadcast( &XGridDataListener::rowsRemoved, aEvent, aGuard ); + return; + } + + bool needReIndex = false; + if ( i_event.FirstRow != i_event.LastRow ) + { + OSL_ENSURE( false, "SortableGridDataModel::rowsRemoved: missing implementation - removal of multiple rows!" ); + needReIndex = true; + } + else if ( o3tl::make_unsigned( i_event.FirstRow ) >= m_privateToPublicRowIndex.size() ) + { + OSL_ENSURE( false, "SortableGridDataModel::rowsRemoved: inconsistent/wrong data!" ); + needReIndex = true; + } + + if ( needReIndex ) + { + impl_rebuildIndexesAndNotify( aGuard ); + return; + } + + // build public event version + GridDataEvent const aEvent( impl_createPublicEvent( i_event ) ); + + // remove the entries from the index maps + sal_Int32 const privateIndex = i_event.FirstRow; + sal_Int32 const publicIndex = aEvent.FirstRow; + + m_publicToPrivateRowIndex.erase( m_publicToPrivateRowIndex.begin() + publicIndex ); + m_privateToPublicRowIndex.erase( m_privateToPublicRowIndex.begin() + privateIndex ); + + // adjust remaining entries in the index maps + lcl_decrementValuesGreaterThan( m_publicToPrivateRowIndex, privateIndex ); + lcl_decrementValuesGreaterThan( m_privateToPublicRowIndex, publicIndex ); + + // broadcast the event + impl_broadcast( &XGridDataListener::rowsRemoved, aEvent, aGuard ); + } + + + void SAL_CALL SortableGridDataModel::dataChanged( const GridDataEvent& i_event ) + { + MethodGuard aGuard( *this, rBHelper ); + + GridDataEvent const aEvent( impl_createPublicEvent( i_event ) ); + impl_broadcast( &XGridDataListener::dataChanged, aEvent, aGuard ); + } + + + void SAL_CALL SortableGridDataModel::rowHeadingChanged( const GridDataEvent& i_event ) + { + MethodGuard aGuard( *this, rBHelper ); + + GridDataEvent const aEvent( impl_createPublicEvent( i_event ) ); + impl_broadcast( &XGridDataListener::rowHeadingChanged, aEvent, aGuard ); + } + + + void SAL_CALL SortableGridDataModel::disposing( const EventObject& ) + { + } + + class CellDataLessComparison + { + public: + CellDataLessComparison( + ::std::vector< Any > const & i_data, + ::comphelper::IKeyPredicateLess const & i_predicate, + bool const i_sortAscending + ) + :m_data( i_data ) + ,m_predicate( i_predicate ) + ,m_sortAscending( i_sortAscending ) + { + } + + bool operator()( sal_Int32 const i_lhs, sal_Int32 const i_rhs ) const + { + Any const & lhs = m_data[ i_lhs ]; + Any const & rhs = m_data[ i_rhs ]; + // <VOID/> is less than everything else + if ( !lhs.hasValue() ) + return m_sortAscending; + if ( !rhs.hasValue() ) + return !m_sortAscending; + + // actually compare + if ( m_sortAscending ) + return m_predicate.isLess( lhs, rhs ); + else + return m_predicate.isLess( rhs, lhs ); + } + + private: + ::std::vector< Any > const & m_data; + ::comphelper::IKeyPredicateLess const & m_predicate; + bool const m_sortAscending; + }; + + bool SortableGridDataModel::impl_reIndex_nothrow( ::sal_Int32 const i_columnIndex, bool const i_sortAscending ) + { + ::sal_Int32 const rowCount( getRowCount() ); + ::std::vector< ::sal_Int32 > aPublicToPrivate( rowCount ); + + try + { + // build an unsorted translation table, and retrieve the unsorted data + ::std::vector< Any > aColumnData( rowCount ); + Type dataType; + for ( ::sal_Int32 rowIndex = 0; rowIndex < rowCount; ++rowIndex ) + { + aColumnData[ rowIndex ] = m_delegator->getCellData( i_columnIndex, rowIndex ); + aPublicToPrivate[ rowIndex ] = rowIndex; + + // determine the data types we assume for the complete column + if ( ( dataType.getTypeClass() == TypeClass_VOID ) && aColumnData[ rowIndex ].hasValue() ) + dataType = aColumnData[ rowIndex ].getValueType(); + } + + // get predicate object + ::std::unique_ptr< ::comphelper::IKeyPredicateLess > const pPredicate( ::comphelper::getStandardLessPredicate( dataType, m_collator ) ); + ENSURE_OR_RETURN_FALSE( + pPredicate, "SortableGridDataModel::impl_reIndex_nothrow: no sortable data found!"); + + // then sort + CellDataLessComparison const aComparator( aColumnData, *pPredicate, i_sortAscending ); + ::std::sort( aPublicToPrivate.begin(), aPublicToPrivate.end(), aComparator ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + return false; + } + + // also build the "private to public" mapping + ::std::vector< sal_Int32 > aPrivateToPublic( aPublicToPrivate.size() ); + for ( size_t i=0; i<aPublicToPrivate.size(); ++i ) + aPrivateToPublic[ aPublicToPrivate[i] ] = i; + + m_publicToPrivateRowIndex.swap( aPublicToPrivate ); + m_privateToPublicRowIndex.swap( aPrivateToPublic ); + + return true; + } + + + void SAL_CALL SortableGridDataModel::sortByColumn( ::sal_Int32 i_columnIndex, sal_Bool i_sortAscending ) + { + MethodGuard aGuard( *this, rBHelper ); + + if ( ( i_columnIndex < 0 ) || ( i_columnIndex >= getColumnCount() ) ) + throw IndexOutOfBoundsException( OUString(), *this ); + + if ( !impl_reIndex_nothrow( i_columnIndex, i_sortAscending ) ) + return; + + m_currentSortColumn = i_columnIndex; + m_sortAscending = i_sortAscending; + + impl_broadcast( + &XGridDataListener::dataChanged, + GridDataEvent( *this, -1, -1, -1, -1 ), + aGuard + ); + } + + + void SortableGridDataModel::impl_removeColumnSort_noBroadcast() + { + lcl_clear( m_publicToPrivateRowIndex ); + lcl_clear( m_privateToPublicRowIndex ); + + m_currentSortColumn = -1; + m_sortAscending = true; + } + + + void SortableGridDataModel::impl_removeColumnSort( MethodGuard& i_instanceLock ) + { + impl_removeColumnSort_noBroadcast(); + impl_broadcast( + &XGridDataListener::dataChanged, + GridDataEvent( *this, -1, -1, -1, -1 ), + i_instanceLock + ); + } + + + void SAL_CALL SortableGridDataModel::removeColumnSort( ) + { + MethodGuard aGuard( *this, rBHelper ); + impl_removeColumnSort( aGuard ); + } + + + css::beans::Pair< ::sal_Int32, sal_Bool > SAL_CALL SortableGridDataModel::getCurrentSortOrder( ) + { + MethodGuard aGuard( *this, rBHelper ); + + return css::beans::Pair< ::sal_Int32, sal_Bool >( m_currentSortColumn, m_sortAscending ); + } + + + void SAL_CALL SortableGridDataModel::addRow( const Any& i_heading, const Sequence< Any >& i_data ) + { + MethodGuard aGuard( *this, rBHelper ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->addRow( i_heading, i_data ); + } + + + void SAL_CALL SortableGridDataModel::addRows( const Sequence< Any >& i_headings, const Sequence< Sequence< Any > >& i_data ) + { + MethodGuard aGuard( *this, rBHelper ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->addRows( i_headings, i_data ); + } + + + void SAL_CALL SortableGridDataModel::insertRow( ::sal_Int32 i_index, const Any& i_heading, const Sequence< Any >& i_data ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = i_index == getRowCount() ? i_index : impl_getPrivateRowIndex_throw( i_index ); + // note that |RowCount| is a valid index in this method, but not for impl_getPrivateRowIndex_throw + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->insertRow( rowIndex, i_heading, i_data ); + } + + + void SAL_CALL SortableGridDataModel::insertRows( ::sal_Int32 i_index, const Sequence< Any>& i_headings, const Sequence< Sequence< Any > >& i_data ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = i_index == getRowCount() ? i_index : impl_getPrivateRowIndex_throw( i_index ); + // note that |RowCount| is a valid index in this method, but not for impl_getPrivateRowIndex_throw + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->insertRows( rowIndex, i_headings, i_data ); + } + + + void SAL_CALL SortableGridDataModel::removeRow( ::sal_Int32 i_rowIndex ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->removeRow( rowIndex ); + } + + + void SAL_CALL SortableGridDataModel::removeAllRows( ) + { + MethodGuard aGuard( *this, rBHelper ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->removeAllRows(); + } + + + void SAL_CALL SortableGridDataModel::updateCellData( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->updateCellData( i_columnIndex, rowIndex, i_value ); + } + + + void SAL_CALL SortableGridDataModel::updateRowData( const Sequence< ::sal_Int32 >& i_columnIndexes, ::sal_Int32 i_rowIndex, const Sequence< Any >& i_values ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->updateRowData( i_columnIndexes, rowIndex, i_values ); + } + + + void SAL_CALL SortableGridDataModel::updateRowHeading( ::sal_Int32 i_rowIndex, const Any& i_heading ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->updateRowHeading( rowIndex, i_heading ); + } + + + void SAL_CALL SortableGridDataModel::updateCellToolTip( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex, const Any& i_value ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->updateCellToolTip( i_columnIndex, rowIndex, i_value ); + } + + + void SAL_CALL SortableGridDataModel::updateRowToolTip( ::sal_Int32 i_rowIndex, const Any& i_value ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + delegator->updateRowToolTip( rowIndex, i_value ); + } + + + void SAL_CALL SortableGridDataModel::addGridDataListener( const Reference< XGridDataListener >& i_listener ) + { + rBHelper.addListener( cppu::UnoType<XGridDataListener>::get(), i_listener ); + } + + + void SAL_CALL SortableGridDataModel::removeGridDataListener( const Reference< XGridDataListener >& i_listener ) + { + rBHelper.removeListener( cppu::UnoType<XGridDataListener>::get(), i_listener ); + } + + + ::sal_Int32 SAL_CALL SortableGridDataModel::getRowCount() + { + MethodGuard aGuard( *this, rBHelper ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getRowCount(); + } + + + ::sal_Int32 SAL_CALL SortableGridDataModel::getColumnCount() + { + MethodGuard aGuard( *this, rBHelper ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getColumnCount(); + } + + + Any SAL_CALL SortableGridDataModel::getCellData( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getCellData( i_columnIndex, rowIndex ); + } + + + Any SAL_CALL SortableGridDataModel::getCellToolTip( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getCellToolTip( i_columnIndex, rowIndex ); + } + + + Any SAL_CALL SortableGridDataModel::getRowHeading( ::sal_Int32 i_rowIndex ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getRowHeading( rowIndex ); + } + + + Sequence< Any > SAL_CALL SortableGridDataModel::getRowData( ::sal_Int32 i_rowIndex ) + { + MethodGuard aGuard( *this, rBHelper ); + + ::sal_Int32 const rowIndex = impl_getPrivateRowIndex_throw( i_rowIndex ); + + Reference< XMutableGridDataModel > const delegator( m_delegator ); + aGuard.clear(); + return delegator->getRowData( rowIndex ); + } + + + void SAL_CALL SortableGridDataModel::disposing() + { + m_currentSortColumn = -1; + + Reference< XComponent > const delegatorComponent( m_delegator ); + m_delegator->removeGridDataListener( this ); + m_delegator.clear(); + delegatorComponent->dispose(); + + Reference< XComponent > const collatorComponent( m_collator, UNO_QUERY ); + m_collator.clear(); + if ( collatorComponent.is() ) + collatorComponent->dispose(); + + lcl_clear( m_publicToPrivateRowIndex ); + lcl_clear( m_privateToPublicRowIndex ); + } + + + Reference< css::util::XCloneable > SAL_CALL SortableGridDataModel::createClone( ) + { + MethodGuard aGuard( *this, rBHelper ); + + return new SortableGridDataModel( *this ); + } + + + OUString SAL_CALL SortableGridDataModel::getImplementationName( ) + { + return "org.openoffice.comp.toolkit.SortableGridDataModel"; + } + + sal_Bool SAL_CALL SortableGridDataModel::supportsService( const OUString& i_serviceName ) + { + return cppu::supportsService(this, i_serviceName); + } + + Sequence< OUString > SAL_CALL SortableGridDataModel::getSupportedServiceNames( ) + { + return { "com.sun.star.awt.grid.SortableGridDataModel" }; + } + + + ::sal_Int32 SortableGridDataModel::impl_getPrivateRowIndex_throw( ::sal_Int32 const i_publicRowIndex ) const + { + if ( ( i_publicRowIndex < 0 ) || ( i_publicRowIndex >= m_delegator->getRowCount() ) ) + throw IndexOutOfBoundsException( OUString(), *const_cast< SortableGridDataModel* >( this ) ); + + if ( !impl_isSorted_nothrow() ) + // no need to translate anything + return i_publicRowIndex; + + ENSURE_OR_RETURN( o3tl::make_unsigned( i_publicRowIndex ) < m_publicToPrivateRowIndex.size(), + "SortableGridDataModel::impl_getPrivateRowIndex_throw: inconsistency!", i_publicRowIndex ); + // obviously the translation table contains too few elements - it should have exactly |getRowCount()| + // elements + + return m_publicToPrivateRowIndex[ i_publicRowIndex ]; + } + + + ::sal_Int32 SortableGridDataModel::impl_getPublicRowIndex_nothrow( ::sal_Int32 const i_privateRowIndex ) const + { + if ( !impl_isSorted_nothrow() ) + // no need to translate anything + return i_privateRowIndex; + + if ( i_privateRowIndex < 0 ) + return i_privateRowIndex; + + ENSURE_OR_RETURN( o3tl::make_unsigned( i_privateRowIndex ) < m_privateToPublicRowIndex.size(), + "SortableGridDataModel::impl_getPublicRowIndex_nothrow: invalid index!", i_privateRowIndex ); + + return m_privateToPublicRowIndex[ i_privateRowIndex ]; + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_openoffice_comp_toolkit_SortableGridDataModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SortableGridDataModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/roadmapcontrol.cxx b/toolkit/source/controls/roadmapcontrol.cxx new file mode 100644 index 0000000000..e46a607ef3 --- /dev/null +++ b/toolkit/source/controls/roadmapcontrol.cxx @@ -0,0 +1,504 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/roadmapcontrol.hxx> +#include <controls/roadmapentry.hxx> +#include <helper/property.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <helper/unopropertyarrayhelper.hxx> + +namespace toolkit +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + + +// helper + + // = UnoControlRoadmapModel + + + UnoControlRoadmapModel::UnoControlRoadmapModel( const Reference< XComponentContext >& i_factory ) + :UnoControlRoadmapModel_Base( i_factory ) + ,maContainerListeners( *this ) + { + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_IMAGEURL ); + ImplRegisterProperty( BASEPROPERTY_GRAPHIC ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_COMPLETE ); + ImplRegisterProperty( BASEPROPERTY_ACTIVATED ); + ImplRegisterProperty( BASEPROPERTY_CURRENTITEMID ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_TEXT ); + } + + + OUString UnoControlRoadmapModel::getServiceName() + { + return "stardiv.vcl.controlmodel.Roadmap"; + } + + OUString UnoControlRoadmapModel::getImplementationName() + { + return "stardiv.Toolkit.UnoControlRoadmapModel"; + } + + css::uno::Sequence<OUString> + UnoControlRoadmapModel::getSupportedServiceNames() + { + auto s(UnoControlRoadmapModel_Base::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlRoadmapModel"; + ps[s.getLength() - 1] = "stardiv.vcl.controlmodel.Roadmap"; + return s; + } + + Any UnoControlRoadmapModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const + { + Any aReturn; + switch (nPropId) + { + case BASEPROPERTY_COMPLETE: + aReturn <<= true; + break; + case BASEPROPERTY_ACTIVATED: + aReturn <<= true; + break; + case BASEPROPERTY_CURRENTITEMID: + aReturn <<= sal_Int16(-1); + break; + case BASEPROPERTY_TEXT: + break; + case BASEPROPERTY_BORDER: + aReturn <<= sal_Int16(2); // No Border + break; + case BASEPROPERTY_DEFAULTCONTROL: + aReturn <<= OUString( "stardiv.vcl.control.Roadmap" ); + break; + default : aReturn = UnoControlRoadmapModel_Base::ImplGetDefaultValue( nPropId ); break; + } + + return aReturn; + } + + + Reference< XInterface > SAL_CALL UnoControlRoadmapModel::createInstance( ) + { + return cppu::getXWeak(new ORoadmapEntry()); + } + + + Reference< XInterface > SAL_CALL UnoControlRoadmapModel::createInstanceWithArguments( const Sequence< Any >& /*aArguments*/ ) + { + // Todo: implementation of the arguments handling + return cppu::getXWeak(new ORoadmapEntry()); + } + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( UnoControlRoadmapModel, UnoControlRoadmapModel_Base, UnoControlRoadmapModel_IBase ) + + + css::uno::Any SAL_CALL UnoControlRoadmapModel::queryAggregation( const css::uno::Type & rType ) + { + Any aRet = UnoControlRoadmapModel_Base::queryAggregation( rType ); + if ( !aRet.hasValue() ) + aRet = UnoControlRoadmapModel_IBase::queryInterface( rType ); + return aRet; + } + + + ::cppu::IPropertyArrayHelper& UnoControlRoadmapModel::getInfoHelper() + { + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; + } + + + // beans::XMultiPropertySet + + Reference< XPropertySetInfo > UnoControlRoadmapModel::getPropertySetInfo( ) + { + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + sal_Int32 SAL_CALL UnoControlRoadmapModel::getCount() + { + return maRoadmapItems.size(); + } + + Any SAL_CALL UnoControlRoadmapModel::getByIndex( sal_Int32 Index ) + { + if ((Index < 0) || ( o3tl::make_unsigned(Index) >= maRoadmapItems.size())) + throw IndexOutOfBoundsException(); + Any aAny( maRoadmapItems.at( Index ) ); + return aAny; + } + + + void UnoControlRoadmapModel::MakeRMItemValidation( sal_Int32 Index, const Reference< XInterface >& xRoadmapItem ) + { + if (( Index < 0 ) || (o3tl::make_unsigned(Index) > maRoadmapItems.size()) ) + throw IndexOutOfBoundsException(); + if ( !xRoadmapItem.is() ) + throw IllegalArgumentException(); + Reference< XServiceInfo > xServiceInfo( xRoadmapItem, UNO_QUERY ); + bool bIsRoadmapItem = xServiceInfo->supportsService("com.sun.star.awt.RoadmapItem"); + if ( !bIsRoadmapItem ) + throw IllegalArgumentException(); + } + + + void UnoControlRoadmapModel::SetRMItemDefaultProperties( const Reference< XInterface >& xRoadmapItem) + { + Reference< XPropertySet > xPropertySet( xRoadmapItem, UNO_QUERY ); + Reference< XPropertySet > xProps( xRoadmapItem, UNO_QUERY ); + if ( xProps.is() ) + { + sal_Int32 LocID = 0; + Any aValue = xPropertySet->getPropertyValue("ID"); + aValue >>= LocID; + if (LocID < 0) // index may not be smaller than zero + { + xPropertySet->setPropertyValue("ID", Any(GetUniqueID()) ); + } + } + } + + +// The performance of this method could certainly be improved. +// As long as only vectors with up to 10 elements are +// involved it should be sufficient + sal_Int32 UnoControlRoadmapModel::GetUniqueID() + { + Any aAny; + bool bIncrement = true; + sal_Int32 CurID = 0; + sal_Int32 n_CurItemID = 0; + Reference< XInterface > CurRoadmapItem; + while ( bIncrement ) + { + bIncrement = false; + for ( const auto& rRoadmapItem : maRoadmapItems ) + { + CurRoadmapItem = rRoadmapItem; + Reference< XPropertySet > xPropertySet( CurRoadmapItem, UNO_QUERY ); + aAny = xPropertySet->getPropertyValue("ID"); + aAny >>= n_CurItemID; + if (n_CurItemID == CurID) + { + bIncrement = true; + CurID++; + break; + } + } + } + return CurID; + } + + + ContainerEvent UnoControlRoadmapModel::GetContainerEvent(sal_Int32 Index, const Reference< XInterface >& xRoadmapItem) + { + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element <<= xRoadmapItem; + aEvent.Accessor <<= Index; + return aEvent; + } + + + sal_Int16 UnoControlRoadmapModel::GetCurrentItemID( const Reference< XPropertySet >& xPropertySet ) + { + Any aAny = xPropertySet->getPropertyValue( GetPropertyName( BASEPROPERTY_CURRENTITEMID ) ); + sal_Int16 n_CurrentItemID = 0; + aAny >>= n_CurrentItemID; + return n_CurrentItemID; + } + + + void SAL_CALL UnoControlRoadmapModel::insertByIndex( const sal_Int32 Index, const Any& Element) + { + if ( ( Index >= ( static_cast<sal_Int32>(maRoadmapItems.size()) + 1 ) ) || (Index < 0)) + throw IndexOutOfBoundsException(); + Reference< XInterface > xRoadmapItem; + Element >>= xRoadmapItem; + MakeRMItemValidation( Index, xRoadmapItem); + SetRMItemDefaultProperties( xRoadmapItem ); + maRoadmapItems.insert( maRoadmapItems.begin() + Index, xRoadmapItem); + ContainerEvent aEvent = GetContainerEvent(Index, xRoadmapItem); + maContainerListeners.elementInserted( aEvent ); + Reference< XPropertySet > xPropertySet( this ); + sal_Int16 n_CurrentItemID = GetCurrentItemID( xPropertySet ); + if ( Index <= n_CurrentItemID ) + { + Any aAny(static_cast<sal_Int16>( n_CurrentItemID + 1 ) ); + xPropertySet->setPropertyValue( GetPropertyName( BASEPROPERTY_CURRENTITEMID ), aAny ); + } + } + + + void SAL_CALL UnoControlRoadmapModel::removeByIndex( sal_Int32 Index) + { + if ((Index < 0) || ( o3tl::make_unsigned(Index) > maRoadmapItems.size())) + throw IndexOutOfBoundsException(); + Reference< XInterface > xRoadmapItem; + maRoadmapItems.erase( maRoadmapItems.begin() + Index ); + ContainerEvent aEvent = GetContainerEvent(Index, xRoadmapItem); + maContainerListeners.elementRemoved( aEvent ); + Reference< XPropertySet > xPropertySet( this ); + sal_Int16 n_CurrentItemID = GetCurrentItemID( xPropertySet ); + Any aAny; + if ( Index > n_CurrentItemID ) + return; + + if ( n_CurrentItemID >= static_cast<sal_Int32>(maRoadmapItems.size()) ) + { + n_CurrentItemID = sal::static_int_cast< sal_Int16 >( + maRoadmapItems.size()-1); + if ( n_CurrentItemID < 0 ) + return; + aAny <<= n_CurrentItemID; + } + else if (Index == n_CurrentItemID) + aAny <<= sal_Int16(-1); + else if( Index < n_CurrentItemID) + aAny <<= static_cast<sal_Int16>( n_CurrentItemID - 1 ); + xPropertySet->setPropertyValue( GetPropertyName( BASEPROPERTY_CURRENTITEMID ), aAny ); + } + + + void SAL_CALL UnoControlRoadmapModel::replaceByIndex( const sal_Int32 Index, const Any& Element) + { + Reference< XInterface > xRoadmapItem; + Element >>= xRoadmapItem; + MakeRMItemValidation( Index, xRoadmapItem); + SetRMItemDefaultProperties( xRoadmapItem ); + maRoadmapItems.erase( maRoadmapItems.begin() + Index ); + maRoadmapItems.insert( maRoadmapItems.begin() + Index, xRoadmapItem); //push_back( xRoadmapItem ); + ContainerEvent aEvent = GetContainerEvent(Index, xRoadmapItem); + maContainerListeners.elementReplaced( aEvent ); + } + + + Type SAL_CALL UnoControlRoadmapModel::getElementType() + { + Type aType = cppu::UnoType<XPropertySet>::get(); + return aType; + } + + + sal_Bool SAL_CALL UnoControlRoadmapModel::hasElements() + { + return !maRoadmapItems.empty(); + } + + + void SAL_CALL UnoControlRoadmapModel::addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) + { + maContainerListeners.addInterface( xListener ); + } + + void SAL_CALL UnoControlRoadmapModel::removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) + { + maContainerListeners.removeInterface( xListener ); + } + + + // = UnoRoadmapControl + + + UnoRoadmapControl::UnoRoadmapControl() + :maItemListeners( *this ) + { + } + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( UnoRoadmapControl, UnoControlRoadmap_Base, UnoControlRoadmap_IBase ) + +css::uno::Any UnoRoadmapControl::queryAggregation(css::uno::Type const & aType) { + auto ret = UnoControlRoadmap_Base::queryAggregation(aType); + if (!ret.hasValue()) { + ret = UnoControlRoadmap_IBase::queryInterface(aType); + } + return ret; +} + + +sal_Bool SAL_CALL UnoRoadmapControl::setModel(const Reference< XControlModel >& _rModel) + { + Reference< XContainer > xC( getModel(), UNO_QUERY ); + if ( xC.is() ) + xC->removeContainerListener( this ); + + bool bReturn = UnoControlBase::setModel( _rModel ); + + xC.set(getModel(), css::uno::UNO_QUERY); + if ( xC.is() ) + xC->addContainerListener( this ); + + return bReturn; + } + + + OUString UnoRoadmapControl::GetComponentServiceName() const + { + return "Roadmap"; + } + + + void UnoRoadmapControl::dispose() + { + EventObject aEvt; + aEvt.Source = getXWeak(); + maItemListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); + } + + +void UnoRoadmapControl::elementInserted( const ContainerEvent& rEvent ) +{ + Reference< XInterface > xRoadmapItem; + rEvent.Element >>= xRoadmapItem; + Reference< XPropertySet > xRoadmapPropertySet( xRoadmapItem, UNO_QUERY ); + if ( xRoadmapPropertySet.is() ) + xRoadmapPropertySet->addPropertyChangeListener( OUString(), this ); + + Reference< XContainerListener > xPeer(getPeer(), UNO_QUERY); + if ( xPeer.is() ) + { + xPeer->elementInserted( rEvent ); + Reference < XPropertySet > xPropertySet( xPeer, UNO_QUERY ); + if ( xPropertySet.is() ) + xPropertySet->addPropertyChangeListener( OUString(), this ); + } +} + + +void UnoRoadmapControl::elementRemoved( const ContainerEvent& rEvent ) +{ + Reference< XContainerListener > xPeer(getPeer(), UNO_QUERY); + if ( xPeer.is() ) + xPeer->elementRemoved( rEvent ); + Reference< XInterface > xRoadmapItem; + rEvent.Element >>= xRoadmapItem; + Reference< XPropertySet > xPropertySet( xRoadmapItem, UNO_QUERY ); + if ( xPropertySet.is() ) + xPropertySet->removePropertyChangeListener( OUString(), this ); +} + + +void UnoRoadmapControl::elementReplaced( const ContainerEvent& rEvent ) +{ + Reference< XContainerListener > xPeer(getPeer(), UNO_QUERY); + if ( xPeer.is() ) + xPeer->elementReplaced( rEvent ); +} + + +void SAL_CALL UnoRoadmapControl::itemStateChanged( const ItemEvent& rEvent ) +{ + sal_Int16 CurItemIndex = sal::static_int_cast< sal_Int16 >(rEvent.ItemId); + Reference< XControlModel > xModel = getModel( ); + Reference< XPropertySet > xPropertySet( xModel, UNO_QUERY ); + xPropertySet->setPropertyValue( GetPropertyName( BASEPROPERTY_CURRENTITEMID ), Any(CurItemIndex) ); + if ( maItemListeners.getLength() ) + maItemListeners.itemStateChanged( rEvent ); +} + + +void SAL_CALL UnoRoadmapControl::addItemListener( const Reference< XItemListener >& l ) +{ + maItemListeners.addInterface( l ); + if( getPeer().is() && maItemListeners.getLength() == 1 ) + { + Reference < XItemEventBroadcaster > xRoadmap( getPeer(), UNO_QUERY ); + xRoadmap->addItemListener( this ); + } +} + + +void SAL_CALL UnoRoadmapControl::removeItemListener( const Reference< XItemListener >& l ) +{ + if( getPeer().is() && maItemListeners.getLength() == 1 ) + { + Reference < XItemEventBroadcaster > xRoadmap( getPeer(), UNO_QUERY ); + xRoadmap->removeItemListener( this ); + } + + maItemListeners.removeInterface( l ); +} + + +void SAL_CALL UnoRoadmapControl::propertyChange( const PropertyChangeEvent& evt ) +{ + Reference< XPropertyChangeListener > xPeer(getPeer(), UNO_QUERY); + if ( xPeer.is() ) + xPeer->propertyChange( evt ); +} + +OUString UnoRoadmapControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoRoadmapControl"; +} + +css::uno::Sequence<OUString> UnoRoadmapControl::getSupportedServiceNames() +{ + auto s(UnoControlBase::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlRoadmap"; + ps[s.getLength() - 1] = "stardiv.vcl.control.Roadmap"; + return s; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlRoadmapModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoControlRoadmapModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoRoadmapControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoRoadmapControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/roadmapentry.cxx b/toolkit/source/controls/roadmapentry.cxx new file mode 100644 index 0000000000..3de60f7071 --- /dev/null +++ b/toolkit/source/controls/roadmapentry.cxx @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <controls/roadmapentry.hxx> +#include <rtl/ustring.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <cppuhelper/supportsservice.hxx> + + +ORoadmapEntry::ORoadmapEntry() : OPropertyContainer( GetBroadcastHelper() ) +{ + // registerProperty or registerMayBeVoidProperty or registerPropertyNoMember + + registerProperty( "Label", RM_PROPERTY_ID_LABEL, + css::beans::PropertyAttribute::BOUND | + css::beans::PropertyAttribute::CONSTRAINED, + & m_sLabel, cppu::UnoType<decltype(m_sLabel)>::get() ); + m_nID = -1; + registerProperty( "ID", RM_PROPERTY_ID_ID, + css::beans::PropertyAttribute::BOUND | + css::beans::PropertyAttribute::CONSTRAINED, + & m_nID, cppu::UnoType<decltype(m_nID)>::get() ); + m_bEnabled = true; + registerProperty( "Enabled", RM_PROPERTY_ID_ENABLED, + css::beans::PropertyAttribute::BOUND | + css::beans::PropertyAttribute::MAYBEDEFAULT, + & m_bEnabled, cppu::UnoType<decltype(m_bEnabled)>::get() ); + + registerProperty( "Interactive", RM_PROPERTY_ID_INTERACTIVE, + css::beans::PropertyAttribute::BOUND | + css::beans::PropertyAttribute::MAYBEDEFAULT, + & m_bInteractive, cppu::UnoType<decltype(m_bInteractive)>::get() ); + + + // Note that the list of registered properties has to be fixed: Different + // instances of this class have to register the same set of properties with + // the same attributes. + + // This is because all instances of the class share the same PropertySetInfo + // which has been built from the registered property of _one_ instance. +} + + +IMPLEMENT_FORWARD_XINTERFACE2( ORoadmapEntry, ORoadmapEntry_Base, ::comphelper::OPropertyContainer ); +IMPLEMENT_FORWARD_XTYPEPROVIDER2( ORoadmapEntry, ORoadmapEntry_Base, ::comphelper::OPropertyContainer ); + // order matters: + // the first is the class name + // the second is the class which implements the ref-counting + // the third up to n-th (when using IMPLEMENT_FORWARD_*3 and so on) are other base classes + // whose XInterface and XTypeProvider implementations should be merged + + +css::uno::Reference< css:: beans::XPropertySetInfo > SAL_CALL + ORoadmapEntry::getPropertySetInfo() +{ + return createPropertySetInfo( getInfoHelper() ); +} + +OUString SAL_CALL ORoadmapEntry::getImplementationName( ) +{ + return "com.sun.star.comp.toolkit.RoadmapItem"; +} + +sal_Bool SAL_CALL ORoadmapEntry::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ORoadmapEntry::getSupportedServiceNames( ) +{ + return { "com.sun.star.awt.RoadmapItem" }; +} + +::cppu::IPropertyArrayHelper& ORoadmapEntry::getInfoHelper() +{ + return *getArrayHelper(); +} + + +::cppu::IPropertyArrayHelper* ORoadmapEntry::createArrayHelper() const +{ + css::uno::Sequence< css::beans::Property > aProps; + // describes all properties which have been registered in the ctor + describeProperties( aProps ); + + return new ::cppu::OPropertyArrayHelper( aProps ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/spinningprogress.cxx b/toolkit/source/controls/spinningprogress.cxx new file mode 100644 index 0000000000..851d624a3d --- /dev/null +++ b/toolkit/source/controls/spinningprogress.cxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <controls/animatedimages.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/toolkit/throbber.hxx> + +using namespace css; +using namespace css::uno; + +namespace { + +typedef toolkit::AnimatedImagesControlModel SpinningProgressControlModel_Base; +class SpinningProgressControlModel : public SpinningProgressControlModel_Base +{ +public: + explicit SpinningProgressControlModel( css::uno::Reference< css::uno::XComponentContext > const & i_factory ); + SpinningProgressControlModel(const SpinningProgressControlModel& rOther) : SpinningProgressControlModel_Base(rOther) {} + + virtual rtl::Reference<UnoControlModel> Clone() const override; + + // XPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XPersistObject + OUString SAL_CALL getServiceName() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +protected: + virtual ~SpinningProgressControlModel() override; +}; + + SpinningProgressControlModel::SpinningProgressControlModel( Reference< XComponentContext > const & i_factory ) + :SpinningProgressControlModel_Base( i_factory ) + { + // default image sets + osl_atomic_increment( &m_refCount ); + { + try + { + Throbber::ImageSet aImageSets[] = + { + Throbber::ImageSet::N16px, Throbber::ImageSet::N32px, Throbber::ImageSet::N64px + }; + for ( size_t i=0; i < SAL_N_ELEMENTS(aImageSets); ++i ) + { + const ::std::vector< OUString > aDefaultURLs( Throbber::getDefaultImageURLs( aImageSets[i] ) ); + const Sequence< OUString > aImageURLs( aDefaultURLs.data(), aDefaultURLs.size() ); + insertImageSet( i, aImageURLs ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + } + osl_atomic_decrement( &m_refCount ); + } + + + SpinningProgressControlModel::~SpinningProgressControlModel() + { + } + + + rtl::Reference<UnoControlModel> SpinningProgressControlModel::Clone() const + { + return new SpinningProgressControlModel( *this ); + } + + + Reference< beans::XPropertySetInfo > SAL_CALL SpinningProgressControlModel::getPropertySetInfo( ) + { + static Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + OUString SAL_CALL SpinningProgressControlModel::getServiceName() + { + return "com.sun.star.awt.SpinningProgressControlModel"; + } + + + OUString SAL_CALL SpinningProgressControlModel::getImplementationName( ) + { + return "org.openoffice.comp.toolkit.SpinningProgressControlModel"; + } + + + Sequence< OUString > SAL_CALL SpinningProgressControlModel::getSupportedServiceNames() + { + return { "com.sun.star.awt.SpinningProgressControlModel", + "com.sun.star.awt.AnimatedImagesControlModel", + "com.sun.star.awt.UnoControlModel" }; + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_openoffice_comp_toolkit_SpinningProgressControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SpinningProgressControlModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/stdtabcontroller.cxx b/toolkit/source/controls/stdtabcontroller.cxx new file mode 100644 index 0000000000..bac4aea585 --- /dev/null +++ b/toolkit/source/controls/stdtabcontroller.cxx @@ -0,0 +1,415 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/awt/XVclContainerPeer.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include <controls/stdtabcontroller.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/helper/macros.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <comphelper/sequence.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; + + + +StdTabController::StdTabController() +{ +} + +StdTabController::~StdTabController() +{ +} + +bool StdTabController::ImplCreateComponentSequence( + Sequence< Reference< XControl > >& rControls, + const Sequence< Reference< XControlModel > >& rModels, + Sequence< Reference< XWindow > >& rComponents, + Sequence< Any>* pTabStops, + bool bPeerComponent ) +{ + // Get only the requested controls + sal_Int32 nModels = rModels.getLength(); + if (nModels != rControls.getLength()) + { + Sequence< Reference< XControl > > aSeq( nModels ); + auto aSeqRange = asNonConstRange(aSeq); + Reference< XControl > xCurrentControl; + + sal_Int32 nRealControls = 0; + for (const Reference< XControlModel >& rModel : rModels) + { + xCurrentControl = FindControl(rControls, rModel); + if (xCurrentControl.is()) + aSeqRange[nRealControls++] = xCurrentControl; + } + aSeq.realloc(nRealControls); + rControls = aSeq; + } + + // there may be less controls than models, but never more controls than models + assert(rControls.getLength() <= rModels.getLength()); + + sal_Int32 nCtrls = rControls.getLength(); + rComponents.realloc( nCtrls ); + Reference< XWindow > * pComps = rComponents.getArray(); + Any* pTabs = nullptr; + + + if ( pTabStops ) + { + *pTabStops = Sequence< Any>( nCtrls ); + pTabs = pTabStops->getArray(); + } + + bool bOK = true; + for ( const Reference< XControl >& xCtrl : std::as_const(rControls) ) + { + // Get the matching control for this model + if ( !xCtrl.is() ) + { + SAL_WARN("toolkit", "Control not found" ); + bOK = false; + break; + } + + if (bPeerComponent) + pComps->set(xCtrl->getPeer(), UNO_QUERY); + else + pComps->set(xCtrl, UNO_QUERY); + + // TabStop-Property + if ( pTabs ) + { + // opt: Constant String for TabStop name + static constexpr OUString aTabStopName = u"Tabstop"_ustr; + + Reference< XPropertySet > xPSet( xCtrl->getModel(), UNO_QUERY ); + Reference< XPropertySetInfo > xInfo = xPSet->getPropertySetInfo(); + if( xInfo->hasPropertyByName( aTabStopName ) ) + *pTabs++ = xPSet->getPropertyValue( aTabStopName ); + else + ++pTabs; + } + + ++pComps; + } + return bOK; +} + +void StdTabController::ImplActivateControl( bool bFirst ) const +{ + // HACK due to bug #53688#, map controls onto an interface if remote controls may occur + Sequence< Reference< XControl > > aCtrls = const_cast<StdTabController*>(this)->getControls(); + const Reference< XControl > * pControls = aCtrls.getConstArray(); + sal_uInt32 nCount = aCtrls.getLength(); + + for ( sal_uInt32 n = bFirst ? 0 : nCount; bFirst ? n < nCount : n != 0; ) + { + sal_uInt32 nCtrl = bFirst ? n++ : --n; + DBG_ASSERT( pControls[nCtrl].is(), "Control not in Container!" ); + if ( pControls[nCtrl].is() ) + { + Reference< XWindowPeer > xCP = pControls[nCtrl]->getPeer(); + if ( xCP.is() ) + { + VCLXWindow* pC = dynamic_cast<VCLXWindow*>( xCP.get() ); + if ( pC && pC->GetWindow() && ( pC->GetWindow()->GetStyle() & WB_TABSTOP ) ) + { + pC->GetWindow()->GrabFocus(); + break; + } + } + } + } +} + +// XInterface +Any StdTabController::queryAggregation( const Type & rType ) +{ + Any aRet = ::cppu::queryInterface( rType, + static_cast< XTabController* >(this), + static_cast< XServiceInfo* >(this), + static_cast< XTypeProvider* >(this) ); + return (aRet.hasValue() ? aRet : OWeakAggObject::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( StdTabController ) + +// XTypeProvider +css::uno::Sequence< css::uno::Type > StdTabController::getTypes() +{ + static const css::uno::Sequence< css::uno::Type > aTypeList { + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<XTabController>::get(), + cppu::UnoType<XServiceInfo>::get() + }; + return aTypeList; +} + +void StdTabController::setModel( const Reference< XTabControllerModel >& Model ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + mxModel = Model; +} + +Reference< XTabControllerModel > StdTabController::getModel( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + return mxModel; +} + +void StdTabController::setContainer( const Reference< XControlContainer >& Container ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + mxControlContainer = Container; +} + +Reference< XControlContainer > StdTabController::getContainer( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + return mxControlContainer; +} + +Sequence< Reference< XControl > > StdTabController::getControls( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + Sequence< Reference< XControl > > aSeq; + + if ( mxControlContainer.is() ) + { + const Sequence< Reference< XControlModel > > aModels = mxModel->getControlModels(); + + Sequence< Reference< XControl > > xCtrls = mxControlContainer->getControls(); + + sal_Int32 nCtrls = aModels.getLength(); + aSeq = Sequence< Reference< XControl > >( nCtrls ); + std::transform(aModels.begin(), aModels.end(), aSeq.getArray(), + [&xCtrls](const Reference< XControlModel >& xCtrlModel) -> Reference< XControl > { + return FindControl( xCtrls, xCtrlModel ); }); + } + return aSeq; +} + +namespace { + +struct ComponentEntry +{ + css::awt::XWindow* pComponent; + ::Point aPos; +}; + +} + +void StdTabController::autoTabOrder( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + DBG_ASSERT( mxControlContainer.is(), "autoTabOrder: No ControlContainer!" ); + if ( !mxControlContainer.is() ) + return; + + Sequence< Reference< XControlModel > > aSeq = mxModel->getControlModels(); + Sequence< Reference< XWindow > > aCompSeq; + + // This may return a TabController, which returns desired list of controls faster + Sequence< Reference< XControl > > aControls = getControls(); + + // #58317# Some Models may be missing from the Container. Plus there is a + // autoTabOrder call later on. + if( !ImplCreateComponentSequence( aControls, aSeq, aCompSeq, nullptr, false ) ) + return; + + sal_uInt32 nCtrls = aCompSeq.getLength(); + + // insert sort algorithm + std::vector< ComponentEntry > aCtrls; + aCtrls.reserve(nCtrls); + for ( const Reference< XWindow >& rComponent : std::as_const(aCompSeq) ) + { + XWindow* pC = rComponent.get(); + ComponentEntry newEntry; + newEntry.pComponent = pC; + awt::Rectangle aPosSize = pC->getPosSize(); + newEntry.aPos.setX( aPosSize.X ); + newEntry.aPos.setY( aPosSize.Y ); + + decltype(aCtrls)::size_type nPos; + for ( nPos = 0; nPos < aCtrls.size(); nPos++ ) + { + ComponentEntry& rEntry = aCtrls[ nPos ]; + if ( ( rEntry.aPos.Y() > newEntry.aPos.Y() ) || + ( ( rEntry.aPos.Y() == newEntry.aPos.Y() ) && ( rEntry.aPos.X() > newEntry.aPos.X() ) ) ) + break; + } + if ( nPos < aCtrls.size() ) { + aCtrls.insert( aCtrls.begin() + nPos, newEntry ); + } else { + aCtrls.push_back( newEntry ); + } + } + + Sequence< Reference< XControlModel > > aNewSeq( nCtrls ); + std::transform(aCtrls.begin(), aCtrls.end(), aNewSeq.getArray(), + [](const ComponentEntry& rEntry) -> Reference< XControlModel > { + Reference< XControl > xUC( rEntry.pComponent, UNO_QUERY ); + return xUC->getModel(); + }); + + mxModel->setControlModels( aNewSeq ); +} + +void StdTabController::activateTabOrder( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + // Activate tab order for the control container + + Reference< XControl > xC( mxControlContainer, UNO_QUERY ); + Reference< XVclContainerPeer > xVclContainerPeer; + if ( xC.is() ) + xVclContainerPeer.set(xC->getPeer(), css::uno::UNO_QUERY); + if ( !xC.is() || !xVclContainerPeer.is() ) + return; + + // This may return a TabController, which returns desired list of controls faster + // (the dreaded UNO aggregation, retrieve the thing that we are part of) + Reference<XTabController> xTabController( static_cast<XTabController*>(this), UNO_QUERY ); + + // Get a flattened list of controls sequences + Sequence< Reference< XControlModel > > aModels = mxModel->getControlModels(); + Sequence< Reference< XWindow > > aCompSeq; + Sequence< Any> aTabSeq; + + // DG: For the sake of optimization, retrieve Controls from getControls(), + // this may sound counterproductive, but leads to performance improvements + // in practical scenarios (Forms) + Sequence< Reference< XControl > > aControls = xTabController->getControls(); + + // #58317# Some Models may be missing from the Container. Plus there is a + // autoTabOrder call later on. + if( !ImplCreateComponentSequence( aControls, aModels, aCompSeq, &aTabSeq, true ) ) + return; + + xVclContainerPeer->setTabOrder( aCompSeq, aTabSeq, mxModel->getGroupControl() ); + + OUString aName; + Sequence< Reference< XControlModel > > aThisGroupModels; + Sequence< Reference< XWindow > > aControlComponents; + + sal_uInt32 nGroups = mxModel->getGroupCount(); + for ( sal_uInt32 nG = 0; nG < nGroups; nG++ ) + { + mxModel->getGroup( nG, aThisGroupModels, aName ); + + aControls = xTabController->getControls(); + // ImplCreateComponentSequence has a really strange semantics regarding it's first parameter: + // upon method entry, it expects a super set of the controls which it returns + // this means we need to completely fill this sequence with all available controls before + // calling into ImplCreateComponentSequence + + aControlComponents.realloc( 0 ); + + ImplCreateComponentSequence( aControls, aThisGroupModels, aControlComponents, nullptr, true ); + xVclContainerPeer->setGroup( aControlComponents ); + } +} + +void StdTabController::activateFirst( ) +{ + SolarMutexGuard aSolarGuard; + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); //TODO: necessary? + + ImplActivateControl( true ); +} + +void StdTabController::activateLast( ) +{ + SolarMutexGuard aSolarGuard; + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); //TODO: necessary? + + ImplActivateControl( false ); +} + +OUString StdTabController::getImplementationName() +{ + return "stardiv.Toolkit.StdTabController"; +} + +sal_Bool StdTabController::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> StdTabController::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.TabController", + "stardiv.vcl.control.TabController"}; +} + +Reference< XControl > StdTabController::FindControl( Sequence< Reference< XControl > >& rCtrls, + const Reference< XControlModel > & rxCtrlModel ) +{ + if (!rxCtrlModel.is()) + throw lang::IllegalArgumentException("No valid XControlModel", + uno::Reference<uno::XInterface>(), 0); + + auto pCtrl = std::find_if(std::cbegin(rCtrls), std::cend(rCtrls), + [&rxCtrlModel](const Reference< XControl >& rCtrl) { + Reference< XControlModel > xModel(rCtrl.is() ? rCtrl->getModel() : Reference< XControlModel > ()); + return xModel.get() == rxCtrlModel.get(); + }); + if (pCtrl != std::cend(rCtrls)) + { + auto n = static_cast<sal_Int32>(std::distance(std::cbegin(rCtrls), pCtrl)); + Reference< XControl > xCtrl( *pCtrl ); + ::comphelper::removeElementAt( rCtrls, n ); + return xCtrl; + } + return Reference< XControl > (); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_StdTabController_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new StdTabController()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/stdtabcontrollermodel.cxx b/toolkit/source/controls/stdtabcontrollermodel.cxx new file mode 100644 index 0000000000..43fd80f170 --- /dev/null +++ b/toolkit/source/controls/stdtabcontrollermodel.cxx @@ -0,0 +1,439 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/io/XMarkableStream.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <controls/stdtabcontrollermodel.hxx> +#include <toolkit/helper/macros.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <tools/debug.hxx> + +#define UNOCONTROL_STREAMVERSION short(2) + + + +UnoControlModelEntryList::UnoControlModelEntryList() +{ +} + +UnoControlModelEntryList::~UnoControlModelEntryList() +{ + Reset(); +} + +void UnoControlModelEntryList::Reset() +{ + for ( size_t n = maList.size(); n; ) + DestroyEntry( --n ); +} + +void UnoControlModelEntryList::DestroyEntry( size_t nEntry ) +{ + UnoControlModelEntryListBase::iterator it = maList.begin(); + ::std::advance( it, nEntry ); + + if ( (*it)->bGroup ) + delete (*it)->pGroup; + else + delete (*it)->pxControl; + + delete *it; + maList.erase( it ); +} + +size_t UnoControlModelEntryList::size() const { + return maList.size(); +} + +UnoControlModelEntry* UnoControlModelEntryList::operator[]( size_t i ) const { + return ( i < maList.size() ) ? maList[ i ] : nullptr; +} + +void UnoControlModelEntryList::push_back( UnoControlModelEntry* item ) { + maList.push_back( item ); +} + +void UnoControlModelEntryList::insert( size_t i, UnoControlModelEntry* item ) { + if ( i < maList.size() ) { + UnoControlModelEntryListBase::iterator it = maList.begin(); + ::std::advance( it, i ); + maList.insert( it, item ); + } else { + maList.push_back( item ); + } +} + + + +StdTabControllerModel::StdTabControllerModel() +{ + mbGroupControl = true; +} + +StdTabControllerModel::~StdTabControllerModel() +{ +} + +sal_uInt32 StdTabControllerModel::ImplGetControlCount( const UnoControlModelEntryList& rList ) const +{ + sal_uInt32 nCount = 0; + size_t nEntries = rList.size(); + for ( size_t n = 0; n < nEntries; n++ ) + { + UnoControlModelEntry* pEntry = rList[ n ]; + if ( pEntry->bGroup ) + nCount += ImplGetControlCount( *pEntry->pGroup ); + else + nCount++; + } + return nCount; +} + +void StdTabControllerModel::ImplGetControlModels( css::uno::Reference< css::awt::XControlModel > ** ppRefs, const UnoControlModelEntryList& rList ) const +{ + size_t nEntries = rList.size(); + for ( size_t n = 0; n < nEntries; n++ ) + { + UnoControlModelEntry* pEntry = rList[ n ]; + if ( pEntry->bGroup ) + ImplGetControlModels( ppRefs, *pEntry->pGroup ); + else + { + **ppRefs = *pEntry->pxControl; + (*ppRefs)++; + } + } +} + +void StdTabControllerModel::ImplSetControlModels( UnoControlModelEntryList& rList, const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Controls ) +{ + for ( const css::uno::Reference< css::awt::XControlModel >& rRef : Controls ) + { + UnoControlModelEntry* pNewEntry = new UnoControlModelEntry; + pNewEntry->bGroup = false; + pNewEntry->pxControl = new css::uno::Reference< css::awt::XControlModel > ; + *pNewEntry->pxControl = rRef; + rList.push_back( pNewEntry ); + } +} + +sal_uInt32 StdTabControllerModel::ImplGetControlPos( const css::uno::Reference< css::awt::XControlModel >& rCtrl, const UnoControlModelEntryList& rList ) +{ + for ( size_t n = rList.size(); n; ) + { + UnoControlModelEntry* pEntry = rList[ --n ]; + if ( !pEntry->bGroup && ( *pEntry->pxControl == rCtrl ) ) + return n; + } + return CONTROLPOS_NOTFOUND; +} + +static void ImplWriteControls( const css::uno::Reference< css::io::XObjectOutputStream > & OutStream, const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& rCtrls ) +{ + css::uno::Reference< css::io::XMarkableStream > xMark( OutStream, css::uno::UNO_QUERY ); + DBG_ASSERT( xMark.is(), "write: no XMarkableStream!" ); + + sal_uInt32 nStoredControls = 0; + sal_Int32 nDataBeginMark = xMark->createMark(); + + OutStream->writeLong( 0 ); // DataLen + OutStream->writeLong( 0 ); // nStoredControls + + for ( const css::uno::Reference< css::awt::XControlModel >& xI : rCtrls ) + { + css::uno::Reference< css::io::XPersistObject > xPO( xI, css::uno::UNO_QUERY ); + DBG_ASSERT( xPO.is(), "write: Control doesn't support XPersistObject" ); + if ( xPO.is() ) + { + OutStream->writeObject( xPO ); + nStoredControls++; + } + } + sal_Int32 nDataLen = xMark->offsetToMark( nDataBeginMark ); + xMark->jumpToMark( nDataBeginMark ); + OutStream->writeLong( nDataLen ); + OutStream->writeLong( nStoredControls ); + xMark->jumpToFurthest(); + xMark->deleteMark(nDataBeginMark); +} + +static css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > ImplReadControls( const css::uno::Reference< css::io::XObjectInputStream > & InStream ) +{ + css::uno::Reference< css::io::XMarkableStream > xMark( InStream, css::uno::UNO_QUERY ); + DBG_ASSERT( xMark.is(), "write: no XMarkableStream!" ); + + sal_Int32 nDataBeginMark = xMark->createMark(); + + sal_Int32 nDataLen = InStream->readLong(); + sal_uInt32 nCtrls = InStream->readLong(); + + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aSeq( nCtrls ); + for ( sal_uInt32 n = 0; n < nCtrls; n++ ) + { + css::uno::Reference< css::io::XPersistObject > xObj = InStream->readObject(); + css::uno::Reference< css::awt::XControlModel > xI( xObj, css::uno::UNO_QUERY ); + aSeq.getArray()[n] = xI; + } + + // Skip remainder if more data exists than this version recognizes + xMark->jumpToMark( nDataBeginMark ); + InStream->skipBytes( nDataLen ); + xMark->deleteMark(nDataBeginMark); + return aSeq; +} + + +// css::uno::XInterface +css::uno::Any StdTabControllerModel::queryAggregation( const css::uno::Type & rType ) +{ + css::uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< css::awt::XTabControllerModel* >(this), + static_cast< css::lang::XServiceInfo* >(this), + static_cast< css::io::XPersistObject* >(this), + static_cast< css::lang::XTypeProvider* >(this) ); + return (aRet.hasValue() ? aRet : OWeakAggObject::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( StdTabControllerModel ) + +// css::lang::XTypeProvider +css::uno::Sequence< css::uno::Type > StdTabControllerModel::getTypes() +{ + static const css::uno::Sequence< css::uno::Type > aTypeList { + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<css::awt::XTabControllerModel>::get(), + cppu::UnoType<css::lang::XServiceInfo>::get(), + cppu::UnoType<css::io::XPersistObject>::get() + }; + return aTypeList; +} + +sal_Bool StdTabControllerModel::getGroupControl( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + return mbGroupControl; +} + +void StdTabControllerModel::setGroupControl( sal_Bool GroupControl ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + mbGroupControl = GroupControl; +} + +void StdTabControllerModel::setControlModels( const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Controls ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + maControls.Reset(); + ImplSetControlModels( maControls, Controls ); +} + +css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > StdTabControllerModel::getControlModels( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aSeq( ImplGetControlCount( maControls ) ); + css::uno::Reference< css::awt::XControlModel > * pRefs = aSeq.getArray(); + ImplGetControlModels( &pRefs, maControls ); + return aSeq; +} + +void StdTabControllerModel::setGroup( const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& Group, const OUString& GroupName ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + // The controls might occur as a flat list and will be grouped. + // Nested groups are not possible. + // The first element of a group determines its position. + UnoControlModelEntry* pNewEntry = new UnoControlModelEntry; + pNewEntry->bGroup = true; + pNewEntry->pGroup = new UnoControlModelEntryList; + pNewEntry->pGroup->SetName( GroupName ); + ImplSetControlModels( *pNewEntry->pGroup, Group ); + + bool bInserted = false; + size_t nElements = pNewEntry->pGroup->size(); + for ( size_t n = 0; n < nElements; n++ ) + { + UnoControlModelEntry* pEntry = (*pNewEntry->pGroup)[ n ]; + if ( !pEntry->bGroup ) + { + sal_uInt32 nPos = ImplGetControlPos( *pEntry->pxControl, maControls ); + // At the beginning, all Controls should be in a flattened list + DBG_ASSERT( nPos != CONTROLPOS_NOTFOUND, "setGroup - Element not found" ); + if ( nPos != CONTROLPOS_NOTFOUND ) + { + maControls.DestroyEntry( nPos ); + if ( !bInserted ) + { + maControls.insert( nPos, pNewEntry ); + bInserted = true; + } + } + } + } + if ( !bInserted ) + maControls.push_back( pNewEntry ); +} + +sal_Int32 StdTabControllerModel::getGroupCount( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + // Start with only one group layer, even though Model and Impl-methods + // work recursively, this is not presented to the outside. + + sal_Int32 nGroups = 0; + size_t nEntries = maControls.size(); + for ( size_t n = 0; n < nEntries; n++ ) + { + UnoControlModelEntry* pEntry = maControls[ n ]; + if ( pEntry->bGroup ) + nGroups++; + } + return nGroups; +} + +void StdTabControllerModel::getGroup( sal_Int32 nGroup, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& rGroup, OUString& rName ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aSeq; + sal_uInt32 nG = 0; + size_t nEntries = maControls.size(); + for ( size_t n = 0; n < nEntries; n++ ) + { + UnoControlModelEntry* pEntry = maControls[ n ]; + if ( pEntry->bGroup ) + { + if ( nG == static_cast<sal_uInt32>(nGroup) ) + { + sal_uInt32 nCount = ImplGetControlCount( *pEntry->pGroup ); + aSeq = css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >( nCount ); + css::uno::Reference< css::awt::XControlModel > * pRefs = aSeq.getArray(); + ImplGetControlModels( &pRefs, *pEntry->pGroup ); + rName = pEntry->pGroup->GetName(); + break; + } + nG++; + } + } + rGroup = aSeq; +} + +void StdTabControllerModel::getGroupByName( const OUString& rName, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& rGroup ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + sal_uInt32 nGroup = 0; + size_t nEntries = maControls.size(); + for ( size_t n = 0; n < nEntries; n++ ) + { + UnoControlModelEntry* pEntry = maControls[ n ]; + if ( pEntry->bGroup ) + { + if ( pEntry->pGroup->GetName() == rName ) + { + OUString Dummy; + getGroup( nGroup, rGroup, Dummy ); + break; + } + nGroup++; + } + } +} + + +// css::io::XPersistObject +OUString StdTabControllerModel::getServiceName( ) +{ + return "stardiv.vcl.controlmodel.TabController"; +} + +void StdTabControllerModel::write( const css::uno::Reference< css::io::XObjectOutputStream >& OutStream ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + css::uno::Reference< css::io::XMarkableStream > xMark( OutStream, css::uno::UNO_QUERY ); + DBG_ASSERT( xMark.is(), "write: no XMarkableStream!" ); + + OutStream->writeShort( UNOCONTROL_STREAMVERSION ); + + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aCtrls = getControlModels(); + ImplWriteControls( OutStream, aCtrls ); + + sal_uInt32 nGroups = getGroupCount(); + OutStream->writeLong( nGroups ); + for ( sal_uInt32 n = 0; n < nGroups; n++ ) + { + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aGroupCtrls; + OUString aGroupName; + getGroup( n, aGroupCtrls, aGroupName ); + OutStream->writeUTF( aGroupName ); + ImplWriteControls( OutStream, aGroupCtrls ); + } +} + +void StdTabControllerModel::read( const css::uno::Reference< css::io::XObjectInputStream >& InStream ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aSeq = ImplReadControls( InStream ); + setControlModels( aSeq ); + + sal_uInt32 nGroups = InStream->readLong(); + for ( sal_uInt32 n = 0; n < nGroups; n++ ) + { + OUString aGroupName = InStream->readUTF(); + css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > aCtrlSeq = ImplReadControls( InStream ); + setGroup( aCtrlSeq, aGroupName ); + } +} + +OUString StdTabControllerModel::getImplementationName() +{ + return "stardiv.Toolkit.StdTabControllerModel"; +} + +sal_Bool StdTabControllerModel::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> StdTabControllerModel::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.awt.TabControllerModel", + "stardiv.vcl.controlmodel.TabController"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_StdTabControllerModel_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new StdTabControllerModel()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/svmedit.cxx b/toolkit/source/controls/svmedit.cxx new file mode 100644 index 0000000000..1bc51155f7 --- /dev/null +++ b/toolkit/source/controls/svmedit.cxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <awt/vclxwindows.hxx> +#include <controls/svmedit.hxx> + +MultiLineEdit::MultiLineEdit(vcl::Window* pParent, WinBits nWinStyle) + : VclMultiLineEdit(pParent, nWinStyle) +{ +} + +// virtual +css::uno::Reference<css::awt::XVclWindowPeer> MultiLineEdit::GetComponentInterface(bool bCreate) +{ + css::uno::Reference<css::awt::XVclWindowPeer> xPeer( + VclMultiLineEdit::GetComponentInterface(false)); + if (!xPeer.is() && bCreate) + { + rtl::Reference<VCLXMultiLineEdit> xVCLMEdit(new VCLXMultiLineEdit); + xVCLMEdit->SetWindow(this); + xPeer = xVCLMEdit.get(); + SetComponentInterface(xPeer); + } + return xPeer; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/svtxgridcontrol.cxx b/toolkit/source/controls/svtxgridcontrol.cxx new file mode 100644 index 0000000000..1ca789db9a --- /dev/null +++ b/toolkit/source/controls/svtxgridcontrol.cxx @@ -0,0 +1,911 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <awt/vclxwindows.hxx> +#include <cppuhelper/implbase.hxx> +#include <toolkit/helper/listenermultiplexer.hxx> +#include <com/sun/star/view/SelectionType.hpp> +#include <controls/table/tablecontrol.hxx> +#include <controls/table/tablecontrolinterface.hxx> +#include <controls/table/gridtablerenderer.hxx> +#include "unocontroltablemodel.hxx" +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <helper/property.hxx> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/awt/grid/GridInvalidDataException.hpp> +#include <com/sun/star/awt/grid/GridInvalidModelException.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/util/Color.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +#include <vcl/svapp.hxx> + +#include <algorithm> + +using css::uno::Reference; +using css::uno::Exception; +using css::uno::UNO_QUERY; +using css::uno::UNO_QUERY_THROW; +using css::uno::Any; +using css::uno::Sequence; +using css::awt::grid::XGridSelectionListener; +using css::style::VerticalAlignment; +using css::style::VerticalAlignment_TOP; +using css::view::SelectionType; +using css::view::SelectionType_NONE; +using css::view::SelectionType_RANGE; +using css::view::SelectionType_SINGLE; +using css::view::SelectionType_MULTI; +using css::awt::grid::XGridDataModel; +using css::awt::grid::GridInvalidDataException; +using css::lang::EventObject; +using css::lang::IndexOutOfBoundsException; +using css::awt::grid::XGridColumnModel; +using css::awt::grid::GridSelectionEvent; +using css::awt::grid::XGridColumn; +using css::container::ContainerEvent; +using css::awt::grid::GridDataEvent; +using css::awt::grid::GridInvalidModelException; + +namespace AccessibleEventId = css::accessibility::AccessibleEventId; +namespace AccessibleStateType = css::accessibility::AccessibleStateType; + +using namespace ::svt::table; + + +SVTXGridControl::SVTXGridControl() + :m_xTableModel( std::make_shared<UnoControlTableModel>() ) + ,m_bTableModelInitCompleted( false ) + ,m_aSelectionListeners( *this ) +{ +} + + +SVTXGridControl::~SVTXGridControl() +{ +} + + +void SVTXGridControl::SetWindow( const VclPtr< vcl::Window > &pWindow ) +{ + SVTXGridControl_Base::SetWindow( pWindow ); + impl_checkTableModelInit(); +} + + +void SVTXGridControl::impl_checkColumnIndex_throw( ::svt::table::TableControl const & i_table, sal_Int32 const i_columnIndex ) const +{ + if ( ( i_columnIndex < 0 ) || ( i_columnIndex >= i_table.GetColumnCount() ) ) + throw IndexOutOfBoundsException( OUString(), *const_cast< SVTXGridControl* >( this ) ); +} + + +void SVTXGridControl::impl_checkRowIndex_throw( ::svt::table::TableControl const & i_table, sal_Int32 const i_rowIndex ) const +{ + if ( ( i_rowIndex < 0 ) || ( i_rowIndex >= i_table.GetRowCount() ) ) + throw IndexOutOfBoundsException( OUString(), *const_cast< SVTXGridControl* >( this ) ); +} + + +sal_Int32 SAL_CALL SVTXGridControl::getRowAtPoint(::sal_Int32 x, ::sal_Int32 y) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getRowAtPoint: no control (anymore)!", -1 ); + + TableCell const tableCell = pTable->getTableControlInterface().hitTest( Point( x, y ) ); + return ( tableCell.nRow >= 0 ) ? tableCell.nRow : -1; +} + + +sal_Int32 SAL_CALL SVTXGridControl::getColumnAtPoint(::sal_Int32 x, ::sal_Int32 y) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getColumnAtPoint: no control (anymore)!", -1 ); + + TableCell const tableCell = pTable->getTableControlInterface().hitTest( Point( x, y ) ); + return ( tableCell.nColumn >= 0 ) ? tableCell.nColumn : -1; +} + + +sal_Int32 SAL_CALL SVTXGridControl::getCurrentColumn( ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getCurrentColumn: no control (anymore)!", -1 ); + + sal_Int32 const nColumn = pTable->GetCurrentColumn(); + return ( nColumn >= 0 ) ? nColumn : -1; +} + + +sal_Int32 SAL_CALL SVTXGridControl::getCurrentRow( ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getCurrentRow: no control (anymore)!", -1 ); + + sal_Int32 const nRow = pTable->GetCurrentRow(); + return ( nRow >= 0 ) ? nRow : -1; +} + + +void SAL_CALL SVTXGridControl::goToCell( ::sal_Int32 i_columnIndex, ::sal_Int32 i_rowIndex ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::getCurrentRow: no control (anymore)!" ); + + impl_checkColumnIndex_throw( *pTable, i_columnIndex ); + impl_checkRowIndex_throw( *pTable, i_rowIndex ); + + pTable->GoTo( i_columnIndex, i_rowIndex ); +} + + +void SAL_CALL SVTXGridControl::addSelectionListener(const Reference< XGridSelectionListener > & listener) +{ + m_aSelectionListeners.addInterface(listener); +} + + +void SAL_CALL SVTXGridControl::removeSelectionListener(const Reference< XGridSelectionListener > & listener) +{ + m_aSelectionListeners.removeInterface(listener); +} + + +void SVTXGridControl::setProperty( const OUString& PropertyName, const Any& aValue) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::setProperty: no control (anymore)!" ); + + switch( GetPropertyId( PropertyName ) ) + { + case BASEPROPERTY_ROW_HEADER_WIDTH: + { + sal_Int32 rowHeaderWidth( -1 ); + aValue >>= rowHeaderWidth; + if ( rowHeaderWidth <= 0 ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty: illegal row header width!" ); + break; + } + + m_xTableModel->setRowHeaderWidth( rowHeaderWidth ); + // TODO: the model should broadcast this change itself, and the table should invalidate itself as needed + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_COLUMN_HEADER_HEIGHT: + { + sal_Int32 columnHeaderHeight = 0; + if ( !aValue.hasValue() ) + { + columnHeaderHeight = pTable->PixelToLogic(Size(0, pTable->GetTextHeight() + 3), MapMode(MapUnit::MapAppFont)).Height(); + } + else + { + aValue >>= columnHeaderHeight; + } + if ( columnHeaderHeight <= 0 ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty: illegal column header width!" ); + break; + } + + m_xTableModel->setColumnHeaderHeight( columnHeaderHeight ); + // TODO: the model should broadcast this change itself, and the table should invalidate itself as needed + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_USE_GRID_LINES: + { + GridTableRenderer* pGridRenderer = dynamic_cast< GridTableRenderer* >( + m_xTableModel->getRenderer().get() ); + if ( !pGridRenderer ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty(UseGridLines): invalid renderer!" ); + break; + } + + bool bUseGridLines = false; + OSL_VERIFY( aValue >>= bUseGridLines ); + pGridRenderer->useGridLines( bUseGridLines ); + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_ROW_HEIGHT: + { + sal_Int32 rowHeight = 0; + if ( !aValue.hasValue() ) + { + rowHeight = pTable->PixelToLogic(Size(0, pTable->GetTextHeight() + 3), MapMode(MapUnit::MapAppFont)).Height(); + } + else + { + aValue >>= rowHeight; + } + m_xTableModel->setRowHeight( rowHeight ); + if ( rowHeight <= 0 ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::setProperty: illegal row height!" ); + break; + } + + // TODO: the model should broadcast this change itself, and the table should invalidate itself as needed + pTable->Invalidate(); + } + break; + + case BASEPROPERTY_BACKGROUNDCOLOR: + { + // let the base class handle this for the TableControl + VCLXWindow::setProperty( PropertyName, aValue ); + // and forward to the grid control's data window + if ( pTable->IsBackground() ) + pTable->getDataWindow().SetBackground( pTable->GetBackground() ); + else + pTable->getDataWindow().SetBackground(); + } + break; + + case BASEPROPERTY_GRID_SELECTIONMODE: + { + SelectionType eSelectionType; + if( aValue >>= eSelectionType ) + { + SelectionMode eSelMode; + switch( eSelectionType ) + { + case SelectionType_SINGLE: eSelMode = SelectionMode::Single; break; + case SelectionType_RANGE: eSelMode = SelectionMode::Range; break; + case SelectionType_MULTI: eSelMode = SelectionMode::Multiple; break; + default: eSelMode = SelectionMode::NONE; break; + } + if( pTable->getSelEngine()->GetSelectionMode() != eSelMode ) + pTable->getSelEngine()->SetSelectionMode( eSelMode ); + } + break; + } + case BASEPROPERTY_HSCROLL: + { + bool bHScroll = true; + if( aValue >>= bHScroll ) + m_xTableModel->setHorizontalScrollbarVisibility( bHScroll ? ScrollbarShowAlways : ScrollbarShowSmart ); + break; + } + + case BASEPROPERTY_VSCROLL: + { + bool bVScroll = true; + if( aValue >>= bVScroll ) + { + m_xTableModel->setVerticalScrollbarVisibility( bVScroll ? ScrollbarShowAlways : ScrollbarShowSmart ); + } + break; + } + + case BASEPROPERTY_GRID_SHOWROWHEADER: + { + bool rowHeader = true; + if( aValue >>= rowHeader ) + { + m_xTableModel->setRowHeaders(rowHeader); + } + break; + } + + case BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS: + m_xTableModel->setRowBackgroundColors( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_GRID_LINE_COLOR: + m_xTableModel->setLineColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_GRID_HEADER_BACKGROUND: + m_xTableModel->setHeaderBackgroundColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_GRID_HEADER_TEXT_COLOR: + m_xTableModel->setHeaderTextColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR: + m_xTableModel->setActiveSelectionBackColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR: + m_xTableModel->setInactiveSelectionBackColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR: + m_xTableModel->setActiveSelectionTextColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR: + m_xTableModel->setInactiveSelectionTextColor( aValue ); + pTable->Invalidate(); + break; + + + case BASEPROPERTY_TEXTCOLOR: + m_xTableModel->setTextColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_TEXTLINECOLOR: + m_xTableModel->setTextLineColor( aValue ); + pTable->Invalidate(); + break; + + case BASEPROPERTY_VERTICALALIGN: + { + VerticalAlignment eAlign( VerticalAlignment_TOP ); + if ( aValue >>= eAlign ) + m_xTableModel->setVerticalAlign( eAlign ); + break; + } + + case BASEPROPERTY_GRID_SHOWCOLUMNHEADER: + { + bool colHeader = true; + if( aValue >>= colHeader ) + { + m_xTableModel->setColumnHeaders(colHeader); + } + break; + } + case BASEPROPERTY_GRID_DATAMODEL: + { + Reference< XGridDataModel > const xDataModel( aValue, UNO_QUERY ); + if ( !xDataModel.is() ) + throw GridInvalidDataException("Invalid data model.", *this ); + + m_xTableModel->setDataModel( xDataModel ); + impl_checkTableModelInit(); + } + break; + + case BASEPROPERTY_GRID_COLUMNMODEL: + { + // obtain new col model + Reference< XGridColumnModel > const xColumnModel( aValue, UNO_QUERY ); + if ( !xColumnModel.is() ) + throw GridInvalidModelException("Invalid column model.", *this ); + + // remove all old columns + m_xTableModel->removeAllColumns(); + + // announce to the TableModel + m_xTableModel->setColumnModel( xColumnModel ); + impl_checkTableModelInit(); + + // add new columns + impl_updateColumnsFromModel_nothrow(); + break; + } + default: + VCLXWindow::setProperty( PropertyName, aValue ); + break; + } +} + + +void SVTXGridControl::impl_checkTableModelInit() +{ + if ( !(!m_bTableModelInitCompleted && m_xTableModel->hasColumnModel() && m_xTableModel->hasDataModel()) ) + return; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + if ( !pTable ) + return; + + pTable->SetModel( PTableModel( m_xTableModel ) ); + + m_bTableModelInitCompleted = true; + + // ensure default columns exist, if they have not previously been added + Reference< XGridDataModel > const xDataModel( m_xTableModel->getDataModel(), css::uno::UNO_SET_THROW ); + Reference< XGridColumnModel > const xColumnModel( m_xTableModel->getColumnModel(), css::uno::UNO_SET_THROW ); + + sal_Int32 const nDataColumnCount = xDataModel->getColumnCount(); + if ( ( nDataColumnCount > 0 ) && ( xColumnModel->getColumnCount() == 0 ) ) + xColumnModel->setDefaultColumns( nDataColumnCount ); + // this will trigger notifications, which in turn will let us update our m_xTableModel +} + +namespace +{ + void lcl_convertColor( ::std::optional< ::Color > const & i_color, Any & o_colorValue ) + { + if ( !i_color ) + o_colorValue.clear(); + else + o_colorValue <<= sal_Int32(*i_color); + } +} + +Any SVTXGridControl::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getProperty: no control (anymore)!", Any() ); + + Any aPropertyValue; + + const sal_uInt16 nPropId = GetPropertyId( PropertyName ); + switch(nPropId) + { + case BASEPROPERTY_GRID_SELECTIONMODE: + { + SelectionType eSelectionType; + + SelectionMode eSelMode = pTable->getSelEngine()->GetSelectionMode(); + switch( eSelMode ) + { + case SelectionMode::Single: eSelectionType = SelectionType_SINGLE; break; + case SelectionMode::Range: eSelectionType = SelectionType_RANGE; break; + case SelectionMode::Multiple:eSelectionType = SelectionType_MULTI; break; + default: eSelectionType = SelectionType_NONE; break; + } + aPropertyValue <<= eSelectionType; + break; + } + + case BASEPROPERTY_GRID_SHOWROWHEADER: + aPropertyValue <<= m_xTableModel->hasRowHeaders(); + break; + + case BASEPROPERTY_GRID_SHOWCOLUMNHEADER: + aPropertyValue <<= m_xTableModel->hasColumnHeaders(); + break; + + case BASEPROPERTY_GRID_DATAMODEL: + aPropertyValue <<= m_xTableModel->getDataModel(); + break; + + case BASEPROPERTY_GRID_COLUMNMODEL: + aPropertyValue <<= m_xTableModel->getColumnModel(); + break; + + case BASEPROPERTY_HSCROLL: + { + bool const bHasScrollbar = ( m_xTableModel->getHorizontalScrollbarVisibility() != ScrollbarShowNever ); + aPropertyValue <<= bHasScrollbar; + break; + } + + case BASEPROPERTY_VSCROLL: + { + bool const bHasScrollbar = ( m_xTableModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ); + aPropertyValue <<= bHasScrollbar; + break; + } + + case BASEPROPERTY_USE_GRID_LINES: + { + GridTableRenderer* pGridRenderer = dynamic_cast< GridTableRenderer* >( + m_xTableModel->getRenderer().get() ); + if ( !pGridRenderer ) + { + SAL_WARN( "svtools.uno", "SVTXGridControl::getProperty(UseGridLines): invalid renderer!" ); + break; + } + + aPropertyValue <<= pGridRenderer->useGridLines(); + } + break; + + case BASEPROPERTY_GRID_ROW_BACKGROUND_COLORS: + { + ::std::optional< ::std::vector< ::Color > > aColors( m_xTableModel->getRowBackgroundColors() ); + if ( !aColors ) + aPropertyValue.clear(); + else + { + Sequence< css::util::Color > aAPIColors( aColors->size() ); + std::transform(aColors->begin(), aColors->end(), aAPIColors.getArray(), + [](const auto& color) { return sal_Int32(color); }); + aPropertyValue <<= aAPIColors; + } + } + break; + + case BASEPROPERTY_GRID_LINE_COLOR: + lcl_convertColor( m_xTableModel->getLineColor(), aPropertyValue ); + break; + + case BASEPROPERTY_GRID_HEADER_BACKGROUND: + lcl_convertColor( m_xTableModel->getHeaderBackgroundColor(), aPropertyValue ); + break; + + case BASEPROPERTY_GRID_HEADER_TEXT_COLOR: + lcl_convertColor( m_xTableModel->getHeaderTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_ACTIVE_SEL_BACKGROUND_COLOR: + lcl_convertColor( m_xTableModel->getActiveSelectionBackColor(), aPropertyValue ); + break; + + case BASEPROPERTY_INACTIVE_SEL_BACKGROUND_COLOR: + lcl_convertColor( m_xTableModel->getInactiveSelectionBackColor(), aPropertyValue ); + break; + + case BASEPROPERTY_ACTIVE_SEL_TEXT_COLOR: + lcl_convertColor( m_xTableModel->getActiveSelectionTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_INACTIVE_SEL_TEXT_COLOR: + lcl_convertColor( m_xTableModel->getInactiveSelectionTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_TEXTCOLOR: + lcl_convertColor( m_xTableModel->getTextColor(), aPropertyValue ); + break; + + case BASEPROPERTY_TEXTLINECOLOR: + lcl_convertColor( m_xTableModel->getTextLineColor(), aPropertyValue ); + break; + + default: + aPropertyValue = VCLXWindow::getProperty( PropertyName ); + break; + } + + return aPropertyValue; +} + + +void SAL_CALL SVTXGridControl::rowsInserted( const GridDataEvent& i_event ) +{ + SolarMutexGuard aGuard; + m_xTableModel->notifyRowsInserted( i_event ); +} + + +void SAL_CALL + SVTXGridControl::rowsRemoved( const GridDataEvent& i_event ) +{ + SolarMutexGuard aGuard; + m_xTableModel->notifyRowsRemoved( i_event ); +} + + +void SAL_CALL SVTXGridControl::dataChanged( const GridDataEvent& i_event ) +{ + SolarMutexGuard aGuard; + + m_xTableModel->notifyDataChanged( i_event ); + + // if the data model is sortable, a dataChanged event is also fired in case the sort order changed. + // So, just in case, invalidate the column header area, too. + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::dataChanged: no control (anymore)!" ); + pTable->getTableControlInterface().invalidate( TableArea::ColumnHeaders ); +} + + +void SAL_CALL SVTXGridControl::rowHeadingChanged( const GridDataEvent& ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::rowHeadingChanged: no control (anymore)!" ); + + // TODO: we could do better than this - invalidate the header area only + pTable->getTableControlInterface().invalidate( TableArea::RowHeaders ); +} + + +void SAL_CALL SVTXGridControl::elementInserted( const ContainerEvent& i_event ) +{ + SolarMutexGuard aGuard; + + Reference< XGridColumn > const xGridColumn( i_event.Element, UNO_QUERY_THROW ); + + sal_Int32 nIndex( m_xTableModel->getColumnCount() ); + OSL_VERIFY( i_event.Accessor >>= nIndex ); + m_xTableModel->insertColumn( nIndex, xGridColumn ); +} + + +void SAL_CALL SVTXGridControl::elementRemoved( const ContainerEvent& i_event ) +{ + SolarMutexGuard aGuard; + + sal_Int32 nIndex( -1 ); + OSL_VERIFY( i_event.Accessor >>= nIndex ); + m_xTableModel->removeColumn( nIndex ); +} + + +void SAL_CALL SVTXGridControl::elementReplaced( const ContainerEvent& ) +{ + OSL_ENSURE( false, "SVTXGridControl::elementReplaced: not implemented!" ); + // at the moment, the XGridColumnModel API does not allow replacing columns + // TODO: replace the respective column in our table model +} + + +void SAL_CALL SVTXGridControl::disposing( const EventObject& Source ) +{ + VCLXWindow::disposing( Source ); +} + + +void SAL_CALL SVTXGridControl::selectRow( ::sal_Int32 i_rowIndex ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::selectRow: no control (anymore)!" ); + + impl_checkRowIndex_throw( *pTable, i_rowIndex ); + + pTable->SelectRow( i_rowIndex, true ); +} + + +void SAL_CALL SVTXGridControl::selectAllRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::selectAllRows: no control (anymore)!" ); + + pTable->SelectAllRows( true ); +} + + +void SAL_CALL SVTXGridControl::deselectRow( ::sal_Int32 i_rowIndex ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::deselectRow: no control (anymore)!" ); + + impl_checkRowIndex_throw( *pTable, i_rowIndex ); + + pTable->SelectRow( i_rowIndex, false ); +} + + +void SAL_CALL SVTXGridControl::deselectAllRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::deselectAllRows: no control (anymore)!" ); + + pTable->SelectAllRows( false ); +} + + +Sequence< ::sal_Int32 > SAL_CALL SVTXGridControl::getSelectedRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::getSelectedRows: no control (anymore)!", Sequence< sal_Int32 >() ); + + sal_Int32 selectionCount = pTable->GetSelectedRowCount(); + Sequence< sal_Int32 > selectedRows( selectionCount ); + auto selectedRowsRange = asNonConstRange(selectedRows); + for ( sal_Int32 i=0; i<selectionCount; ++i ) + selectedRowsRange[i] = pTable->GetSelectedRowIndex(i); + return selectedRows; +} + + +sal_Bool SAL_CALL SVTXGridControl::hasSelectedRows() +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::hasSelectedRows: no control (anymore)!", true ); + + return pTable->GetSelectedRowCount() > 0; +} + + +sal_Bool SAL_CALL SVTXGridControl::isRowSelected( ::sal_Int32 index ) +{ + SolarMutexGuard aGuard; + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN( pTable, "SVTXGridControl::isRowSelected: no control (anymore)!", false ); + + return pTable->IsRowSelected( index ); +} + + +void SVTXGridControl::dispose() +{ + EventObject aObj; + aObj.Source = getXWeak(); + m_aSelectionListeners.disposeAndClear( aObj ); + VCLXWindow::dispose(); +} + + +void SVTXGridControl::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + SolarMutexGuard aGuard; + + Reference< XWindow > xKeepAlive( this ); + + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::ProcessWindowEvent: no control (anymore)!" ); + + bool handled = false; + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::TableRowSelect: + { + if ( m_aSelectionListeners.getLength() ) + ImplCallItemListeners(); + handled = true; + } + break; + + case VclEventId::ControlGetFocus: + { + // TODO: this doesn't belong here. It belongs into the TableControl/_Impl, so A11Y also + // works when the control is used outside the UNO context + if ( pTable->GetRowCount()>0 ) + { + pTable->commitCellEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any( AccessibleStateType::FOCUSED ), + Any() + ); + pTable->commitTableEventIfAccessibleAlive( + AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, + Any(), + Any() + ); + } + else + { + pTable->commitTableEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any( AccessibleStateType::FOCUSED ), + Any() + ); + } + } + break; + + case VclEventId::ControlLoseFocus: + { + // TODO: this doesn't belong here. It belongs into the TableControl/_Impl, so A11Y also + // works when the control is used outside the UNO context + if ( pTable->GetRowCount()>0 ) + { + pTable->commitCellEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any(), + Any( AccessibleStateType::FOCUSED ) + ); + } + else + { + pTable->commitTableEventIfAccessibleAlive( + AccessibleEventId::STATE_CHANGED, + Any(), + Any( AccessibleStateType::FOCUSED ) + ); + } + } + break; + + default: break; + } + + if ( !handled ) + VCLXWindow::ProcessWindowEvent( rVclWindowEvent ); +} + + +void SVTXGridControl::setEnable( sal_Bool bEnable ) +{ + SolarMutexGuard aGuard; + + m_xTableModel->setEnabled( bEnable ); + VclPtr<vcl::Window> pWindow = GetWindow(); + if ( pWindow ) + { + pWindow->Enable( bEnable ); + pWindow->EnableInput( bEnable ); + pWindow->Invalidate(); + } +} + + +void SVTXGridControl::ImplCallItemListeners() +{ + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "SVTXGridControl::ImplCallItemListeners: no control (anymore)!" ); + + if ( m_aSelectionListeners.getLength() ) + { + GridSelectionEvent aEvent; + aEvent.Source = getXWeak(); + + sal_Int32 const nSelectedRowCount( pTable->GetSelectedRowCount() ); + aEvent.SelectedRowIndexes.realloc( nSelectedRowCount ); + auto pSelectedRowIndexes = aEvent.SelectedRowIndexes.getArray(); + for ( sal_Int32 i=0; i<nSelectedRowCount; ++i ) + pSelectedRowIndexes[i] = pTable->GetSelectedRowIndex( i ); + m_aSelectionListeners.selectionChanged( aEvent ); + } +} + + +void SVTXGridControl::impl_updateColumnsFromModel_nothrow() +{ + Reference< XGridColumnModel > const xColumnModel( m_xTableModel->getColumnModel() ); + ENSURE_OR_RETURN_VOID( xColumnModel.is(), "no model!" ); + VclPtr< TableControl > pTable = GetAsDynamic< TableControl >(); + ENSURE_OR_RETURN_VOID( pTable, "no table!" ); + + try + { + const Sequence< Reference< XGridColumn > > columns = xColumnModel->getColumns(); + for ( auto const & colRef : columns ) + { + if ( !colRef.is() ) + { + SAL_WARN( "svtools.uno", "illegal column!" ); + continue; + } + + m_xTableModel->appendColumn( colRef ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/cellvalueconversion.cxx b/toolkit/source/controls/table/cellvalueconversion.cxx new file mode 100644 index 0000000000..e07ef35494 --- /dev/null +++ b/toolkit/source/controls/table/cellvalueconversion.cxx @@ -0,0 +1,376 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "cellvalueconversion.hxx" + +#include <com/sun/star/util/NumberFormatsSupplier.hpp> +#include <com/sun/star/util/NumberFormatter.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <sal/log.hxx> +#include <tools/date.hxx> +#include <tools/time.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/long.hxx> +#include <unotools/syslocale.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <comphelper/processfactory.hxx> + +#include <limits> +#include <memory> + +namespace svt +{ +using namespace ::com::sun::star::uno; +using ::com::sun::star::util::XNumberFormatter; +using ::com::sun::star::util::NumberFormatter; +using ::com::sun::star::util::XNumberFormatsSupplier; +using ::com::sun::star::util::NumberFormatsSupplier; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::lang::Locale; +using ::com::sun::star::util::DateTime; +using ::com::sun::star::util::XNumberFormatTypes; + +namespace NumberFormat = ::com::sun::star::util::NumberFormat; + +//= helper + +namespace +{ +double lcl_convertDateToDays(sal_uInt16 const i_day, sal_uInt16 const i_month, + sal_Int16 const i_year) +{ + tools::Long const nNullDateDays = ::Date::DateToDays(1, 1, 1900); + tools::Long const nValueDateDays = ::Date::DateToDays(i_day, i_month, i_year); + + return nValueDateDays - nNullDateDays; +} + +double lcl_convertTimeToDays(tools::Long const i_hours, tools::Long const i_minutes, + tools::Long const i_seconds, tools::Long const i_100thSeconds) +{ + return tools::Time(i_hours, i_minutes, i_seconds, i_100thSeconds).GetTimeInDays(); +} +} + +//= StandardFormatNormalizer + +StandardFormatNormalizer::StandardFormatNormalizer(Reference<XNumberFormatter> const& i_formatter, + ::sal_Int32 const i_numberFormatType) + : m_nFormatKey(0) +{ + try + { + ENSURE_OR_THROW(i_formatter.is(), "StandardFormatNormalizer: no formatter!"); + Reference<XNumberFormatsSupplier> const xSupplier(i_formatter->getNumberFormatsSupplier(), + UNO_SET_THROW); + Reference<XNumberFormatTypes> const xTypes(xSupplier->getNumberFormats(), UNO_QUERY_THROW); + m_nFormatKey = xTypes->getStandardFormat(i_numberFormatType, + SvtSysLocale().GetLanguageTag().getLocale()); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("svtools.table"); + } +} + +//= DoubleNormalization + +namespace +{ +class DoubleNormalization : public StandardFormatNormalizer +{ +public: + explicit DoubleNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::NUMBER) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + double returnValue = std::numeric_limits<double>::quiet_NaN(); + OSL_VERIFY(i_value >>= returnValue); + return returnValue; + } +}; + +//= IntegerNormalization + +class IntegerNormalization : public StandardFormatNormalizer +{ +public: + explicit IntegerNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::NUMBER) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + sal_Int64 value(0); + OSL_VERIFY(i_value >>= value); + return value; + } +}; + +//= BooleanNormalization + +class BooleanNormalization : public StandardFormatNormalizer +{ +public: + explicit BooleanNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::LOGICAL) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + bool value(false); + OSL_VERIFY(i_value >>= value); + return value ? 1 : 0; + } +}; + +//= DateTimeNormalization + +class DateTimeNormalization : public StandardFormatNormalizer +{ +public: + explicit DateTimeNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::DATETIME) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + double returnValue = std::numeric_limits<double>::quiet_NaN(); + + // extract actual UNO value + DateTime aDateTimeValue; + ENSURE_OR_RETURN(i_value >>= aDateTimeValue, "allowed for DateTime values only", + returnValue); + + // date part + returnValue + = lcl_convertDateToDays(aDateTimeValue.Day, aDateTimeValue.Month, aDateTimeValue.Year); + + // time part + returnValue += lcl_convertTimeToDays(aDateTimeValue.Hours, aDateTimeValue.Minutes, + aDateTimeValue.Seconds, aDateTimeValue.NanoSeconds); + + // done + return returnValue; + } +}; + +//= DateNormalization + +class DateNormalization : public StandardFormatNormalizer +{ +public: + explicit DateNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::DATE) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + double returnValue = std::numeric_limits<double>::quiet_NaN(); + + // extract + css::util::Date aDateValue; + ENSURE_OR_RETURN(i_value >>= aDateValue, "allowed for Date values only", returnValue); + + // convert + returnValue = lcl_convertDateToDays(aDateValue.Day, aDateValue.Month, aDateValue.Year); + + // done + return returnValue; + } +}; + +//= TimeNormalization + +class TimeNormalization : public StandardFormatNormalizer +{ +public: + explicit TimeNormalization(Reference<XNumberFormatter> const& i_formatter) + : StandardFormatNormalizer(i_formatter, NumberFormat::TIME) + { + } + + virtual double convertToDouble(Any const& i_value) const override + { + double returnValue = std::numeric_limits<double>::quiet_NaN(); + + // extract + css::util::Time aTimeValue; + ENSURE_OR_RETURN(i_value >>= aTimeValue, "allowed for tools::Time values only", + returnValue); + + // convert + returnValue += lcl_convertTimeToDays(aTimeValue.Hours, aTimeValue.Minutes, + aTimeValue.Seconds, aTimeValue.NanoSeconds); + + // done + return returnValue; + } +}; +} + +//= operations + +bool CellValueConversion::ensureNumberFormatter() +{ + if (bAttemptedFormatterCreation) + return xNumberFormatter.is(); + bAttemptedFormatterCreation = true; + + try + { + Reference<XComponentContext> xContext = ::comphelper::getProcessComponentContext(); + // a number formatter + Reference<XNumberFormatter> const xFormatter(NumberFormatter::create(xContext), + UNO_QUERY_THROW); + + // a supplier of number formats + Locale aLocale = SvtSysLocale().GetLanguageTag().getLocale(); + + Reference<XNumberFormatsSupplier> const xSupplier + = NumberFormatsSupplier::createWithLocale(xContext, aLocale); + + // ensure a NullDate we will assume later on + css::util::Date const aNullDate(1, 1, 1900); + Reference<XPropertySet> const xFormatSettings(xSupplier->getNumberFormatSettings(), + UNO_SET_THROW); + xFormatSettings->setPropertyValue("NullDate", Any(aNullDate)); + + // knit + xFormatter->attachNumberFormatsSupplier(xSupplier); + + // done + xNumberFormatter = xFormatter; + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("svtools.table"); + } + + return xNumberFormatter.is(); +} + +bool CellValueConversion::getValueNormalizer(Type const& i_valueType, + std::shared_ptr<StandardFormatNormalizer>& o_formatter) +{ + auto pos = aNormalizers.find(i_valueType.getTypeName()); + if (pos == aNormalizers.end()) + { + // never encountered this type before + o_formatter.reset(); + + OUString const sTypeName(i_valueType.getTypeName()); + TypeClass const eTypeClass = i_valueType.getTypeClass(); + + if (sTypeName == ::cppu::UnoType<DateTime>::get().getTypeName()) + { + o_formatter = std::make_shared<DateTimeNormalization>(xNumberFormatter); + } + else if (sTypeName == ::cppu::UnoType<css::util::Date>::get().getTypeName()) + { + o_formatter = std::make_shared<DateNormalization>(xNumberFormatter); + } + else if (sTypeName == ::cppu::UnoType<css::util::Time>::get().getTypeName()) + { + o_formatter = std::make_shared<TimeNormalization>(xNumberFormatter); + } + else if (sTypeName == ::cppu::UnoType<sal_Bool>::get().getTypeName()) + { + o_formatter = std::make_shared<BooleanNormalization>(xNumberFormatter); + } + else if (sTypeName == ::cppu::UnoType<double>::get().getTypeName() + || sTypeName == ::cppu::UnoType<float>::get().getTypeName()) + { + o_formatter = std::make_shared<DoubleNormalization>(xNumberFormatter); + } + else if ((eTypeClass == TypeClass_BYTE) || (eTypeClass == TypeClass_SHORT) + || (eTypeClass == TypeClass_UNSIGNED_SHORT) || (eTypeClass == TypeClass_LONG) + || (eTypeClass == TypeClass_UNSIGNED_LONG) || (eTypeClass == TypeClass_HYPER)) + { + o_formatter = std::make_shared<IntegerNormalization>(xNumberFormatter); + } + else + { + SAL_WARN("svtools.table", "unsupported type '" << sTypeName << "'!"); + } + aNormalizers[sTypeName] = o_formatter; + } + else + o_formatter = pos->second; + + return bool(o_formatter); +} + +//= CellValueConversion + +CellValueConversion::CellValueConversion() + : xNumberFormatter() + , bAttemptedFormatterCreation(false) + , aNormalizers() +{ +} + +CellValueConversion::~CellValueConversion() {} + +OUString CellValueConversion::convertToString(const Any& i_value) +{ + OUString sStringValue; + if (!i_value.hasValue()) + return sStringValue; + + if (!(i_value >>= sStringValue)) + { + if (ensureNumberFormatter()) + { + std::shared_ptr<StandardFormatNormalizer> pNormalizer; + if (getValueNormalizer(i_value.getValueType(), pNormalizer)) + { + try + { + double const formatterCompliantValue = pNormalizer->convertToDouble(i_value); + sal_Int32 const formatKey = pNormalizer->getFormatKey(); + sStringValue = xNumberFormatter->convertNumberToString(formatKey, + formatterCompliantValue); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("svtools.table"); + } + } + } + } + + return sStringValue; +} + +} // namespace svt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/cellvalueconversion.hxx b/toolkit/source/controls/table/cellvalueconversion.hxx new file mode 100644 index 0000000000..2e05707e5b --- /dev/null +++ b/toolkit/source/controls/table/cellvalueconversion.hxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/util/XNumberFormatter.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <unordered_map> +#include <memory> + +namespace svt +{ +class StandardFormatNormalizer +{ +public: + /** converts the given <code>Any</code> into a <code>double</code> value to be fed into a number formatter + */ + virtual double convertToDouble(css::uno::Any const& i_value) const = 0; + + /** returns the format key to be used for formatting values + */ + sal_Int32 getFormatKey() const { return m_nFormatKey; } + +protected: + StandardFormatNormalizer(css::uno::Reference<css::util::XNumberFormatter> const& i_formatter, + ::sal_Int32 const i_numberFormatType); + + virtual ~StandardFormatNormalizer() {} + +private: + ::sal_Int32 m_nFormatKey; +}; + +class CellValueConversion +{ +public: + CellValueConversion(); + ~CellValueConversion(); + + OUString convertToString(const css::uno::Any& i_cellValue); + +private: + bool ensureNumberFormatter(); + bool getValueNormalizer(css::uno::Type const& i_valueType, + std::shared_ptr<StandardFormatNormalizer>& o_formatter); + + typedef std::unordered_map<OUString, std::shared_ptr<StandardFormatNormalizer>> NormalizerCache; + + css::uno::Reference<css::util::XNumberFormatter> xNumberFormatter; + bool bAttemptedFormatterCreation; + NormalizerCache aNormalizers; +}; + +} // namespace svt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/defaultinputhandler.cxx b/toolkit/source/controls/table/defaultinputhandler.cxx new file mode 100644 index 0000000000..171458ef7a --- /dev/null +++ b/toolkit/source/controls/table/defaultinputhandler.cxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/table/defaultinputhandler.hxx> +#include <controls/table/tablecontrolinterface.hxx> + +#include <vcl/event.hxx> +#include <osl/diagnose.h> + +namespace svt::table +{ + + + //= DefaultInputHandler + + + DefaultInputHandler::DefaultInputHandler() + { + aMouseFunctions.push_back( new ColumnResize ); + aMouseFunctions.push_back( new RowSelection ); + aMouseFunctions.push_back( new ColumnSortHandler ); + } + + + DefaultInputHandler::~DefaultInputHandler() + { + } + + + bool DefaultInputHandler::delegateMouseEvent( ITableControl& i_control, const MouseEvent& i_event, + FunctionResult ( MouseFunction::*i_handlerMethod )( ITableControl&, const MouseEvent& ) ) + { + if ( pActiveFunction.is() ) + { + bool furtherHandler = false; + switch ( (pActiveFunction.get()->*i_handlerMethod)( i_control, i_event ) ) + { + case ActivateFunction: + OSL_ENSURE( false, "lcl_delegateMouseEvent: unexpected - function already *is* active!" ); + break; + case ContinueFunction: + break; + case DeactivateFunction: + pActiveFunction.clear(); + break; + case SkipFunction: + furtherHandler = true; + break; + } + if ( !furtherHandler ) + // handled the event + return true; + } + + // ask all other handlers + bool handled = false; + for (auto const& mouseFunction : aMouseFunctions) + { + if (handled) + break; + if (mouseFunction == pActiveFunction) + // we already invoked this function + continue; + + switch ( (mouseFunction.get()->*i_handlerMethod)( i_control, i_event ) ) + { + case ActivateFunction: + pActiveFunction = mouseFunction; + handled = true; + break; + case ContinueFunction: + case DeactivateFunction: + OSL_ENSURE( false, "lcl_delegateMouseEvent: unexpected: inactive handler cannot be continued or deactivated!" ); + break; + case SkipFunction: + handled = false; + break; + } + } + return handled; + } + + + bool DefaultInputHandler::MouseMove( ITableControl& i_tableControl, const MouseEvent& i_event ) + { + return delegateMouseEvent( i_tableControl, i_event, &MouseFunction::handleMouseMove ); + } + + + bool DefaultInputHandler::MouseButtonDown( ITableControl& i_tableControl, const MouseEvent& i_event ) + { + return delegateMouseEvent( i_tableControl, i_event, &MouseFunction::handleMouseDown ); + } + + + bool DefaultInputHandler::MouseButtonUp( ITableControl& i_tableControl, const MouseEvent& i_event ) + { + return delegateMouseEvent( i_tableControl, i_event, &MouseFunction::handleMouseUp ); + } + + + bool DefaultInputHandler::KeyInput( ITableControl& _rControl, const KeyEvent& rKEvt ) + { + bool bHandled = false; + + const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); + sal_uInt16 nKeyCode = rKeyCode.GetCode(); + + struct ActionMapEntry + { + sal_uInt16 nKeyCode; + sal_uInt16 nKeyModifier; + TableControlAction eAction; + } + static const aKnownActions[] = { + { KEY_DOWN, 0, cursorDown }, + { KEY_UP, 0, cursorUp }, + { KEY_LEFT, 0, cursorLeft }, + { KEY_RIGHT, 0, cursorRight }, + { KEY_HOME, 0, cursorToLineStart }, + { KEY_END, 0, cursorToLineEnd }, + { KEY_PAGEUP, 0, cursorPageUp }, + { KEY_PAGEDOWN, 0, cursorPageDown }, + { KEY_PAGEUP, KEY_MOD1, cursorToFirstLine }, + { KEY_PAGEDOWN, KEY_MOD1, cursorToLastLine }, + { KEY_HOME, KEY_MOD1, cursorTopLeft }, + { KEY_END, KEY_MOD1, cursorBottomRight }, + { KEY_SPACE, KEY_MOD1, cursorSelectRow }, + { KEY_UP, KEY_SHIFT, cursorSelectRowUp }, + { KEY_DOWN, KEY_SHIFT, cursorSelectRowDown }, + { KEY_END, KEY_SHIFT, cursorSelectRowAreaBottom }, + { KEY_HOME, KEY_SHIFT, cursorSelectRowAreaTop } + }; + for (const ActionMapEntry& rAction : aKnownActions) + { + if ( ( rAction.nKeyCode == nKeyCode ) && ( rAction.nKeyModifier == rKeyCode.GetModifier() ) ) + { + bHandled = _rControl.dispatchAction( rAction.eAction ); + break; + } + } + + return bHandled; + } + + + bool DefaultInputHandler::GetFocus( ITableControl& _rControl ) + { + _rControl.showCursor(); + return false; // continue processing + } + + + bool DefaultInputHandler::LoseFocus( ITableControl& _rControl ) + { + _rControl.hideCursor(); + return false; // continue processing + } + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/gridtablerenderer.cxx b/toolkit/source/controls/table/gridtablerenderer.cxx new file mode 100644 index 0000000000..aa49f4afdd --- /dev/null +++ b/toolkit/source/controls/table/gridtablerenderer.cxx @@ -0,0 +1,597 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "cellvalueconversion.hxx" +#include <controls/table/gridtablerenderer.hxx> +#include <controls/table/tablesort.hxx> + +#include <com/sun/star/graphic/XGraphic.hpp> + +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/window.hxx> +#include <vcl/image.hxx> +#include <vcl/virdev.hxx> +#include <vcl/decoview.hxx> +#include <vcl/settings.hxx> + + +namespace svt::table +{ + using ::css::uno::Any; + using ::css::uno::Reference; + using ::css::uno::UNO_QUERY; + using ::css::uno::XInterface; + using ::css::uno::TypeClass_INTERFACE; + using ::css::graphic::XGraphic; + using ::css::style::HorizontalAlignment; + using ::css::style::HorizontalAlignment_CENTER; + using ::css::style::HorizontalAlignment_RIGHT; + using ::css::style::VerticalAlignment; + using ::css::style::VerticalAlignment_MIDDLE; + using ::css::style::VerticalAlignment_BOTTOM; + + + //= CachedSortIndicator + + namespace { + + class CachedSortIndicator + { + public: + CachedSortIndicator() + : m_lastHeaderHeight( 0 ) + , m_lastArrowColor( COL_TRANSPARENT ) + { + } + + BitmapEx const & getBitmapFor(vcl::RenderContext const & i_device, tools::Long const i_headerHeight, + StyleSettings const & i_style, bool const i_sortAscending); + + private: + tools::Long m_lastHeaderHeight; + Color m_lastArrowColor; + BitmapEx m_sortAscending; + BitmapEx m_sortDescending; + }; + + } + + BitmapEx const & CachedSortIndicator::getBitmapFor(vcl::RenderContext const& i_device, tools::Long const i_headerHeight, + StyleSettings const & i_style, bool const i_sortAscending ) + { + BitmapEx& rBitmap(i_sortAscending ? m_sortAscending : m_sortDescending); + if (rBitmap.IsEmpty() || (i_headerHeight != m_lastHeaderHeight) || (i_style.GetActiveColor() != m_lastArrowColor)) + { + tools::Long const nSortIndicatorWidth = 2 * i_headerHeight / 3; + tools::Long const nSortIndicatorHeight = 2 * nSortIndicatorWidth / 3; + + Point const aBitmapPos( 0, 0 ); + Size const aBitmapSize( nSortIndicatorWidth, nSortIndicatorHeight ); + ScopedVclPtrInstance< VirtualDevice > aDevice(i_device, DeviceFormat::WITH_ALPHA); + aDevice->SetOutputSizePixel( aBitmapSize ); + + DecorationView aDecoView(aDevice.get()); + aDecoView.DrawSymbol(tools::Rectangle(aBitmapPos, aBitmapSize), + i_sortAscending ? SymbolType::SPIN_UP : SymbolType::SPIN_DOWN, + i_style.GetActiveColor()); + + rBitmap = aDevice->GetBitmapEx(aBitmapPos, aBitmapSize); + m_lastHeaderHeight = i_headerHeight; + m_lastArrowColor = i_style.GetActiveColor(); + } + return rBitmap; + } + + + //= GridTableRenderer_Impl + + struct GridTableRenderer_Impl + { + ITableModel& rModel; + RowPos nCurrentRow; + bool bUseGridLines; + CachedSortIndicator aSortIndicator; + CellValueConversion aStringConverter; + + explicit GridTableRenderer_Impl( ITableModel& _rModel ) + : rModel( _rModel ) + , nCurrentRow( ROW_INVALID ) + , bUseGridLines( true ) + , aSortIndicator( ) + , aStringConverter() + { + } + }; + + + //= helper + + namespace + { + tools::Rectangle lcl_getContentArea( GridTableRenderer_Impl const & i_impl, tools::Rectangle const & i_cellArea ) + { + tools::Rectangle aContentArea( i_cellArea ); + if ( i_impl.bUseGridLines ) + { + aContentArea.AdjustRight( -1 ); + aContentArea.AdjustBottom( -1 ); + } + return aContentArea; + } + tools::Rectangle lcl_getTextRenderingArea( tools::Rectangle const & i_contentArea ) + { + tools::Rectangle aTextArea( i_contentArea ); + aTextArea.AdjustLeft(2 ); aTextArea.AdjustRight( -2 ); + aTextArea.AdjustTop( 1 ); aTextArea.AdjustBottom( -1 ); + return aTextArea; + } + + DrawTextFlags lcl_getAlignmentTextDrawFlags( GridTableRenderer_Impl const & i_impl, ColPos const i_columnPos ) + { + DrawTextFlags nVertFlag = DrawTextFlags::Top; + VerticalAlignment const eVertAlign = i_impl.rModel.getVerticalAlign(); + switch ( eVertAlign ) + { + case VerticalAlignment_MIDDLE: nVertFlag = DrawTextFlags::VCenter; break; + case VerticalAlignment_BOTTOM: nVertFlag = DrawTextFlags::Bottom; break; + default: + break; + } + + DrawTextFlags nHorzFlag = DrawTextFlags::Left; + HorizontalAlignment const eHorzAlign = i_impl.rModel.getColumnCount() > 0 + ? i_impl.rModel.getColumnModel( i_columnPos )->getHorizontalAlign() + : HorizontalAlignment_CENTER; + switch ( eHorzAlign ) + { + case HorizontalAlignment_CENTER: nHorzFlag = DrawTextFlags::Center; break; + case HorizontalAlignment_RIGHT: nHorzFlag = DrawTextFlags::Right; break; + default: + break; + } + + return nVertFlag | nHorzFlag; + } + + } + + + //= GridTableRenderer + + + GridTableRenderer::GridTableRenderer( ITableModel& _rModel ) + :m_pImpl( new GridTableRenderer_Impl( _rModel ) ) + { + } + + + GridTableRenderer::~GridTableRenderer() + { + } + + + bool GridTableRenderer::useGridLines() const + { + return m_pImpl->bUseGridLines; + } + + + void GridTableRenderer::useGridLines( bool const i_use ) + { + m_pImpl->bUseGridLines = i_use; + } + + + namespace + { + Color lcl_getEffectiveColor(std::optional<Color> const& i_modelColor, + StyleSettings const& i_styleSettings, + Color const& (StyleSettings::*i_getDefaultColor) () const) + { + if (!!i_modelColor) + return *i_modelColor; + return (i_styleSettings.*i_getDefaultColor)(); + } + } + + + void GridTableRenderer::PaintHeaderArea(vcl::RenderContext& rRenderContext, const tools::Rectangle& _rArea, + bool _bIsColHeaderArea, bool _bIsRowHeaderArea, const StyleSettings& _rStyle) + { + OSL_PRECOND(_bIsColHeaderArea || _bIsRowHeaderArea, "GridTableRenderer::PaintHeaderArea: invalid area flags!"); + + rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR); + + Color const background = lcl_getEffectiveColor(m_pImpl->rModel.getHeaderBackgroundColor(), + _rStyle, &StyleSettings::GetDialogColor); + rRenderContext.SetFillColor(background); + + rRenderContext.SetLineColor(); + rRenderContext.DrawRect(_rArea); + + // delimiter lines at bottom/right + std::optional<Color> aLineColor(m_pImpl->rModel.getLineColor()); + Color const lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + rRenderContext.SetLineColor(lineColor); + rRenderContext.DrawLine(_rArea.BottomLeft(), _rArea.BottomRight()); + rRenderContext.DrawLine(_rArea.BottomRight(), _rArea.TopRight()); + + rRenderContext.Pop(); + } + + + void GridTableRenderer::PaintColumnHeader( + ColPos _nCol, + vcl::RenderContext& rRenderContext, + const tools::Rectangle& _rArea, const StyleSettings& _rStyle) + { + rRenderContext.Push(vcl::PushFlags::LINECOLOR); + + OUString sHeaderText; + PColumnModel const pColumn = m_pImpl->rModel.getColumnModel( _nCol ); + DBG_ASSERT( pColumn, "GridTableRenderer::PaintColumnHeader: invalid column model object!" ); + if ( pColumn ) + sHeaderText = pColumn->getName(); + + Color const textColor = lcl_getEffectiveColor( m_pImpl->rModel.getTextColor(), _rStyle, &StyleSettings::GetFieldTextColor ); + rRenderContext.SetTextColor(textColor); + + tools::Rectangle const aTextRect( lcl_getTextRenderingArea( lcl_getContentArea( *m_pImpl, _rArea ) ) ); + DrawTextFlags nDrawTextFlags = lcl_getAlignmentTextDrawFlags( *m_pImpl, _nCol ) | DrawTextFlags::Clip; + if (!m_pImpl->rModel.isEnabled()) + nDrawTextFlags |= DrawTextFlags::Disable; + rRenderContext.DrawText( aTextRect, sHeaderText, nDrawTextFlags ); + + std::optional<Color> const aLineColor( m_pImpl->rModel.getLineColor() ); + Color const lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + rRenderContext.SetLineColor( lineColor ); + rRenderContext.DrawLine( _rArea.BottomRight(), _rArea.TopRight()); + rRenderContext.DrawLine( _rArea.BottomLeft(), _rArea.BottomRight() ); + + // draw sort indicator if the model data is sorted by the given column + ITableDataSort const * pSortAdapter = m_pImpl->rModel.getSortAdapter(); + ColumnSort aCurrentSortOrder; + if ( pSortAdapter != nullptr ) + aCurrentSortOrder = pSortAdapter->getCurrentSortOrder(); + if ( aCurrentSortOrder.nColumnPos == _nCol ) + { + tools::Long const nHeaderHeight( _rArea.GetHeight() ); + BitmapEx const aIndicatorBitmap = m_pImpl->aSortIndicator.getBitmapFor(rRenderContext, nHeaderHeight, _rStyle, + aCurrentSortOrder.eSortDirection == ColumnSortAscending); + Size const aBitmapSize( aIndicatorBitmap.GetSizePixel() ); + tools::Long const nSortIndicatorPaddingX = 2; + tools::Long const nSortIndicatorPaddingY = ( nHeaderHeight - aBitmapSize.Height() ) / 2; + + if ( nDrawTextFlags & DrawTextFlags::Right ) + { + // text is right aligned => draw the sort indicator at the left hand side + rRenderContext.DrawBitmapEx(Point(_rArea.Left() + nSortIndicatorPaddingX, _rArea.Top() + nSortIndicatorPaddingY), + aIndicatorBitmap); + } + else + { + // text is left-aligned or centered => draw the sort indicator at the right hand side + rRenderContext.DrawBitmapEx(Point(_rArea.Right() - nSortIndicatorPaddingX - aBitmapSize.Width(), nSortIndicatorPaddingY), + aIndicatorBitmap); + } + } + + rRenderContext.Pop(); + } + + + void GridTableRenderer::PrepareRow(RowPos _nRow, bool i_hasControlFocus, bool _bSelected, vcl::RenderContext& rRenderContext, + const tools::Rectangle& _rRowArea, const StyleSettings& _rStyle) + { + // remember the row for subsequent calls to the other ->ITableRenderer methods + m_pImpl->nCurrentRow = _nRow; + + rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR); + + Color backgroundColor = _rStyle.GetFieldColor(); + + Color const activeSelectionBackColor = lcl_getEffectiveColor(m_pImpl->rModel.getActiveSelectionBackColor(), + _rStyle, &StyleSettings::GetHighlightColor); + if (_bSelected) + { + // selected rows use the background color from the style + backgroundColor = i_hasControlFocus + ? activeSelectionBackColor + : lcl_getEffectiveColor(m_pImpl->rModel.getInactiveSelectionBackColor(), _rStyle, &StyleSettings::GetDeactiveColor); + } + else + { + std::optional< std::vector<Color> > aRowColors = m_pImpl->rModel.getRowBackgroundColors(); + if (!aRowColors) + { + // use alternating default colors + Color const fieldColor = _rStyle.GetFieldColor(); + if (_rStyle.GetHighContrastMode() || ((m_pImpl->nCurrentRow % 2) == 0)) + { + backgroundColor = fieldColor; + } + else + { + Color hilightColor = activeSelectionBackColor; + hilightColor.SetRed( 9 * ( fieldColor.GetRed() - hilightColor.GetRed() ) / 10 + hilightColor.GetRed() ); + hilightColor.SetGreen( 9 * ( fieldColor.GetGreen() - hilightColor.GetGreen() ) / 10 + hilightColor.GetGreen() ); + hilightColor.SetBlue( 9 * ( fieldColor.GetBlue() - hilightColor.GetBlue() ) / 10 + hilightColor.GetBlue() ); + backgroundColor = hilightColor; + } + } + else + { + if (aRowColors->empty()) + { + // all colors have the same background color + backgroundColor = _rStyle.GetFieldColor(); + } + else + { + backgroundColor = aRowColors->at(m_pImpl->nCurrentRow % aRowColors->size()); + } + } + } + + rRenderContext.SetLineColor(); + rRenderContext.SetFillColor(backgroundColor); + rRenderContext.DrawRect(_rRowArea); + + rRenderContext.Pop(); + } + + + void GridTableRenderer::PaintRowHeader(vcl::RenderContext& rRenderContext, + const tools::Rectangle& _rArea, const StyleSettings& _rStyle) + { + rRenderContext.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::TEXTCOLOR ); + + std::optional<Color> const aLineColor( m_pImpl->rModel.getLineColor() ); + Color const lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + rRenderContext.SetLineColor(lineColor); + rRenderContext.DrawLine(_rArea.BottomLeft(), _rArea.BottomRight()); + + Any const rowHeading( m_pImpl->rModel.getRowHeading( m_pImpl->nCurrentRow ) ); + OUString const rowTitle( m_pImpl->aStringConverter.convertToString( rowHeading ) ); + if (!rowTitle.isEmpty()) + { + Color const textColor = lcl_getEffectiveColor(m_pImpl->rModel.getHeaderTextColor(), + _rStyle, &StyleSettings::GetFieldTextColor); + rRenderContext.SetTextColor(textColor); + + tools::Rectangle const aTextRect(lcl_getTextRenderingArea(lcl_getContentArea(*m_pImpl, _rArea))); + DrawTextFlags nDrawTextFlags = lcl_getAlignmentTextDrawFlags(*m_pImpl, 0) | DrawTextFlags::Clip; + if (!m_pImpl->rModel.isEnabled()) + nDrawTextFlags |= DrawTextFlags::Disable; + // TODO: is using the horizontal alignment of the 0'th column a good idea here? This is pretty ... arbitrary .. + rRenderContext.DrawText(aTextRect, rowTitle, nDrawTextFlags); + } + + rRenderContext.Pop(); + } + + + struct GridTableRenderer::CellRenderContext + { + OutputDevice& rDevice; + tools::Rectangle const aContentArea; + StyleSettings const & rStyle; + ColPos const nColumn; + bool const bSelected; + bool const bHasControlFocus; + + CellRenderContext( OutputDevice& i_device, tools::Rectangle const & i_contentArea, + StyleSettings const & i_style, ColPos const i_column, bool const i_selected, bool const i_hasControlFocus ) + :rDevice( i_device ) + ,aContentArea( i_contentArea ) + ,rStyle( i_style ) + ,nColumn( i_column ) + ,bSelected( i_selected ) + ,bHasControlFocus( i_hasControlFocus ) + { + } + }; + + + void GridTableRenderer::PaintCell(ColPos const i_column, bool _bSelected, bool i_hasControlFocus, + vcl::RenderContext& rRenderContext, const tools::Rectangle& _rArea, const StyleSettings& _rStyle) + { + rRenderContext.Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR); + + tools::Rectangle const aContentArea(lcl_getContentArea(*m_pImpl, _rArea)); + CellRenderContext const aCellRenderContext(rRenderContext, aContentArea, _rStyle, i_column, _bSelected, i_hasControlFocus); + impl_paintCellContent(aCellRenderContext); + + if ( m_pImpl->bUseGridLines ) + { + ::std::optional< ::Color > aLineColor( m_pImpl->rModel.getLineColor() ); + ::Color lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + + if ( _bSelected && !aLineColor ) + { + // if no line color is specified by the model, use the usual selection color for lines in selected cells + lineColor = i_hasControlFocus + ? lcl_getEffectiveColor( m_pImpl->rModel.getActiveSelectionBackColor(), _rStyle, &StyleSettings::GetHighlightColor ) + : lcl_getEffectiveColor( m_pImpl->rModel.getInactiveSelectionBackColor(), _rStyle, &StyleSettings::GetDeactiveColor ); + } + + rRenderContext.SetLineColor( lineColor ); + rRenderContext.DrawLine( _rArea.BottomLeft(), _rArea.BottomRight() ); + rRenderContext.DrawLine( _rArea.BottomRight(), _rArea.TopRight() ); + } + + rRenderContext.Pop(); + } + + + void GridTableRenderer::impl_paintCellImage( CellRenderContext const & i_context, Image const & i_image ) + { + Point imagePos( i_context.aContentArea.Left(), i_context.aContentArea.Top() ); + Size imageSize = i_image.GetSizePixel(); + if ( i_context.aContentArea.GetWidth() > imageSize.Width() ) + { + const HorizontalAlignment eHorzAlign = m_pImpl->rModel.getColumnModel( i_context.nColumn )->getHorizontalAlign(); + switch ( eHorzAlign ) + { + case HorizontalAlignment_CENTER: + imagePos.AdjustX(( i_context.aContentArea.GetWidth() - imageSize.Width() ) / 2 ); + break; + case HorizontalAlignment_RIGHT: + imagePos.setX( i_context.aContentArea.Right() - imageSize.Width() ); + break; + default: + break; + } + + } + else + imageSize.setWidth( i_context.aContentArea.GetWidth() ); + + if ( i_context.aContentArea.GetHeight() > imageSize.Height() ) + { + const VerticalAlignment eVertAlign = m_pImpl->rModel.getVerticalAlign(); + switch ( eVertAlign ) + { + case VerticalAlignment_MIDDLE: + imagePos.AdjustY(( i_context.aContentArea.GetHeight() - imageSize.Height() ) / 2 ); + break; + case VerticalAlignment_BOTTOM: + imagePos.setY( i_context.aContentArea.Bottom() - imageSize.Height() ); + break; + default: + break; + } + } + else + imageSize.setHeight( i_context.aContentArea.GetHeight() - 1 ); + DrawImageFlags const nStyle = m_pImpl->rModel.isEnabled() ? DrawImageFlags::NONE : DrawImageFlags::Disable; + i_context.rDevice.DrawImage( imagePos, imageSize, i_image, nStyle ); + } + + + void GridTableRenderer::impl_paintCellContent( CellRenderContext const & i_context ) + { + Any aCellContent; + m_pImpl->rModel.getCellContent( i_context.nColumn, m_pImpl->nCurrentRow, aCellContent ); + + if ( aCellContent.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference< XInterface > const xContentInterface( aCellContent, UNO_QUERY ); + if ( !xContentInterface.is() ) + // allowed. kind of. + return; + + Reference< XGraphic > const xGraphic( aCellContent, UNO_QUERY ); + ENSURE_OR_RETURN_VOID( xGraphic.is(), "GridTableRenderer::impl_paintCellContent: only XGraphic interfaces (or NULL) are supported for painting." ); + + const Image aImage( xGraphic ); + impl_paintCellImage( i_context, aImage ); + return; + } + + const OUString sText( m_pImpl->aStringConverter.convertToString( aCellContent ) ); + impl_paintCellText( i_context, sText ); + } + + + void GridTableRenderer::impl_paintCellText( CellRenderContext const & i_context, OUString const & i_text ) + { + if ( i_context.bSelected ) + { + ::Color const textColor = i_context.bHasControlFocus + ? lcl_getEffectiveColor( m_pImpl->rModel.getActiveSelectionTextColor(), i_context.rStyle, &StyleSettings::GetHighlightTextColor ) + : lcl_getEffectiveColor( m_pImpl->rModel.getInactiveSelectionTextColor(), i_context.rStyle, &StyleSettings::GetDeactiveTextColor ); + i_context.rDevice.SetTextColor( textColor ); + } + else + { + ::Color const textColor = lcl_getEffectiveColor( m_pImpl->rModel.getTextColor(), i_context.rStyle, &StyleSettings::GetFieldTextColor ); + i_context.rDevice.SetTextColor( textColor ); + } + + tools::Rectangle const textRect( lcl_getTextRenderingArea( i_context.aContentArea ) ); + DrawTextFlags nDrawTextFlags = lcl_getAlignmentTextDrawFlags( *m_pImpl, i_context.nColumn ) | DrawTextFlags::Clip; + if ( !m_pImpl->rModel.isEnabled() ) + nDrawTextFlags |= DrawTextFlags::Disable; + i_context.rDevice.DrawText( textRect, i_text, nDrawTextFlags ); + } + + + void GridTableRenderer::ShowCellCursor( vcl::Window& _rView, const tools::Rectangle& _rCursorRect) + { + _rView.ShowFocus( _rCursorRect ); + } + + + void GridTableRenderer::HideCellCursor( vcl::Window& _rView ) + { + _rView.HideFocus(); + } + + + bool GridTableRenderer::FitsIntoCell( Any const & i_cellContent, + OutputDevice& i_targetDevice, tools::Rectangle const & i_targetArea ) const + { + if ( !i_cellContent.hasValue() ) + return true; + + if ( i_cellContent.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference< XInterface > const xContentInterface( i_cellContent, UNO_QUERY ); + if ( !xContentInterface.is() ) + return true; + + Reference< XGraphic > const xGraphic( i_cellContent, UNO_QUERY ); + if ( xGraphic.is() ) + // for the moment, assume it fits. We can always scale it down during painting ... + return true; + + OSL_ENSURE( false, "GridTableRenderer::FitsIntoCell: only XGraphic interfaces (or NULL) are supported for painting." ); + return true; + } + + OUString const sText( m_pImpl->aStringConverter.convertToString( i_cellContent ) ); + if ( sText.isEmpty() ) + return true; + + tools::Rectangle const aTargetArea( lcl_getTextRenderingArea( lcl_getContentArea( *m_pImpl, i_targetArea ) ) ); + + tools::Long const nTextHeight = i_targetDevice.GetTextHeight(); + if ( nTextHeight > aTargetArea.GetHeight() ) + return false; + + tools::Long const nTextWidth = i_targetDevice.GetTextWidth( sText ); + return nTextWidth <= aTargetArea.GetWidth(); + } + + + bool GridTableRenderer::GetFormattedCellString( Any const & i_cellValue, OUString & o_cellString ) const + { + o_cellString = m_pImpl->aStringConverter.convertToString( i_cellValue ); + + return true; + } + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/mousefunction.cxx b/toolkit/source/controls/table/mousefunction.cxx new file mode 100644 index 0000000000..d0a784fdf1 --- /dev/null +++ b/toolkit/source/controls/table/mousefunction.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 <controls/table/mousefunction.hxx> +#include <controls/table/tablecontrolinterface.hxx> +#include <controls/table/tablesort.hxx> + +#include <comphelper/diagnose_ex.hxx> +#include <vcl/ptrstyle.hxx> + +namespace svt::table +{ + + + //= ColumnResize + + + FunctionResult ColumnResize::handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + Point const aPoint = i_event.GetPosPixel(); + + if ( m_nResizingColumn == COL_INVALID ) + { + // if we hit a column divider, change the mouse pointer accordingly + PointerStyle aNewPointer( PointerStyle::Arrow ); + TableCell const tableCell = i_tableControl.hitTest( aPoint ); + if ( ( tableCell.nRow == ROW_COL_HEADERS ) && ( tableCell.eArea == ColumnDivider ) ) + { + aNewPointer = PointerStyle::HSplit; + } + i_tableControl.setPointer( aNewPointer ); + + return SkipFunction; // TODO: is this correct? + } + + ::Size const tableSize = i_tableControl.getTableSizePixel(); + + // set proper pointer + PointerStyle aNewPointer( PointerStyle::Arrow ); + ColumnMetrics const & columnMetrics( i_tableControl.getColumnMetrics( m_nResizingColumn ) ); + if ( ( aPoint.X() > tableSize.Width() ) + || ( aPoint.X() < columnMetrics.nStartPixel ) + ) + { + aNewPointer = PointerStyle::NotAllowed; + } + else + { + aNewPointer = PointerStyle::HSplit; + } + i_tableControl.setPointer( aNewPointer ); + + // show tracking line + i_tableControl.hideTracking(); + i_tableControl.showTracking( + tools::Rectangle( + Point( aPoint.X(), 0 ), + Size( 1, tableSize.Height() ) + ), + ShowTrackFlags::Split | ShowTrackFlags::TrackWindow + ); + + return ContinueFunction; + } + + + FunctionResult ColumnResize::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nResizingColumn != COL_INVALID ) + { + OSL_ENSURE( false, "ColumnResize::handleMouseDown: suspicious: MouseButtonDown while still tracking?" ); + return ContinueFunction; + } + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( tableCell.nRow == ROW_COL_HEADERS ) + { + if ( ( tableCell.nColumn != COL_INVALID ) + && ( tableCell.eArea == ColumnDivider ) + ) + { + m_nResizingColumn = tableCell.nColumn; + i_tableControl.captureMouse(); + return ActivateFunction; + } + } + + return SkipFunction; + } + + + FunctionResult ColumnResize::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nResizingColumn == COL_INVALID ) + return SkipFunction; + + Point const aPoint = i_event.GetPosPixel(); + + i_tableControl.hideTracking(); + PColumnModel const pColumn = i_tableControl.getModel()->getColumnModel( m_nResizingColumn ); + tools::Long const maxWidthLogical = pColumn->getMaxWidth(); + tools::Long const minWidthLogical = pColumn->getMinWidth(); + + // new position of mouse + tools::Long const requestedEnd = aPoint.X(); + + // old position of right border + tools::Long const oldEnd = i_tableControl.getColumnMetrics( m_nResizingColumn ).nEndPixel; + + // position of left border if cursor in the to-be-resized column + tools::Long const columnStart = i_tableControl.getColumnMetrics( m_nResizingColumn ).nStartPixel; + tools::Long const requestedWidth = requestedEnd - columnStart; + // TODO: this is not correct, strictly: It assumes that the mouse was pressed exactly on the "end" pos, + // but for a while now, we have relaxed this, and allow clicking a few pixels aside, too + + if ( requestedEnd >= columnStart ) + { + tools::Long requestedWidthLogical = i_tableControl.pixelWidthToAppFont( requestedWidth ); + // respect column width limits + if ( oldEnd > requestedEnd ) + { + // column has become smaller, check against minimum width + if ( ( minWidthLogical != 0 ) && ( requestedWidthLogical < minWidthLogical ) ) + requestedWidthLogical = minWidthLogical; + } + else if ( oldEnd < requestedEnd ) + { + // column has become larger, check against max width + if ( ( maxWidthLogical != 0 ) && ( requestedWidthLogical >= maxWidthLogical ) ) + requestedWidthLogical = maxWidthLogical; + } + pColumn->setWidth( requestedWidthLogical ); + i_tableControl.invalidate( TableArea::All ); + } + + i_tableControl.setPointer( PointerStyle::Arrow ); + i_tableControl.releaseMouse(); + + m_nResizingColumn = COL_INVALID; + return DeactivateFunction; + } + + + //= RowSelection + + + FunctionResult RowSelection::handleMouseMove( ITableControl&, MouseEvent const & ) + { + return SkipFunction; + } + + + FunctionResult RowSelection::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + bool handled = false; + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( tableCell.nRow >= 0 ) + { + if ( i_tableControl.getSelEngine()->GetSelectionMode() == SelectionMode::NONE ) + { + i_tableControl.activateCell( tableCell.nColumn, tableCell.nRow ); + handled = true; + } + else + { + handled = i_tableControl.getSelEngine()->SelMouseButtonDown( i_event ); + } + } + + if ( handled ) + m_bActive = true; + return handled ? ActivateFunction : SkipFunction; + } + + + FunctionResult RowSelection::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + TableCell const tableCell = i_tableControl.hitTest( i_event.GetPosPixel() ); + if ( tableCell.nRow >= 0 ) + { + if ( i_tableControl.getSelEngine()->GetSelectionMode() != SelectionMode::NONE ) + { + i_tableControl.getSelEngine()->SelMouseButtonUp( i_event ); + } + } + if ( m_bActive ) + { + m_bActive = false; + return DeactivateFunction; + } + return SkipFunction; + } + + + //= ColumnSortHandler + + + FunctionResult ColumnSortHandler::handleMouseMove( ITableControl&, MouseEvent const & ) + { + return SkipFunction; + } + + + FunctionResult ColumnSortHandler::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nActiveColumn != COL_INVALID ) + { + OSL_ENSURE( false, "ColumnSortHandler::handleMouseDown: called while already active - suspicious!" ); + return ContinueFunction; + } + + if ( i_tableControl.getModel()->getSortAdapter() == nullptr ) + // no sorting support at the model + return SkipFunction; + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( ( tableCell.nRow != ROW_COL_HEADERS ) || ( tableCell.nColumn < 0 ) ) + return SkipFunction; + + // TODO: ensure the column header is rendered in some special way, indicating its current state + + m_nActiveColumn = tableCell.nColumn; + return ActivateFunction; + } + + + FunctionResult ColumnSortHandler::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nActiveColumn == COL_INVALID ) + return SkipFunction; + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( ( tableCell.nRow == ROW_COL_HEADERS ) && ( tableCell.nColumn == m_nActiveColumn ) ) + { + ITableDataSort* pSort = i_tableControl.getModel()->getSortAdapter(); + ENSURE_OR_RETURN( pSort != nullptr, "ColumnSortHandler::handleMouseUp: somebody is mocking with us!", DeactivateFunction ); + // in handleMousButtonDown, the model claimed to have sort support ... + + ColumnSortDirection eSortDirection = ColumnSortAscending; + ColumnSort const aCurrentSort = pSort->getCurrentSortOrder(); + if ( aCurrentSort.nColumnPos == m_nActiveColumn ) + // invert existing sort order + eSortDirection = ( aCurrentSort.eSortDirection == ColumnSortAscending ) ? ColumnSortDescending : ColumnSortAscending; + + pSort->sortByColumn( m_nActiveColumn, eSortDirection ); + } + + m_nActiveColumn = COL_INVALID; + return DeactivateFunction; + } + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tablecontrol.cxx b/toolkit/source/controls/table/tablecontrol.cxx new file mode 100644 index 0000000000..42b314569e --- /dev/null +++ b/toolkit/source/controls/table/tablecontrol.cxx @@ -0,0 +1,642 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/table/tablecontrol.hxx> + +#include "tablecontrol_impl.hxx" +#include "tabledatawindow.hxx" + +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> + +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/settings.hxx> +#include <vcl/vclevent.hxx> + +using namespace ::com::sun::star::uno; +using ::com::sun::star::accessibility::XAccessible; +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::lang; + +namespace svt::table +{ + + + namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId; + + + //= TableControl + + + TableControl::TableControl( vcl::Window* _pParent, WinBits _nStyle ) + :Control( _pParent, _nStyle ) + ,m_pImpl( std::make_shared<TableControl_Impl>( *this ) ) + { + TableDataWindow& rDataWindow = m_pImpl->getDataWindow(); + rDataWindow.SetSelectHdl( LINK( this, TableControl, ImplSelectHdl ) ); + + // by default, use the background as determined by the style settings + const Color aWindowColor( GetSettings().GetStyleSettings().GetFieldColor() ); + SetBackground( Wallpaper( aWindowColor ) ); + GetOutDev()->SetFillColor( aWindowColor ); + + SetCompoundControl( true ); + } + + + TableControl::~TableControl() + { + disposeOnce(); + } + + void TableControl::dispose() + { + CallEventListeners( VclEventId::ObjectDying ); + + m_pImpl->setModel( PTableModel() ); + m_pImpl->disposeAccessible(); + m_pImpl.reset(); + Control::dispose(); + } + + + void TableControl::GetFocus() + { + if ( !m_pImpl || !m_pImpl->getInputHandler()->GetFocus( *m_pImpl ) ) + Control::GetFocus(); + } + + + void TableControl::LoseFocus() + { + if ( !m_pImpl || !m_pImpl->getInputHandler()->LoseFocus( *m_pImpl ) ) + Control::LoseFocus(); + } + + + void TableControl::KeyInput( const KeyEvent& rKEvt ) + { + if ( !m_pImpl->getInputHandler()->KeyInput( *m_pImpl, rKEvt ) ) + Control::KeyInput( rKEvt ); + else + { + if ( m_pImpl->isAccessibleAlive() ) + { + m_pImpl->commitCellEvent( AccessibleEventId::STATE_CHANGED, + Any( AccessibleStateType::FOCUSED ), + Any() + ); + // Huh? What the heck? Why do we unconditionally notify a STATE_CHANGE/FOCUSED after each and every + // (handled) key stroke? + + m_pImpl->commitTableEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, + Any(), + Any() + ); + // ditto: Why do we notify this unconditionally? We should find the right place to notify the + // ACTIVE_DESCENDANT_CHANGED event. + // Also, we should check if STATE_CHANGED/FOCUSED is really necessary: finally, the children are + // transient, aren't they? + } + } + } + + + void TableControl::StateChanged( StateChangedType i_nStateChange ) + { + Control::StateChanged( i_nStateChange ); + + // forward certain settings to the data window + switch ( i_nStateChange ) + { + case StateChangedType::ControlFocus: + m_pImpl->invalidateSelectedRows(); + break; + + case StateChangedType::ControlBackground: + if ( IsControlBackground() ) + getDataWindow().SetControlBackground( GetControlBackground() ); + else + getDataWindow().SetControlBackground(); + break; + + case StateChangedType::ControlForeground: + if ( IsControlForeground() ) + getDataWindow().SetControlForeground( GetControlForeground() ); + else + getDataWindow().SetControlForeground(); + break; + + case StateChangedType::ControlFont: + if ( IsControlFont() ) + getDataWindow().SetControlFont( GetControlFont() ); + else + getDataWindow().SetControlFont(); + break; + default:; + } + } + + + void TableControl::Resize() + { + Control::Resize(); + m_pImpl->onResize(); + } + + + void TableControl::SetModel( const PTableModel& _pModel ) + { + m_pImpl->setModel( _pModel ); + } + + + PTableModel TableControl::GetModel() const + { + return m_pImpl->getModel(); + } + + + sal_Int32 TableControl::GetCurrentRow() const + { + return m_pImpl->getCurrentRow(); + } + + + sal_Int32 TableControl::GetCurrentColumn() const + { + return m_pImpl->getCurrentColumn(); + } + + + void TableControl::GoTo( ColPos _nColumn, RowPos _nRow ) + { + m_pImpl->goTo( _nColumn, _nRow ); + } + + + void TableControl::GoToCell(sal_Int32 _nColPos, sal_Int32 _nRowPos) + { + m_pImpl->goTo( _nColPos, _nRowPos ); + } + + + sal_Int32 TableControl::GetSelectedRowCount() const + { + return sal_Int32( m_pImpl->getSelectedRowCount() ); + } + + + sal_Int32 TableControl::GetSelectedRowIndex( sal_Int32 const i_selectionIndex ) const + { + return m_pImpl->getSelectedRowIndex( i_selectionIndex ); + } + + + bool TableControl::IsRowSelected( sal_Int32 const i_rowIndex ) const + { + return m_pImpl->isRowSelected( i_rowIndex ); + } + + + void TableControl::SelectRow( sal_Int32 const i_rowIndex, bool const i_select ) + { + ENSURE_OR_RETURN_VOID( ( i_rowIndex >= 0 ) && ( i_rowIndex < m_pImpl->getModel()->getRowCount() ), + "TableControl::SelectRow: invalid row index!" ); + + if ( i_select ) + { + if ( !m_pImpl->markRowAsSelected( i_rowIndex ) ) + // nothing to do + return; + } + else + { + m_pImpl->markRowAsDeselected( i_rowIndex ); + } + + m_pImpl->invalidateRowRange( i_rowIndex, i_rowIndex ); + Select(); + } + + + void TableControl::SelectAllRows( bool const i_select ) + { + if ( i_select ) + { + if ( !m_pImpl->markAllRowsAsSelected() ) + // nothing to do + return; + } + else + { + if ( !m_pImpl->markAllRowsAsDeselected() ) + // nothing to do + return; + } + + + Invalidate(); + // TODO: can't we do better than this, and invalidate only the rows which changed? + Select(); + } + + + ITableControl& TableControl::getTableControlInterface() + { + return *m_pImpl; + } + + + SelectionEngine* TableControl::getSelEngine() + { + return m_pImpl->getSelEngine(); + } + + + vcl::Window& TableControl::getDataWindow() + { + return m_pImpl->getDataWindow(); + } + + + Reference< XAccessible > TableControl::CreateAccessible() + { + vcl::Window* pParent = GetAccessibleParentWindow(); + ENSURE_OR_RETURN( pParent, "TableControl::CreateAccessible - parent not found", nullptr ); + + return m_pImpl->getAccessible( *pParent ); + } + + + Reference<XAccessible> TableControl::CreateAccessibleControl( sal_Int32 ) + { + SAL_WARN( "svtools", "TableControl::CreateAccessibleControl: to be overwritten!" ); + return nullptr; + } + + + OUString TableControl::GetAccessibleObjectName( vcl::table::AccessibleTableControlObjType eObjType, sal_Int32 _nRow, sal_Int32 _nCol) const + { + OUString aRetText; + //Window* pWin; + switch( eObjType ) + { + case vcl::table::AccessibleTableControlObjType::GRIDCONTROL: + aRetText = "Grid control"; + break; + case vcl::table::AccessibleTableControlObjType::TABLE: + aRetText = "Grid control"; + break; + case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR: + aRetText = "RowHeaderBar"; + break; + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR: + aRetText = "ColumnHeaderBar"; + break; + case vcl::table::AccessibleTableControlObjType::TABLECELL: + //the name of the cell consists of column name and row name if defined + //if the name is equal to cell content, it'll be read twice + if(GetModel()->hasColumnHeaders()) + { + aRetText = GetColumnName(_nCol) + " , "; + } + if(GetModel()->hasRowHeaders()) + { + aRetText += GetRowName(_nRow) + " , "; + } + //aRetText = GetAccessibleCellText(_nRow, _nCol); + break; + case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL: + aRetText = GetRowName(_nRow); + break; + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL: + aRetText = GetColumnName(_nCol); + break; + default: + OSL_FAIL("GridControl::GetAccessibleName: invalid enum!"); + } + return aRetText; + } + + + OUString TableControl::GetAccessibleObjectDescription( vcl::table::AccessibleTableControlObjType eObjType ) const + { + OUString aRetText; + switch( eObjType ) + { + case vcl::table::AccessibleTableControlObjType::GRIDCONTROL: + aRetText = "Grid control description"; + break; + case vcl::table::AccessibleTableControlObjType::TABLE: + aRetText = "TABLE description"; + break; + case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR: + aRetText = "ROWHEADERBAR description"; + break; + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR: + aRetText = "COLUMNHEADERBAR description"; + break; + case vcl::table::AccessibleTableControlObjType::TABLECELL: + // the description of the cell consists of column name and row name if defined + // if the name is equal to cell content, it'll be read twice + if ( GetModel()->hasColumnHeaders() ) + { + aRetText = GetColumnName( GetCurrentColumn() ) + " , "; + } + if ( GetModel()->hasRowHeaders() ) + { + aRetText += GetRowName( GetCurrentRow() ); + } + break; + case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL: + aRetText = "ROWHEADERCELL description"; + break; + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL: + aRetText = "COLUMNHEADERCELL description"; + break; + } + return aRetText; + } + + + OUString TableControl::GetRowName( sal_Int32 _nIndex) const + { + OUString sRowName; + GetModel()->getRowHeading( _nIndex ) >>= sRowName; + return sRowName; + } + + + OUString TableControl::GetColumnName( sal_Int32 _nIndex) const + { + return GetModel()->getColumnModel(_nIndex)->getName(); + } + + + OUString TableControl::GetAccessibleCellText( sal_Int32 _nRowPos, sal_Int32 _nColPos) const + { + return m_pImpl->getCellContentAsString( _nRowPos, _nColPos ); + } + + + void TableControl::FillAccessibleStateSet( + sal_Int64& rStateSet, + vcl::table::AccessibleTableControlObjType eObjType ) const + { + switch( eObjType ) + { + case vcl::table::AccessibleTableControlObjType::GRIDCONTROL: + case vcl::table::AccessibleTableControlObjType::TABLE: + + rStateSet |= AccessibleStateType::FOCUSABLE; + + if ( m_pImpl->getSelEngine()->GetSelectionMode() == SelectionMode::Multiple ) + rStateSet |= AccessibleStateType::MULTI_SELECTABLE; + + if ( HasChildPathFocus() ) + rStateSet |= AccessibleStateType::FOCUSED; + + if ( IsActive() ) + rStateSet |= AccessibleStateType::ACTIVE; + + if ( m_pImpl->getDataWindow().IsEnabled() ) + { + rStateSet |= AccessibleStateType::ENABLED; + rStateSet |= AccessibleStateType::SENSITIVE; + } + + if ( IsReallyVisible() ) + rStateSet |= AccessibleStateType::VISIBLE; + + if ( eObjType == vcl::table::AccessibleTableControlObjType::TABLE ) + rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS; + break; + + case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR: + rStateSet |= AccessibleStateType::VISIBLE; + rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS; + break; + + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR: + rStateSet |= AccessibleStateType::VISIBLE; + rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS; + break; + + case vcl::table::AccessibleTableControlObjType::TABLECELL: + { + rStateSet |= AccessibleStateType::FOCUSABLE; + if ( HasChildPathFocus() ) + rStateSet |= AccessibleStateType::FOCUSED; + rStateSet |= AccessibleStateType::ACTIVE; + rStateSet |= AccessibleStateType::TRANSIENT; + rStateSet |= AccessibleStateType::SELECTABLE; + rStateSet |= AccessibleStateType::VISIBLE; + rStateSet |= AccessibleStateType::SHOWING; + if ( IsRowSelected( GetCurrentRow() ) ) + // Hmm? Wouldn't we expect the affected row to be a parameter to this function? + rStateSet |= AccessibleStateType::SELECTED; + } + break; + + case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL: + rStateSet |= AccessibleStateType::VISIBLE; + rStateSet |= AccessibleStateType::TRANSIENT; + break; + + case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL: + rStateSet |= AccessibleStateType::VISIBLE; + break; + } + } + + void TableControl::commitCellEventIfAccessibleAlive( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) + { + if ( m_pImpl->isAccessibleAlive() ) + m_pImpl->commitCellEvent( i_eventID, i_newValue, i_oldValue ); + } + + void TableControl::commitTableEventIfAccessibleAlive( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) + { + if ( m_pImpl->isAccessibleAlive() ) + m_pImpl->commitTableEvent( i_eventID, i_newValue, i_oldValue ); + } + + AbsoluteScreenPixelRectangle TableControl::GetWindowExtentsAbsolute() const + { + return Control::GetWindowExtentsAbsolute(); + } + + tools::Rectangle TableControl::GetWindowExtentsRelative(const vcl::Window& rRelativeWindow) const + { + return Control::GetWindowExtentsRelative( rRelativeWindow ); + } + + void TableControl::GrabFocus() + { + Control::GrabFocus(); + } + + Reference< XAccessible > TableControl::GetAccessible() + { + return Control::GetAccessible(); + } + + vcl::Window* TableControl::GetAccessibleParentWindow() const + { + return Control::GetAccessibleParentWindow(); + } + + vcl::Window* TableControl::GetWindowInstance() + { + return this; + } + + + bool TableControl::HasRowHeader() + { + return GetModel()->hasRowHeaders(); + } + + + bool TableControl::HasColHeader() + { + return GetModel()->hasColumnHeaders(); + } + + + sal_Int32 TableControl::GetAccessibleControlCount() const + { + // TC_TABLE is always defined, no matter whether empty or not + sal_Int32 count = 1; + if ( GetModel()->hasRowHeaders() ) + ++count; + if ( GetModel()->hasColumnHeaders() ) + ++count; + return count; + } + + + bool TableControl::ConvertPointToControlIndex( sal_Int32& _rnIndex, const Point& _rPoint ) + { + sal_Int32 nRow = m_pImpl->getRowAtPoint( _rPoint ); + sal_Int32 nCol = m_pImpl->getColAtPoint( _rPoint ); + _rnIndex = nRow * GetColumnCount() + nCol; + return nRow >= 0; + } + + + sal_Int32 TableControl::GetRowCount() const + { + return GetModel()->getRowCount(); + } + + + sal_Int32 TableControl::GetColumnCount() const + { + return GetModel()->getColumnCount(); + } + + + bool TableControl::ConvertPointToCellAddress( sal_Int32& _rnRow, sal_Int32& _rnColPos, const Point& _rPoint ) + { + _rnRow = m_pImpl->getRowAtPoint( _rPoint ); + _rnColPos = m_pImpl->getColAtPoint( _rPoint ); + return _rnRow >= 0; + } + + + void TableControl::FillAccessibleStateSetForCell( sal_Int64& _rStateSet, sal_Int32 _nRow, sal_uInt16 ) const + { + if ( IsRowSelected( _nRow ) ) + _rStateSet |= AccessibleStateType::SELECTED; + if ( HasChildPathFocus() ) + _rStateSet |= AccessibleStateType::FOCUSED; + else // only transient when column is not focused + _rStateSet |= AccessibleStateType::TRANSIENT; + + _rStateSet |= AccessibleStateType::VISIBLE; + _rStateSet |= AccessibleStateType::SHOWING; + _rStateSet |= AccessibleStateType::ENABLED; + _rStateSet |= AccessibleStateType::SENSITIVE; + _rStateSet |= AccessibleStateType::ACTIVE; + } + + + tools::Rectangle TableControl::GetFieldCharacterBounds(sal_Int32,sal_Int32,sal_Int32 nIndex) + { + return GetCharacterBounds(nIndex); + } + + + sal_Int32 TableControl::GetFieldIndexAtPoint(sal_Int32,sal_Int32,const Point& _rPoint) + { + return GetIndexForPoint(_rPoint); + } + + + tools::Rectangle TableControl::calcHeaderRect(bool _bIsColumnBar ) + { + return m_pImpl->calcHeaderRect( !_bIsColumnBar ); + } + + + tools::Rectangle TableControl::calcHeaderCellRect( bool _bIsColumnBar, sal_Int32 nPos ) + { + return m_pImpl->calcHeaderCellRect( _bIsColumnBar, nPos ); + } + + + tools::Rectangle TableControl::calcTableRect() + { + return m_pImpl->calcTableRect(); + } + + + tools::Rectangle TableControl::calcCellRect( sal_Int32 _nRowPos, sal_Int32 _nColPos ) + { + return m_pImpl->calcCellRect( _nRowPos, _nColPos ); + } + + + IMPL_LINK_NOARG(TableControl, ImplSelectHdl, LinkParamNone*, void) + { + Select(); + } + + + void TableControl::Select() + { + ImplCallEventListenersAndHandler( VclEventId::TableRowSelect, nullptr ); + + if ( m_pImpl->isAccessibleAlive() ) + { + m_pImpl->commitAccessibleEvent( AccessibleEventId::SELECTION_CHANGED ); + + m_pImpl->commitTableEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, Any(), Any() ); + // TODO: why do we notify this when the *selection* changed? Shouldn't we find a better place for this, + // actually, when the active descendant, i.e. the current cell, *really* changed? + } + } + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tablecontrol_impl.cxx b/toolkit/source/controls/table/tablecontrol_impl.cxx new file mode 100644 index 0000000000..99c7b815ca --- /dev/null +++ b/toolkit/source/controls/table/tablecontrol_impl.cxx @@ -0,0 +1,2551 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/table/tablecontrol.hxx> +#include <controls/table/defaultinputhandler.hxx> +#include <controls/table/tablemodel.hxx> + +#include "tabledatawindow.hxx" +#include "tablecontrol_impl.hxx" +#include "tablegeometry.hxx" + +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> + +#include <comphelper/flagguard.hxx> +#include <vcl/accessiblefactory.hxx> +#include <vcl/toolkit/scrbar.hxx> +#include <vcl/seleng.hxx> +#include <vcl/settings.hxx> +#include <vcl/image.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/debug.hxx> + +#include <cstdlib> +#include <numeric> + +#define MIN_COLUMN_WIDTH_PIXEL 4 + + +namespace svt::table +{ + + + using ::com::sun::star::accessibility::AccessibleTableModelChange; + using ::com::sun::star::uno::Any; + using ::com::sun::star::accessibility::XAccessible; + using ::com::sun::star::uno::Reference; + + namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId; + namespace AccessibleTableModelChangeType = ::com::sun::star::accessibility::AccessibleTableModelChangeType; + + + //= SuppressCursor + + namespace { + + class SuppressCursor + { + private: + ITableControl& m_rTable; + + public: + explicit SuppressCursor( ITableControl& _rTable ) + :m_rTable( _rTable ) + { + m_rTable.hideCursor(); + } + ~SuppressCursor() + { + m_rTable.showCursor(); + } + }; + + + //= EmptyTableModel + + /** default implementation of an ->ITableModel, used as fallback when no + real model is present + + Instances of this class are static in any way, and provide the least + necessary default functionality for a table model. + */ + class EmptyTableModel : public ITableModel + { + public: + EmptyTableModel() + { + } + + // ITableModel overridables + virtual TableSize getColumnCount() const override + { + return 0; + } + virtual TableSize getRowCount() const override + { + return 0; + } + virtual bool hasColumnHeaders() const override + { + return false; + } + virtual bool hasRowHeaders() const override + { + return false; + } + virtual PColumnModel getColumnModel( ColPos ) override + { + OSL_FAIL( "EmptyTableModel::getColumnModel: invalid call!" ); + return PColumnModel(); + } + virtual PTableRenderer getRenderer() const override + { + return PTableRenderer(); + } + virtual PTableInputHandler getInputHandler() const override + { + return PTableInputHandler(); + } + virtual TableMetrics getRowHeight() const override + { + return 5 * 100; + } + virtual TableMetrics getColumnHeaderHeight() const override + { + return 0; + } + virtual TableMetrics getRowHeaderWidth() const override + { + return 0; + } + virtual ScrollbarVisibility getVerticalScrollbarVisibility() const override + { + return ScrollbarShowNever; + } + virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const override + { + return ScrollbarShowNever; + } + virtual void addTableModelListener( const PTableModelListener& ) override {} + virtual void removeTableModelListener( const PTableModelListener& ) override {} + virtual ::std::optional< ::Color > getLineColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getHeaderBackgroundColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getHeaderTextColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getActiveSelectionBackColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getInactiveSelectionBackColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getActiveSelectionTextColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getInactiveSelectionTextColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getTextColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::Color > getTextLineColor() const override + { + return ::std::optional< ::Color >(); + } + virtual ::std::optional< ::std::vector< ::Color > > getRowBackgroundColors() const override + { + return ::std::optional< ::std::vector< ::Color > >(); + } + virtual css::style::VerticalAlignment getVerticalAlign() const override + { + return css::style::VerticalAlignment(0); + } + virtual ITableDataSort* getSortAdapter() override + { + return nullptr; + } + virtual bool isEnabled() const override + { + return true; + } + virtual void getCellContent( ColPos const, RowPos const, css::uno::Any& o_cellContent ) override + { + o_cellContent.clear(); + } + virtual void getCellToolTip( ColPos const, RowPos const, css::uno::Any& ) override + { + } + virtual Any getRowHeading( RowPos const ) const override + { + return Any(); + } + }; + + } + + TableControl_Impl::TableControl_Impl( TableControl& _rAntiImpl ) + :m_rAntiImpl ( _rAntiImpl ) + ,m_pModel ( std::make_shared<EmptyTableModel>() ) + ,m_pInputHandler ( ) + ,m_nRowHeightPixel ( 15 ) + ,m_nColHeaderHeightPixel( 0 ) + ,m_nRowHeaderWidthPixel ( 0 ) + ,m_nColumnCount ( 0 ) + ,m_nRowCount ( 0 ) + ,m_nCurColumn ( COL_INVALID ) + ,m_nCurRow ( ROW_INVALID ) + ,m_nLeftColumn ( 0 ) + ,m_nTopRow ( 0 ) + ,m_nCursorHidden ( 1 ) + ,m_pDataWindow ( VclPtr<TableDataWindow>::Create( *this ) ) + ,m_pVScroll ( nullptr ) + ,m_pHScroll ( nullptr ) + ,m_pScrollCorner ( nullptr ) + ,m_aSelectedRows ( ) + ,m_pTableFunctionSet ( new TableFunctionSet( this ) ) + ,m_nAnchor ( -1 ) + ,m_bUpdatingColWidths ( false ) + { + m_pSelEngine.reset( new SelectionEngine( m_pDataWindow.get(), m_pTableFunctionSet.get() ) ); + m_pSelEngine->SetSelectionMode(SelectionMode::Single); + m_pDataWindow->SetPosPixel( Point( 0, 0 ) ); + m_pDataWindow->Show(); + } + + TableControl_Impl::~TableControl_Impl() + { + m_pVScroll.disposeAndClear(); + m_pHScroll.disposeAndClear(); + m_pScrollCorner.disposeAndClear(); + m_pDataWindow.disposeAndClear(); + m_pTableFunctionSet.reset(); + m_pSelEngine.reset(); + } + + void TableControl_Impl::setModel( const PTableModel& _pModel ) + { + SuppressCursor aHideCursor( *this ); + + if ( m_pModel ) + m_pModel->removeTableModelListener( shared_from_this() ); + + m_pModel = _pModel; + if ( !m_pModel) + m_pModel = std::make_shared<EmptyTableModel>(); + + m_pModel->addTableModelListener( shared_from_this() ); + + m_nCurRow = ROW_INVALID; + m_nCurColumn = COL_INVALID; + + // recalc some model-dependent cached info + impl_ni_updateCachedModelValues(); + impl_ni_relayout(); + + // completely invalidate + m_rAntiImpl.Invalidate(); + + // reset cursor to (0,0) + if ( m_nRowCount ) m_nCurRow = 0; + if ( m_nColumnCount ) m_nCurColumn = 0; + } + + + namespace + { + bool lcl_adjustSelectedRows( ::std::vector< RowPos >& io_selectionIndexes, RowPos const i_firstAffectedRowIndex, TableSize const i_offset ) + { + bool didChanges = false; + for (auto & selectionIndex : io_selectionIndexes) + { + if ( selectionIndex < i_firstAffectedRowIndex ) + continue; + selectionIndex += i_offset; + didChanges = true; + } + return didChanges; + } + } + + + void TableControl_Impl::rowsInserted( RowPos i_first, RowPos i_last ) + { + OSL_PRECOND( i_last >= i_first, "TableControl_Impl::rowsInserted: invalid row indexes!" ); + + TableSize const insertedRows = i_last - i_first + 1; + + // adjust selection, if necessary + bool const selectionChanged = lcl_adjustSelectedRows( m_aSelectedRows, i_first, insertedRows ); + + // adjust our cached row count + m_nRowCount = m_pModel->getRowCount(); + + // if the rows have been inserted before the current row, adjust this + if ( i_first <= m_nCurRow ) + goTo( m_nCurColumn, m_nCurRow + insertedRows ); + + // relayout, since the scrollbar need might have changed + impl_ni_relayout(); + + // notify A1YY events + if ( impl_isAccessibleAlive() ) + { + impl_commitAccessibleEvent( AccessibleEventId::TABLE_MODEL_CHANGED, + Any( AccessibleTableModelChange( AccessibleTableModelChangeType::ROWS_INSERTED, i_first, i_last, -1, -1 ) ) + ); + } + + // schedule repaint + invalidateRowRange( i_first, ROW_INVALID ); + + // call selection handlers, if necessary + if ( selectionChanged ) + m_rAntiImpl.Select(); + } + + + void TableControl_Impl::rowsRemoved( RowPos i_first, RowPos i_last ) + { + sal_Int32 firstRemovedRow = i_first; + sal_Int32 lastRemovedRow = i_last; + + // adjust selection, if necessary + bool selectionChanged = false; + if ( i_first == -1 ) + { + selectionChanged = markAllRowsAsDeselected(); + + firstRemovedRow = 0; + lastRemovedRow = m_nRowCount - 1; + } + else + { + ENSURE_OR_RETURN_VOID( i_last >= i_first, "TableControl_Impl::rowsRemoved: illegal indexes!" ); + + for ( sal_Int32 row = i_first; row <= i_last; ++row ) + { + if ( markRowAsDeselected( row ) ) + selectionChanged = true; + } + + if ( lcl_adjustSelectedRows( m_aSelectedRows, i_last + 1, i_first - i_last - 1 ) ) + selectionChanged = true; + } + + // adjust cached row count + m_nRowCount = m_pModel->getRowCount(); + + // adjust the current row, if it is larger than the row count now + if ( m_nCurRow >= m_nRowCount ) + { + if ( m_nRowCount > 0 ) + goTo( m_nCurColumn, m_nRowCount - 1 ); + else + { + m_nCurRow = ROW_INVALID; + m_nTopRow = 0; + } + } + else if ( m_nRowCount == 0 ) + { + m_nTopRow = 0; + } + + + // relayout, since the scrollbar need might have changed + impl_ni_relayout(); + + // notify A11Y events + if ( impl_isAccessibleAlive() ) + { + commitTableEvent( + AccessibleEventId::TABLE_MODEL_CHANGED, + Any( AccessibleTableModelChange( + AccessibleTableModelChangeType::ROWS_REMOVED, + firstRemovedRow, + lastRemovedRow, + -1, + -1 + ) ), + Any() + ); + } + + // schedule a repaint + invalidateRowRange( firstRemovedRow, ROW_INVALID ); + + // call selection handlers, if necessary + if ( selectionChanged ) + m_rAntiImpl.Select(); + } + + + void TableControl_Impl::columnInserted() + { + m_nColumnCount = m_pModel->getColumnCount(); + impl_ni_relayout(); + + m_rAntiImpl.Invalidate(); + } + + + void TableControl_Impl::columnRemoved() + { + m_nColumnCount = m_pModel->getColumnCount(); + + // adjust the current column, if it is larger than the column count now + if ( m_nCurColumn >= m_nColumnCount ) + { + if ( m_nColumnCount > 0 ) + goTo( m_nCurColumn - 1, m_nCurRow ); + else + m_nCurColumn = COL_INVALID; + } + + impl_ni_relayout(); + + m_rAntiImpl.Invalidate(); + } + + + void TableControl_Impl::allColumnsRemoved() + { + m_nColumnCount = m_pModel->getColumnCount(); + impl_ni_relayout(); + + m_rAntiImpl.Invalidate(); + } + + + void TableControl_Impl::cellsUpdated( RowPos const i_firstRow, RowPos const i_lastRow ) + { + invalidateRowRange( i_firstRow, i_lastRow ); + } + + + void TableControl_Impl::tableMetricsChanged() + { + impl_ni_updateCachedTableMetrics(); + impl_ni_relayout(); + m_rAntiImpl.Invalidate(); + } + + + void TableControl_Impl::impl_invalidateColumn( ColPos const i_column ) + { + tools::Rectangle const aAllCellsArea( impl_getAllVisibleCellsArea() ); + + const TableColumnGeometry aColumn( *this, aAllCellsArea, i_column ); + if ( aColumn.isValid() ) + m_rAntiImpl.Invalidate( aColumn.getRect() ); + } + + + void TableControl_Impl::columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup ) + { + ColumnAttributeGroup nGroup( i_attributeGroup ); + if ( nGroup & ColumnAttributeGroup::APPEARANCE ) + { + impl_invalidateColumn( i_column ); + nGroup &= ~ColumnAttributeGroup::APPEARANCE; + } + + if ( nGroup & ColumnAttributeGroup::WIDTH ) + { + if ( !m_bUpdatingColWidths ) + { + impl_ni_relayout( i_column ); + invalidate( TableArea::All ); + } + + nGroup &= ~ColumnAttributeGroup::WIDTH; + } + + OSL_ENSURE( ( nGroup == ColumnAttributeGroup::NONE ) || ( i_attributeGroup == ColumnAttributeGroup::ALL ), + "TableControl_Impl::columnChanged: don't know how to handle this change!" ); + } + + + tools::Rectangle TableControl_Impl::impl_getAllVisibleCellsArea() const + { + tools::Rectangle aArea( Point( 0, 0 ), Size( 0, 0 ) ); + + // determine the right-most border of the last column which is + // at least partially visible + aArea.SetRight( m_nRowHeaderWidthPixel ); + if ( !m_aColumnWidths.empty() ) + { + // the number of pixels which are scrolled out of the left hand + // side of the window + const tools::Long nScrolledOutLeft = m_nLeftColumn == 0 ? 0 : m_aColumnWidths[ m_nLeftColumn - 1 ].getEnd(); + + ColumnPositions::const_reverse_iterator loop = m_aColumnWidths.rbegin(); + do + { + aArea.SetRight(loop->getEnd() - nScrolledOutLeft); + ++loop; + } + while ( ( loop != m_aColumnWidths.rend() ) + && ( loop->getEnd() - nScrolledOutLeft >= aArea.Right() ) + ); + } + // so far, aArea.Right() denotes the first pixel *after* the cell area + aArea.AdjustRight( -1 ); + + // determine the last row which is at least partially visible + aArea.SetBottom( + m_nColHeaderHeightPixel + + impl_getVisibleRows( true ) * m_nRowHeightPixel + - 1 ); + + return aArea; + } + + + tools::Rectangle TableControl_Impl::impl_getAllVisibleDataCellArea() const + { + tools::Rectangle aArea( impl_getAllVisibleCellsArea() ); + aArea.SetLeft( m_nRowHeaderWidthPixel ); + aArea.SetTop( m_nColHeaderHeightPixel ); + return aArea; + } + + + void TableControl_Impl::impl_ni_updateCachedTableMetrics() + { + m_nRowHeightPixel = m_rAntiImpl.LogicToPixel(Size(0, m_pModel->getRowHeight()), MapMode(MapUnit::MapAppFont)).Height(); + + m_nColHeaderHeightPixel = 0; + if ( m_pModel->hasColumnHeaders() ) + m_nColHeaderHeightPixel = m_rAntiImpl.LogicToPixel(Size(0, m_pModel->getColumnHeaderHeight()), MapMode(MapUnit::MapAppFont)).Height(); + + m_nRowHeaderWidthPixel = 0; + if ( m_pModel->hasRowHeaders() ) + m_nRowHeaderWidthPixel = m_rAntiImpl.LogicToPixel(Size(m_pModel->getRowHeaderWidth(), 0), MapMode(MapUnit::MapAppFont)).Width(); + } + + + void TableControl_Impl::impl_ni_updateCachedModelValues() + { + m_pInputHandler = m_pModel->getInputHandler(); + if ( !m_pInputHandler ) + m_pInputHandler = std::make_shared<DefaultInputHandler>(); + + m_nColumnCount = m_pModel->getColumnCount(); + if ( m_nLeftColumn >= m_nColumnCount ) + m_nLeftColumn = ( m_nColumnCount > 0 ) ? m_nColumnCount - 1 : 0; + + m_nRowCount = m_pModel->getRowCount(); + if ( m_nTopRow >= m_nRowCount ) + m_nTopRow = ( m_nRowCount > 0 ) ? m_nRowCount - 1 : 0; + + impl_ni_updateCachedTableMetrics(); + } + + + namespace + { + + /// determines whether a scrollbar is needed for the given values + bool lcl_determineScrollbarNeed( tools::Long const i_position, ScrollbarVisibility const i_visibility, + tools::Long const i_availableSpace, tools::Long const i_neededSpace ) + { + if ( i_visibility == ScrollbarShowNever ) + return false; + if ( i_visibility == ScrollbarShowAlways ) + return true; + if ( i_position > 0 ) + return true; + if ( i_availableSpace >= i_neededSpace ) + return false; + return true; + } + + + void lcl_setButtonRepeat( vcl::Window& _rWindow ) + { + AllSettings aSettings = _rWindow.GetSettings(); + MouseSettings aMouseSettings = aSettings.GetMouseSettings(); + + aMouseSettings.SetButtonRepeat( 0 ); + aSettings.SetMouseSettings( aMouseSettings ); + + _rWindow.SetSettings( aSettings, true ); + } + + + bool lcl_updateScrollbar( vcl::Window& _rParent, VclPtr<ScrollBar>& _rpBar, + bool const i_needBar, tools::Long _nVisibleUnits, + tools::Long _nPosition, tools::Long _nRange, + bool _bHorizontal, const Link<ScrollBar*,void>& _rScrollHandler ) + { + // do we currently have the scrollbar? + bool bHaveBar = _rpBar != nullptr; + + // do we need to correct the scrollbar visibility? + if ( bHaveBar && !i_needBar ) + { + if ( _rpBar->IsTracking() ) + _rpBar->EndTracking(); + _rpBar.disposeAndClear(); + } + else if ( !bHaveBar && i_needBar ) + { + _rpBar = VclPtr<ScrollBar>::Create( + + &_rParent, + WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL ) + ); + _rpBar->SetScrollHdl( _rScrollHandler ); + // get some speed into the scrolling... + lcl_setButtonRepeat( *_rpBar ); + } + + if ( _rpBar ) + { + _rpBar->SetRange( Range( 0, _nRange ) ); + _rpBar->SetVisibleSize( _nVisibleUnits ); + _rpBar->SetPageSize( _nVisibleUnits ); + _rpBar->SetLineSize( 1 ); + _rpBar->SetThumbPos( _nPosition ); + _rpBar->Show(); + } + + return ( bHaveBar != i_needBar ); + } + + + /** returns the number of rows fitting into the given range, + for the given row height. Partially fitting rows are counted, too, if the + respective parameter says so. + */ + TableSize lcl_getRowsFittingInto( tools::Long _nOverallHeight, tools::Long _nRowHeightPixel, bool _bAcceptPartialRow ) + { + return _bAcceptPartialRow + ? ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel + : _nOverallHeight / _nRowHeightPixel; + } + + + /** returns the number of columns fitting into the given area, + with the first visible column as given. Partially fitting columns are counted, too, + if the respective parameter says so. + */ + TableSize lcl_getColumnsVisibleWithin( const tools::Rectangle& _rArea, ColPos _nFirstVisibleColumn, + const TableControl_Impl& _rControl, bool _bAcceptPartialRow ) + { + TableSize visibleColumns = 0; + TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn ); + while ( aColumn.isValid() ) + { + if ( !_bAcceptPartialRow ) + if ( aColumn.getRect().Right() > _rArea.Right() ) + // this column is only partially visible, and this is not allowed + break; + + aColumn.moveRight(); + ++visibleColumns; + } + return visibleColumns; + } + + } + + + tools::Long TableControl_Impl::impl_ni_calculateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding, + bool const i_assumeVerticalScrollbar, ::std::vector< tools::Long >& o_newColWidthsPixel ) const + { + // the available horizontal space + tools::Long gridWidthPixel = m_rAntiImpl.GetOutputSizePixel().Width(); + ENSURE_OR_RETURN( !!m_pModel, "TableControl_Impl::impl_ni_calculateColumnWidths: not allowed without a model!", gridWidthPixel ); + if ( m_pModel->hasRowHeaders() && ( gridWidthPixel != 0 ) ) + { + gridWidthPixel -= m_nRowHeaderWidthPixel; + } + + if ( i_assumeVerticalScrollbar && ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ) ) + { + tools::Long nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); + gridWidthPixel -= nScrollbarMetrics; + } + + // no need to do anything without columns + TableSize const colCount = m_pModel->getColumnCount(); + if ( colCount == 0 ) + return gridWidthPixel; + + // collect some meta data for our columns: + // - their current (pixel) metrics + tools::Long accumulatedCurrentWidth = 0; + ::std::vector< tools::Long > currentColWidths; + currentColWidths.reserve( colCount ); + typedef ::std::vector< ::std::pair< tools::Long, long > > ColumnLimits; + ColumnLimits effectiveColumnLimits; + effectiveColumnLimits.reserve( colCount ); + tools::Long accumulatedMinWidth = 0; + tools::Long accumulatedMaxWidth = 0; + // - their relative flexibility + ::std::vector< ::sal_Int32 > columnFlexibilities; + columnFlexibilities.reserve( colCount ); + tools::Long flexibilityDenominator = 0; + size_t flexibleColumnCount = 0; + for ( ColPos col = 0; col < colCount; ++col ) + { + PColumnModel const pColumn = m_pModel->getColumnModel( col ); + ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); + + // current width + tools::Long const currentWidth = appFontWidthToPixel( pColumn->getWidth() ); + currentColWidths.push_back( currentWidth ); + + // accumulated width + accumulatedCurrentWidth += currentWidth; + + // flexibility + ::sal_Int32 flexibility = pColumn->getFlexibility(); + OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_calculateColumnWidths: a column's flexibility should be non-negative." ); + if ( ( flexibility < 0 ) // normalization + || ( !pColumn->isResizable() ) // column not resizable => no auto-resize + || ( col <= i_assumeInflexibleColumnsUpToIncluding ) // column shall be treated as inflexible => respect this + ) + flexibility = 0; + + // min/max width + tools::Long effectiveMin = currentWidth, effectiveMax = currentWidth; + // if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then + if ( flexibility > 0 ) + { + tools::Long const minWidth = appFontWidthToPixel( pColumn->getMinWidth() ); + if ( minWidth > 0 ) + effectiveMin = minWidth; + else + effectiveMin = MIN_COLUMN_WIDTH_PIXEL; + + tools::Long const maxWidth = appFontWidthToPixel( pColumn->getMaxWidth() ); + OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_calculateColumnWidths: pretty undecided 'bout its width limits, this column!" ); + if ( ( maxWidth > 0 ) && ( maxWidth >= minWidth ) ) + effectiveMax = maxWidth; + else + effectiveMax = gridWidthPixel; // TODO: any better guess here? + + if ( effectiveMin == effectiveMax ) + // if the min and the max are identical, this implies no flexibility at all + flexibility = 0; + } + + columnFlexibilities.push_back( flexibility ); + flexibilityDenominator += flexibility; + if ( flexibility > 0 ) + ++flexibleColumnCount; + + effectiveColumnLimits.emplace_back( effectiveMin, effectiveMax ); + accumulatedMinWidth += effectiveMin; + accumulatedMaxWidth += effectiveMax; + } + + o_newColWidthsPixel = currentColWidths; + if ( flexibilityDenominator == 0 ) + { + // no column is flexible => don't adjust anything + } + else if ( gridWidthPixel > accumulatedCurrentWidth ) + { // we have space to give away ... + tools::Long distributePixel = gridWidthPixel - accumulatedCurrentWidth; + if ( gridWidthPixel > accumulatedMaxWidth ) + { + // ... but the column's maximal widths are still less than we have + // => set them all to max + for ( svt::table::TableSize i = 0; i < colCount; ++i ) + { + o_newColWidthsPixel[i] = effectiveColumnLimits[i].second; + } + } + else + { + bool startOver = false; + do + { + startOver = false; + // distribute the remaining space amongst all columns with a positive flexibility + for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i ) + { + tools::Long const columnFlexibility = columnFlexibilities[i]; + if ( columnFlexibility == 0 ) + continue; + + tools::Long newColWidth = currentColWidths[i] + columnFlexibility * distributePixel / flexibilityDenominator; + + if ( newColWidth > effectiveColumnLimits[i].second ) + { // that was too much, we hit the col's maximum + // set the new width to exactly this maximum + newColWidth = effectiveColumnLimits[i].second; + // adjust the flexibility denominator ... + flexibilityDenominator -= columnFlexibility; + columnFlexibilities[i] = 0; + --flexibleColumnCount; + // ... and the remaining width ... + tools::Long const difference = newColWidth - currentColWidths[i]; + distributePixel -= difference; + // ... this way, we ensure that the width not taken up by this column is consumed by the other + // flexible ones (if there are some) + + // and start over with the first column, since there might be earlier columns which need + // to be recalculated now + startOver = true; + } + + o_newColWidthsPixel[i] = newColWidth; + } + } + while ( startOver ); + + // are there pixels left (might be caused by rounding errors)? + distributePixel = gridWidthPixel - ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ); + while ( ( distributePixel > 0 ) && ( flexibleColumnCount > 0 ) ) + { + // yes => ignore relative flexibilities, and subsequently distribute single pixels to all flexible + // columns which did not yet reach their maximum. + for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( distributePixel > 0 ); ++i ) + { + if ( columnFlexibilities[i] == 0 ) + continue; + + OSL_ENSURE( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].second, + "TableControl_Impl::impl_ni_calculateColumnWidths: inconsistency!" ); + if ( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first ) + { + columnFlexibilities[i] = 0; + --flexibleColumnCount; + continue; + } + + ++o_newColWidthsPixel[i]; + --distributePixel; + } + } + } + } + else if ( gridWidthPixel < accumulatedCurrentWidth ) + { // we need to take away some space from the columns which allow it ... + tools::Long takeAwayPixel = accumulatedCurrentWidth - gridWidthPixel; + if ( gridWidthPixel < accumulatedMinWidth ) + { + // ... but the column's minimal widths are still more than we have + // => set them all to min + for ( svt::table::TableSize i = 0; i < colCount; ++i ) + { + o_newColWidthsPixel[i] = effectiveColumnLimits[i].first; + } + } + else + { + bool startOver = false; + do + { + startOver = false; + // take away the space we need from the columns with a positive flexibility + for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i ) + { + tools::Long const columnFlexibility = columnFlexibilities[i]; + if ( columnFlexibility == 0 ) + continue; + + tools::Long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayPixel / flexibilityDenominator; + + if ( newColWidth < effectiveColumnLimits[i].first ) + { // that was too much, we hit the col's minimum + // set the new width to exactly this minimum + newColWidth = effectiveColumnLimits[i].first; + // adjust the flexibility denominator ... + flexibilityDenominator -= columnFlexibility; + columnFlexibilities[i] = 0; + --flexibleColumnCount; + // ... and the remaining width ... + tools::Long const difference = currentColWidths[i] - newColWidth; + takeAwayPixel -= difference; + + // and start over with the first column, since there might be earlier columns which need + // to be recalculated now + startOver = true; + } + + o_newColWidthsPixel[i] = newColWidth; + } + } + while ( startOver ); + + // are there pixels left (might be caused by rounding errors)? + takeAwayPixel = ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ) - gridWidthPixel; + while ( ( takeAwayPixel > 0 ) && ( flexibleColumnCount > 0 ) ) + { + // yes => ignore relative flexibilities, and subsequently take away pixels from all flexible + // columns which did not yet reach their minimum. + for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( takeAwayPixel > 0 ); ++i ) + { + if ( columnFlexibilities[i] == 0 ) + continue; + + OSL_ENSURE( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first, + "TableControl_Impl::impl_ni_calculateColumnWidths: inconsistency!" ); + if ( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].first ) + { + columnFlexibilities[i] = 0; + --flexibleColumnCount; + continue; + } + + --o_newColWidthsPixel[i]; + --takeAwayPixel; + } + } + } + } + + return gridWidthPixel; + } + + + void TableControl_Impl::impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding ) + { + ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_relayout: recursive call detected!" ); + + m_aColumnWidths.resize( 0 ); + if ( !m_pModel ) + return; + + ::comphelper::FlagRestorationGuard const aWidthUpdateFlag( m_bUpdatingColWidths, true ); + SuppressCursor aHideCursor( *this ); + + // layouting steps: + + // 1. adjust column widths, leaving space for a vertical scrollbar + // 2. determine need for a vertical scrollbar + // - V-YES: all fine, result from 1. is still valid + // - V-NO: result from 1. is still under consideration + + // 3. determine need for a horizontal scrollbar + // - H-NO: all fine, result from 2. is still valid + // - H-YES: reconsider need for a vertical scrollbar, if result of 2. was V-NO + // - V-YES: all fine, result from 1. is still valid + // - V-NO: redistribute the remaining space (if any) amongst all columns which allow it + + ::std::vector< tools::Long > newWidthsPixel; + tools::Long gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, true, newWidthsPixel ); + + // the width/height of a scrollbar, needed several times below + tools::Long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); + + // determine the playground for the data cells (excluding headers) + // TODO: what if the control is smaller than needed for the headers/scrollbars? + tools::Rectangle aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl.GetOutputSizePixel() ); + aDataCellPlayground.SetLeft( m_nRowHeaderWidthPixel ); + aDataCellPlayground.SetTop( m_nColHeaderHeightPixel ); + + OSL_ENSURE( ( m_nRowCount == m_pModel->getRowCount() ) && ( m_nColumnCount == m_pModel->getColumnCount() ), + "TableControl_Impl::impl_ni_relayout: how is this expected to work with invalid data?" ); + tools::Long const nAllColumnsWidth = ::std::accumulate( newWidthsPixel.begin(), newWidthsPixel.end(), 0 ); + + ScrollbarVisibility const eVertScrollbar = m_pModel->getVerticalScrollbarVisibility(); + ScrollbarVisibility const eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility(); + + // do we need a vertical scrollbar? + bool bNeedVerticalScrollbar = lcl_determineScrollbarNeed( + m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount ); + bool bFirstRoundVScrollNeed = false; + if ( bNeedVerticalScrollbar ) + { + aDataCellPlayground.AdjustRight( -nScrollbarMetrics ); + bFirstRoundVScrollNeed = true; + } + + // do we need a horizontal scrollbar? + bool const bNeedHorizontalScrollbar = lcl_determineScrollbarNeed( + m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth ); + if ( bNeedHorizontalScrollbar ) + { + aDataCellPlayground.AdjustBottom( -nScrollbarMetrics ); + + // now that we just found that we need a horizontal scrollbar, + // the need for a vertical one may have changed, since the horizontal + // SB might just occupy enough space so that not all rows do fit + // anymore + if ( !bFirstRoundVScrollNeed ) + { + bNeedVerticalScrollbar = lcl_determineScrollbarNeed( + m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount ); + if ( bNeedVerticalScrollbar ) + { + aDataCellPlayground.AdjustRight( -nScrollbarMetrics ); + } + } + } + + // the initial call to impl_ni_calculateColumnWidths assumed that we need a vertical scrollbar. If, by now, + // we know that this is not the case, re-calculate the column widths. + if ( !bNeedVerticalScrollbar ) + gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, false, newWidthsPixel ); + + // update the column objects with the new widths we finally calculated + TableSize const colCount = m_pModel->getColumnCount(); + m_aColumnWidths.reserve( colCount ); + tools::Long accumulatedWidthPixel = m_nRowHeaderWidthPixel; + bool anyColumnWidthChanged = false; + for ( ColPos col = 0; col < colCount; ++col ) + { + const tools::Long columnStart = accumulatedWidthPixel; + const tools::Long columnEnd = columnStart + newWidthsPixel[col]; + m_aColumnWidths.emplace_back( columnStart, columnEnd ); + accumulatedWidthPixel = columnEnd; + + // and don't forget to forward this to the column models + PColumnModel const pColumn = m_pModel->getColumnModel( col ); + ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); + + tools::Long const oldColumnWidthAppFont = pColumn->getWidth(); + tools::Long const newColumnWidthAppFont = pixelWidthToAppFont( newWidthsPixel[col] ); + pColumn->setWidth( newColumnWidthAppFont ); + + anyColumnWidthChanged |= ( oldColumnWidthAppFont != newColumnWidthAppFont ); + } + + // if the column widths changed, ensure everything is repainted + if ( anyColumnWidthChanged ) + invalidate( TableArea::All ); + + // if the column resizing happened to leave some space at the right, but there are columns + // scrolled out to the left, scroll them in + while ( ( m_nLeftColumn > 0 ) + && ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel ) + ) + { + --m_nLeftColumn; + } + + // now adjust the column metrics, since they currently ignore the horizontal scroll position + if ( m_nLeftColumn > 0 ) + { + const tools::Long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart(); + for (auto & columnWidth : m_aColumnWidths) + { + columnWidth.move( offsetPixel ); + } + } + + // show or hide the scrollbars as needed, and position the data window + impl_ni_positionChildWindows( aDataCellPlayground, bNeedVerticalScrollbar, bNeedHorizontalScrollbar ); + } + + + void TableControl_Impl::impl_ni_positionChildWindows( tools::Rectangle const & i_dataCellPlayground, + bool const i_verticalScrollbar, bool const i_horizontalScrollbar ) + { + tools::Long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); + + // create or destroy the vertical scrollbar, as needed + lcl_updateScrollbar( + m_rAntiImpl, + m_pVScroll, + i_verticalScrollbar, + lcl_getRowsFittingInto( i_dataCellPlayground.GetHeight(), m_nRowHeightPixel, false ), + // visible units + m_nTopRow, // current position + m_nRowCount, // range + false, // vertical + LINK( this, TableControl_Impl, OnScroll ) // scroll handler + ); + + // position it + if ( m_pVScroll ) + { + tools::Rectangle aScrollbarArea( + Point( i_dataCellPlayground.Right() + 1, 0 ), + Size( nScrollbarMetrics, i_dataCellPlayground.Bottom() + 1 ) + ); + m_pVScroll->SetPosSizePixel( + aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() ); + } + + // create or destroy the horizontal scrollbar, as needed + lcl_updateScrollbar( + m_rAntiImpl, + m_pHScroll, + i_horizontalScrollbar, + lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ), + // visible units + m_nLeftColumn, // current position + m_nColumnCount, // range + true, // horizontal + LINK( this, TableControl_Impl, OnScroll ) // scroll handler + ); + + // position it + if ( m_pHScroll ) + { + TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ); + TableMetrics const nRange = m_nColumnCount; + if( m_nLeftColumn + nVisibleUnits == nRange - 1 ) + { + if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > i_dataCellPlayground.GetWidth() ) + { + m_pHScroll->SetVisibleSize( nVisibleUnits -1 ); + m_pHScroll->SetPageSize( nVisibleUnits - 1 ); + } + } + tools::Rectangle aScrollbarArea( + Point( 0, i_dataCellPlayground.Bottom() + 1 ), + Size( i_dataCellPlayground.Right() + 1, nScrollbarMetrics ) + ); + m_pHScroll->SetPosSizePixel( + aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() ); + } + + // the corner window connecting the two scrollbars in the lower right corner + bool bHaveScrollCorner = nullptr != m_pScrollCorner; + bool bNeedScrollCorner = ( nullptr != m_pHScroll ) && ( nullptr != m_pVScroll ); + if ( bHaveScrollCorner && !bNeedScrollCorner ) + { + m_pScrollCorner.disposeAndClear(); + } + else if ( !bHaveScrollCorner && bNeedScrollCorner ) + { + m_pScrollCorner = VclPtr<ScrollBarBox>::Create( &m_rAntiImpl ); + m_pScrollCorner->SetSizePixel( Size( nScrollbarMetrics, nScrollbarMetrics ) ); + m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) ); + m_pScrollCorner->Show(); + } + else if(bHaveScrollCorner && bNeedScrollCorner) + { + m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) ); + m_pScrollCorner->Show(); + } + + // resize the data window + m_pDataWindow->SetSizePixel( Size( + i_dataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel, + i_dataCellPlayground.GetHeight() + m_nColHeaderHeightPixel + ) ); + } + + + void TableControl_Impl::onResize() + { + impl_ni_relayout(); + checkCursorPosition(); + } + + + void TableControl_Impl::doPaintContent(vcl::RenderContext& rRenderContext, const tools::Rectangle& _rUpdateRect) + { + if (!getModel()) + return; + PTableRenderer pRenderer = getModel()->getRenderer(); + DBG_ASSERT(!!pRenderer, "TableDataWindow::doPaintContent: invalid renderer!"); + if (!pRenderer) + return; + + // our current style settings, to be passed to the renderer + const StyleSettings& rStyle = rRenderContext.GetSettings().GetStyleSettings(); + m_nRowCount = m_pModel->getRowCount(); + // the area occupied by all (at least partially) visible cells, including + // headers + tools::Rectangle const aAllCellsWithHeaders( impl_getAllVisibleCellsArea() ); + + // draw the header column area + if (m_pModel->hasColumnHeaders()) + { + TableRowGeometry const aHeaderRow(*this, tools::Rectangle(Point(0, 0), aAllCellsWithHeaders.BottomRight()), ROW_COL_HEADERS); + tools::Rectangle const aColRect(aHeaderRow.getRect()); + pRenderer->PaintHeaderArea(rRenderContext, aColRect, true, false, rStyle); + // Note that strictly, aHeaderRow.getRect() also contains the intersection between column + // and row header area. However, below we go to paint this intersection, again, + // so this hopefully doesn't hurt if we already paint it here. + + for (TableCellGeometry aCell(aHeaderRow, m_nLeftColumn); aCell.isValid(); aCell.moveRight()) + { + if (_rUpdateRect.GetIntersection(aCell.getRect()).IsEmpty()) + continue; + + pRenderer->PaintColumnHeader(aCell.getColumn(), rRenderContext, aCell.getRect(), rStyle); + } + } + // the area occupied by the row header, if any + tools::Rectangle aRowHeaderArea; + if (m_pModel->hasRowHeaders()) + { + aRowHeaderArea = aAllCellsWithHeaders; + aRowHeaderArea.SetRight( m_nRowHeaderWidthPixel - 1 ); + + TableSize const nVisibleRows = impl_getVisibleRows(true); + TableSize nActualRows = nVisibleRows; + if (m_nTopRow + nActualRows > m_nRowCount) + nActualRows = m_nRowCount - m_nTopRow; + aRowHeaderArea.SetBottom( m_nColHeaderHeightPixel + m_nRowHeightPixel * nActualRows - 1 ); + + pRenderer->PaintHeaderArea(rRenderContext, aRowHeaderArea, false, true, rStyle); + // Note that strictly, aRowHeaderArea also contains the intersection between column + // and row header area. However, below we go to paint this intersection, again, + // so this hopefully doesn't hurt if we already paint it here. + + if (m_pModel->hasColumnHeaders()) + { + TableCellGeometry const aIntersection(*this, tools::Rectangle(Point(0, 0), aAllCellsWithHeaders.BottomRight()), + COL_ROW_HEADERS, ROW_COL_HEADERS); + tools::Rectangle const aInters(aIntersection.getRect()); + pRenderer->PaintHeaderArea(rRenderContext, aInters, true, true, rStyle); + } + } + + // draw the table content row by row + TableSize colCount = getModel()->getColumnCount(); + + // paint all rows + tools::Rectangle const aAllDataCellsArea(impl_getAllVisibleDataCellArea()); + for (TableRowGeometry aRowIterator(*this, aAllCellsWithHeaders, getTopRow()); aRowIterator.isValid(); aRowIterator.moveDown()) + { + if (_rUpdateRect.GetIntersection(aRowIterator.getRect() ).IsEmpty()) + continue; + + bool const isControlFocused = m_rAntiImpl.HasControlFocus(); + bool const isSelectedRow = isRowSelected(aRowIterator.getRow()); + + tools::Rectangle const aRect = aRowIterator.getRect().GetIntersection(aAllDataCellsArea); + + // give the renderer a chance to prepare the row + pRenderer->PrepareRow(aRowIterator.getRow(), isControlFocused, isSelectedRow, rRenderContext, aRect, rStyle); + + // paint the row header + if (m_pModel->hasRowHeaders()) + { + const tools::Rectangle aCurrentRowHeader(aRowHeaderArea.GetIntersection(aRowIterator.getRect())); + pRenderer->PaintRowHeader(rRenderContext, aCurrentRowHeader, rStyle); + } + + if (!colCount) + continue; + + // paint all cells in this row + for (TableCellGeometry aCell(aRowIterator, m_nLeftColumn); aCell.isValid(); aCell.moveRight()) + { + pRenderer->PaintCell(aCell.getColumn(), isSelectedRow, isControlFocused, + rRenderContext, aCell.getRect(), rStyle); + } + } + } + + void TableControl_Impl::hideCursor() + { + if ( ++m_nCursorHidden == 1 ) + impl_ni_doSwitchCursor( false ); + } + + + void TableControl_Impl::showCursor() + { + DBG_ASSERT( m_nCursorHidden > 0, "TableControl_Impl::showCursor: cursor not hidden!" ); + if ( --m_nCursorHidden == 0 ) + impl_ni_doSwitchCursor( true ); + } + + + bool TableControl_Impl::dispatchAction( TableControlAction _eAction ) + { + bool bSuccess = false; + bool selectionChanged = false; + + switch ( _eAction ) + { + case cursorDown: + if ( m_pSelEngine->GetSelectionMode() == SelectionMode::Single ) + { + //if other rows already selected, deselect them + if(!m_aSelectedRows.empty()) + { + invalidateSelectedRows(); + m_aSelectedRows.clear(); + } + if ( m_nCurRow < m_nRowCount-1 ) + { + ++m_nCurRow; + m_aSelectedRows.push_back(m_nCurRow); + } + else + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + ensureVisible(m_nCurColumn,m_nCurRow); + selectionChanged = true; + bSuccess = true; + } + else + { + if ( m_nCurRow < m_nRowCount - 1 ) + bSuccess = goTo( m_nCurColumn, m_nCurRow + 1 ); + } + break; + + case cursorUp: + if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single) + { + if(!m_aSelectedRows.empty()) + { + invalidateSelectedRows(); + m_aSelectedRows.clear(); + } + if(m_nCurRow>0) + { + --m_nCurRow; + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + ensureVisible(m_nCurColumn,m_nCurRow); + selectionChanged = true; + bSuccess = true; + } + else + { + if ( m_nCurRow > 0 ) + bSuccess = goTo( m_nCurColumn, m_nCurRow - 1 ); + } + break; + case cursorLeft: + if ( m_nCurColumn > 0 ) + bSuccess = goTo( m_nCurColumn - 1, m_nCurRow ); + else + if ( ( m_nCurColumn == 0) && ( m_nCurRow > 0 ) ) + bSuccess = goTo( m_nColumnCount - 1, m_nCurRow - 1 ); + break; + + case cursorRight: + if ( m_nCurColumn < m_nColumnCount - 1 ) + bSuccess = goTo( m_nCurColumn + 1, m_nCurRow ); + else + if ( ( m_nCurColumn == m_nColumnCount - 1 ) && ( m_nCurRow < m_nRowCount - 1 ) ) + bSuccess = goTo( 0, m_nCurRow + 1 ); + break; + + case cursorToLineStart: + bSuccess = goTo( 0, m_nCurRow ); + break; + + case cursorToLineEnd: + bSuccess = goTo( m_nColumnCount - 1, m_nCurRow ); + break; + + case cursorToFirstLine: + bSuccess = goTo( m_nCurColumn, 0 ); + break; + + case cursorToLastLine: + bSuccess = goTo( m_nCurColumn, m_nRowCount - 1 ); + break; + + case cursorPageUp: + { + RowPos nNewRow = ::std::max( RowPos(0), m_nCurRow - impl_getVisibleRows( false ) ); + bSuccess = goTo( m_nCurColumn, nNewRow ); + } + break; + + case cursorPageDown: + { + RowPos nNewRow = ::std::min( m_nRowCount - 1, m_nCurRow + impl_getVisibleRows( false ) ); + bSuccess = goTo( m_nCurColumn, nNewRow ); + } + break; + + case cursorTopLeft: + bSuccess = goTo( 0, 0 ); + break; + + case cursorBottomRight: + bSuccess = goTo( m_nColumnCount - 1, m_nRowCount - 1 ); + break; + + case cursorSelectRow: + { + if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE) + return false; + //pos is the position of the current row in the vector of selected rows, if current row is selected + int pos = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); + //if current row is selected, it should be deselected, when ALT+SPACE are pressed + if(pos>-1) + { + m_aSelectedRows.erase(m_aSelectedRows.begin()+pos); + if(m_aSelectedRows.empty() && m_nAnchor != -1) + m_nAnchor = -1; + } + //else select the row->put it in the vector + else + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + selectionChanged = true; + bSuccess = true; + } + break; + case cursorSelectRowUp: + { + if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE) + return false; + else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single) + { + //if there are other selected rows, deselect them + return false; + } + else + { + //there are other selected rows + if(!m_aSelectedRows.empty()) + { + //the anchor wasn't set -> a region is not selected, that's why clear all selection + //and select the current row + if(m_nAnchor==-1) + { + invalidateSelectedRows(); + m_aSelectedRows.clear(); + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + else + { + //a region is already selected, prevRow is last selected row and the row above - nextRow - should be selected + int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); + int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow-1); + if(prevRow>-1) + { + //if m_nCurRow isn't the upper one, can move up, otherwise not + if(m_nCurRow>0) + m_nCurRow--; + else + return true; + //if nextRow already selected, deselect it, otherwise select it + if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow) + { + m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow); + invalidateRow( m_nCurRow + 1 ); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + } + else + { + if(m_nCurRow>0) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow--; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion( m_nCurRow+1, m_nCurRow ); + } + } + } + } + else + { + //if nothing is selected and the current row isn't the upper one + //select the current and one row above + //otherwise select only the upper row + if(m_nCurRow>0) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow--; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion( m_nCurRow+1, m_nCurRow ); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + } + m_pSelEngine->SetAnchor(true); + m_nAnchor = m_nCurRow; + ensureVisible(m_nCurColumn, m_nCurRow); + selectionChanged = true; + bSuccess = true; + } + } + break; + case cursorSelectRowDown: + { + if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE) + bSuccess = false; + else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single) + { + bSuccess = false; + } + else + { + if(!m_aSelectedRows.empty()) + { + //the anchor wasn't set -> a region is not selected, that's why clear all selection + //and select the current row + if(m_nAnchor==-1) + { + invalidateSelectedRows(); + m_aSelectedRows.clear(); + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + else + { + //a region is already selected, prevRow is last selected row and the row beneath - nextRow - should be selected + int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); + int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow+1); + if(prevRow>-1) + { + //if m_nCurRow isn't the last one, can move down, otherwise not + if(m_nCurRow<m_nRowCount-1) + m_nCurRow++; + else + return true; + //if next row already selected, deselect it, otherwise select it + if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow) + { + m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow); + invalidateRow( m_nCurRow - 1 ); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + } + else + { + if(m_nCurRow<m_nRowCount-1) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow++; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion( m_nCurRow-1, m_nCurRow ); + } + } + } + } + else + { + //there wasn't any selection, select current and row beneath, otherwise only row beneath + if(m_nCurRow<m_nRowCount-1) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow++; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion( m_nCurRow-1, m_nCurRow ); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateRow( m_nCurRow ); + } + } + m_pSelEngine->SetAnchor(true); + m_nAnchor = m_nCurRow; + ensureVisible(m_nCurColumn, m_nCurRow); + selectionChanged = true; + bSuccess = true; + } + } + break; + + case cursorSelectRowAreaTop: + { + if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE) + bSuccess = false; + else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single) + bSuccess = false; + else + { + //select the region between the current and the upper row + RowPos iter = m_nCurRow; + invalidateSelectedRegion( m_nCurRow, 0 ); + //put the rows in vector + while(iter>=0) + { + if ( !isRowSelected( iter ) ) + m_aSelectedRows.push_back(iter); + --iter; + } + m_nCurRow = 0; + m_nAnchor = m_nCurRow; + m_pSelEngine->SetAnchor(true); + ensureVisible(m_nCurColumn, 0); + selectionChanged = true; + bSuccess = true; + } + } + break; + + case cursorSelectRowAreaBottom: + { + if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE) + return false; + else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single) + return false; + //select the region between the current and the last row + RowPos iter = m_nCurRow; + invalidateSelectedRegion( m_nCurRow, m_nRowCount-1 ); + //put the rows in the vector + while(iter<=m_nRowCount) + { + if ( !isRowSelected( iter ) ) + m_aSelectedRows.push_back(iter); + ++iter; + } + m_nCurRow = m_nRowCount-1; + m_nAnchor = m_nCurRow; + m_pSelEngine->SetAnchor(true); + ensureVisible(m_nCurColumn, m_nRowCount-1); + selectionChanged = true; + bSuccess = true; + } + break; + default: + OSL_FAIL( "TableControl_Impl::dispatchAction: unsupported action!" ); + break; + } + + if ( bSuccess && selectionChanged ) + { + m_rAntiImpl.Select(); + } + + return bSuccess; + } + + + void TableControl_Impl::impl_ni_doSwitchCursor( bool _bShow ) + { + PTableRenderer pRenderer = m_pModel ? m_pModel->getRenderer() : PTableRenderer(); + if ( pRenderer ) + { + tools::Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, m_nCurRow, aCellRect ); + if ( _bShow ) + pRenderer->ShowCellCursor( *m_pDataWindow, aCellRect ); + else + pRenderer->HideCellCursor( *m_pDataWindow ); + } + } + + + void TableControl_Impl::impl_getCellRect( ColPos _nColumn, RowPos _nRow, tools::Rectangle& _rCellRect ) const + { + if ( !m_pModel + || ( COL_INVALID == _nColumn ) + || ( ROW_INVALID == _nRow ) + ) + { + _rCellRect.SetEmpty(); + return; + } + + TableCellGeometry aCell( *this, impl_getAllVisibleCellsArea(), _nColumn, _nRow ); + _rCellRect = aCell.getRect(); + } + + + RowPos TableControl_Impl::getRowAtPoint( const Point& rPoint ) const + { + return impl_getRowForAbscissa( rPoint.Y() ); + } + + + ColPos TableControl_Impl::getColAtPoint( const Point& rPoint ) const + { + return impl_getColumnForOrdinate( rPoint.X() ); + } + + + TableCell TableControl_Impl::hitTest( Point const & i_point ) const + { + TableCell aCell( getColAtPoint( i_point ), getRowAtPoint( i_point ) ); + if ( aCell.nColumn > COL_ROW_HEADERS ) + { + PColumnModel const pColumn = m_pModel->getColumnModel( aCell.nColumn ); + MutableColumnMetrics const & rColInfo( m_aColumnWidths[ aCell.nColumn ] ); + if ( ( rColInfo.getEnd() - 3 <= i_point.X() ) + && ( rColInfo.getEnd() >= i_point.X() ) + && pColumn->isResizable() + ) + { + aCell.eArea = ColumnDivider; + } + } + return aCell; + } + + + ColumnMetrics TableControl_Impl::getColumnMetrics( ColPos const i_column ) const + { + ENSURE_OR_RETURN( ( i_column >= 0 ) && ( i_column < m_pModel->getColumnCount() ), + "TableControl_Impl::getColumnMetrics: illegal column index!", ColumnMetrics() ); + return m_aColumnWidths[ i_column ]; + } + + + PTableModel TableControl_Impl::getModel() const + { + return m_pModel; + } + + + ColPos TableControl_Impl::getCurrentColumn() const + { + return m_nCurColumn; + } + + + RowPos TableControl_Impl::getCurrentRow() const + { + return m_nCurRow; + } + + + ::Size TableControl_Impl::getTableSizePixel() const + { + return m_pDataWindow->GetOutputSizePixel(); + } + + + void TableControl_Impl::setPointer( PointerStyle i_pointer ) + { + m_pDataWindow->SetPointer( i_pointer ); + } + + + void TableControl_Impl::captureMouse() + { + m_pDataWindow->CaptureMouse(); + } + + + void TableControl_Impl::releaseMouse() + { + m_pDataWindow->ReleaseMouse(); + } + + + void TableControl_Impl::invalidate( TableArea const i_what ) + { + switch ( i_what ) + { + case TableArea::ColumnHeaders: + m_pDataWindow->Invalidate( calcHeaderRect( true ) ); + break; + + case TableArea::RowHeaders: + m_pDataWindow->Invalidate( calcHeaderRect( false ) ); + break; + + case TableArea::All: + m_pDataWindow->Invalidate(); + m_pDataWindow->GetParent()->Invalidate( InvalidateFlags::Transparent ); + break; + } + } + + + tools::Long TableControl_Impl::pixelWidthToAppFont( tools::Long const i_pixels ) const + { + return m_pDataWindow->PixelToLogic(Size(i_pixels, 0), MapMode(MapUnit::MapAppFont)).Width(); + } + + + tools::Long TableControl_Impl::appFontWidthToPixel( tools::Long const i_appFontUnits ) const + { + return m_pDataWindow->LogicToPixel(Size(i_appFontUnits, 0), MapMode(MapUnit::MapAppFont)).Width(); + } + + + void TableControl_Impl::hideTracking() + { + m_pDataWindow->HideTracking(); + } + + + void TableControl_Impl::showTracking( tools::Rectangle const & i_location, ShowTrackFlags const i_flags ) + { + m_pDataWindow->ShowTracking( i_location, i_flags ); + } + + + void TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row ) + { + goTo( i_col, i_row ); + } + + + void TableControl_Impl::invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow ) + { + // get the visible area of the table control and set the Left and right border of the region to be repainted + tools::Rectangle const aAllCells( impl_getAllVisibleCellsArea() ); + + tools::Rectangle aInvalidateRect; + aInvalidateRect.SetLeft( aAllCells.Left() ); + aInvalidateRect.SetRight( aAllCells.Right() ); + // if only one row is selected + if ( _nPrevRow == _nCurRow ) + { + tools::Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); + aInvalidateRect.SetTop( aCellRect.Top() ); + aInvalidateRect.SetBottom( aCellRect.Bottom() ); + } + //if the region is above the current row + else if(_nPrevRow < _nCurRow ) + { + tools::Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); + aInvalidateRect.SetTop( aCellRect.Top() ); + impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); + aInvalidateRect.SetBottom( aCellRect.Bottom() ); + } + //if the region is beneath the current row + else + { + tools::Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); + aInvalidateRect.SetTop( aCellRect.Top() ); + impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); + aInvalidateRect.SetBottom( aCellRect.Bottom() ); + } + + invalidateRect(aInvalidateRect); + } + + void TableControl_Impl::invalidateRect(const tools::Rectangle &rInvalidateRect) + { + m_pDataWindow->Invalidate( rInvalidateRect, + m_pDataWindow->GetControlBackground().IsTransparent() ? InvalidateFlags::Transparent : InvalidateFlags::NONE ); + } + + + void TableControl_Impl::invalidateSelectedRows() + { + for (auto const& selectedRow : m_aSelectedRows) + { + invalidateRow(selectedRow); + } + } + + + void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow ) + { + RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow; + RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1; + RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow; + + tools::Rectangle aInvalidateRect; + + tools::Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() ); + TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true ); + while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) ) + { + aInvalidateRect.Union( aRow.getRect() ); + aRow.moveDown(); + } + + if ( i_lastRow == ROW_INVALID ) + aInvalidateRect.SetBottom( m_pDataWindow->GetOutputSizePixel().Height() ); + + invalidateRect(aInvalidateRect); + } + + + void TableControl_Impl::checkCursorPosition() + { + + TableSize nVisibleRows = impl_getVisibleRows(true); + TableSize nVisibleCols = impl_getVisibleColumns(true); + if ( ( m_nTopRow + nVisibleRows > m_nRowCount ) + && ( m_nRowCount >= nVisibleRows ) + ) + { + --m_nTopRow; + } + else + { + m_nTopRow = 0; + } + + if ( ( m_nLeftColumn + nVisibleCols > m_nColumnCount ) + && ( m_nColumnCount >= nVisibleCols ) + ) + { + --m_nLeftColumn; + } + else + { + m_nLeftColumn = 0; + } + + m_pDataWindow->Invalidate(); + } + + + TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const + { + DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" ); + + return lcl_getRowsFittingInto( + m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel, + m_nRowHeightPixel, + _bAcceptPartialRow + ); + } + + + TableSize TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol ) const + { + DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" ); + + return lcl_getColumnsVisibleWithin( + tools::Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ), + m_nLeftColumn, + *this, + _bAcceptPartialCol + ); + } + + + bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow ) + { + // TODO: give veto listeners a chance + + if ( ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount ) + || ( _nRow < 0 ) || ( _nRow >= m_nRowCount ) + ) + { + OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" ); + return false; + } + + SuppressCursor aHideCursor( *this ); + m_nCurColumn = _nColumn; + m_nCurRow = _nRow; + + // ensure that the new cell is visible + ensureVisible( m_nCurColumn, m_nCurRow ); + return true; + } + + + void TableControl_Impl::ensureVisible( ColPos _nColumn, RowPos _nRow ) + { + DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount ) + && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ), + "TableControl_Impl::ensureVisible: invalid coordinates!" ); + + SuppressCursor aHideCursor( *this ); + + if ( _nColumn < m_nLeftColumn ) + impl_scrollColumns( _nColumn - m_nLeftColumn ); + else + { + TableSize nVisibleColumns = impl_getVisibleColumns( false/*bAcceptPartialVisibility*/ ); + if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 ) + { + impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) ); + // TODO: since not all columns have the same width, this might in theory result + // in the column still not being visible. + } + } + + if ( _nRow < m_nTopRow ) + impl_scrollRows( _nRow - m_nTopRow ); + else + { + TableSize nVisibleRows = impl_getVisibleRows( false/*_bAcceptPartialVisibility*/ ); + if ( _nRow > m_nTopRow + nVisibleRows - 1 ) + impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) ); + } + } + + + OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col ) + { + Any aCellValue; + m_pModel->getCellContent( i_col, i_row, aCellValue ); + + OUString sCellStringContent; + m_pModel->getRenderer()->GetFormattedCellString( aCellValue, sCellStringContent ); + + return sCellStringContent; + } + + + TableSize TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta ) + { + // compute new top row + RowPos nNewTopRow = + ::std::max( + ::std::min( static_cast<RowPos>( m_nTopRow + _nRowDelta ), static_cast<RowPos>( m_nRowCount - 1 ) ), + RowPos(0) + ); + + RowPos nOldTopRow = m_nTopRow; + m_nTopRow = nNewTopRow; + + // if updates are enabled currently, scroll the viewport + if ( m_nTopRow != nOldTopRow ) + { + SuppressCursor aHideCursor( *this ); + // TODO: call an onStartScroll at our listener (or better an own onStartScroll, + // which hides the cursor and then calls the listener) + // Same for onEndScroll + + // scroll the view port, if possible + tools::Long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow ); + + tools::Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() ); + + if ( m_pDataWindow->GetBackground().IsScrollable() + && std::abs( nPixelDelta ) < aDataArea.GetHeight() + ) + { + m_pDataWindow->Scroll( 0, static_cast<tools::Long>(-nPixelDelta), aDataArea, ScrollFlags::Clip | ScrollFlags::Update | ScrollFlags::Children); + } + else + { + m_pDataWindow->Invalidate( InvalidateFlags::Update ); + m_pDataWindow->GetParent()->Invalidate( InvalidateFlags::Transparent ); + } + + // update the position at the vertical scrollbar + if ( m_pVScroll != nullptr ) + m_pVScroll->SetThumbPos( m_nTopRow ); + } + + // The scroll bar availability might change when we scrolled. + // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10. + // Now let + // - the user scroll to row number 6, so the last 5 rows are visible + // - somebody remove the last 4 rows + // - the user scroll to row number 5 being the top row, so the last two rows are visible + // - somebody remove row number 6 + // - the user scroll to row number 1 + // => in this case, the need for the scrollbar vanishes immediately. + if ( m_nTopRow == 0 ) + m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); + + return static_cast<TableSize>( m_nTopRow - nOldTopRow ); + } + + + TableSize TableControl_Impl::impl_scrollRows( TableSize const i_rowDelta ) + { + return impl_ni_ScrollRows( i_rowDelta ); + } + + + TableSize TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta ) + { + // compute new left column + const ColPos nNewLeftColumn = + ::std::max( + ::std::min( static_cast<ColPos>( m_nLeftColumn + _nColumnDelta ), static_cast<ColPos>( m_nColumnCount - 1 ) ), + ColPos(0) + ); + + const ColPos nOldLeftColumn = m_nLeftColumn; + m_nLeftColumn = nNewLeftColumn; + + // if updates are enabled currently, scroll the viewport + if ( m_nLeftColumn != nOldLeftColumn ) + { + SuppressCursor aHideCursor( *this ); + // TODO: call an onStartScroll at our listener (or better an own onStartScroll, + // which hides the cursor and then calls the listener) + // Same for onEndScroll + + // scroll the view port, if possible + const tools::Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() ); + + tools::Long nPixelDelta = + m_aColumnWidths[ nOldLeftColumn ].getStart() + - m_aColumnWidths[ m_nLeftColumn ].getStart(); + + // update our column positions + // Do this *before* scrolling, as ScrollFlags::Update will trigger a paint, which already needs the correct + // information in m_aColumnWidths + for (auto & columnWidth : m_aColumnWidths) + { + columnWidth.move(nPixelDelta); + } + + // scroll the window content (if supported and possible), or invalidate the complete window + if ( m_pDataWindow->GetBackground().IsScrollable() + && std::abs( nPixelDelta ) < aDataArea.GetWidth() + ) + { + m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, ScrollFlags::Clip | ScrollFlags::Update ); + } + else + { + m_pDataWindow->Invalidate( InvalidateFlags::Update ); + m_pDataWindow->GetParent()->Invalidate( InvalidateFlags::Transparent ); + } + + // update the position at the horizontal scrollbar + if ( m_pHScroll != nullptr ) + m_pHScroll->SetThumbPos( m_nLeftColumn ); + } + + // The scroll bar availability might change when we scrolled. This is because we do not hide + // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will + // be auto-hidden when it's scrolled back to pos 0. + if ( m_nLeftColumn == 0 ) + m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); + + return static_cast<TableSize>( m_nLeftColumn - nOldLeftColumn ); + } + + + TableSize TableControl_Impl::impl_scrollColumns( TableSize const i_columnDelta ) + { + return impl_ni_ScrollColumns( i_columnDelta ); + } + + + SelectionEngine* TableControl_Impl::getSelEngine() + { + return m_pSelEngine.get(); + } + + bool TableControl_Impl::isRowSelected( RowPos i_row ) const + { + return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end(); + } + + + RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const + { + if ( i_selectionIndex < m_aSelectedRows.size() ) + return m_aSelectedRows[ i_selectionIndex ]; + return ROW_INVALID; + } + + + int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current) + { + std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current); + if ( it != selectedRows.end() ) + { + return it - selectedRows.begin(); + } + return -1; + } + + + ColPos TableControl_Impl::impl_getColumnForOrdinate( tools::Long const i_ordinate ) const + { + if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) ) + return COL_INVALID; + + if ( i_ordinate < m_nRowHeaderWidthPixel ) + return COL_ROW_HEADERS; + + ColumnPositions::const_iterator lowerBound = ::std::lower_bound( + m_aColumnWidths.begin(), + m_aColumnWidths.end(), + MutableColumnMetrics(i_ordinate+1, i_ordinate+1), + ColumnInfoPositionLess() + ); + if ( lowerBound == m_aColumnWidths.end() ) + { + // point is *behind* the start of the last column ... + if ( i_ordinate < m_aColumnWidths.rbegin()->getEnd() ) + // ... but still before its end + return m_nColumnCount - 1; + return COL_INVALID; + } + return lowerBound - m_aColumnWidths.begin(); + } + + + RowPos TableControl_Impl::impl_getRowForAbscissa( tools::Long const i_abscissa ) const + { + if ( i_abscissa < 0 ) + return ROW_INVALID; + + if ( i_abscissa < m_nColHeaderHeightPixel ) + return ROW_COL_HEADERS; + + tools::Long const abscissa = i_abscissa - m_nColHeaderHeightPixel; + tools::Long const row = m_nTopRow + abscissa / m_nRowHeightPixel; + return row < m_pModel->getRowCount() ? row : ROW_INVALID; + } + + + bool TableControl_Impl::markRowAsDeselected( RowPos const i_rowIndex ) + { + ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex ); + if ( selPos == m_aSelectedRows.end() ) + return false; + + m_aSelectedRows.erase( selPos ); + return true; + } + + + bool TableControl_Impl::markRowAsSelected( RowPos const i_rowIndex ) + { + if ( isRowSelected( i_rowIndex ) ) + return false; + + SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); + switch ( eSelMode ) + { + case SelectionMode::Single: + if ( !m_aSelectedRows.empty() ) + { + OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" ); + m_aSelectedRows[0] = i_rowIndex; + break; + } + [[fallthrough]]; + + case SelectionMode::Multiple: + m_aSelectedRows.push_back( i_rowIndex ); + break; + + default: + OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" ); + return false; + } + + return true; + } + + + bool TableControl_Impl::markAllRowsAsDeselected() + { + if ( m_aSelectedRows.empty() ) + return false; + + m_aSelectedRows.clear(); + return true; + } + + + bool TableControl_Impl::markAllRowsAsSelected() + { + SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); + ENSURE_OR_RETURN_FALSE( eSelMode == SelectionMode::Multiple, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" ); + + if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) ) + { + #if OSL_DEBUG_LEVEL > 0 + for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row ) + { + OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" ); + } + #endif + // already all rows marked as selected + return false; + } + + m_aSelectedRows.clear(); + for ( RowPos i=0; i < m_pModel->getRowCount(); ++i ) + m_aSelectedRows.push_back(i); + + return true; + } + + + void TableControl_Impl::commitAccessibleEvent( sal_Int16 const i_eventID ) + { + impl_commitAccessibleEvent( i_eventID, Any() ); + } + + + void TableControl_Impl::commitCellEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) + { + if ( impl_isAccessibleAlive() ) + m_pAccessibleTable->commitCellEvent( i_eventID, i_newValue, i_oldValue ); + } + + + void TableControl_Impl::commitTableEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) + { + if ( impl_isAccessibleAlive() ) + m_pAccessibleTable->commitTableEvent( i_eventID, i_newValue, i_oldValue ); + } + + + tools::Rectangle TableControl_Impl::calcHeaderRect(bool bColHeader) + { + tools::Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() ); + Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() ); + if ( bColHeader ) + return tools::Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) ); + else + return tools::Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) ); + } + + + tools::Rectangle TableControl_Impl::calcHeaderCellRect( bool bColHeader, sal_Int32 nPos ) + { + tools::Rectangle const aHeaderRect = calcHeaderRect( bColHeader ); + TableCellGeometry const aGeometry( + *this, aHeaderRect, + bColHeader ? nPos : COL_ROW_HEADERS, + bColHeader ? ROW_COL_HEADERS : nPos + ); + return aGeometry.getRect(); + } + + + tools::Rectangle TableControl_Impl::calcTableRect() const + { + return impl_getAllVisibleDataCellArea(); + } + + + tools::Rectangle TableControl_Impl::calcCellRect( sal_Int32 nRow, sal_Int32 nCol ) const + { + tools::Rectangle aCellRect; + impl_getCellRect( nRow, nCol, aCellRect ); + return aCellRect; + } + + + IMPL_LINK_NOARG( TableControl_Impl, OnUpdateScrollbars, void*, void ) + { + // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of + // doing a complete re-layout? + impl_ni_relayout(); + } + + + IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar, void ) + { + DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ), + "TableControl_Impl::OnScroll: where did this come from?" ); + + if ( _pScrollbar == m_pVScroll ) + impl_ni_ScrollRows( _pScrollbar->GetDelta() ); + else + impl_ni_ScrollColumns( _pScrollbar->GetDelta() ); + } + + + rtl::Reference<vcl::table::IAccessibleTableControl> TableControl_Impl::getAccessible( vcl::Window& i_parentWindow ) + { + if (m_pAccessibleTable) + return m_pAccessibleTable; + + DBG_TESTSOLARMUTEX(); + if ( m_pAccessibleTable == nullptr ) + { + Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible(); + if ( xAccParent.is() ) + { + m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl( + xAccParent, m_rAntiImpl + ); + } + } + + return m_pAccessibleTable; + } + + + void TableControl_Impl::disposeAccessible() + { + if ( m_pAccessibleTable ) + m_pAccessibleTable->DisposeAccessImpl(); + m_pAccessibleTable = nullptr; + } + + + bool TableControl_Impl::impl_isAccessibleAlive() const + { + return m_pAccessibleTable && m_pAccessibleTable->isAlive(); + } + + + void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue ) + { + if ( impl_isAccessibleAlive() ) + m_pAccessibleTable->commitEvent( i_eventID, i_newValue ); + } + + + //= TableFunctionSet + + + TableFunctionSet::TableFunctionSet(TableControl_Impl* _pTableControl) + :m_pTableControl( _pTableControl) + ,m_nCurrentRow( ROW_INVALID ) + { + } + + TableFunctionSet::~TableFunctionSet() + { + } + + void TableFunctionSet::BeginDrag() + { + } + + void TableFunctionSet::CreateAnchor() + { + m_pTableControl->setAnchor( m_pTableControl->getCurRow() ); + } + + + void TableFunctionSet::DestroyAnchor() + { + m_pTableControl->setAnchor( ROW_INVALID ); + } + + + void TableFunctionSet::SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor) + { + // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click + RowPos newRow = m_pTableControl->getRowAtPoint( rPoint ); + if ( newRow == ROW_COL_HEADERS ) + newRow = m_pTableControl->getTopRow(); + + ColPos newCol = m_pTableControl->getColAtPoint( rPoint ); + if ( newCol == COL_ROW_HEADERS ) + newCol = m_pTableControl->getLeftColumn(); + + if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) ) + return; + + if ( bDontSelectAtCursor ) + { + if ( m_pTableControl->getSelectedRowCount() > 1 ) + m_pTableControl->getSelEngine()->AddAlways(true); + } + else if ( m_pTableControl->getAnchor() == m_pTableControl->getCurRow() ) + { + //selected region lies above the last selection + if( m_pTableControl->getCurRow() >= newRow) + { + //put selected rows in vector + while ( m_pTableControl->getAnchor() >= newRow ) + { + m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); + m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); + } + m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); + } + //selected region lies beneath the last selected row + else + { + while ( m_pTableControl->getAnchor() <= newRow ) + { + m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); + m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); + } + m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); + } + m_pTableControl->invalidateSelectedRegion( m_pTableControl->getCurRow(), newRow ); + } + //no region selected + else + { + if ( !m_pTableControl->hasRowSelection() ) + m_pTableControl->markRowAsSelected( newRow ); + else + { + if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SelectionMode::Single ) + { + DeselectAll(); + m_pTableControl->markRowAsSelected( newRow ); + } + else + { + m_pTableControl->markRowAsSelected( newRow ); + } + } + if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SelectionMode::Single ) + m_pTableControl->getSelEngine()->AddAlways(true); + + m_pTableControl->invalidateRow( newRow ); + } + m_pTableControl->goTo( newCol, newRow ); + } + + bool TableFunctionSet::IsSelectionAtPoint( const Point& rPoint ) + { + m_pTableControl->getSelEngine()->AddAlways(false); + if ( !m_pTableControl->hasRowSelection() ) + return false; + else + { + RowPos curRow = m_pTableControl->getRowAtPoint( rPoint ); + m_pTableControl->setAnchor( ROW_INVALID ); + bool selected = m_pTableControl->isRowSelected( curRow ); + m_nCurrentRow = curRow; + return selected; + } + } + + void TableFunctionSet::DeselectAtPoint( const Point& ) + { + m_pTableControl->invalidateRow( m_nCurrentRow ); + m_pTableControl->markRowAsDeselected( m_nCurrentRow ); + } + + + void TableFunctionSet::DeselectAll() + { + if ( m_pTableControl->hasRowSelection() ) + { + for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i ) + { + RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i); + m_pTableControl->invalidateRow( rowIndex ); + } + + m_pTableControl->markAllRowsAsDeselected(); + } + } + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tablecontrol_impl.hxx b/toolkit/source/controls/table/tablecontrol_impl.hxx new file mode 100644 index 0000000000..a9498958e7 --- /dev/null +++ b/toolkit/source/controls/table/tablecontrol_impl.hxx @@ -0,0 +1,480 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/XAccessible.hpp> +#include <controls/table/tablemodel.hxx> +#include <controls/table/tablecontrolinterface.hxx> + +#include <vcl/svtaccessiblefactory.hxx> +#include <vcl/accessibletable.hxx> + +#include <vcl/seleng.hxx> + +#include <vector> + +class ScrollBar; +class ScrollBarBox; + +namespace svt::table +{ + struct MutableColumnMetrics : public ColumnMetrics + { + MutableColumnMetrics() + :ColumnMetrics() + { + } + + MutableColumnMetrics( tools::Long const i_startPixel, tools::Long const i_endPixel ) + :ColumnMetrics( i_startPixel, i_endPixel ) + { + } + + tools::Long getStart() const { return nStartPixel; } + tools::Long getEnd() const { return nEndPixel; } + + void move( tools::Long const i_offset ) { nStartPixel += i_offset; nEndPixel += i_offset; } + + tools::Long getWidth() const { return nEndPixel - nStartPixel; } + }; + + struct ColumnInfoPositionLess + { + bool operator()( MutableColumnMetrics const& i_lhs, MutableColumnMetrics const& i_rhs ) + { + return i_lhs.getEnd() < i_rhs.getStart(); + } + }; + + typedef ::std::vector< MutableColumnMetrics > ColumnPositions; + + class TableControl; + class TableDataWindow; + class TableFunctionSet; + + + //= TableControl_Impl + + class TableControl_Impl :public ITableControl + ,public ITableModelListener + { + friend class TableGeometry; + friend class TableRowGeometry; + friend class TableColumnGeometry; + friend class SuspendInvariants; + + private: + /// the control whose impl-instance we implement + TableControl& m_rAntiImpl; + /// the model of the table control + PTableModel m_pModel; + /// the input handler to use, usually the input handler as provided by ->m_pModel + PTableInputHandler m_pInputHandler; + /// info about the widths of our columns + ColumnPositions m_aColumnWidths; + + /// the height of a single row in the table, measured in pixels + tools::Long m_nRowHeightPixel; + /// the height of the column header row in the table, measured in pixels + tools::Long m_nColHeaderHeightPixel; + /// the width of the row header column in the table, measured in pixels + tools::Long m_nRowHeaderWidthPixel; + + /// the number of columns in the table control. Cached model value. + TableSize m_nColumnCount; + + /// the number of rows in the table control. Cached model value. + TableSize m_nRowCount; + + ColPos m_nCurColumn; + RowPos m_nCurRow; + ColPos m_nLeftColumn; + RowPos m_nTopRow; + + sal_Int32 m_nCursorHidden; + + /** the window to contain all data content, including header bars + + The window's upper left corner is at position (0,0), relative to the + table control, which is the direct parent of the data window. + */ + VclPtr<TableDataWindow> m_pDataWindow; + /// the vertical scrollbar, if any + VclPtr<ScrollBar> m_pVScroll; + /// the horizontal scrollbar, if any + VclPtr<ScrollBar> m_pHScroll; + VclPtr<ScrollBarBox> m_pScrollCorner; + //selection engine - for determining selection range, e.g. single, multiple + std::unique_ptr<SelectionEngine> m_pSelEngine; + //vector which contains the selected rows + std::vector<RowPos> m_aSelectedRows; + //part of selection engine + std::unique_ptr<TableFunctionSet> m_pTableFunctionSet; + //part of selection engine + RowPos m_nAnchor; + bool m_bUpdatingColWidths; + + vcl::AccessibleFactoryAccess m_aFactoryAccess; + rtl::Reference<vcl::table::IAccessibleTableControl> m_pAccessibleTable; + + public: + void setModel( const PTableModel& _pModel ); + + const PTableInputHandler& getInputHandler() const { return m_pInputHandler; } + + RowPos getCurRow() const { return m_nCurRow; } + + RowPos getAnchor() const { return m_nAnchor; } + void setAnchor( RowPos const i_anchor ) { m_nAnchor = i_anchor; } + + RowPos getTopRow() const { return m_nTopRow; } + ColPos getLeftColumn() const { return m_nLeftColumn; } + + const TableControl& getAntiImpl() const { return m_rAntiImpl; } + TableControl& getAntiImpl() { return m_rAntiImpl; } + + public: + explicit TableControl_Impl( TableControl& _rAntiImpl ); + virtual ~TableControl_Impl() override; + + /** to be called when the anti-impl instance has been resized + */ + void onResize(); + + /** paints the table control content which intersects with the given rectangle + */ + void doPaintContent(vcl::RenderContext& rRenderContext, const tools::Rectangle& _rUpdateRect); + + /** moves the cursor to the cell with the given coordinates + + To ease the caller's code, the coordinates must not necessarily denote a + valid position. If they don't, <FALSE/> will be returned. + */ + bool goTo( ColPos _nColumn, RowPos _nRow ); + + /** ensures that the given coordinate is visible + @param _nColumn + the column position which should be visible. Must be non-negative, and smaller + than the column count. + @param _nRow + the row position which should be visibleMust be non-negative, and smaller + than the row count. + */ + void ensureVisible( ColPos _nColumn, RowPos _nRow ); + + /** retrieves the content of the given cell, converted to a string + */ + OUString getCellContentAsString( RowPos const i_row, ColPos const i_col ); + + /** returns the position of the current row in the selection vector */ + static int getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current); + + void invalidateRect(const tools::Rectangle &rInvalidateRect); + + /** ??? */ + void invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow ); + + /** invalidates the part of the data window which is covered by the given rows + @param i_firstRow + the index of the first row to include in the invalidation + @param i_lastRow + the index of the last row to include in the invalidation, or ROW_INVALID if the invalidation + should happen down to the bottom of the data window. + */ + void invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow ); + + /** invalidates the part of the data window which is covered by the given row + */ + void invalidateRow( RowPos const i_row ) { invalidateRowRange( i_row, i_row ); } + + /** invalidates all selected rows + */ + void invalidateSelectedRows(); + + void checkCursorPosition(); + + bool hasRowSelection() const { return !m_aSelectedRows.empty(); } + size_t getSelectedRowCount() const { return m_aSelectedRows.size(); } + RowPos getSelectedRowIndex( size_t const i_selectionIndex ) const; + + /** removes the given row index from m_aSelectedRows + + @return + <TRUE/> if and only if the row was previously marked as selected + */ + bool markRowAsDeselected( RowPos const i_rowIndex ); + + /** marks the given row as selected, by putting it into m_aSelectedRows + @return + <TRUE/> if and only if the row was previously <em>not</em> marked as selected + */ + bool markRowAsSelected( RowPos const i_rowIndex ); + + /** marks all rows as deselected + @return + <TRUE/> if and only if the selection actually changed by this operation + */ + bool markAllRowsAsDeselected(); + + /** marks all rows as selected + @return + <FALSE/> if and only if all rows were selected already. + */ + bool markAllRowsAsSelected(); + + void commitAccessibleEvent( sal_Int16 const i_eventID ); + void commitCellEvent( sal_Int16 const i_eventID, const css::uno::Any& i_newValue, const css::uno::Any& i_oldValue ); + void commitTableEvent( sal_Int16 const i_eventID, const css::uno::Any& i_newValue, const css::uno::Any& i_oldValue ); + + // ITableControl + virtual void hideCursor() override; + virtual void showCursor() override; + virtual bool dispatchAction( TableControlAction _eAction ) override; + virtual SelectionEngine* getSelEngine() override; + virtual PTableModel getModel() const override; + virtual ColPos getCurrentColumn() const override; + virtual RowPos getCurrentRow() const override; + virtual void activateCell( ColPos const i_col, RowPos const i_row ) override; + virtual ::Size getTableSizePixel() const override; + virtual void setPointer( PointerStyle i_pointer ) override; + virtual void captureMouse() override; + virtual void releaseMouse() override; + virtual void invalidate( TableArea const i_what ) override; + virtual tools::Long pixelWidthToAppFont( tools::Long const i_pixels ) const override; + virtual void hideTracking() override; + virtual void showTracking( tools::Rectangle const & i_location, ShowTrackFlags const i_flags ) override; + RowPos getRowAtPoint( const Point& rPoint ) const; + ColPos getColAtPoint( const Point& rPoint ) const; + virtual TableCell hitTest( const Point& rPoint ) const override; + virtual ColumnMetrics getColumnMetrics( ColPos const i_column ) const override; + virtual bool isRowSelected( RowPos i_row ) const override; + + + tools::Long appFontWidthToPixel( tools::Long const i_appFontUnits ) const; + + TableDataWindow& getDataWindow() { return *m_pDataWindow; } + const TableDataWindow& getDataWindow() const { return *m_pDataWindow; } + ScrollBar* getHorzScrollbar() { return m_pHScroll; } + ScrollBar* getVertScrollbar() { return m_pVScroll; } + + tools::Rectangle calcHeaderRect( bool bColHeader ); + tools::Rectangle calcHeaderCellRect( bool bColHeader, sal_Int32 nPos ); + tools::Rectangle calcTableRect() const; + tools::Rectangle calcCellRect( sal_Int32 nRow, sal_Int32 nCol ) const; + + // A11Y + rtl::Reference<vcl::table::IAccessibleTableControl> + getAccessible( vcl::Window& i_parentWindow ); + void disposeAccessible(); + + bool isAccessibleAlive() const { return impl_isAccessibleAlive(); } + + // ITableModelListener + virtual void rowsInserted( RowPos first, RowPos last ) override; + virtual void rowsRemoved( RowPos first, RowPos last ) override; + virtual void columnInserted() override; + virtual void columnRemoved() override; + virtual void allColumnsRemoved() override; + virtual void cellsUpdated( RowPos const i_firstRow, RowPos const i_lastRow ) override; + virtual void columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup ) override; + virtual void tableMetricsChanged() override; + + private: + bool impl_isAccessibleAlive() const; + void impl_commitAccessibleEvent( + sal_Int16 const i_eventID, + css::uno::Any const & i_newValue + ); + + /** toggles the cursor visibility + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fulfilled. + */ + void impl_ni_doSwitchCursor( bool _bOn ); + + /** returns the number of visible rows. + + @param _bAcceptPartialRow + specifies whether a possible only partially visible last row is + counted, too. + */ + TableSize impl_getVisibleRows( bool _bAcceptPartialRow ) const; + + /** returns the number of visible columns + + The value may change with different horizontal scroll positions, as + different columns have different widths. For instance, if your control is + 100 pixels wide, and has three columns of width 50, 50, 100, respectively, + then this method will return either "2" or "1", depending on which column + is the first visible one. + + @param _bAcceptPartialRow + specifies whether a possible only partially visible last row is + counted, too. + */ + TableSize impl_getVisibleColumns( bool _bAcceptPartialCol ) const; + + /** determines the rectangle occupied by the given cell + */ + void impl_getCellRect( ColPos _nColumn, RowPos _nRow, tools::Rectangle& _rCellRect ) const; + + /** updates all cached model values + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fulfilled. + */ + void impl_ni_updateCachedModelValues(); + + /** updates the cached table metrics (row height etc.) + */ + void impl_ni_updateCachedTableMetrics(); + + /** does a relayout of the table control + + Column widths, and consequently the availability of the vertical and horizontal scrollbar, are updated + with a call to this method. + + @param i_assumeInflexibleColumnsUpToIncluding + the index of a column up to which all columns should be considered as inflexible, or + <code>COL_INVALID</code>. + */ + void impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding = COL_INVALID ); + + /** calculates the new width of our columns, taking into account their min and max widths, and their relative + flexibility. + + @param i_assumeInflexibleColumnsUpToIncluding + the index of a column up to which all columns should be considered as inflexible, or + <code>COL_INVALID</code>. + + @param i_assumeVerticalScrollbar + controls whether or not we should assume the presence of a vertical scrollbar. If <true/>, and + if the model has a VerticalScrollbarVisibility != ScrollbarShowNever, the method will leave + space for a vertical scrollbar. + + @return + the overall width of the grid, which is available for columns + */ + tools::Long impl_ni_calculateColumnWidths( + ColPos const i_assumeInflexibleColumnsUpToIncluding, + bool const i_assumeVerticalScrollbar, + ::std::vector< tools::Long >& o_newColWidthsPixel + ) const; + + /** positions all child windows, e.g. the both scrollbars, the corner window, and the data window + */ + void impl_ni_positionChildWindows( + tools::Rectangle const & i_dataCellPlayground, + bool const i_verticalScrollbar, + bool const i_horizontalScrollbar + ); + + /** scrolls the view by the given number of rows + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fulfilled. + + @return + the number of rows by which the viewport was scrolled. This may differ + from the given numbers to scroll in case the begin or the end of the + row range were reached. + */ + TableSize impl_ni_ScrollRows( TableSize _nRowDelta ); + + /** equivalent to impl_ni_ScrollRows, but checks the instances invariants beforehand (in a non-product build only) + */ + TableSize impl_scrollRows( TableSize const i_rowDelta ); + + /** scrolls the view by the given number of columns + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fulfilled. + + @return + the number of columns by which the viewport was scrolled. This may differ + from the given numbers to scroll in case the begin or the end of the + column range were reached. + */ + TableSize impl_ni_ScrollColumns( TableSize _nColumnDelta ); + + /** equivalent to impl_ni_ScrollColumns, but checks the instances invariants beforehand (in a non-product build only) + */ + TableSize impl_scrollColumns( TableSize const i_columnDelta ); + + /** retrieves the area occupied by the totality of (at least partially) visible cells + + The returned area includes row and column headers. Also, it takes into + account the fact that there might be less columns than would normally + find room in the control. + + As a result of respecting the partial visibility of rows and columns, + the returned area might be larger than the data window's output size. + */ + tools::Rectangle impl_getAllVisibleCellsArea() const; + + /** retrieves the area occupied by all (at least partially) visible data cells. + + Effectively, the returned area is the same as returned by ->impl_getAllVisibleCellsArea, + minus the row and column header areas. + */ + tools::Rectangle impl_getAllVisibleDataCellArea() const; + + /** retrieves the column which covers the given ordinate + */ + ColPos impl_getColumnForOrdinate( tools::Long const i_ordinate ) const; + + /** retrieves the row which covers the given abscissa + */ + RowPos impl_getRowForAbscissa( tools::Long const i_abscissa ) const; + + /// invalidates the window area occupied by the given column + void impl_invalidateColumn( ColPos const i_column ); + + DECL_LINK( OnScroll, ScrollBar*, void ); + DECL_LINK( OnUpdateScrollbars, void*, void ); + }; + + //see seleng.hxx, seleng.cxx, FunctionSet overridables, part of selection engine + class TableFunctionSet : public FunctionSet + { + private: + TableControl_Impl* m_pTableControl; + RowPos m_nCurrentRow; + + public: + explicit TableFunctionSet(TableControl_Impl* _pTableControl); + virtual ~TableFunctionSet() override; + + virtual void BeginDrag() override; + virtual void CreateAnchor() override; + virtual void DestroyAnchor() override; + virtual void SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor = false) override; + virtual bool IsSelectionAtPoint( const Point& rPoint ) override; + virtual void DeselectAtPoint( const Point& rPoint ) override; + virtual void DeselectAll() override; + }; + + +} // namespace svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tabledatawindow.cxx b/toolkit/source/controls/table/tabledatawindow.cxx new file mode 100644 index 0000000000..fc49ee08d6 --- /dev/null +++ b/toolkit/source/controls/table/tabledatawindow.cxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <controls/table/tablecontrol.hxx> + +#include "tabledatawindow.hxx" +#include "tablecontrol_impl.hxx" +#include "tablegeometry.hxx" + +#include <vcl/help.hxx> +#include <vcl/toolkit/scrbar.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandevent.hxx> + +namespace svt::table +{ + using css::uno::Any; + + TableDataWindow::TableDataWindow( TableControl_Impl& _rTableControl ) + :Window( &_rTableControl.getAntiImpl() ) + ,m_rTableControl( _rTableControl ) + { + // by default, use the background as determined by the style settings + const Color aWindowColor( GetSettings().GetStyleSettings().GetFieldColor() ); + SetBackground( Wallpaper( aWindowColor ) ); + GetOutDev()->SetFillColor( aWindowColor ); + } + + TableDataWindow::~TableDataWindow() + { + disposeOnce(); + } + + void TableDataWindow::dispose() + { + impl_hideTipWindow(); + Window::dispose(); + } + + void TableDataWindow::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rUpdateRect ) + { + m_rTableControl.doPaintContent(rRenderContext, rUpdateRect); + } + + void TableDataWindow::RequestHelp( const HelpEvent& rHEvt ) + { + HelpEventMode const nHelpMode = rHEvt.GetMode(); + if ( IsMouseCaptured() + || !( nHelpMode & HelpEventMode::QUICK ) + ) + { + Window::RequestHelp( rHEvt ); + return; + } + + OUString sHelpText; + QuickHelpFlags nHelpStyle = QuickHelpFlags::NONE; + + Point const aMousePos( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) ); + RowPos const hitRow = m_rTableControl.getRowAtPoint( aMousePos ); + ColPos const hitCol = m_rTableControl.getColAtPoint( aMousePos ); + + PTableModel const pTableModel( m_rTableControl.getModel() ); + if ( ( hitCol >= 0 ) && ( hitCol < pTableModel->getColumnCount() ) ) + { + if ( hitRow == ROW_COL_HEADERS ) + { + sHelpText = pTableModel->getColumnModel( hitCol )->getHelpText(); + } + else if ( ( hitRow >= 0 ) && ( hitRow < pTableModel->getRowCount() ) ) + { + Any aCellToolTip; + pTableModel->getCellToolTip( hitCol, hitRow, aCellToolTip ); + if ( !aCellToolTip.hasValue() ) + { + // use the cell content + pTableModel->getCellContent( hitCol, hitRow, aCellToolTip ); + + // use the cell content as tool tip only if it doesn't fit into the cell. + tools::Rectangle const aWindowRect( Point( 0, 0 ), GetOutputSizePixel() ); + TableCellGeometry const aCell( m_rTableControl, aWindowRect, hitCol, hitRow ); + tools::Rectangle const aCellRect( aCell.getRect() ); + + PTableRenderer const pRenderer = pTableModel->getRenderer(); + if ( pRenderer->FitsIntoCell( aCellToolTip, *GetOutDev(), aCellRect ) ) + aCellToolTip.clear(); + } + + pTableModel->getRenderer()->GetFormattedCellString( aCellToolTip, sHelpText ); + + if ( sHelpText.indexOf( '\n' ) >= 0 ) + nHelpStyle = QuickHelpFlags::TipStyleBalloon; + } + } + + if ( !sHelpText.isEmpty() ) + { + // hide the standard (singleton) help window, so we do not have two help windows open at the same time + Help::HideBalloonAndQuickHelp(); + + tools::Rectangle const aControlScreenRect( + OutputToScreenPixel( Point( 0, 0 ) ), + GetOutputSizePixel() + ); + + Help::ShowQuickHelp(this, aControlScreenRect, sHelpText, nHelpStyle); + } + else + { + impl_hideTipWindow(); + Window::RequestHelp( rHEvt ); + } + } + + void TableDataWindow::impl_hideTipWindow() + { + Help::HideBalloonAndQuickHelp(); + } + + void TableDataWindow::MouseMove( const MouseEvent& rMEvt ) + { + if ( rMEvt.IsLeaveWindow() ) + impl_hideTipWindow(); + + if ( !m_rTableControl.getInputHandler()->MouseMove( m_rTableControl, rMEvt ) ) + { + Window::MouseMove( rMEvt ); + } + } + + void TableDataWindow::MouseButtonDown( const MouseEvent& rMEvt ) + { + impl_hideTipWindow(); + + Point const aPoint = rMEvt.GetPosPixel(); + RowPos const hitRow = m_rTableControl.getRowAtPoint( aPoint ); + bool const wasRowSelected = m_rTableControl.isRowSelected( hitRow ); + size_t const nPrevSelRowCount = m_rTableControl.getSelectedRowCount(); + + if ( !m_rTableControl.getInputHandler()->MouseButtonDown( m_rTableControl, rMEvt ) ) + { + Window::MouseButtonDown( rMEvt ); + return; + } + + bool const isRowSelected = m_rTableControl.isRowSelected( hitRow ); + size_t const nCurSelRowCount = m_rTableControl.getSelectedRowCount(); + if ( isRowSelected != wasRowSelected || nCurSelRowCount != nPrevSelRowCount ) + { + m_aSelectHdl.Call( nullptr ); + } + } + + + void TableDataWindow::MouseButtonUp( const MouseEvent& rMEvt ) + { + if ( !m_rTableControl.getInputHandler()->MouseButtonUp( m_rTableControl, rMEvt ) ) + Window::MouseButtonUp( rMEvt ); + + m_rTableControl.getAntiImpl().GrabFocus(); + } + + + bool TableDataWindow::EventNotify(NotifyEvent& rNEvt ) + { + bool bDone = false; + if ( rNEvt.GetType() == NotifyEventType::COMMAND ) + { + const CommandEvent& rCEvt = *rNEvt.GetCommandEvent(); + if ( rCEvt.GetCommand() == CommandEventId::Wheel ) + { + const CommandWheelData* pData = rCEvt.GetWheelData(); + if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) ) + { + bDone = HandleScrollCommand( rCEvt, m_rTableControl.getHorzScrollbar(), m_rTableControl.getVertScrollbar() ); + } + } + } + return bDone || Window::EventNotify( rNEvt ); + } + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tabledatawindow.hxx b/toolkit/source/controls/table/tabledatawindow.hxx new file mode 100644 index 0000000000..e42a054939 --- /dev/null +++ b/toolkit/source/controls/table/tabledatawindow.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <vcl/window.hxx> + + +namespace svt::table +{ + class TableControl_Impl; + class TableFunctionSet; + + /** the window containing the content area (including headers) of + a table control + */ + class TableDataWindow : public vcl::Window + { + friend class TableFunctionSet; + private: + TableControl_Impl& m_rTableControl; + Link<LinkParamNone*,void> m_aSelectHdl; + + public: + explicit TableDataWindow( TableControl_Impl& _rTableControl ); + virtual ~TableDataWindow() override; + virtual void dispose() override; + + void SetSelectHdl(const Link<LinkParamNone*,void>& rLink) + { + m_aSelectHdl = rLink; + } + + // Window overridables + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual void MouseMove( const MouseEvent& rMEvt) override; + virtual void MouseButtonDown( const MouseEvent& rMEvt) override; + virtual void MouseButtonUp( const MouseEvent& rMEvt) override; + virtual bool EventNotify(NotifyEvent& rNEvt) override; + virtual void RequestHelp( const HelpEvent& rHEvt ) override; + + private: + static void impl_hideTipWindow(); + }; + +} // namespace svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tablegeometry.cxx b/toolkit/source/controls/table/tablegeometry.cxx new file mode 100644 index 0000000000..5b18826c50 --- /dev/null +++ b/toolkit/source/controls/table/tablegeometry.cxx @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "tablegeometry.hxx" +#include "tablecontrol_impl.hxx" + + +namespace svt::table +{ + + + //= TableRowGeometry + + + TableRowGeometry::TableRowGeometry( TableControl_Impl const & _rControl, tools::Rectangle const & _rBoundaries, + RowPos const _nRow, bool const i_allowVirtualRows ) + :TableGeometry( _rControl, _rBoundaries ) + ,m_nRowPos( _nRow ) + ,m_bAllowVirtualRows( i_allowVirtualRows ) + { + if ( m_nRowPos == ROW_COL_HEADERS ) + { + m_aRect.SetTop( 0 ); + m_aRect.SetBottom( m_rControl.m_nColHeaderHeightPixel - 1 ); + } + else + { + impl_initRect(); + } + } + + + void TableRowGeometry::impl_initRect() + { + if ( ( m_nRowPos >= m_rControl.m_nTopRow ) && impl_isValidRow( m_nRowPos ) ) + { + m_aRect.SetTop( m_rControl.m_nColHeaderHeightPixel + ( m_nRowPos - m_rControl.m_nTopRow ) * m_rControl.m_nRowHeightPixel ); + m_aRect.SetBottom( m_aRect.Top() + m_rControl.m_nRowHeightPixel - 1 ); + } + else + m_aRect.SetEmpty(); + } + + + bool TableRowGeometry::impl_isValidRow( RowPos const i_row ) const + { + return m_bAllowVirtualRows || ( i_row < m_rControl.m_pModel->getRowCount() ); + } + + + bool TableRowGeometry::moveDown() + { + if ( m_nRowPos == ROW_COL_HEADERS ) + { + m_nRowPos = m_rControl.m_nTopRow; + impl_initRect(); + } + else + { + if ( impl_isValidRow( ++m_nRowPos ) ) + m_aRect.Move( 0, m_rControl.m_nRowHeightPixel ); + else + m_aRect.SetEmpty(); + } + return isValid(); + } + + + //= TableColumnGeometry + + + TableColumnGeometry::TableColumnGeometry( TableControl_Impl const & _rControl, tools::Rectangle const & _rBoundaries, + ColPos const _nCol ) + :TableGeometry( _rControl, _rBoundaries ) + ,m_nColPos( _nCol ) + { + if ( m_nColPos == COL_ROW_HEADERS ) + { + m_aRect.SetLeft( 0 ); + m_aRect.SetRight( m_rControl.m_nRowHeaderWidthPixel - 1 ); + } + else + { + impl_initRect(); + } + } + + + void TableColumnGeometry::impl_initRect() + { + ColPos nLeftColumn = m_rControl.m_nLeftColumn; + if ( ( m_nColPos >= nLeftColumn ) && impl_isValidColumn( m_nColPos ) ) + { + m_aRect.SetLeft( m_rControl.m_nRowHeaderWidthPixel ); + // TODO: take into account any possibly frozen columns + + for ( ColPos col = nLeftColumn; col < m_nColPos; ++col ) + m_aRect.AdjustLeft(m_rControl.m_aColumnWidths[ col ].getWidth() ); + m_aRect.SetRight( m_aRect.Left() + m_rControl.m_aColumnWidths[ m_nColPos ].getWidth() - 1 ); + } + else + m_aRect.SetEmpty(); + } + + + bool TableColumnGeometry::impl_isValidColumn( ColPos const i_column ) const + { + return i_column < ColPos( m_rControl.m_aColumnWidths.size() ); + } + + + bool TableColumnGeometry::moveRight() + { + if ( m_nColPos == COL_ROW_HEADERS ) + { + m_nColPos = m_rControl.m_nLeftColumn; + impl_initRect(); + } + else + { + if ( impl_isValidColumn( ++m_nColPos ) ) + { + m_aRect.SetLeft( m_aRect.Right() + 1 ); + m_aRect.AdjustRight(m_rControl.m_aColumnWidths[ m_nColPos ].getWidth() ); + } + else + m_aRect.SetEmpty(); + } + + return isValid(); + } + + +} // namespace svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/table/tablegeometry.hxx b/toolkit/source/controls/table/tablegeometry.hxx new file mode 100644 index 0000000000..dde156ffd2 --- /dev/null +++ b/toolkit/source/controls/table/tablegeometry.hxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <controls/table/tabletypes.hxx> +#include <tools/gen.hxx> + +namespace svt::table +{ + + + class TableControl_Impl; + + + //= TableGeometry + + class TableGeometry + { + protected: + const TableControl_Impl& m_rControl; + const tools::Rectangle& m_rBoundaries; + tools::Rectangle m_aRect; + + protected: + TableGeometry( + const TableControl_Impl& _rControl, + const tools::Rectangle& _rBoundaries + ) + :m_rControl( _rControl ) + ,m_rBoundaries( _rBoundaries ) + ,m_aRect( _rBoundaries ) + { + } + + public: + // attribute access + const TableControl_Impl& getControl() const { return m_rControl; } + + // status + const tools::Rectangle& getRect() const { return m_aRect; } + bool isValid() const { return !m_aRect.GetIntersection( m_rBoundaries ).IsEmpty(); } + }; + + + //= TableRowGeometry + + class TableRowGeometry final : public TableGeometry + { + public: + TableRowGeometry( + TableControl_Impl const & _rControl, + tools::Rectangle const & _rBoundaries, + RowPos const _nRow, + bool const i_allowVirtualRows = false + // allow rows >= getRowCount()? + ); + + // status + RowPos getRow() const { return m_nRowPos; } + // operations + bool moveDown(); + + private: + void impl_initRect(); + bool impl_isValidRow( RowPos const i_row ) const; + + RowPos m_nRowPos; + bool m_bAllowVirtualRows; + }; + + + //= TableColumnGeometry + + class TableColumnGeometry final : public TableGeometry + { + public: + TableColumnGeometry( + TableControl_Impl const & _rControl, + tools::Rectangle const & _rBoundaries, + ColPos const _nCol + ); + + // status + ColPos getCol() const { return m_nColPos; } + // operations + bool moveRight(); + + private: + void impl_initRect(); + bool impl_isValidColumn( ColPos const i_column ) const; + + ColPos m_nColPos; + }; + + + //= TableCellGeometry + + /** a helper representing geometry information of a cell + */ + class TableCellGeometry + { + private: + TableRowGeometry m_aRow; + TableColumnGeometry m_aCol; + + public: + TableCellGeometry( + TableControl_Impl const & _rControl, + tools::Rectangle const & _rBoundaries, + ColPos const _nCol, + RowPos const _nRow + ) + :m_aRow( _rControl, _rBoundaries, _nRow, false/*allowVirtualCells*/ ) + ,m_aCol( _rControl, _rBoundaries, _nCol ) + { + } + + TableCellGeometry( + const TableRowGeometry& _rRow, + ColPos _nCol + ) + :m_aRow( _rRow ) + ,m_aCol( _rRow.getControl(), _rRow.getRect(), _nCol ) + { + } + + tools::Rectangle getRect() const { return m_aRow.getRect().GetIntersection( m_aCol.getRect() ); } + ColPos getColumn() const { return m_aCol.getCol(); } + bool isValid() const { return !getRect().IsEmpty(); } + + bool moveRight() {return m_aCol.moveRight(); } + }; + + +} // namespace svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tabpagecontainer.cxx b/toolkit/source/controls/tabpagecontainer.cxx new file mode 100644 index 0000000000..367b5c4f22 --- /dev/null +++ b/toolkit/source/controls/tabpagecontainer.cxx @@ -0,0 +1,351 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <controls/geometrycontrolmodel.hxx> +#include <controls/tabpagecontainer.hxx> +#include <controls/tabpagemodel.hxx> +#include <helper/property.hxx> + +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <o3tl/safeint.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/svapp.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::view; +using ::com::sun::star::awt::tab::XTabPageModel; + +constexpr OUStringLiteral WRONG_TYPE_EXCEPTION = u"Type must be css::awt::tab::XTabPageModel!"; + + +UnoControlTabPageContainerModel::UnoControlTabPageContainerModel( const Reference< XComponentContext >& i_factory ) + :UnoControlTabPageContainerModel_Base( i_factory ) + ,maContainerListeners( *this ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_TEXT ); +} + +OUString UnoControlTabPageContainerModel::getServiceName() +{ + return "com.sun.star.awt.tab.UnoControlTabPageContainerModel"; +} + +uno::Any UnoControlTabPageContainerModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch(nPropId) + { + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString("com.sun.star.awt.tab.UnoControlTabPageContainer") ); + case BASEPROPERTY_BORDER: + return uno::Any(sal_Int16(0)); // No Border + default: + return UnoControlModel::ImplGetDefaultValue( nPropId ); + } +} + +::cppu::IPropertyArrayHelper& UnoControlTabPageContainerModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} +Reference< css::beans::XPropertySetInfo > UnoControlTabPageContainerModel::getPropertySetInfo( ) +{ + static Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +namespace +{ + Reference< XTabPageModel > lcl_createTabPageModel( Reference<XComponentContext> const & i_context, + Sequence< Any > const & i_initArguments, Reference< XPropertySet > const & i_parentModel ) + { + try + { + Reference< XPropertySetInfo > const xPSI( i_parentModel->getPropertySetInfo() ); + bool const isGeometryControlModel = xPSI.is() && xPSI->hasPropertyByName("PositionX"); + + Reference< XInterface > xInstance; + if ( isGeometryControlModel ) + xInstance = *( new OGeometryControlModel< UnoControlTabPageModel >( i_context ) ); + else + xInstance = *( new UnoControlTabPageModel( i_context ) ); + + Reference< XTabPageModel > const xTabPageModel( xInstance, UNO_QUERY_THROW ); + Reference< XInitialization > const xInit( xTabPageModel, UNO_QUERY_THROW ); + xInit->initialize( i_initArguments ); + + return xTabPageModel; + } + catch( const RuntimeException& ) + { + throw; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + return nullptr; + } +} + +Reference< XTabPageModel > SAL_CALL UnoControlTabPageContainerModel::createTabPage( ::sal_Int16 i_tabPageID ) +{ + Sequence< Any > aInitArgs{ Any(i_tabPageID) }; + return lcl_createTabPageModel( m_xContext, aInitArgs, this ); +} + +Reference< XTabPageModel > SAL_CALL UnoControlTabPageContainerModel::loadTabPage( ::sal_Int16 i_tabPageID, const OUString& i_resourceURL ) +{ + Sequence< Any > aInitArgs{ Any(i_tabPageID), Any(i_resourceURL) }; + return lcl_createTabPageModel( m_xContext, aInitArgs, this ); +} + +void SAL_CALL UnoControlTabPageContainerModel::insertByIndex( ::sal_Int32 nIndex, const css::uno::Any& aElement) +{ + SolarMutexGuard aSolarGuard; + uno::Reference < XTabPageModel > xTabPageModel; + if(!(aElement >>= xTabPageModel)) + throw IllegalArgumentException( WRONG_TYPE_EXCEPTION, getXWeak(), 2 ); + + if ( sal_Int32( m_aTabPageVector.size()) ==nIndex ) + m_aTabPageVector.push_back( xTabPageModel ); + else if ( sal_Int32( m_aTabPageVector.size()) > nIndex ) + { + std::vector< uno::Reference< XTabPageModel > >::iterator aIter = m_aTabPageVector.begin(); + aIter += nIndex; + m_aTabPageVector.insert( aIter, xTabPageModel ); + } + else + throw IndexOutOfBoundsException( OUString(), getXWeak() ); + ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Element = aElement; + aEvent.Accessor <<= OUString::number(nIndex); + maContainerListeners.elementInserted( aEvent ); + +} + +void SAL_CALL UnoControlTabPageContainerModel::removeByIndex( ::sal_Int32 /*Index*/ ) +{ +} +// XIndexReplace +void SAL_CALL UnoControlTabPageContainerModel::replaceByIndex( ::sal_Int32 /*Index*/, const uno::Any& /*Element*/ ) +{ +} + +// XIndexAccess +::sal_Int32 SAL_CALL UnoControlTabPageContainerModel::getCount( ) +{ + std::unique_lock aGuard( m_aMutex ); + return sal_Int32( m_aTabPageVector.size()); +} + +uno::Any SAL_CALL UnoControlTabPageContainerModel::getByIndex( ::sal_Int32 nIndex ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( nIndex < 0 || o3tl::make_unsigned(nIndex) > m_aTabPageVector.size() ) + throw lang::IndexOutOfBoundsException(); + return uno::Any(m_aTabPageVector[nIndex]); +} + +// XElementAccess +uno::Type SAL_CALL UnoControlTabPageContainerModel::getElementType( ) +{ + return cppu::UnoType<css::awt::XControlModel>::get(); +} + +sal_Bool SAL_CALL UnoControlTabPageContainerModel::hasElements( ) +{ + std::unique_lock aGuard( m_aMutex ); + return !m_aTabPageVector.empty(); +} +// XContainer +void UnoControlTabPageContainerModel::addContainerListener( const Reference< XContainerListener >& l ) +{ + maContainerListeners.addInterface( l ); +} + +void UnoControlTabPageContainerModel::removeContainerListener( const Reference< XContainerListener >& l ) +{ + maContainerListeners.removeInterface( l ); +} + + + +UnoControlTabPageContainer::UnoControlTabPageContainer( const uno::Reference< uno::XComponentContext >& rxContext ) + :UnoControlTabPageContainer_Base(rxContext) + ,m_aTabPageListeners( *this ) +{ +} + +OUString UnoControlTabPageContainer::GetComponentServiceName() const +{ + return "TabPageContainer"; +} + +void SAL_CALL UnoControlTabPageContainer::dispose( ) +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + m_aTabPageListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + +void UnoControlTabPageContainer::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + if ( m_aTabPageListeners.getLength() ) + xTPContainer->addTabPageContainerListener(&m_aTabPageListeners); +} + + +// XTabPageContainer + +::sal_Int16 SAL_CALL UnoControlTabPageContainer::getActiveTabPageID() +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + return xTPContainer->getActiveTabPageID(); +} +void SAL_CALL UnoControlTabPageContainer::setActiveTabPageID( ::sal_Int16 _activetabpageid ) +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + xTPContainer->setActiveTabPageID(_activetabpageid); +} +::sal_Int16 SAL_CALL UnoControlTabPageContainer::getTabPageCount( ) +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + return xTPContainer->getTabPageCount(); +} +sal_Bool SAL_CALL UnoControlTabPageContainer::isTabPageActive( ::sal_Int16 tabPageIndex ) +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + return xTPContainer->isTabPageActive(tabPageIndex); +} +Reference< css::awt::tab::XTabPage > SAL_CALL UnoControlTabPageContainer::getTabPage( ::sal_Int16 tabPageIndex ) +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + return xTPContainer->getTabPage(tabPageIndex); +} +Reference< css::awt::tab::XTabPage > SAL_CALL UnoControlTabPageContainer::getTabPageByID( ::sal_Int16 tabPageID ) +{ + SolarMutexGuard aSolarGuard; + Reference< XTabPageContainer > xTPContainer( getPeer(), UNO_QUERY_THROW ); + return xTPContainer->getTabPageByID(tabPageID); +} +void SAL_CALL UnoControlTabPageContainer::addTabPageContainerListener( const Reference< css::awt::tab::XTabPageContainerListener >& listener ) +{ + m_aTabPageListeners.addInterface( listener ); + if( getPeer().is() && m_aTabPageListeners.getLength() == 1 ) + { + uno::Reference < awt::tab::XTabPageContainer > xTabPageContainer( getPeer(), uno::UNO_QUERY ); + xTabPageContainer->addTabPageContainerListener( &m_aTabPageListeners ); + } +} +void SAL_CALL UnoControlTabPageContainer::removeTabPageContainerListener( const Reference< css::awt::tab::XTabPageContainerListener >& listener ) +{ + if( getPeer().is() && m_aTabPageListeners.getLength() == 1 ) + { + uno::Reference < awt::tab::XTabPageContainer > xTabPageContainer( getPeer(), uno::UNO_QUERY ); + xTabPageContainer->removeTabPageContainerListener( &m_aTabPageListeners ); + } + m_aTabPageListeners.removeInterface( listener ); +} + +void UnoControlTabPageContainer::propertiesChange(const::css::uno::Sequence<PropertyChangeEvent> &aEvent) +{ + UnoControlTabPageContainer_Base::propertiesChange(aEvent); + + SolarMutexGuard aSolarGuard; + Reference< XPropertiesChangeListener > xPropertiesChangeListener( getPeer(), UNO_QUERY_THROW ); + return xPropertiesChangeListener->propertiesChange(aEvent); +} + +void UnoControlTabPageContainer::updateFromModel() +{ + UnoControlTabPageContainer_Base::updateFromModel(); + if (!getPeer().is()) + throw RuntimeException("No peer for tabpage container!"); + Reference< XContainerListener > xContainerListener( getPeer(), UNO_QUERY ); + ENSURE_OR_RETURN_VOID( xContainerListener.is(), "UnoListBoxControl::updateFromModel: a peer which is no ItemListListener?!" ); + + ContainerEvent aEvent; + aEvent.Source = getModel(); + const Sequence< Reference< XControl > > aControls = getControls(); + + for ( const Reference< XControl >& rCtrl : aControls ) + { + aEvent.Element <<= rCtrl; + xContainerListener->elementInserted( aEvent ); + } +} +void SAL_CALL UnoControlTabPageContainer::addControl( const OUString& Name, const Reference< css::awt::XControl >& Control ) +{ + SolarMutexGuard aSolarGuard; + ControlContainerBase::addControl(Name,Control); + if (!getPeer().is()) + throw RuntimeException("No peer for tabpage container!"); + Reference< XContainerListener > xContainerListener( getPeer(), UNO_QUERY ); + ContainerEvent aEvent; + aEvent.Source = getModel(); + aEvent.Element <<= Control; + xContainerListener->elementInserted( aEvent ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlTabPageContainerModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlTabPageContainerModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlTabPageContainer_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlTabPageContainer(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tabpagemodel.cxx b/toolkit/source/controls/tabpagemodel.cxx new file mode 100644 index 0000000000..7185f82419 --- /dev/null +++ b/toolkit/source/controls/tabpagemodel.cxx @@ -0,0 +1,295 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/tabpagemodel.hxx> + +#include <vcl/svapp.hxx> +#include <helper/property.hxx> +#include <com/sun/star/awt/UnoControlDialogModelProvider.hpp> +#include <com/sun/star/awt/tab/XTabPage.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <tools/debug.hxx> +#include <vcl/outdev.hxx> + +#include <controls/controlmodelcontainerbase.hxx> +#include <controls/unocontrolcontainer.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; + +UnoControlTabPageModel::UnoControlTabPageModel( Reference< XComponentContext > const & i_factory ) + :ControlModelContainerBase( i_factory ) +{ + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_TITLE ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_USERFORMCONTAINEES ); + ImplRegisterProperty( BASEPROPERTY_HSCROLL ); + ImplRegisterProperty( BASEPROPERTY_VSCROLL ); + ImplRegisterProperty( BASEPROPERTY_SCROLLWIDTH ); + ImplRegisterProperty( BASEPROPERTY_SCROLLHEIGHT ); + ImplRegisterProperty( BASEPROPERTY_SCROLLTOP ); + ImplRegisterProperty( BASEPROPERTY_SCROLLLEFT ); + ImplRegisterProperty( BASEPROPERTY_IMAGEURL ); +} + +OUString SAL_CALL UnoControlTabPageModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlTabPageModel"; +} + +css::uno::Sequence< OUString > SAL_CALL UnoControlTabPageModel::getSupportedServiceNames() +{ + css::uno::Sequence< OUString > aNames = ControlModelContainerBase::getSupportedServiceNames( ); + aNames.realloc( aNames.getLength() + 1 ); + aNames.getArray()[ aNames.getLength() - 1 ] = "com.sun.star.awt.tab.UnoControlTabPageModel"; + return aNames; +} + +OUString UnoControlTabPageModel::getServiceName( ) +{ + return "com.sun.star.awt.tab.UnoControlTabPageModel"; +} + +Any UnoControlTabPageModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + Any aAny; + + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + aAny <<= OUString("com.sun.star.awt.tab.UnoControlTabPage"); + break; + case BASEPROPERTY_USERFORMCONTAINEES: + { + // We do not have here any usercontainers (yet?), but let's return empty container back + // so normal properties could be set without triggering UnknownPropertyException + aAny <<= uno::Reference< XNameContainer >(); + break; + } + default: + aAny = UnoControlModel::ImplGetDefaultValue( nPropId ); + } + + return aAny; +} + +::cppu::IPropertyArrayHelper& UnoControlTabPageModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlTabPageModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +////----- XInitialization ------------------------------------------------------------------- +void SAL_CALL UnoControlTabPageModel::initialize (const Sequence<Any>& rArguments) +{ + sal_Int16 nPageId = -1; + if ( rArguments.getLength() == 1 ) + { + if ( !( rArguments[ 0 ] >>= nPageId )) + throw lang::IllegalArgumentException(); + m_nTabPageId = nPageId; + } + else if ( rArguments.getLength() == 2 ) + { + if ( !( rArguments[ 0 ] >>= nPageId )) + throw lang::IllegalArgumentException(); + m_nTabPageId = nPageId; + OUString sURL; + if ( !( rArguments[ 1 ] >>= sURL )) + throw lang::IllegalArgumentException(); + Reference<container::XNameContainer > xDialogModel = awt::UnoControlDialogModelProvider::create( m_xContext, sURL ); + if ( xDialogModel.is() ) + { + const Sequence< OUString> aNames = xDialogModel->getElementNames(); + for(const OUString& rName : aNames) + { + try + { + Any aElement(xDialogModel->getByName(rName)); + xDialogModel->removeByName(rName); + insertByName(rName,aElement); + } + catch(const Exception&) {} + } + Reference<XPropertySet> xDialogProp(xDialogModel,UNO_QUERY); + if ( xDialogProp.is() ) + { + static constexpr OUString s_sResourceResolver = u"ResourceResolver"_ustr; + setPropertyValue(s_sResourceResolver,xDialogProp->getPropertyValue(s_sResourceResolver)); + setPropertyValue(GetPropertyName(BASEPROPERTY_TITLE),xDialogProp->getPropertyValue(GetPropertyName(BASEPROPERTY_TITLE))); + setPropertyValue(GetPropertyName(BASEPROPERTY_HELPTEXT),xDialogProp->getPropertyValue(GetPropertyName(BASEPROPERTY_HELPTEXT))); + setPropertyValue(GetPropertyName(BASEPROPERTY_HELPURL),xDialogProp->getPropertyValue(GetPropertyName(BASEPROPERTY_HELPURL))); + } + } + } + else + m_nTabPageId = -1; +} + + +UnoControlTabPage::UnoControlTabPage( const uno::Reference< uno::XComponentContext >& rxContext ) + :UnoControlTabPage_Base(rxContext) + ,m_bWindowListener(false) +{ + maComponentInfos.nWidth = 280; + maComponentInfos.nHeight = 400; +} +UnoControlTabPage::~UnoControlTabPage() +{ +} + +OUString UnoControlTabPage::GetComponentServiceName() const +{ + return "TabPageModel"; +} + +OUString SAL_CALL UnoControlTabPage::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlTabPage"; +} + +sal_Bool SAL_CALL UnoControlTabPage::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL UnoControlTabPage::getSupportedServiceNames() +{ + return { "com.sun.star.awt.tab.UnoControlTabPage" }; +} + +void SAL_CALL UnoControlTabPage::disposing( const lang::EventObject& Source ) +{ + ControlContainerBase::disposing( Source ); +} + +void UnoControlTabPage::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) +{ + SolarMutexGuard aSolarGuard; + ImplUpdateResourceResolver(); + + UnoControlContainer::createPeer( rxToolkit, rParentPeer ); + + Reference < tab::XTabPage > xTabPage( getPeer(), UNO_QUERY ); + if ( xTabPage.is() ) + { + if ( !m_bWindowListener ) + { + Reference< XWindowListener > xWL(this); + addWindowListener( xWL ); + m_bWindowListener = true; + } + } +} + +static ::Size ImplMapPixelToAppFont( OutputDevice const * pOutDev, const ::Size& aSize ) +{ + ::Size aTmp = pOutDev->PixelToLogic(aSize, MapMode(MapUnit::MapAppFont)); + return aTmp; +} +// css::awt::XWindowListener +void SAL_CALL UnoControlTabPage::windowResized( const css::awt::WindowEvent& e ) +{ + OutputDevice*pOutDev = Application::GetDefaultDevice(); + DBG_ASSERT( pOutDev, "Missing Default Device!" ); + if ( !pOutDev || mbSizeModified ) + return; + + // Currently we are simply using MapUnit::MapAppFont + ::Size aAppFontSize( e.Width, e.Height ); + + Reference< XControl > xDialogControl( *this, UNO_QUERY_THROW ); + Reference< XDevice > xDialogDevice( xDialogControl->getPeer(), UNO_QUERY ); + OSL_ENSURE( xDialogDevice.is(), "UnoDialogControl::windowResized: no peer, but a windowResized event?" ); + if ( xDialogDevice.is() ) + { + DeviceInfo aDeviceInfo( xDialogDevice->getInfo() ); + aAppFontSize.AdjustWidth( -(aDeviceInfo.LeftInset + aDeviceInfo.RightInset) ); + aAppFontSize.AdjustHeight( -(aDeviceInfo.TopInset + aDeviceInfo.BottomInset) ); + } + + aAppFontSize = ImplMapPixelToAppFont( pOutDev, aAppFontSize ); + + // Remember that changes have been done by listener. No need to + // update the position because of property change event. + mbSizeModified = true; + // Properties in a sequence must be sorted! + Sequence< OUString > aProps{ "Height", "Width" }; + Sequence< Any > aValues{ Any(aAppFontSize.Height()), Any(aAppFontSize.Width()) }; + + ImplSetPropertyValues( aProps, aValues, true ); + mbSizeModified = false; + +} + +void SAL_CALL UnoControlTabPage::windowMoved( const css::awt::WindowEvent& e ) +{ + OutputDevice*pOutDev = Application::GetDefaultDevice(); + DBG_ASSERT( pOutDev, "Missing Default Device!" ); + if ( !pOutDev || mbPosModified ) + return; + + // Currently we are simply using MapUnit::MapAppFont + ::Size aTmp( e.X, e.Y ); + aTmp = ImplMapPixelToAppFont( pOutDev, aTmp ); + + // Remember that changes have been done by listener. No need to + // update the position because of property change event. + mbPosModified = true; + Sequence< OUString > aProps{ "PositionX", "PositionY" }; + Sequence< Any > aValues{ Any(aTmp.Width()), Any(aTmp.Height()) }; + + ImplSetPropertyValues( aProps, aValues, true ); + mbPosModified = false; + +} + +void SAL_CALL UnoControlTabPage::windowShown( const css::lang::EventObject& ) {} + +void SAL_CALL UnoControlTabPage::windowHidden( const css::lang::EventObject& ) {} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlTabPageModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlTabPageModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlTabPage_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlTabPage(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tkscrollbar.cxx b/toolkit/source/controls/tkscrollbar.cxx new file mode 100644 index 0000000000..ff2c8cd623 --- /dev/null +++ b/toolkit/source/controls/tkscrollbar.cxx @@ -0,0 +1,324 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/tkscrollbar.hxx> +#include <helper/property.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <toolkit/awt/vclxwindows.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +namespace toolkit +{ + + + using namespace ::com::sun::star; + + + //= UnoControlScrollBarModel + + + UnoControlScrollBarModel::UnoControlScrollBarModel( const uno::Reference< uno::XComponentContext >& i_factory ) + :UnoControlModel( i_factory ) + { + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXScrollBar>(); + } + + + OUString UnoControlScrollBarModel::getServiceName( ) + { + return "stardiv.vcl.controlmodel.ScrollBar"; + } + + OUString UnoControlScrollBarModel::getImplementationName() + { + return "stardiv.Toolkit.UnoControlScrollBarModel"; + } + + css::uno::Sequence<OUString> + UnoControlScrollBarModel::getSupportedServiceNames() + { + auto s(UnoControlModel::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlScrollBarModel"; + ps[s.getLength() - 1] = "stardiv.vcl.controlmodel.ScrollBar"; + return s; + } + + uno::Any UnoControlScrollBarModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const + { + switch ( nPropId ) + { + case BASEPROPERTY_LIVE_SCROLL: + return uno::Any( false ); + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString( "stardiv.vcl.control.ScrollBar" ) ); + + default: + return UnoControlModel::ImplGetDefaultValue( nPropId ); + } + } + + + ::cppu::IPropertyArrayHelper& UnoControlScrollBarModel::getInfoHelper() + { + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; + } + + + uno::Reference< beans::XPropertySetInfo > UnoControlScrollBarModel::getPropertySetInfo( ) + { + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + //= UnoControlScrollBarModel + + UnoScrollBarControl::UnoScrollBarControl() + :maAdjustmentListeners( *this ) + { + } + + OUString UnoScrollBarControl::GetComponentServiceName() const + { + return "ScrollBar"; + } + + // css::uno::XInterface + uno::Any UnoScrollBarControl::queryAggregation( const uno::Type & rType ) + { + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XAdjustmentListener* >(this), + static_cast< awt::XScrollBar* >(this) ); + return (aRet.hasValue() ? aRet : UnoControlBase::queryAggregation( rType )); + } + + IMPL_IMPLEMENTATION_ID( UnoScrollBarControl ) + + // css::lang::XTypeProvider + css::uno::Sequence< css::uno::Type > UnoScrollBarControl::getTypes() + { + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XAdjustmentListener>::get(), + cppu::UnoType<awt::XScrollBar>::get(), + UnoControlBase::getTypes() + ); + return aTypeList.getTypes(); + } + + void UnoScrollBarControl::dispose() + { + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maAdjustmentListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); + } + + void UnoScrollBarControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) + { + UnoControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + xScrollBar->addAdjustmentListener( this ); + } + + // css::awt::XAdjustmentListener + void UnoScrollBarControl::adjustmentValueChanged( const css::awt::AdjustmentEvent& rEvent ) + { + switch ( rEvent.Type ) + { + case css::awt::AdjustmentType_ADJUST_LINE: + case css::awt::AdjustmentType_ADJUST_PAGE: + case css::awt::AdjustmentType_ADJUST_ABS: + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + + if ( xScrollBar.is() ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLVALUE ), uno::Any(xScrollBar->getValue()), false ); + } + } + break; + default: + { + OSL_FAIL( "UnoScrollBarControl::adjustmentValueChanged - unknown Type" ); + + } + } + + if ( maAdjustmentListeners.getLength() ) + maAdjustmentListeners.adjustmentValueChanged( rEvent ); + } + + // css::awt::XScrollBar + void UnoScrollBarControl::addAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener > & l ) + { + maAdjustmentListeners.addInterface( l ); + } + + void UnoScrollBarControl::removeAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener > & l ) + { + maAdjustmentListeners.removeInterface( l ); + } + + void UnoScrollBarControl::setValue( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLVALUE ), uno::Any( n ), true ); + } + + void UnoScrollBarControl::setValues( sal_Int32 nValue, sal_Int32 nVisible, sal_Int32 nMax ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLVALUE ), uno::Any(nValue), true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VISIBLESIZE ), uno::Any(nVisible), true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLVALUE_MAX ), uno::Any(nMax), true ); + } + + sal_Int32 UnoScrollBarControl::getValue() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getValue(); + } + return n; + } + + void UnoScrollBarControl::setMaximum( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SCROLLVALUE_MAX ), uno::Any( n ), true ); + } + + sal_Int32 UnoScrollBarControl::getMaximum() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getMaximum(); + } + return n; + } + + void UnoScrollBarControl::setLineIncrement( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LINEINCREMENT ), uno::Any( n ), true ); + } + + sal_Int32 UnoScrollBarControl::getLineIncrement() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getLineIncrement(); + } + return n; + } + + void UnoScrollBarControl::setBlockIncrement( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_BLOCKINCREMENT ), uno::Any( n ), true ); + } + + sal_Int32 UnoScrollBarControl::getBlockIncrement() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getBlockIncrement(); + } + return n; + } + + void UnoScrollBarControl::setVisibleSize( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VISIBLESIZE ), uno::Any( n ), true ); + } + + sal_Int32 UnoScrollBarControl::getVisibleSize() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getVisibleSize(); + } + return n; + } + + void UnoScrollBarControl::setOrientation( sal_Int32 n ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_ORIENTATION ), uno::Any( n ), true ); + } + + sal_Int32 UnoScrollBarControl::getOrientation() + { + sal_Int32 n = 0; + if ( getPeer().is() ) + { + uno::Reference< awt::XScrollBar > xScrollBar( getPeer(), uno::UNO_QUERY ); + n = xScrollBar->getOrientation(); + } + return n; + } + + OUString UnoScrollBarControl::getImplementationName() + { + return "stardiv.Toolkit.UnoScrollBarControl"; + } + + css::uno::Sequence<OUString> UnoScrollBarControl::getSupportedServiceNames() + { + auto s(UnoControlBase::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlScrollBar"; + ps[s.getLength() - 1] = "stardiv.vcl.control.ScrollBar"; + return s; + } + +} // namespace toolkit + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlScrollBarModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoControlScrollBarModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoScrollBarControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoScrollBarControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tkspinbutton.cxx b/toolkit/source/controls/tkspinbutton.cxx new file mode 100644 index 0000000000..59dad491c5 --- /dev/null +++ b/toolkit/source/controls/tkspinbutton.cxx @@ -0,0 +1,422 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/awt/XSpinValue.hpp> +#include <com/sun/star/awt/XAdjustmentListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase2.hxx> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <toolkit/controls/unocontrolbase.hxx> +#include <helper/property.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; + +namespace { + +class UnoSpinButtonModel : public UnoControlModel +{ +protected: + css::uno::Any ImplGetDefaultValue( sal_uInt16 nPropId ) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + +public: + explicit UnoSpinButtonModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ); + UnoSpinButtonModel(const UnoSpinButtonModel & rOther) : UnoControlModel(rOther) {} + + rtl::Reference<UnoControlModel> Clone() const override { return new UnoSpinButtonModel( *this ); } + + // XMultiPropertySet + css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XPersistObject + OUString SAL_CALL getServiceName() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + + +//= UnoSpinButtonControl + + +typedef ::cppu::ImplHelper2 < css::awt::XAdjustmentListener + , css::awt::XSpinValue + > UnoSpinButtonControl_Base; + +class UnoSpinButtonControl :public UnoControlBase + ,public UnoSpinButtonControl_Base +{ +private: + AdjustmentListenerMultiplexer maAdjustmentListeners; + +public: + UnoSpinButtonControl(); + OUString GetComponentServiceName() const override; + + DECLARE_UNO3_AGG_DEFAULTS( UnoSpinButtonControl, UnoControlBase ) + css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override; + + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + void SAL_CALL disposing( const css::lang::EventObject& Source ) override { UnoControlBase::disposing( Source ); } + void SAL_CALL dispose( ) override; + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + + // XAdjustmentListener + void SAL_CALL adjustmentValueChanged( const css::awt::AdjustmentEvent& rEvent ) override; + + // XSpinValue + virtual void SAL_CALL addAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener >& listener ) override; + virtual void SAL_CALL removeAdjustmentListener( const css::uno::Reference< css::awt::XAdjustmentListener >& listener ) override; + virtual void SAL_CALL setValue( sal_Int32 value ) override; + virtual void SAL_CALL setValues( sal_Int32 minValue, sal_Int32 maxValue, sal_Int32 currentValue ) override; + virtual sal_Int32 SAL_CALL getValue( ) override; + virtual void SAL_CALL setMinimum( sal_Int32 minValue ) override; + virtual void SAL_CALL setMaximum( sal_Int32 maxValue ) override; + virtual sal_Int32 SAL_CALL getMinimum( ) override; + virtual sal_Int32 SAL_CALL getMaximum( ) override; + virtual void SAL_CALL setSpinIncrement( sal_Int32 spinIncrement ) override; + virtual sal_Int32 SAL_CALL getSpinIncrement( ) override; + virtual void SAL_CALL setOrientation( sal_Int32 orientation ) override; + virtual sal_Int32 SAL_CALL getOrientation( ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName( ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + + + //= UnoSpinButtonModel + + + UnoSpinButtonModel::UnoSpinButtonModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ) + :UnoControlModel( i_factory ) + { + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_ORIENTATION ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_REPEAT ); + ImplRegisterProperty( BASEPROPERTY_REPEAT_DELAY ); + ImplRegisterProperty( BASEPROPERTY_SYMBOL_COLOR ); + ImplRegisterProperty( BASEPROPERTY_SPINVALUE ); + ImplRegisterProperty( BASEPROPERTY_SPINVALUE_MIN ); + ImplRegisterProperty( BASEPROPERTY_SPINVALUE_MAX ); + ImplRegisterProperty( BASEPROPERTY_SPININCREMENT ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_CONTEXT_WRITING_MODE ); + } + + + OUString UnoSpinButtonModel::getServiceName( ) + { + return "com.sun.star.awt.UnoControlSpinButtonModel"; + } + + + Any UnoSpinButtonModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const + { + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return Any( OUString("com.sun.star.awt.UnoControlSpinButton") ); + + case BASEPROPERTY_BORDER: + return Any( sal_Int16(0) ); + + case BASEPROPERTY_REPEAT: + return Any( true ); + + default: + return UnoControlModel::ImplGetDefaultValue( nPropId ); + } + } + + + ::cppu::IPropertyArrayHelper& UnoSpinButtonModel::getInfoHelper() + { + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; + } + + + Reference< XPropertySetInfo > UnoSpinButtonModel::getPropertySetInfo( ) + { + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + OUString SAL_CALL UnoSpinButtonModel::getImplementationName( ) + { + return "stardiv.Toolkit.UnoSpinButtonModel"; + } + + + Sequence< OUString > SAL_CALL UnoSpinButtonModel::getSupportedServiceNames() + { + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlSpinButtonModel" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); + } + + + //= UnoSpinButtonControl + + + UnoSpinButtonControl::UnoSpinButtonControl() + :maAdjustmentListeners( *this ) + { + } + + + OUString UnoSpinButtonControl::GetComponentServiceName() const + { + return "SpinButton"; + } + + + Any UnoSpinButtonControl::queryAggregation( const Type & rType ) + { + Any aRet = UnoControlBase::queryAggregation( rType ); + if ( !aRet.hasValue() ) + aRet = UnoSpinButtonControl_Base::queryInterface( rType ); + return aRet; + } + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( UnoSpinButtonControl, UnoControlBase, UnoSpinButtonControl_Base ) + + + void UnoSpinButtonControl::dispose() + { + ::osl::ClearableMutexGuard aGuard( GetMutex() ); + if ( maAdjustmentListeners.getLength() ) + { + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + xSpinnable->removeAdjustmentListener( this ); + + EventObject aDisposeEvent; + aDisposeEvent.Source = *this; + + aGuard.clear(); + maAdjustmentListeners.disposeAndClear( aDisposeEvent ); + } + + UnoControl::dispose(); + } + + + OUString SAL_CALL UnoSpinButtonControl::getImplementationName( ) + { + return "stardiv.Toolkit.UnoSpinButtonControl"; + } + + + Sequence< OUString > SAL_CALL UnoSpinButtonControl::getSupportedServiceNames() + { + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlSpinButton" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); + } + + + void UnoSpinButtonControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) + { + UnoControl::createPeer( rxToolkit, rParentPeer ); + + Reference < XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + xSpinnable->addAdjustmentListener( this ); + } + + + void UnoSpinButtonControl::adjustmentValueChanged( const AdjustmentEvent& rEvent ) + { + switch ( rEvent.Type ) + { + case AdjustmentType_ADJUST_LINE: + case AdjustmentType_ADJUST_PAGE: + case AdjustmentType_ADJUST_ABS: + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE ), Any( rEvent.Value ), false ); + break; + default: + OSL_FAIL( "UnoSpinButtonControl::adjustmentValueChanged - unknown Type" ); + } + + if ( maAdjustmentListeners.getLength() ) + { + AdjustmentEvent aEvent( rEvent ); + aEvent.Source = *this; + maAdjustmentListeners.adjustmentValueChanged( aEvent ); + } + } + + + void UnoSpinButtonControl::addAdjustmentListener( const Reference< XAdjustmentListener > & listener ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + maAdjustmentListeners.addInterface( listener ); + } + + + void UnoSpinButtonControl::removeAdjustmentListener( const Reference< XAdjustmentListener > & listener ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + maAdjustmentListeners.removeInterface( listener ); + } + + + void SAL_CALL UnoSpinButtonControl::setValue( sal_Int32 value ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE ), Any( value ), true ); + } + + + void SAL_CALL UnoSpinButtonControl::setValues( sal_Int32 minValue, sal_Int32 maxValue, sal_Int32 currentValue ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE_MIN ), Any( minValue ), true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE_MAX ), Any( maxValue ), true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE ), Any( currentValue ), true ); + } + + + sal_Int32 SAL_CALL UnoSpinButtonControl::getValue( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + sal_Int32 nValue = 0; + + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + nValue = xSpinnable->getValue(); + + return nValue; + } + + + void SAL_CALL UnoSpinButtonControl::setMinimum( sal_Int32 minValue ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE_MIN ), Any( minValue ), true ); + } + + + void SAL_CALL UnoSpinButtonControl::setMaximum( sal_Int32 maxValue ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPINVALUE_MAX ), Any( maxValue ), true ); + } + + + sal_Int32 SAL_CALL UnoSpinButtonControl::getMinimum( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + sal_Int32 nMin = 0; + + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + nMin = xSpinnable->getMinimum(); + + return nMin; + } + + + sal_Int32 SAL_CALL UnoSpinButtonControl::getMaximum( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + sal_Int32 nMax = 0; + + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + nMax = xSpinnable->getMaximum(); + + return nMax; + } + + + void SAL_CALL UnoSpinButtonControl::setSpinIncrement( sal_Int32 spinIncrement ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SPININCREMENT ), Any( spinIncrement ), true ); + } + + + sal_Int32 SAL_CALL UnoSpinButtonControl::getSpinIncrement( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + sal_Int32 nIncrement = 0; + + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + nIncrement = xSpinnable->getSpinIncrement(); + + return nIncrement; + } + + + void SAL_CALL UnoSpinButtonControl::setOrientation( sal_Int32 orientation ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_ORIENTATION ), Any( orientation ), true ); + } + + + sal_Int32 SAL_CALL UnoSpinButtonControl::getOrientation( ) + { + ::osl::MutexGuard aGuard( GetMutex() ); + sal_Int32 nOrientation = ScrollBarOrientation::HORIZONTAL; + + Reference< XSpinValue > xSpinnable( getPeer(), UNO_QUERY ); + if ( xSpinnable.is() ) + nOrientation = xSpinnable->getOrientation(); + + return nOrientation; + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoSpinButtonModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoSpinButtonModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoSpinButtonControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoSpinButtonControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tree/treecontrol.cxx b/toolkit/source/controls/tree/treecontrol.cxx new file mode 100644 index 0000000000..c696220001 --- /dev/null +++ b/toolkit/source/controls/tree/treecontrol.cxx @@ -0,0 +1,524 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "treecontrol.hxx" + +#include <com/sun/star/awt/tree/XTreeControl.hpp> +#include <com/sun/star/awt/tree/XTreeDataModel.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/view/SelectionType.hpp> +#include <toolkit/controls/unocontrolbase.hxx> +#include <helper/property.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/implbase1.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::awt::tree; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::view; + +namespace toolkit +{ + + +UnoTreeModel::UnoTreeModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ) + :UnoControlModel( i_factory ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FILLCOLOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_TREE_SELECTIONTYPE ); + ImplRegisterProperty( BASEPROPERTY_TREE_EDITABLE ); + ImplRegisterProperty( BASEPROPERTY_TREE_DATAMODEL ); + ImplRegisterProperty( BASEPROPERTY_TREE_ROOTDISPLAYED ); + ImplRegisterProperty( BASEPROPERTY_TREE_SHOWSHANDLES ); + ImplRegisterProperty( BASEPROPERTY_TREE_SHOWSROOTHANDLES ); + ImplRegisterProperty( BASEPROPERTY_ROW_HEIGHT ); + ImplRegisterProperty( BASEPROPERTY_TREE_INVOKESSTOPNODEEDITING ); + ImplRegisterProperty( BASEPROPERTY_HIDEINACTIVESELECTION ); +} + +rtl::Reference<UnoControlModel> UnoTreeModel::Clone() const +{ + return new UnoTreeModel( *this ); +} + +OUString UnoTreeModel::getServiceName() +{ + return "com.sun.star.awt.tree.TreeControlModel"; +} + +Any UnoTreeModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch( nPropId ) + { + case BASEPROPERTY_TREE_SELECTIONTYPE: + return Any( SelectionType_NONE ); + case BASEPROPERTY_ROW_HEIGHT: + return Any( sal_Int32( 0 ) ); + case BASEPROPERTY_TREE_DATAMODEL: + return Any( Reference< XTreeDataModel >( nullptr ) ); + case BASEPROPERTY_TREE_EDITABLE: + case BASEPROPERTY_TREE_INVOKESSTOPNODEEDITING: + return Any( false ); + case BASEPROPERTY_TREE_ROOTDISPLAYED: + case BASEPROPERTY_TREE_SHOWSROOTHANDLES: + case BASEPROPERTY_TREE_SHOWSHANDLES: + return Any( true ); + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString( "com.sun.star.awt.tree.TreeControl" ) ); + default: + return UnoControlModel::ImplGetDefaultValue( nPropId ); + } +} + +::cppu::IPropertyArrayHelper& UnoTreeModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// XMultiPropertySet +Reference< XPropertySetInfo > UnoTreeModel::getPropertySetInfo( ) +{ + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +} + +namespace { + +typedef ::cppu::AggImplInheritanceHelper1< UnoControlBase, css::awt::tree::XTreeControl > UnoTreeControl_Base; +class UnoTreeControl : public UnoTreeControl_Base +{ +public: + UnoTreeControl(); + OUString GetComponentServiceName() const override; + + // css::lang::XComponent + void SAL_CALL dispose( ) override; + + // css::awt::XControl + void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& Toolkit, const css::uno::Reference< css::awt::XWindowPeer >& Parent ) override; + + // css::view::XSelectionSupplier + virtual sal_Bool SAL_CALL select( const css::uno::Any& xSelection ) override; + virtual css::uno::Any SAL_CALL getSelection( ) override; + virtual void SAL_CALL addSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + virtual void SAL_CALL removeSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + + // css::view::XMultiSelectionSupplier + virtual sal_Bool SAL_CALL addSelection( const css::uno::Any& Selection ) override; + virtual void SAL_CALL removeSelection( const css::uno::Any& Selection ) override; + virtual void SAL_CALL clearSelection( ) override; + virtual ::sal_Int32 SAL_CALL getSelectionCount( ) override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSelectionEnumeration( ) override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createReverseSelectionEnumeration( ) override; + + // css::awt::XTreeControl + virtual OUString SAL_CALL getDefaultExpandedGraphicURL() override; + virtual void SAL_CALL setDefaultExpandedGraphicURL( const OUString& _defaultexpandedgraphicurl ) override; + virtual OUString SAL_CALL getDefaultCollapsedGraphicURL() override; + virtual void SAL_CALL setDefaultCollapsedGraphicURL( const OUString& _defaultcollapsedgraphicurl ) override; + virtual sal_Bool SAL_CALL isNodeExpanded( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL isNodeCollapsed( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL makeNodeVisible( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL isNodeVisible( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL expandNode( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL collapseNode( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL addTreeExpansionListener( const css::uno::Reference< css::awt::tree::XTreeExpansionListener >& Listener ) override; + virtual void SAL_CALL removeTreeExpansionListener( const css::uno::Reference< css::awt::tree::XTreeExpansionListener >& Listener ) override; + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getNodeForLocation( ::sal_Int32 x, ::sal_Int32 y ) override; + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getClosestNodeForLocation( ::sal_Int32 x, ::sal_Int32 y ) override; + virtual css::awt::Rectangle SAL_CALL getNodeRect( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL isEditing( ) override; + virtual sal_Bool SAL_CALL stopEditing( ) override; + virtual void SAL_CALL cancelEditing( ) override; + virtual void SAL_CALL startEditingAtNode( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual void SAL_CALL addTreeEditListener( const css::uno::Reference< css::awt::tree::XTreeEditListener >& Listener ) override; + virtual void SAL_CALL removeTreeEditListener( const css::uno::Reference< css::awt::tree::XTreeEditListener >& Listener ) override; + + // css::lang::XServiceInfo + DECLIMPL_SERVICEINFO_DERIVED( UnoTreeControl, UnoControlBase, "com.sun.star.awt.tree.TreeControl" ) + + using UnoControl::getPeer; +private: + TreeSelectionListenerMultiplexer maSelectionListeners; + TreeExpansionListenerMultiplexer maTreeExpansionListeners; + TreeEditListenerMultiplexer maTreeEditListeners; +}; + +UnoTreeControl::UnoTreeControl() +: maSelectionListeners( *this ) +, maTreeExpansionListeners( *this ) +, maTreeEditListeners( *this ) +{ +} + +OUString UnoTreeControl::GetComponentServiceName() const +{ + return "Tree"; +} + + +// css::view::XSelectionSupplier + + +sal_Bool SAL_CALL UnoTreeControl::select( const Any& rSelection ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->select( rSelection ); +} + + +Any SAL_CALL UnoTreeControl::getSelection() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getSelection(); +} + + +void SAL_CALL UnoTreeControl::addSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) +{ + maSelectionListeners.addInterface( xListener ); + if( getPeer().is() && (maSelectionListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // add it to the peer if this is the first listener added to that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->addSelectionChangeListener(&maSelectionListeners); + } +} + + +void SAL_CALL UnoTreeControl::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) +{ + if( getPeer().is() && (maSelectionListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // remove it from the peer if this is the last listener removed from that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->removeSelectionChangeListener(&maSelectionListeners); + } + maSelectionListeners.removeInterface( xListener ); +} + + +// css::view::XMultiSelectionSupplier + + +sal_Bool SAL_CALL UnoTreeControl::addSelection( const Any& rSelection ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->addSelection(rSelection); +} + + +void SAL_CALL UnoTreeControl::removeSelection( const Any& rSelection ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->removeSelection(rSelection); +} + + +void SAL_CALL UnoTreeControl::clearSelection() +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->clearSelection(); +} + + +sal_Int32 SAL_CALL UnoTreeControl::getSelectionCount() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getSelectionCount(); +} + + +Reference< XEnumeration > SAL_CALL UnoTreeControl::createSelectionEnumeration() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->createSelectionEnumeration(); +} + + +Reference< XEnumeration > SAL_CALL UnoTreeControl::createReverseSelectionEnumeration() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->createReverseSelectionEnumeration(); +} + + +// XTreeControl + + +OUString SAL_CALL UnoTreeControl::getDefaultExpandedGraphicURL() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getDefaultExpandedGraphicURL(); +} + + +void SAL_CALL UnoTreeControl::setDefaultExpandedGraphicURL( const OUString& _defaultexpansiongraphicurl ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->setDefaultExpandedGraphicURL(_defaultexpansiongraphicurl); +} + + +OUString SAL_CALL UnoTreeControl::getDefaultCollapsedGraphicURL() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getDefaultCollapsedGraphicURL(); +} + + +void SAL_CALL UnoTreeControl::setDefaultCollapsedGraphicURL( const OUString& _defaultcollapsedgraphicurl ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->setDefaultCollapsedGraphicURL(_defaultcollapsedgraphicurl); +} + + +sal_Bool SAL_CALL UnoTreeControl::isNodeExpanded( const Reference< XTreeNode >& xNode ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isNodeExpanded(xNode); +} + + +sal_Bool SAL_CALL UnoTreeControl::isNodeCollapsed( const Reference< XTreeNode >& xNode ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isNodeCollapsed(xNode); +} + + +void SAL_CALL UnoTreeControl::makeNodeVisible( const Reference< XTreeNode >& xNode ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->makeNodeVisible(xNode); +} + + +sal_Bool SAL_CALL UnoTreeControl::isNodeVisible( const Reference< XTreeNode >& xNode ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isNodeVisible(xNode); +} + + +void SAL_CALL UnoTreeControl::expandNode( const Reference< XTreeNode >& xNode ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->expandNode(xNode); +} + + +void SAL_CALL UnoTreeControl::collapseNode( const Reference< XTreeNode >& xNode ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->collapseNode(xNode); +} + + +void SAL_CALL UnoTreeControl::addTreeExpansionListener( const Reference< XTreeExpansionListener >& xListener ) +{ + maTreeExpansionListeners.addInterface( xListener ); + if( getPeer().is() && (maTreeExpansionListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // add it to the peer if this is the first listener added to that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->addTreeExpansionListener(&maTreeExpansionListeners); + } +} + + +void SAL_CALL UnoTreeControl::removeTreeExpansionListener( const Reference< XTreeExpansionListener >& xListener ) +{ + if( getPeer().is() && (maTreeExpansionListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // remove it from the peer if this is the last listener removed from that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->removeTreeExpansionListener(&maTreeExpansionListeners); + } + maTreeExpansionListeners.removeInterface( xListener ); +} + + +Reference< XTreeNode > SAL_CALL UnoTreeControl::getNodeForLocation( sal_Int32 x, sal_Int32 y ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getNodeForLocation(x,y); +} + + +Reference< XTreeNode > SAL_CALL UnoTreeControl::getClosestNodeForLocation( sal_Int32 x, sal_Int32 y ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getClosestNodeForLocation(x,y); +} + + +awt::Rectangle SAL_CALL UnoTreeControl::getNodeRect( const Reference< XTreeNode >& Node ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->getNodeRect( Node ); +} + + +sal_Bool SAL_CALL UnoTreeControl::isEditing( ) +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->isEditing(); +} + + +sal_Bool SAL_CALL UnoTreeControl::stopEditing() +{ + return Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->stopEditing(); +} + + +void SAL_CALL UnoTreeControl::cancelEditing() +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->cancelEditing(); +} + + +void SAL_CALL UnoTreeControl::startEditingAtNode( const Reference< XTreeNode >& xNode ) +{ + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->startEditingAtNode(xNode); +} + + +void SAL_CALL UnoTreeControl::addTreeEditListener( const Reference< XTreeEditListener >& xListener ) +{ + maTreeEditListeners.addInterface( xListener ); + if( getPeer().is() && (maTreeEditListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // add it to the peer if this is the first listener added to that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->addTreeEditListener(&maTreeEditListeners); + } +} + + +void SAL_CALL UnoTreeControl::removeTreeEditListener( const Reference< XTreeEditListener >& xListener ) +{ + if( getPeer().is() && (maTreeEditListeners.getLength() == 1) ) + { + // maSelectionListeners acts as a proxy, + // remove it from the peer if this is the last listener removed from that proxy + Reference< XTreeControl >( getPeer(), UNO_QUERY_THROW )->removeTreeEditListener(&maTreeEditListeners); + } + maTreeEditListeners.removeInterface( xListener ); +} + + +// XComponent + + +void SAL_CALL UnoTreeControl::dispose( ) +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maSelectionListeners.disposeAndClear( aEvt ); + maTreeExpansionListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + +void UnoTreeControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + Reference< XTreeControl > xTree( getPeer(), UNO_QUERY_THROW ); + if( maSelectionListeners.getLength() ) + xTree->addSelectionChangeListener( &maSelectionListeners ); + if( maTreeExpansionListeners.getLength() ) + xTree->addTreeExpansionListener( &maTreeExpansionListeners ); +} + +} + +void SAL_CALL TreeEditListenerMultiplexer::nodeEditing( const Reference< XTreeNode >& Node ) +{ + std::unique_lock g(m_aMutex); + ::comphelper::OInterfaceIteratorHelper4 aIt(g, maListeners); + g.unlock(); + while( aIt.hasMoreElements() ) + { + Reference<XTreeEditListener> xListener(aIt.next()); + try + { + xListener->nodeEditing( Node ); + } + catch( const DisposedException& e ) + { + OSL_ENSURE( e.Context.is(), "caught DisposedException with empty Context field" ); + if ( e.Context == xListener || !e.Context.is() ) + { + std::unique_lock g2(m_aMutex); + aIt.remove(g2); + } + } + catch( const RuntimeException& ) + { + DISPLAY_EXCEPTION( TreeEditListenerMultiplexer, nodeEditing ) + } + } +} + +void SAL_CALL TreeEditListenerMultiplexer::nodeEdited( const Reference< XTreeNode >& Node, const OUString& NewText ) +{ + std::unique_lock g(m_aMutex); + ::comphelper::OInterfaceIteratorHelper4 aIt(g, maListeners); + g.unlock(); + while( aIt.hasMoreElements() ) + { + Reference<XTreeEditListener> xListener(aIt.next()); + try + { + xListener->nodeEdited( Node, NewText ); + } + catch( const DisposedException& e ) + { + OSL_ENSURE( e.Context.is(), "caught DisposedException with empty Context field" ); + if ( e.Context == xListener || !e.Context.is() ) + { + std::unique_lock g2(m_aMutex); + aIt.remove(g2); + } + } + catch( const RuntimeException& ) + { + DISPLAY_EXCEPTION( TreeEditListenerMultiplexer, nodeEdited ) + } + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_TreeControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new toolkit::UnoTreeModel(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_TreeControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoTreeControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tree/treecontrol.hxx b/toolkit/source/controls/tree/treecontrol.hxx new file mode 100644 index 0000000000..357afddb0f --- /dev/null +++ b/toolkit/source/controls/tree/treecontrol.hxx @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_TOOLKIT_SOURCE_CONTROLS_TREE_TREECONTROL_HXX +#define INCLUDED_TOOLKIT_SOURCE_CONTROLS_TREE_TREECONTROL_HXX + +#include <toolkit/controls/unocontrolmodel.hxx> + +namespace toolkit +{ +// = UnoTreeModel + +class UnoTreeModel : public UnoControlModel +{ +protected: + css::uno::Any ImplGetDefaultValue(sal_uInt16 nPropId) const override; + ::cppu::IPropertyArrayHelper& getInfoHelper() override; + +public: + explicit UnoTreeModel(const css::uno::Reference<css::uno::XComponentContext>& i_factory); + UnoTreeModel(const UnoTreeModel& rOther) + : UnoControlModel(rOther) + { + } + + rtl::Reference<UnoControlModel> Clone() const override; + + // css::beans::XMultiPropertySet + css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + + // css::io::XPersistObject + OUString SAL_CALL getServiceName() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { + return "stardiv.Toolkit.TreeControlModel"; + } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + auto s(UnoControlModel::getSupportedServiceNames()); + s.realloc(s.getLength() + 1); + s.getArray()[s.getLength() - 1] = "com.sun.star.awt.tree.TreeControlModel"; + return s; + } +}; + +} // toolkit + +#endif // _ INCLUDED_TOOLKIT_SOURCE_CONTROLS_TREE_TREECONTROL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tree/treecontrolpeer.cxx b/toolkit/source/controls/tree/treecontrolpeer.cxx new file mode 100644 index 0000000000..c05650f972 --- /dev/null +++ b/toolkit/source/controls/tree/treecontrolpeer.cxx @@ -0,0 +1,1579 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/graphic/GraphicProvider.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/view/SelectionType.hpp> +#include <com/sun/star/util/VetoException.hpp> +#include <o3tl/any.hxx> +#include <helper/property.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <com/sun/star/awt/tree/XMutableTreeNode.hpp> +#include <controls/treecontrolpeer.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> + +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolkit/treelistbox.hxx> +#include <vcl/toolkit/treelistentry.hxx> +#include <vcl/toolkit/viewdataentry.hxx> +#include <vcl/toolkit/svlbitm.hxx> + +#include <map> +#include <memory> +#include <list> + +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::lang; +using namespace css::awt::tree; +using namespace css::beans; +using namespace css::view; +using namespace css::container; +using namespace css::util; +using namespace css::graphic; + +namespace { + +struct LockGuard +{ +public: + explicit LockGuard( sal_Int32& rLock ) + : mrLock( rLock ) + { + rLock++; + } + + ~LockGuard() + { + mrLock--; + } + + sal_Int32& mrLock; +}; + + +class ImplContextGraphicItem : public SvLBoxContextBmp +{ +public: + ImplContextGraphicItem( Image const & rI1, Image const & rI2, bool bExpanded) + : SvLBoxContextBmp(rI1, rI2, bExpanded) {} + + OUString msExpandedGraphicURL; + OUString msCollapsedGraphicURL; +}; + + +} + +class UnoTreeListBoxImpl : public SvTreeListBox +{ +public: + UnoTreeListBoxImpl( TreeControlPeer* pPeer, vcl::Window* pParent, WinBits nWinStyle ); + virtual ~UnoTreeListBoxImpl() override; + virtual void dispose() override; + + void insert( SvTreeListEntry* pEntry, SvTreeListEntry* pParent, sal_uLong nPos ); + + virtual void RequestingChildren( SvTreeListEntry* pParent ) override; + + virtual bool EditingEntry( SvTreeListEntry* pEntry ) override; + virtual bool EditedEntry( SvTreeListEntry* pEntry, const OUString& rNewText ) override; + + DECL_LINK(OnSelectionChangeHdl, SvTreeListBox*, void); + DECL_LINK(OnExpandingHdl, SvTreeListBox*, bool); + DECL_LINK(OnExpandedHdl, SvTreeListBox*, void); + +private: + rtl::Reference< TreeControlPeer > mxPeer; +}; + + +namespace { + +class UnoTreeListItem : public SvLBoxString +{ +public: + UnoTreeListItem(); + + void InitViewData( SvTreeListBox*,SvTreeListEntry*,SvViewDataItem * = nullptr ) override; + void SetImage( const Image& rImage ); + const OUString& GetGraphicURL() const { return maGraphicURL;} + void SetGraphicURL( const OUString& rGraphicURL ); + virtual void Paint(const Point& rPos, SvTreeListBox& rOutDev, vcl::RenderContext& rRenderContext, + const SvViewDataEntry* pView, const SvTreeListEntry& rEntry) override; + std::unique_ptr<SvLBoxItem> Clone( SvLBoxItem const * pSource ) const override; + +private: + OUString maGraphicURL; + Image maImage; +}; + +} + +class UnoTreeListEntry : public SvTreeListEntry +{ +public: + UnoTreeListEntry( const Reference< XTreeNode >& xNode, TreeControlPeer* pPeer ); + virtual ~UnoTreeListEntry() override; + + Reference< XTreeNode > mxNode; + TreeControlPeer* mpPeer; +}; + +TreeControlPeer::TreeControlPeer() + : maSelectionListeners( *this ) + , maTreeExpansionListeners( *this ) + , maTreeEditListeners( *this ) + , mbIsRootDisplayed(false) + , mpTreeImpl( nullptr ) + , mnEditLock( 0 ) +{ +} + + +TreeControlPeer::~TreeControlPeer() +{ + if( mpTreeImpl ) + mpTreeImpl->Clear(); +} + + +void TreeControlPeer::addEntry( UnoTreeListEntry* pEntry ) +{ + if( pEntry && pEntry->mxNode.is() ) + { + if( !mpTreeNodeMap ) + { + mpTreeNodeMap.reset( new TreeNodeMap ); + } + + (*mpTreeNodeMap)[ pEntry->mxNode ] = pEntry; + } +} + + +void TreeControlPeer::removeEntry( UnoTreeListEntry const * pEntry ) +{ + if( mpTreeNodeMap && pEntry && pEntry->mxNode.is() ) + { + TreeNodeMap::iterator aIter( mpTreeNodeMap->find( pEntry->mxNode ) ); + if( aIter != mpTreeNodeMap->end() ) + { + mpTreeNodeMap->erase( aIter ); + } + } +} + + +UnoTreeListEntry* TreeControlPeer::getEntry( const Reference< XTreeNode >& xNode, bool bThrow /* = true */ ) +{ + if( mpTreeNodeMap ) + { + TreeNodeMap::iterator aIter( mpTreeNodeMap->find( xNode ) ); + if( aIter != mpTreeNodeMap->end() ) + return (*aIter).second; + } + + if( bThrow ) + throw IllegalArgumentException(); + + return nullptr; +} + + +vcl::Window* TreeControlPeer::createVclControl( vcl::Window* pParent, sal_Int64 nWinStyle ) +{ + mpTreeImpl = VclPtr<UnoTreeListBoxImpl>::Create( this, pParent, nWinStyle ); + return mpTreeImpl; +} + + +/** called from the UnoTreeListBoxImpl when it gets deleted */ +void TreeControlPeer::disposeControl() +{ + mpTreeNodeMap.reset(); + mpTreeImpl = nullptr; +} + + +UnoTreeListEntry* TreeControlPeer::createEntry( const Reference< XTreeNode >& xNode, UnoTreeListEntry* pParent, sal_uLong nPos /* = TREELIST_APPEND */ ) +{ + UnoTreeListEntry* pEntry = nullptr; + if( mpTreeImpl ) + { + Image aImage; + pEntry = new UnoTreeListEntry( xNode, this ); + pEntry->AddItem(std::make_unique<ImplContextGraphicItem>(aImage, aImage, true)); + + std::unique_ptr<UnoTreeListItem> pUnoItem(new UnoTreeListItem); + + if( !xNode->getNodeGraphicURL().isEmpty() ) + { + pUnoItem->SetGraphicURL( xNode->getNodeGraphicURL() ); + Image aNodeImage; + loadImage( xNode->getNodeGraphicURL(), aNodeImage ); + pUnoItem->SetImage( aNodeImage ); + mpTreeImpl->AdjustEntryHeight( aNodeImage ); + } + + pEntry->AddItem(std::move(pUnoItem)); + + mpTreeImpl->insert( pEntry, pParent, nPos ); + + if( !msDefaultExpandedGraphicURL.isEmpty() ) + mpTreeImpl->SetExpandedEntryBmp( pEntry, maDefaultExpandedImage ); + + if( !msDefaultCollapsedGraphicURL.isEmpty() ) + mpTreeImpl->SetCollapsedEntryBmp( pEntry, maDefaultCollapsedImage ); + + updateEntry( pEntry ); + } + return pEntry; +} + + +void TreeControlPeer::updateEntry( UnoTreeListEntry* pEntry ) +{ + bool bChanged = false; + if( !(pEntry && pEntry->mxNode.is() && mpTreeImpl) ) + return; + + const OUString aValue( getEntryString( pEntry->mxNode->getDisplayValue() ) ); + UnoTreeListItem* pUnoItem = dynamic_cast< UnoTreeListItem* >( &pEntry->GetItem( 1 ) ); + if( pUnoItem ) + { + if( aValue != pUnoItem->GetText() ) + { + pUnoItem->SetText( aValue ); + bChanged = true; + } + + if( pUnoItem->GetGraphicURL() != pEntry->mxNode->getNodeGraphicURL() ) + { + Image aImage; + if( loadImage( pEntry->mxNode->getNodeGraphicURL(), aImage ) ) + { + pUnoItem->SetGraphicURL( pEntry->mxNode->getNodeGraphicURL() ); + pUnoItem->SetImage( aImage ); + mpTreeImpl->AdjustEntryHeight( aImage ); + bChanged = true; + } + } + } + + if( bool(pEntry->mxNode->hasChildrenOnDemand()) != pEntry->HasChildrenOnDemand() ) + { + pEntry->EnableChildrenOnDemand( pEntry->mxNode->hasChildrenOnDemand() ); + bChanged = true; + } + + ImplContextGraphicItem* pContextGraphicItem = dynamic_cast< ImplContextGraphicItem* >( &pEntry->GetItem( 0 ) ); + if( pContextGraphicItem ) + { + if( pContextGraphicItem->msExpandedGraphicURL != pEntry->mxNode->getExpandedGraphicURL() ) + { + Image aImage; + if( loadImage( pEntry->mxNode->getExpandedGraphicURL(), aImage ) ) + { + pContextGraphicItem->msExpandedGraphicURL = pEntry->mxNode->getExpandedGraphicURL(); + mpTreeImpl->SetExpandedEntryBmp( pEntry, aImage ); + bChanged = true; + } + } + if( pContextGraphicItem->msCollapsedGraphicURL != pEntry->mxNode->getCollapsedGraphicURL() ) + { + Image aImage; + if( loadImage( pEntry->mxNode->getCollapsedGraphicURL(), aImage ) ) + { + pContextGraphicItem->msCollapsedGraphicURL = pEntry->mxNode->getCollapsedGraphicURL(); + mpTreeImpl->SetCollapsedEntryBmp( pEntry, aImage ); + bChanged = true; + } + } + } + + if( bChanged ) + mpTreeImpl->GetModel()->InvalidateEntry( pEntry ); +} + + +void TreeControlPeer::onSelectionChanged() +{ + Reference< XInterface > xSource( getXWeak() ); + EventObject aEvent( xSource ); + maSelectionListeners.selectionChanged( aEvent ); +} + + +void TreeControlPeer::onRequestChildNodes( const Reference< XTreeNode >& xNode ) +{ + try + { + Reference< XInterface > xSource( getXWeak() ); + TreeExpansionEvent aEvent( xSource, xNode ); + maTreeExpansionListeners.requestChildNodes( aEvent ); + } + catch( Exception& ) + { + } +} + + +bool TreeControlPeer::onExpanding( const Reference< XTreeNode >& xNode, bool bExpanding ) +{ + try + { + Reference< XInterface > xSource( getXWeak() ); + TreeExpansionEvent aEvent( xSource, xNode ); + if( bExpanding ) + { + maTreeExpansionListeners.treeExpanding( aEvent ); + } + else + { + maTreeExpansionListeners.treeCollapsing( aEvent ); + } + } + catch( Exception& ) + { + return false; + } + return true; +} + + +void TreeControlPeer::onExpanded( const Reference< XTreeNode >& xNode, bool bExpanding ) +{ + try + { + Reference< XInterface > xSource( getXWeak() ); + TreeExpansionEvent aEvent( xSource, xNode ); + + if( bExpanding ) + { + maTreeExpansionListeners.treeExpanded( aEvent ); + } + else + { + maTreeExpansionListeners.treeCollapsed( aEvent ); + } + } + catch( Exception& ) + { + } +} + + +void TreeControlPeer::fillTree( UnoTreeListBoxImpl& rTree, const Reference< XTreeDataModel >& xDataModel ) +{ + rTree.Clear(); + + if( !xDataModel.is() ) + return; + + Reference< XTreeNode > xRootNode( xDataModel->getRoot() ); + if( !xRootNode.is() ) + return; + + if( mbIsRootDisplayed ) + { + addNode( rTree, xRootNode, nullptr ); + } + else + { + const sal_Int32 nChildCount = xRootNode->getChildCount(); + for( sal_Int32 nChild = 0; nChild < nChildCount; nChild++ ) + addNode( rTree, xRootNode->getChildAt( nChild ), nullptr ); + } +} + + +void TreeControlPeer::addNode( UnoTreeListBoxImpl& rTree, const Reference< XTreeNode >& xNode, UnoTreeListEntry* pParentEntry ) +{ + if( xNode.is() ) + { + UnoTreeListEntry* pEntry = createEntry( xNode, pParentEntry, TREELIST_APPEND ); + const sal_Int32 nChildCount = xNode->getChildCount(); + for( sal_Int32 nChild = 0; nChild < nChildCount; nChild++ ) + addNode( rTree, xNode->getChildAt( nChild ), pEntry ); + } +} + + +UnoTreeListBoxImpl& TreeControlPeer::getTreeListBoxOrThrow() const +{ + if( !mpTreeImpl ) + throw DisposedException(); + return *mpTreeImpl; +} + + +void TreeControlPeer::ChangeNodesSelection( const Any& rSelection, bool bSelect, bool bSetSelection ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + Reference< XTreeNode > xTempNode; + + Sequence<Reference<XTreeNode>> pNodes; + sal_Int32 nCount = 0; + + if( rSelection.hasValue() ) + { + switch( rSelection.getValueTypeClass() ) + { + case TypeClass_INTERFACE: + { + rSelection >>= xTempNode; + if( xTempNode.is() ) + { + nCount = 1; + pNodes = {xTempNode}; + } + break; + } + case TypeClass_SEQUENCE: + { + if( auto rSeq = o3tl::tryAccess<Sequence<Reference<XTreeNode>>>( + rSelection) ) + { + nCount = rSeq->getLength(); + pNodes = *rSeq; + } + break; + } + default: + break; + } + + if( nCount == 0 ) + throw IllegalArgumentException(); + } + + if( bSetSelection ) + rTree.SelectAll( false ); + + for( sal_Int32 i = 0; i != nCount; ++i ) + { + UnoTreeListEntry* pEntry = getEntry( pNodes[i] ); + rTree.Select( pEntry, bSelect ); + } +} + + +// css::view::XSelectionSupplier + + +sal_Bool SAL_CALL TreeControlPeer::select( const Any& rSelection ) +{ + SolarMutexGuard aGuard; + ChangeNodesSelection( rSelection, true, true ); + return true; +} + + +Any SAL_CALL TreeControlPeer::getSelection() +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + Any aRet; + + sal_uLong nSelectionCount = rTree.GetSelectionCount(); + if( nSelectionCount == 1 ) + { + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.FirstSelected() ); + if( pEntry && pEntry->mxNode.is() ) + aRet <<= pEntry->mxNode; + } + else if( nSelectionCount > 1 ) + { + Sequence< Reference< XTreeNode > > aSelection( nSelectionCount ); + Reference< XTreeNode >* pNodes = aSelection.getArray(); + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.FirstSelected() ); + while( pEntry && nSelectionCount ) + { + *pNodes++ = pEntry->mxNode; + pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.NextSelected( pEntry ) ); + --nSelectionCount; + } + + OSL_ASSERT( (pEntry == nullptr) && (nSelectionCount == 0) ); + aRet <<= aSelection; + } + + return aRet; +} + + +void SAL_CALL TreeControlPeer::addSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) +{ + maSelectionListeners.addInterface( xListener ); +} + + +void SAL_CALL TreeControlPeer::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) +{ + maSelectionListeners.addInterface( xListener ); +} + + +// css::view::XMultiSelectionSupplier + + +sal_Bool SAL_CALL TreeControlPeer::addSelection( const Any& rSelection ) +{ + ChangeNodesSelection( rSelection, true, false ); + return true; +} + + +void SAL_CALL TreeControlPeer::removeSelection( const Any& rSelection ) +{ + ChangeNodesSelection( rSelection, false, false ); +} + + +void SAL_CALL TreeControlPeer::clearSelection() +{ + SolarMutexGuard aGuard; + getTreeListBoxOrThrow().SelectAll( false ); +} + + +sal_Int32 SAL_CALL TreeControlPeer::getSelectionCount() +{ + SolarMutexGuard aGuard; + return getTreeListBoxOrThrow().GetSelectionCount(); +} + +namespace { + +class TreeSelectionEnumeration : public ::cppu::WeakImplHelper< XEnumeration > +{ +public: + explicit TreeSelectionEnumeration( std::list< Any >& rSelection ); + virtual sal_Bool SAL_CALL hasMoreElements() override; + virtual Any SAL_CALL nextElement() override; + + std::list< Any > maSelection; + std::list< Any >::iterator maIter; +}; + +} + +TreeSelectionEnumeration::TreeSelectionEnumeration( std::list< Any >& rSelection ) +{ + maSelection.swap( rSelection ); + maIter = maSelection.begin(); +} + + +sal_Bool SAL_CALL TreeSelectionEnumeration::hasMoreElements() +{ + return maIter != maSelection.end(); +} + + +Any SAL_CALL TreeSelectionEnumeration::nextElement() +{ + if( maIter == maSelection.end() ) + throw NoSuchElementException(); + + return (*maIter++); +} + + +Reference< XEnumeration > SAL_CALL TreeControlPeer::createSelectionEnumeration() +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + sal_uInt32 nSelectionCount = rTree.GetSelectionCount(); + std::list< Any > aSelection( nSelectionCount ); + + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.FirstSelected() ); + while( pEntry && nSelectionCount ) + { + aSelection.emplace_back( pEntry->mxNode ); + pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.NextSelected( pEntry ) ); + --nSelectionCount; + } + + OSL_ASSERT( (pEntry == nullptr) && (nSelectionCount == 0) ); + + return Reference< XEnumeration >( new TreeSelectionEnumeration( aSelection ) ); +} + + +Reference< XEnumeration > SAL_CALL TreeControlPeer::createReverseSelectionEnumeration() +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + sal_uInt32 nSelectionCount = rTree.GetSelectionCount(); + std::list< Any > aSelection; + + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.FirstSelected() ); + while( pEntry && nSelectionCount ) + { + aSelection.push_front( Any( pEntry->mxNode ) ); + pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.NextSelected( pEntry ) ); + --nSelectionCount; + } + + OSL_ASSERT( (pEntry == nullptr) && (nSelectionCount == 0) ); + + return Reference< XEnumeration >( new TreeSelectionEnumeration( aSelection ) ); +} + + +// css::awt::XTreeControl + + +OUString SAL_CALL TreeControlPeer::getDefaultExpandedGraphicURL() +{ + SolarMutexGuard aGuard; + return msDefaultExpandedGraphicURL; +} + + +void SAL_CALL TreeControlPeer::setDefaultExpandedGraphicURL( const OUString& sDefaultExpandedGraphicURL ) +{ + SolarMutexGuard aGuard; + if( msDefaultExpandedGraphicURL == sDefaultExpandedGraphicURL ) + return; + + if( !sDefaultExpandedGraphicURL.isEmpty() ) + loadImage( sDefaultExpandedGraphicURL, maDefaultExpandedImage ); + else + maDefaultExpandedImage = Image(); + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + SvTreeListEntry* pEntry = rTree.First(); + while( pEntry ) + { + ImplContextGraphicItem* pContextGraphicItem = dynamic_cast< ImplContextGraphicItem* >( &pEntry->GetItem( 0 ) ); + if( pContextGraphicItem ) + { + if( pContextGraphicItem->msExpandedGraphicURL.isEmpty() ) + rTree.SetExpandedEntryBmp( pEntry, maDefaultExpandedImage ); + } + pEntry = rTree.Next( pEntry ); + } + + msDefaultExpandedGraphicURL = sDefaultExpandedGraphicURL; +} + + +OUString SAL_CALL TreeControlPeer::getDefaultCollapsedGraphicURL() +{ + SolarMutexGuard aGuard; + return msDefaultCollapsedGraphicURL; +} + + +void SAL_CALL TreeControlPeer::setDefaultCollapsedGraphicURL( const OUString& sDefaultCollapsedGraphicURL ) +{ + SolarMutexGuard aGuard; + if( msDefaultCollapsedGraphicURL == sDefaultCollapsedGraphicURL ) + return; + + if( !sDefaultCollapsedGraphicURL.isEmpty() ) + loadImage( sDefaultCollapsedGraphicURL, maDefaultCollapsedImage ); + else + maDefaultCollapsedImage = Image(); + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + SvTreeListEntry* pEntry = rTree.First(); + while( pEntry ) + { + ImplContextGraphicItem* pContextGraphicItem = dynamic_cast< ImplContextGraphicItem* >( &pEntry->GetItem( 0 ) ); + if( pContextGraphicItem ) + { + if( pContextGraphicItem->msCollapsedGraphicURL.isEmpty() ) + rTree.SetCollapsedEntryBmp( pEntry, maDefaultCollapsedImage ); + } + pEntry = rTree.Next( pEntry ); + } + + msDefaultCollapsedGraphicURL = sDefaultCollapsedGraphicURL; +} + + +sal_Bool SAL_CALL TreeControlPeer::isNodeExpanded( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + return pEntry && rTree.IsExpanded( pEntry ); +} + + +sal_Bool SAL_CALL TreeControlPeer::isNodeCollapsed( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + return !isNodeExpanded( xNode ); +} + + +void SAL_CALL TreeControlPeer::makeNodeVisible( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + if( pEntry ) + rTree.MakeVisible( pEntry ); +} + + +sal_Bool SAL_CALL TreeControlPeer::isNodeVisible( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + return pEntry && rTree.IsEntryVisible( pEntry ); +} + + +void SAL_CALL TreeControlPeer::expandNode( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + if( pEntry ) + rTree.Expand( pEntry ); +} + + +void SAL_CALL TreeControlPeer::collapseNode( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + if( pEntry ) + rTree.Collapse( pEntry ); +} + + +void SAL_CALL TreeControlPeer::addTreeExpansionListener( const Reference< XTreeExpansionListener >& xListener ) +{ + maTreeExpansionListeners.addInterface( xListener ); +} + + +void SAL_CALL TreeControlPeer::removeTreeExpansionListener( const Reference< XTreeExpansionListener >& xListener ) +{ + maTreeExpansionListeners.removeInterface( xListener ); +} + + +Reference< XTreeNode > SAL_CALL TreeControlPeer::getNodeForLocation( sal_Int32 x, sal_Int32 y ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + Reference< XTreeNode > xNode; + + const Point aPos( x, y ); + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.GetEntry( aPos, true ) ); + if( pEntry ) + xNode = pEntry->mxNode; + + return xNode; +} + + +Reference< XTreeNode > SAL_CALL TreeControlPeer::getClosestNodeForLocation( sal_Int32 x, sal_Int32 y ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + Reference< XTreeNode > xNode; + + const Point aPos( x, y ); + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( rTree.GetEntry( aPos, true ) ); + if( pEntry ) + xNode = pEntry->mxNode; + + return xNode; +} + + +awt::Rectangle SAL_CALL TreeControlPeer::getNodeRect( const Reference< XTreeNode >& i_Node ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( i_Node ); + + ::tools::Rectangle aEntryRect( rTree.GetFocusRect( pEntry, rTree.GetEntryPosition( pEntry ).Y() ) ); + return VCLUnoHelper::ConvertToAWTRect( aEntryRect ); +} + + +sal_Bool SAL_CALL TreeControlPeer::isEditing( ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + return rTree.IsEditingActive(); +} + + +sal_Bool SAL_CALL TreeControlPeer::stopEditing() +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + if( rTree.IsEditingActive() ) + { + rTree.EndEditing(); + return true; + } + else + { + return false; + } +} + + +void SAL_CALL TreeControlPeer::cancelEditing( ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + rTree.EndEditing(); +} + + +void SAL_CALL TreeControlPeer::startEditingAtNode( const Reference< XTreeNode >& xNode ) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + UnoTreeListEntry* pEntry = getEntry( xNode ); + rTree.EditEntry( pEntry ); +} + +void SAL_CALL TreeControlPeer::addTreeEditListener( const Reference< XTreeEditListener >& xListener ) +{ + maTreeEditListeners.addInterface( xListener ); +} + +void SAL_CALL TreeControlPeer::removeTreeEditListener( const Reference< XTreeEditListener >& xListener ) +{ + maTreeEditListeners.removeInterface( xListener ); +} + +bool TreeControlPeer::onEditingEntry( UnoTreeListEntry const * pEntry ) +{ + if( mpTreeImpl && pEntry && pEntry->mxNode.is() && (maTreeEditListeners.getLength() > 0) ) + { + try + { + maTreeEditListeners.nodeEditing( pEntry->mxNode ); + } + catch( VetoException& ) + { + return false; + } + catch( Exception& ) + { + } + } + return true; +} + +bool TreeControlPeer::onEditedEntry( UnoTreeListEntry const * pEntry, const OUString& rNewText ) +{ + if( mpTreeImpl && pEntry && pEntry->mxNode.is() ) try + { + LockGuard aLockGuard( mnEditLock ); + if( maTreeEditListeners.getLength() > 0 ) + { + maTreeEditListeners.nodeEdited( pEntry->mxNode, rNewText ); + return false; + } + else + { + Reference< XMutableTreeNode > xMutableNode( pEntry->mxNode, UNO_QUERY ); + if( xMutableNode.is() ) + xMutableNode->setDisplayValue( Any( rNewText ) ); + else + return false; + } + + } + catch( Exception& ) + { + } + + return true; +} + + +// css::awt::tree::TreeDataModelListener + + +void SAL_CALL TreeControlPeer::treeNodesChanged( const css::awt::tree::TreeDataModelEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if( mnEditLock != 0 ) + return; + + updateTree( rEvent ); +} + +void SAL_CALL TreeControlPeer::treeNodesInserted( const css::awt::tree::TreeDataModelEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if( mnEditLock != 0 ) + return; + + updateTree( rEvent ); +} + +void SAL_CALL TreeControlPeer::treeNodesRemoved( const css::awt::tree::TreeDataModelEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if( mnEditLock != 0 ) + return; + + updateTree( rEvent ); +} + +void SAL_CALL TreeControlPeer::treeStructureChanged( const css::awt::tree::TreeDataModelEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if( mnEditLock != 0 ) + return; + + updateTree( rEvent ); +} + +void TreeControlPeer::updateTree( const css::awt::tree::TreeDataModelEvent& rEvent ) +{ + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + Sequence< Reference< XTreeNode > > Nodes; + Reference< XTreeNode > xNode( rEvent.ParentNode ); + if( !xNode.is() && Nodes.hasElements() ) + { + xNode = Nodes[0]; + } + + if( xNode.is() ) + updateNode( rTree, xNode ); +} + +void TreeControlPeer::updateNode( UnoTreeListBoxImpl const & rTree, const Reference< XTreeNode >& xNode ) +{ + if( !xNode.is() ) + return; + + UnoTreeListEntry* pNodeEntry = getEntry( xNode, false ); + + if( !pNodeEntry ) + { + Reference< XTreeNode > xParentNode( xNode->getParent() ); + UnoTreeListEntry* pParentEntry = nullptr; + sal_uLong nChild = TREELIST_APPEND; + + if( xParentNode.is() ) + { + pParentEntry = getEntry( xParentNode ); + nChild = xParentNode->getIndex( xNode ); + } + + pNodeEntry = createEntry( xNode, pParentEntry, nChild ); + } + + updateChildNodes( rTree, xNode, pNodeEntry ); +} + +void TreeControlPeer::updateChildNodes( UnoTreeListBoxImpl const & rTree, const Reference< XTreeNode >& xParentNode, UnoTreeListEntry* pParentEntry ) +{ + if( !(xParentNode.is() && pParentEntry) ) + return; + + UnoTreeListEntry* pCurrentChild = dynamic_cast< UnoTreeListEntry* >( rTree.FirstChild( pParentEntry ) ); + + const sal_Int32 nChildCount = xParentNode->getChildCount(); + for( sal_Int32 nChild = 0; nChild < nChildCount; nChild++ ) + { + Reference< XTreeNode > xNode( xParentNode->getChildAt( nChild ) ); + if( !pCurrentChild || ( pCurrentChild->mxNode != xNode ) ) + { + UnoTreeListEntry* pNodeEntry = getEntry( xNode, false ); + if( pNodeEntry == nullptr ) + { + // child node is not yet part of the tree, add it + pCurrentChild = createEntry( xNode, pParentEntry, nChild ); + } + else if( pNodeEntry != pCurrentChild ) + { + // node is already part of the tree, but not on the correct position + rTree.GetModel()->Move( pNodeEntry, pParentEntry, nChild ); + pCurrentChild = pNodeEntry; + updateEntry( pCurrentChild ); + } + } + else + { + // child node has entry and entry is equal to current entry, + // so no structural changes happened + updateEntry( pCurrentChild ); + } + + pCurrentChild = dynamic_cast< UnoTreeListEntry* >( pCurrentChild->NextSibling() ); + } + + // check if we have entries without nodes left, we need to remove them + while( pCurrentChild ) + { + UnoTreeListEntry* pNextChild = dynamic_cast< UnoTreeListEntry* >( pCurrentChild->NextSibling() ); + rTree.GetModel()->Remove( pCurrentChild ); + pCurrentChild = pNextChild; + } +} + +OUString TreeControlPeer::getEntryString( const Any& rValue ) +{ + OUString sValue; + if( rValue.hasValue() ) + { + switch( rValue.getValueTypeClass() ) + { + case TypeClass_SHORT: + case TypeClass_LONG: + { + sal_Int32 nValue = 0; + if( rValue >>= nValue ) + sValue = OUString::number( nValue ); + break; + } + case TypeClass_BYTE: + case TypeClass_UNSIGNED_SHORT: + case TypeClass_UNSIGNED_LONG: + { + sal_uInt32 nValue = 0; + if( rValue >>= nValue ) + sValue = OUString::number( nValue ); + break; + } + case TypeClass_HYPER: + { + sal_Int64 nValue = 0; + if( rValue >>= nValue ) + sValue = OUString::number( nValue ); + break; + } + case TypeClass_UNSIGNED_HYPER: + { + sal_uInt64 nValue = 0; + if( rValue >>= nValue ) + sValue = OUString::number( nValue ); + break; + } + case TypeClass_FLOAT: + case TypeClass_DOUBLE: + { + double fValue = 0.0; + if( rValue >>= fValue ) + sValue = OUString::number( fValue ); + break; + } + case TypeClass_STRING: + rValue >>= sValue; + break; + /* + case TypeClass_INTERFACE: + // @todo + break; + case TypeClass_SEQUENCE: + { + Sequence< Any > aValues; + if( aValue >>= aValues ) + { + updateEntry( SvTreeListEntry& rEntry, aValues ); + return; + } + } + break; + */ + default: + break; + } + } + return sValue; +} + +// XEventListener +void SAL_CALL TreeControlPeer::disposing( const css::lang::EventObject& ) +{ + // model is disposed, so we clear our tree + SolarMutexGuard aGuard; + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + rTree.Clear(); + mxDataModel.clear(); +} + +void TreeControlPeer::onChangeDataModel( UnoTreeListBoxImpl& rTree, const Reference< XTreeDataModel >& xDataModel ) +{ + if( xDataModel.is() && (mxDataModel == xDataModel) ) + return; // do nothing + + Reference< XTreeDataModelListener > xListener( this ); + + if( mxDataModel.is() ) + mxDataModel->removeTreeDataModelListener( xListener ); + + mxDataModel = xDataModel; + + fillTree( rTree, mxDataModel ); + + if( mxDataModel.is() ) + mxDataModel->addTreeDataModelListener( xListener ); +} + + +// css::awt::XLayoutConstrains + + +css::awt::Size TreeControlPeer::getMinimumSize() +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz; +/* todo + MultiLineEdit* pEdit = (MultiLineEdit*) GetWindow(); + if ( pEdit ) + aSz = AWTSize(pEdit->CalcMinimumSize()); +*/ + return aSz; +} + +css::awt::Size TreeControlPeer::getPreferredSize() +{ + return getMinimumSize(); +} + +css::awt::Size TreeControlPeer::calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + SolarMutexGuard aGuard; + + css::awt::Size aSz = rNewSize; +/* todo + MultiLineEdit* pEdit = (MultiLineEdit*) GetWindow(); + if ( pEdit ) + aSz = AWTSize(pEdit->CalcAdjustedSize( VCLSize(rNewSize ))); +*/ + return aSz; +} + + +// css::awt::XVclWindowPeer + + +void TreeControlPeer::setProperty( const OUString& PropertyName, const Any& aValue) +{ + SolarMutexGuard aGuard; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + switch( GetPropertyId( PropertyName ) ) + { + case BASEPROPERTY_HIDEINACTIVESELECTION: + { + bool bEnabled = false; + if ( aValue >>= bEnabled ) + { + WinBits nStyle = rTree.GetStyle(); + if ( bEnabled ) + nStyle |= WB_HIDESELECTION; + else + nStyle &= ~WB_HIDESELECTION; + rTree.SetStyle( nStyle ); + } + } + break; + + case BASEPROPERTY_TREE_SELECTIONTYPE: + { + SelectionType eSelectionType; + if( aValue >>= eSelectionType ) + { + SelectionMode eSelMode; + switch( eSelectionType ) + { + case SelectionType_SINGLE: eSelMode = SelectionMode::Single; break; + case SelectionType_RANGE: eSelMode = SelectionMode::Range; break; + case SelectionType_MULTI: eSelMode = SelectionMode::Multiple; break; + // case SelectionType_NONE: + default: eSelMode = SelectionMode::NONE; break; + } + if( rTree.GetSelectionMode() != eSelMode ) + rTree.SetSelectionMode( eSelMode ); + } + break; + } + + case BASEPROPERTY_TREE_DATAMODEL: + onChangeDataModel( rTree, Reference< XTreeDataModel >( aValue, UNO_QUERY ) ); + break; + case BASEPROPERTY_ROW_HEIGHT: + { + sal_Int32 nHeight = 0; + if( aValue >>= nHeight ) + rTree.SetEntryHeight( static_cast<short>(nHeight) ); + break; + } + case BASEPROPERTY_TREE_EDITABLE: + { + bool bEnabled = false; + if( aValue >>= bEnabled ) + rTree.EnableInplaceEditing( bEnabled ); + break; + } + case BASEPROPERTY_TREE_INVOKESSTOPNODEEDITING: + break; // @todo + case BASEPROPERTY_TREE_ROOTDISPLAYED: + { + bool bDisplayed = false; + if( (aValue >>= bDisplayed) && ( bDisplayed != mbIsRootDisplayed) ) + { + onChangeRootDisplayed(bDisplayed); + } + break; + } + case BASEPROPERTY_TREE_SHOWSHANDLES: + { + bool bEnabled = false; + if( aValue >>= bEnabled ) + { + WinBits nBits = rTree.GetStyle() & (~WB_HASLINES); + if( bEnabled ) + nBits |= WB_HASLINES; + if( nBits != rTree.GetStyle() ) + rTree.SetStyle( nBits ); + } + break; + } + case BASEPROPERTY_TREE_SHOWSROOTHANDLES: + { + bool bEnabled = false; + if( aValue >>= bEnabled ) + { + WinBits nBits = rTree.GetStyle() & (~WB_HASLINESATROOT); + if( bEnabled ) + nBits |= WB_HASLINESATROOT; + if( nBits != rTree.GetStyle() ) + rTree.SetStyle( nBits ); + } + break; + } + default: + VCLXWindow::setProperty( PropertyName, aValue ); + break; + } +} + +Any TreeControlPeer::getProperty( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + const sal_uInt16 nPropId = GetPropertyId( PropertyName ); + if( (nPropId >= BASEPROPERTY_TREE_START) && (nPropId <= BASEPROPERTY_TREE_END) ) + { + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + switch(nPropId) + { + case BASEPROPERTY_TREE_SELECTIONTYPE: + { + SelectionType eSelectionType; + + SelectionMode eSelMode = rTree.GetSelectionMode(); + switch( eSelMode ) + { + case SelectionMode::Single: eSelectionType = SelectionType_SINGLE; break; + case SelectionMode::Range: eSelectionType = SelectionType_RANGE; break; + case SelectionMode::Multiple:eSelectionType = SelectionType_MULTI; break; +// case SelectionMode::NONE: + default: eSelectionType = SelectionType_NONE; break; + } + return Any( eSelectionType ); + } + case BASEPROPERTY_ROW_HEIGHT: + return Any( static_cast<sal_Int32>(rTree.GetEntryHeight()) ); + case BASEPROPERTY_TREE_DATAMODEL: + return Any( mxDataModel ); + case BASEPROPERTY_TREE_EDITABLE: + return Any( rTree.IsInplaceEditingEnabled() ); + case BASEPROPERTY_TREE_INVOKESSTOPNODEEDITING: + return Any( true ); // @todo + case BASEPROPERTY_TREE_ROOTDISPLAYED: + return Any( mbIsRootDisplayed ); + case BASEPROPERTY_TREE_SHOWSHANDLES: + return Any( (rTree.GetStyle() & WB_HASLINES) != 0 ); + case BASEPROPERTY_TREE_SHOWSROOTHANDLES: + return Any( (rTree.GetStyle() & WB_HASLINESATROOT) != 0 ); + } + } + return VCLXWindow::getProperty( PropertyName ); +} + +void TreeControlPeer::onChangeRootDisplayed( bool bIsRootDisplayed ) +{ + if( mbIsRootDisplayed == bIsRootDisplayed ) + return; + + mbIsRootDisplayed = bIsRootDisplayed; + + UnoTreeListBoxImpl& rTree = getTreeListBoxOrThrow(); + + if( rTree.GetEntryCount() == 0 ) + return; + + // todo + fillTree( rTree, mxDataModel ); +} + +bool TreeControlPeer::loadImage( const OUString& rURL, Image& rImage ) +{ + if( !mxGraphicProvider.is() ) + { + mxGraphicProvider = graphic::GraphicProvider::create( + comphelper::getProcessComponentContext()); + } + + try + { + css::beans::PropertyValues aProps{ comphelper::makePropertyValue("URL", rURL) }; + Reference< XGraphic > xGraphic( mxGraphicProvider->queryGraphic( aProps ) ); + + Graphic aGraphic( xGraphic ); + rImage = Image(aGraphic.GetBitmapEx()); + return true; + } + catch( Exception& ) + { + } + + return false; +} + + + + +UnoTreeListBoxImpl::UnoTreeListBoxImpl( TreeControlPeer* pPeer, vcl::Window* pParent, WinBits nWinStyle ) +: SvTreeListBox( pParent, nWinStyle ) +, mxPeer( pPeer ) +{ + SetStyle( WB_BORDER | WB_HASLINES |WB_HASBUTTONS | WB_HASLINESATROOT | WB_HASBUTTONSATROOT | WB_HSCROLL ); + SetNodeDefaultImages(); + SetSelectHdl( LINK(this, UnoTreeListBoxImpl, OnSelectionChangeHdl) ); + SetDeselectHdl( LINK(this, UnoTreeListBoxImpl, OnSelectionChangeHdl) ); + + SetExpandingHdl( LINK(this, UnoTreeListBoxImpl, OnExpandingHdl) ); + SetExpandedHdl( LINK(this, UnoTreeListBoxImpl, OnExpandedHdl) ); + +} + + +UnoTreeListBoxImpl::~UnoTreeListBoxImpl() +{ + disposeOnce(); +} + +void UnoTreeListBoxImpl::dispose() +{ + if( mxPeer.is() ) + mxPeer->disposeControl(); + mxPeer.clear(); + SvTreeListBox::dispose(); +} + + +IMPL_LINK_NOARG(UnoTreeListBoxImpl, OnSelectionChangeHdl, SvTreeListBox*, void) +{ + if( mxPeer.is() ) + mxPeer->onSelectionChanged(); +} + + +IMPL_LINK_NOARG(UnoTreeListBoxImpl, OnExpandingHdl, SvTreeListBox*, bool) +{ + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( GetHdlEntry() ); + + if( pEntry && mxPeer.is() ) + { + return mxPeer->onExpanding( pEntry->mxNode, !IsExpanded( pEntry ) ); + } + return false; +} + + +IMPL_LINK_NOARG(UnoTreeListBoxImpl, OnExpandedHdl, SvTreeListBox*, void) +{ + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( GetHdlEntry() ); + if( pEntry && mxPeer.is() ) + { + mxPeer->onExpanded( pEntry->mxNode, IsExpanded( pEntry ) ); + } +} + + +void UnoTreeListBoxImpl::insert( SvTreeListEntry* pEntry,SvTreeListEntry* pParent,sal_uLong nPos ) +{ + if( pParent ) + SvTreeListBox::Insert( pEntry, pParent, nPos ); + else + SvTreeListBox::Insert( pEntry, nPos ); +} + + +void UnoTreeListBoxImpl::RequestingChildren( SvTreeListEntry* pParent ) +{ + UnoTreeListEntry* pEntry = dynamic_cast< UnoTreeListEntry* >( pParent ); + if( pEntry && pEntry->mxNode.is() && mxPeer.is() ) + mxPeer->onRequestChildNodes( pEntry->mxNode ); +} + + +bool UnoTreeListBoxImpl::EditingEntry( SvTreeListEntry* pEntry ) +{ + return mxPeer.is() && mxPeer->onEditingEntry( dynamic_cast< UnoTreeListEntry* >( pEntry ) ); +} + + +bool UnoTreeListBoxImpl::EditedEntry( SvTreeListEntry* pEntry, const OUString& rNewText ) +{ + return mxPeer.is() && mxPeer->onEditedEntry( dynamic_cast< UnoTreeListEntry* >( pEntry ), rNewText ); +} + + + + +UnoTreeListItem::UnoTreeListItem() +: SvLBoxString(OUString()) +{ +} + +void UnoTreeListItem::Paint( + const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext, const SvViewDataEntry* /*pView*/, const SvTreeListEntry& rEntry) +{ + Point aPos(rPos); + Size aSize(GetWidth(&rDev, &rEntry), GetHeight(&rDev, &rEntry)); + if (!!maImage) + { + rRenderContext.DrawImage(aPos, maImage, rDev.IsEnabled() ? DrawImageFlags::NONE : DrawImageFlags::Disable); + int nWidth = maImage.GetSizePixel().Width() + 6; + aPos.AdjustX(nWidth ); + aSize.AdjustWidth( -nWidth ); + } + rRenderContext.DrawText(tools::Rectangle(aPos,aSize),maText, rDev.IsEnabled() ? DrawTextFlags::NONE : DrawTextFlags::Disable); +} + + +std::unique_ptr<SvLBoxItem> UnoTreeListItem::Clone(SvLBoxItem const * pSource) const +{ + std::unique_ptr<UnoTreeListItem> pNew(new UnoTreeListItem); + UnoTreeListItem const * pSourceItem = static_cast< UnoTreeListItem const * >( pSource ); + pNew->maText = pSourceItem->maText; + pNew->maImage = pSourceItem->maImage; + return std::unique_ptr<SvLBoxItem>(pNew.release()); +} + + +void UnoTreeListItem::SetImage( const Image& rImage ) +{ + maImage = rImage; +} + + +void UnoTreeListItem::SetGraphicURL( const OUString& rGraphicURL ) +{ + maGraphicURL = rGraphicURL; +} + + +void UnoTreeListItem::InitViewData( SvTreeListBox* pView,SvTreeListEntry* pEntry, SvViewDataItem* pViewData) +{ + if( !pViewData ) + pViewData = pView->GetViewDataItem( pEntry, this ); + + Size aSize(maImage.GetSizePixel()); + pViewData->mnWidth = aSize.Width(); + pViewData->mnHeight = aSize.Height(); + + const Size aTextSize(pView->GetTextWidth( maText ), pView->GetTextHeight()); + if( pViewData->mnWidth ) + { + pViewData->mnWidth += (6 + aTextSize.Width()); + if( pViewData->mnHeight < aTextSize.Height() ) + pViewData->mnHeight = aTextSize.Height(); + } + else + { + pViewData->mnWidth = aTextSize.Width(); + pViewData->mnHeight = aTextSize.Height(); + } +} + + +UnoTreeListEntry::UnoTreeListEntry( const Reference< XTreeNode >& xNode, TreeControlPeer* pPeer ) +: mxNode( xNode ) +, mpPeer( pPeer ) +{ + if( mpPeer ) + mpPeer->addEntry( this ); +} + + +UnoTreeListEntry::~UnoTreeListEntry() +{ + if( mpPeer ) + mpPeer->removeEntry( this ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/tree/treedatamodel.cxx b/toolkit/source/controls/tree/treedatamodel.cxx new file mode 100644 index 0000000000..4471697fb6 --- /dev/null +++ b/toolkit/source/controls/tree/treedatamodel.cxx @@ -0,0 +1,535 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/tree/XMutableTreeDataModel.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/ref.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <mutex> +#include <utility> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::awt::tree; +using namespace ::com::sun::star::lang; + +namespace { + + enum broadcast_type { nodes_changed, nodes_inserted, nodes_removed, structure_changed }; + +class MutableTreeNode; +class MutableTreeDataModel; + +typedef std::vector< rtl::Reference< MutableTreeNode > > TreeNodeVector; + +class MutableTreeDataModel : public ::cppu::WeakImplHelper< XMutableTreeDataModel, XServiceInfo > +{ +public: + MutableTreeDataModel(); + + void broadcast( broadcast_type eType, const Reference< XTreeNode >& xParentNode, const Reference< XTreeNode >& rNode ); + + // XMutableTreeDataModel + virtual css::uno::Reference< css::awt::tree::XMutableTreeNode > SAL_CALL createNode( const css::uno::Any& DisplayValue, sal_Bool ChildrenOnDemand ) override; + virtual void SAL_CALL setRoot( const css::uno::Reference< css::awt::tree::XMutableTreeNode >& RootNode ) override; + + // XTreeDataModel + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getRoot( ) override; + virtual void SAL_CALL addTreeDataModelListener( const css::uno::Reference< css::awt::tree::XTreeDataModelListener >& Listener ) override; + virtual void SAL_CALL removeTreeDataModelListener( const css::uno::Reference< css::awt::tree::XTreeDataModelListener >& Listener ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const Reference< XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const Reference< XEventListener >& aListener ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + void broadcastImpl( std::unique_lock<std::mutex>& rGuard, broadcast_type eType, const Reference< XTreeNode >& xParentNode, const Reference< XTreeNode >& rNode ); + + std::mutex m_aMutex; + comphelper::OInterfaceContainerHelper4<XTreeDataModelListener> maTreeDataModelListeners; + comphelper::OInterfaceContainerHelper4<XEventListener> maEventListeners; + bool mbDisposed; + rtl::Reference< MutableTreeNode > mxRootNode; +}; + +class MutableTreeNode: public ::cppu::WeakImplHelper< XMutableTreeNode, XServiceInfo > +{ + friend class MutableTreeDataModel; + +public: + MutableTreeNode( rtl::Reference< MutableTreeDataModel > xModel, Any aValue, bool bChildrenOnDemand ); + virtual ~MutableTreeNode() override; + + void setParent( MutableTreeNode* pParent ); + void broadcast_changes(); + void broadcast_changes(std::unique_lock<std::mutex> & rLock, + const Reference< XTreeNode >& xNode, bool bNew); + + // XMutableTreeNode + virtual css::uno::Any SAL_CALL getDataValue() override; + virtual void SAL_CALL setDataValue( const css::uno::Any& _datavalue ) override; + virtual void SAL_CALL appendChild( const css::uno::Reference< css::awt::tree::XMutableTreeNode >& ChildNode ) override; + virtual void SAL_CALL insertChildByIndex( ::sal_Int32 Index, const css::uno::Reference< css::awt::tree::XMutableTreeNode >& ChildNode ) override; + virtual void SAL_CALL removeChildByIndex( ::sal_Int32 Index ) override; + virtual void SAL_CALL setHasChildrenOnDemand( sal_Bool ChildrenOnDemand ) override; + virtual void SAL_CALL setDisplayValue( const css::uno::Any& Value ) override; + virtual void SAL_CALL setNodeGraphicURL( const OUString& URL ) override; + virtual void SAL_CALL setExpandedGraphicURL( const OUString& URL ) override; + virtual void SAL_CALL setCollapsedGraphicURL( const OUString& URL ) override; + + // XTreeNode + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getChildAt( ::sal_Int32 Index ) override; + virtual ::sal_Int32 SAL_CALL getChildCount( ) override; + virtual css::uno::Reference< css::awt::tree::XTreeNode > SAL_CALL getParent( ) override; + virtual ::sal_Int32 SAL_CALL getIndex( const css::uno::Reference< css::awt::tree::XTreeNode >& Node ) override; + virtual sal_Bool SAL_CALL hasChildrenOnDemand( ) override; + virtual css::uno::Any SAL_CALL getDisplayValue( ) override; + virtual OUString SAL_CALL getNodeGraphicURL( ) override; + virtual OUString SAL_CALL getExpandedGraphicURL( ) override; + virtual OUString SAL_CALL getCollapsedGraphicURL( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + TreeNodeVector maChildren; + Any maDisplayValue; + Any maDataValue; + bool mbHasChildrenOnDemand; + std::mutex maMutex; + MutableTreeNode* mpParent; + rtl::Reference< MutableTreeDataModel > mxModel; + OUString maNodeGraphicURL; + OUString maExpandedGraphicURL; + OUString maCollapsedGraphicURL; + bool mbIsInserted; +}; + +MutableTreeDataModel::MutableTreeDataModel() +: mbDisposed( false ) +{ +} + +void MutableTreeDataModel::broadcast( broadcast_type eType, const Reference< XTreeNode >& xParentNode, const Reference< XTreeNode >& rNode ) +{ + std::unique_lock aGuard(m_aMutex); + broadcastImpl(aGuard, eType, xParentNode, rNode); +} + +void MutableTreeDataModel::broadcastImpl( std::unique_lock<std::mutex>& rGuard, broadcast_type eType, const Reference< XTreeNode >& xParentNode, const Reference< XTreeNode >& rNode ) +{ + if( !maTreeDataModelListeners.getLength(rGuard) ) + return; + + Reference< XInterface > xSource( getXWeak() ); + const Sequence< Reference< XTreeNode > > aNodes { rNode }; + TreeDataModelEvent aEvent( xSource, aNodes, xParentNode ); + + comphelper::OInterfaceIteratorHelper4 aListIter(rGuard, maTreeDataModelListeners); + rGuard.unlock(); + while(aListIter.hasMoreElements()) + { + XTreeDataModelListener* pListener = aListIter.next().get(); + switch( eType ) + { + case nodes_changed: pListener->treeNodesChanged(aEvent); break; + case nodes_inserted: pListener->treeNodesInserted(aEvent); break; + case nodes_removed: pListener->treeNodesRemoved(aEvent); break; + case structure_changed: pListener->treeStructureChanged(aEvent); break; + } + } +} + +Reference< XMutableTreeNode > SAL_CALL MutableTreeDataModel::createNode( const Any& aValue, sal_Bool bChildrenOnDemand ) +{ + return new MutableTreeNode( this, aValue, bChildrenOnDemand ); +} + +void SAL_CALL MutableTreeDataModel::setRoot( const Reference< XMutableTreeNode >& xNode ) +{ + if( !xNode.is() ) + throw IllegalArgumentException(); + + std::unique_lock aGuard( m_aMutex ); + if( xNode.get() == mxRootNode.get() ) + return; + + if( mxRootNode.is() ) + mxRootNode->mbIsInserted = false; + + rtl::Reference< MutableTreeNode > xImpl( dynamic_cast< MutableTreeNode* >( xNode.get() ) ); + if( !xImpl.is() || xImpl->mbIsInserted ) + throw IllegalArgumentException(); + + xImpl->mbIsInserted = true; + mxRootNode = xImpl; + + Reference< XTreeNode > xParentNode; + broadcastImpl( aGuard, structure_changed, xParentNode, mxRootNode ); +} + +Reference< XTreeNode > SAL_CALL MutableTreeDataModel::getRoot( ) +{ + std::unique_lock aGuard( m_aMutex ); + return mxRootNode; +} + +void SAL_CALL MutableTreeDataModel::addTreeDataModelListener( const Reference< XTreeDataModelListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + maTreeDataModelListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL MutableTreeDataModel::removeTreeDataModelListener( const Reference< XTreeDataModelListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + maTreeDataModelListeners.removeInterface( aGuard, xListener ); +} + +void SAL_CALL MutableTreeDataModel::dispose() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mbDisposed ) + { + mbDisposed = true; + css::lang::EventObject aEvent; + aEvent.Source.set( getXWeak() ); + maTreeDataModelListeners.disposeAndClear( aGuard, aEvent ); + maEventListeners.disposeAndClear( aGuard, aEvent ); + } +} + +void SAL_CALL MutableTreeDataModel::addEventListener( const Reference< XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + maEventListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL MutableTreeDataModel::removeEventListener( const Reference< XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + maEventListeners.removeInterface( aGuard, xListener ); +} + +OUString SAL_CALL MutableTreeDataModel::getImplementationName( ) +{ + return "toolkit.MutableTreeDataModel"; +} + +sal_Bool SAL_CALL MutableTreeDataModel::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL MutableTreeDataModel::getSupportedServiceNames( ) +{ + Sequence<OUString> aSeq { "com.sun.star.awt.tree.MutableTreeDataModel" }; + return aSeq; +} + +MutableTreeNode::MutableTreeNode( rtl::Reference< MutableTreeDataModel > xModel, Any aValue, bool bChildrenOnDemand ) +: maDisplayValue(std::move( aValue )) +, mbHasChildrenOnDemand( bChildrenOnDemand ) +, mpParent( nullptr ) +, mxModel(std::move( xModel )) +, mbIsInserted( false ) +{ +} + +MutableTreeNode::~MutableTreeNode() +{ + for( auto& rChild : maChildren ) + rChild->setParent(nullptr); +} + +void MutableTreeNode::setParent( MutableTreeNode* pParent ) +{ + mpParent = pParent; +} + +void MutableTreeNode::broadcast_changes() +{ + if( mxModel.is() ) + { + mxModel->broadcast( nodes_changed, mpParent, this ); + } +} + +void MutableTreeNode::broadcast_changes(std::unique_lock<std::mutex> & rLock, + const Reference< XTreeNode >& xNode, bool const bNew) +{ + auto const xModel(mxModel); + rLock.unlock(); + if (xModel.is()) + { + xModel->broadcast(bNew ? nodes_inserted : nodes_removed, this, xNode); + } +} + +Any SAL_CALL MutableTreeNode::getDataValue() +{ + std::scoped_lock aGuard( maMutex ); + return maDataValue; +} + +void SAL_CALL MutableTreeNode::setDataValue( const Any& _datavalue ) +{ + std::scoped_lock aGuard( maMutex ); + maDataValue = _datavalue; +} + +void SAL_CALL MutableTreeNode::appendChild( const Reference< XMutableTreeNode >& xChildNode ) +{ + std::unique_lock aGuard( maMutex ); + rtl::Reference< MutableTreeNode > xImpl( dynamic_cast< MutableTreeNode* >( xChildNode.get() ) ); + + if( !xImpl.is() || xImpl->mbIsInserted || (this == xImpl.get()) ) + throw IllegalArgumentException(); + + maChildren.push_back( xImpl ); + xImpl->setParent(this); + xImpl->mbIsInserted = true; + + broadcast_changes(aGuard, xChildNode, true); +} + +void SAL_CALL MutableTreeNode::insertChildByIndex( sal_Int32 nChildIndex, const Reference< XMutableTreeNode >& xChildNode ) +{ + std::unique_lock aGuard( maMutex ); + + if( (nChildIndex < 0) || (o3tl::make_unsigned(nChildIndex) > maChildren.size()) ) + throw IndexOutOfBoundsException(); + + rtl::Reference< MutableTreeNode > xImpl( dynamic_cast< MutableTreeNode* >( xChildNode.get() ) ); + if( !xImpl.is() || xImpl->mbIsInserted || (this == xImpl.get()) ) + throw IllegalArgumentException(); + + xImpl->mbIsInserted = true; + + TreeNodeVector::iterator aIter( maChildren.begin() ); + std::advance(aIter, nChildIndex); + + maChildren.insert( aIter, xImpl ); + xImpl->setParent( this ); + + broadcast_changes(aGuard, xChildNode, true); +} + +void SAL_CALL MutableTreeNode::removeChildByIndex( sal_Int32 nChildIndex ) +{ + std::unique_lock aGuard( maMutex ); + + if( (nChildIndex < 0) || (o3tl::make_unsigned(nChildIndex) >= maChildren.size()) ) + throw IndexOutOfBoundsException(); + + rtl::Reference< MutableTreeNode > xImpl; + + TreeNodeVector::iterator aIter( maChildren.begin() ); + std::advance(aIter, nChildIndex); + + xImpl = *aIter; + maChildren.erase( aIter ); + + if( !xImpl.is() ) + throw IndexOutOfBoundsException(); + + xImpl->setParent(nullptr); + xImpl->mbIsInserted = false; + + broadcast_changes(aGuard, xImpl, false); +} + +void SAL_CALL MutableTreeNode::setHasChildrenOnDemand( sal_Bool bChildrenOnDemand ) +{ + bool bChanged; + + { + std::scoped_lock aGuard( maMutex ); + bChanged = mbHasChildrenOnDemand != bool(bChildrenOnDemand); + mbHasChildrenOnDemand = bChildrenOnDemand; + } + + if( bChanged ) + broadcast_changes(); +} + +void SAL_CALL MutableTreeNode::setDisplayValue( const Any& aValue ) +{ + { + std::scoped_lock aGuard( maMutex ); + maDisplayValue = aValue; + } + + broadcast_changes(); +} + +void SAL_CALL MutableTreeNode::setNodeGraphicURL( const OUString& rURL ) +{ + bool bChanged; + + { + std::scoped_lock aGuard( maMutex ); + bChanged = maNodeGraphicURL != rURL; + maNodeGraphicURL = rURL; + } + + if( bChanged ) + broadcast_changes(); +} + +void SAL_CALL MutableTreeNode::setExpandedGraphicURL( const OUString& rURL ) +{ + bool bChanged; + + { + std::scoped_lock aGuard( maMutex ); + bChanged = maExpandedGraphicURL != rURL; + maExpandedGraphicURL = rURL; + } + + if( bChanged ) + broadcast_changes(); +} + +void SAL_CALL MutableTreeNode::setCollapsedGraphicURL( const OUString& rURL ) +{ + bool bChanged; + + { + std::scoped_lock aGuard( maMutex ); + bChanged = maCollapsedGraphicURL != rURL; + maCollapsedGraphicURL = rURL; + } + + if( bChanged ) + broadcast_changes(); +} + +Reference< XTreeNode > SAL_CALL MutableTreeNode::getChildAt( sal_Int32 nChildIndex ) +{ + std::scoped_lock aGuard( maMutex ); + + if( (nChildIndex < 0) || (o3tl::make_unsigned(nChildIndex) >= maChildren.size()) ) + throw IndexOutOfBoundsException(); + return maChildren[nChildIndex]; +} + +sal_Int32 SAL_CALL MutableTreeNode::getChildCount( ) +{ + std::scoped_lock aGuard( maMutex ); + return static_cast<sal_Int32>(maChildren.size()); +} + +Reference< XTreeNode > SAL_CALL MutableTreeNode::getParent( ) +{ + std::scoped_lock aGuard( maMutex ); + return mpParent; +} + +sal_Int32 SAL_CALL MutableTreeNode::getIndex( const Reference< XTreeNode >& xNode ) +{ + std::scoped_lock aGuard( maMutex ); + + rtl::Reference< MutableTreeNode > xImpl( dynamic_cast< MutableTreeNode* >( xNode.get() ) ); + if( xImpl.is() ) + { + sal_Int32 nChildCount = maChildren.size(); + while( nChildCount-- ) + { + if( maChildren[nChildCount] == xImpl ) + return nChildCount; + } + } + + return -1; +} + +sal_Bool SAL_CALL MutableTreeNode::hasChildrenOnDemand( ) +{ + std::scoped_lock aGuard( maMutex ); + return mbHasChildrenOnDemand; +} + +Any SAL_CALL MutableTreeNode::getDisplayValue( ) +{ + std::scoped_lock aGuard( maMutex ); + return maDisplayValue; +} + +OUString SAL_CALL MutableTreeNode::getNodeGraphicURL( ) +{ + std::scoped_lock aGuard( maMutex ); + return maNodeGraphicURL; +} + +OUString SAL_CALL MutableTreeNode::getExpandedGraphicURL( ) +{ + std::scoped_lock aGuard( maMutex ); + return maExpandedGraphicURL; +} + +OUString SAL_CALL MutableTreeNode::getCollapsedGraphicURL( ) +{ + std::scoped_lock aGuard( maMutex ); + return maCollapsedGraphicURL; +} + +OUString SAL_CALL MutableTreeNode::getImplementationName( ) +{ + return "toolkit.MutableTreeNode"; +} + +sal_Bool SAL_CALL MutableTreeNode::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL MutableTreeNode::getSupportedServiceNames( ) +{ + Sequence<OUString> aSeq { "com.sun.star.awt.tree.MutableTreeNode" }; + return aSeq; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_MutableTreeDataModel_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new MutableTreeDataModel()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrol.cxx b/toolkit/source/controls/unocontrol.cxx new file mode 100644 index 0000000000..0880455581 --- /dev/null +++ b/toolkit/source/controls/unocontrol.cxx @@ -0,0 +1,1588 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XMultiPropertySet.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/resource/XStringResourceResolver.hpp> +#include <toolkit/controls/unocontrol.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/mutex.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <helper/property.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <controls/accessiblecontrolcontext.hxx> + +#include <algorithm> +#include <map> +#include <string_view> +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; + +using ::com::sun::star::accessibility::XAccessibleContext; +using ::com::sun::star::accessibility::XAccessible; + +namespace { + +struct LanguageDependentProp +{ + const char* pPropName; + sal_Int32 nPropNameLength; +}; + +} + +const LanguageDependentProp aLanguageDependentProp[] = +{ + { "Text", 4 }, + { "Label", 5 }, + { "Title", 5 }, + { "HelpText", 8 }, + { "CurrencySymbol", 14 }, + { "StringItemList", 14 }, + { nullptr, 0 } +}; + +static Sequence< OUString> lcl_ImplGetPropertyNames( const Reference< XMultiPropertySet > & rxModel ) +{ + Sequence< OUString> aNames; + Reference< XPropertySetInfo > xPSInf = rxModel->getPropertySetInfo(); + DBG_ASSERT( xPSInf.is(), "UpdateFromModel: No PropertySetInfo!" ); + if ( xPSInf.is() ) + { + const Sequence< Property> aProps = xPSInf->getProperties(); + sal_Int32 nLen = aProps.getLength(); + aNames = Sequence< OUString>( nLen ); + std::transform(aProps.begin(), aProps.end(), aNames.getArray(), + [](const Property& rProp) -> OUString { return rProp.Name; }); + } + return aNames; +} + +namespace { + +class VclListenerLock +{ +private: + VCLXWindow* m_pLockWindow; + +public: + explicit VclListenerLock( VCLXWindow* _pLockWindow ) + : m_pLockWindow( _pLockWindow ) + { + if ( m_pLockWindow ) + m_pLockWindow->suspendVclEventListening( ); + } + ~VclListenerLock() + { + if ( m_pLockWindow ) + m_pLockWindow->resumeVclEventListening( ); + } + VclListenerLock(const VclListenerLock&) = delete; + VclListenerLock& operator=(const VclListenerLock&) = delete; +}; + +} + +typedef ::std::map< OUString, sal_Int32 > MapString2Int; +struct UnoControl_Data +{ + MapString2Int aSuspendedPropertyNotifications; + /// true if and only if our model has a property ResourceResolver + bool bLocalizationSupport; + + UnoControl_Data() + :bLocalizationSupport( false ) + { + } +}; + +UnoControl::UnoControl() : + maDisposeListeners( *this ) + , maWindowListeners( *this ) + , maFocusListeners( *this ) + , maKeyListeners( *this ) + , maMouseListeners( *this ) + , maMouseMotionListeners( *this ) + , maPaintListeners( *this ) + , maModeChangeListeners( GetMutex() ) + , mpData( new UnoControl_Data ) +{ + mbDisposePeer = true; + mbRefreshingPeer = false; + mbCreatingPeer = false; + mbCreatingCompatiblePeer = false; + mbDesignMode = false; +} + +UnoControl::~UnoControl() +{ +} + +OUString UnoControl::GetComponentServiceName() const +{ + return OUString(); +} + +Reference< XVclWindowPeer > UnoControl::ImplGetCompatiblePeer() +{ + DBG_ASSERT( !mbCreatingCompatiblePeer, "ImplGetCompatiblePeer - recursive?" ); + + mbCreatingCompatiblePeer = true; + + Reference< XVclWindowPeer > xCompatiblePeer = getVclWindowPeer(); + + if ( !xCompatiblePeer.is() ) + { + // Create the pair as invisible + bool bVis = maComponentInfos.bVisible; + if( bVis ) + maComponentInfos.bVisible = false; + + Reference< XVclWindowPeer > xCurrentPeer = getVclWindowPeer(); + setPeer( nullptr ); + + // queryInterface ourself, to allow aggregation + Reference< XControl > xMe; + OWeakAggObject::queryInterface( cppu::UnoType<decltype(xMe)>::get() ) >>= xMe; + + vcl::Window* pParentWindow( nullptr ); + { + SolarMutexGuard aGuard; + auto pDefaultDevice = Application::GetDefaultDevice(); + if (pDefaultDevice) + pParentWindow = pDefaultDevice->GetOwnerWindow(); + ENSURE_OR_THROW( pParentWindow != nullptr, "could obtain a default parent window!" ); + } + try + { + xMe->createPeer( nullptr, pParentWindow->GetComponentInterface() ); + } + catch( const Exception& ) + { + mbCreatingCompatiblePeer = false; + throw; + } + xCompatiblePeer = getVclWindowPeer(); + setPeer( xCurrentPeer ); + + if ( xCompatiblePeer.is() && mxGraphics.is() ) + { + Reference< XView > xPeerView( xCompatiblePeer, UNO_QUERY ); + if ( xPeerView.is() ) + xPeerView->setGraphics( mxGraphics ); + } + + if( bVis ) + maComponentInfos.bVisible = true; + } + + mbCreatingCompatiblePeer = false; + + return xCompatiblePeer; +} + +bool UnoControl::ImplCheckLocalize( OUString& _rPossiblyLocalizable ) +{ + if ( !mpData->bLocalizationSupport + || ( _rPossiblyLocalizable.isEmpty() ) + || ( _rPossiblyLocalizable[0] != '&' ) + // TODO: make this reasonable. At the moment, everything which by accident starts with a & is considered + // localizable, which is probably wrong. + ) + return false; + + try + { + Reference< XPropertySet > xPropSet( mxModel, UNO_QUERY_THROW ); + Reference< resource::XStringResourceResolver > xStringResourceResolver( + xPropSet->getPropertyValue("ResourceResolver"), + UNO_QUERY + ); + if ( xStringResourceResolver.is() ) + { + OUString aLocalizationKey( _rPossiblyLocalizable.copy( 1 ) ); + _rPossiblyLocalizable = xStringResourceResolver->resolveString( aLocalizationKey ); + return true; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + return false; +} + +void UnoControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) +{ + // since a change made in propertiesChange, we can't be sure that this is called with a valid getPeer(), + // this assumption may be false in some (seldom) multi-threading scenarios (cause propertiesChange + // releases our mutex before calling here in) + // That's why this additional check + + if ( !mxVclWindowPeer.is() ) + return; + + Any aConvertedValue( rVal ); + + if ( mpData->bLocalizationSupport ) + { + // We now support a mapping for language dependent properties. This is the + // central method to implement it. + if( rPropName == "Text" || + rPropName == "Label" || + rPropName == "Title" || + rPropName == "HelpText" || + rPropName == "CurrencySymbol" || + rPropName == "StringItemList" ) + { + OUString aValue; + uno::Sequence< OUString > aSeqValue; + if ( aConvertedValue >>= aValue ) + { + if ( ImplCheckLocalize( aValue ) ) + aConvertedValue <<= aValue; + } + else if ( aConvertedValue >>= aSeqValue ) + { + for ( auto& rValue : asNonConstRange(aSeqValue) ) + ImplCheckLocalize( rValue ); + aConvertedValue <<= aSeqValue; + } + } + } + + mxVclWindowPeer->setProperty( rPropName, aConvertedValue ); +} + +void UnoControl::PrepareWindowDescriptor( WindowDescriptor& ) +{ +} + +Reference< XWindow > UnoControl::getParentPeer() const +{ + Reference< XWindow > xPeer; + if( mxContext.is() ) + { + Reference< XControl > xContComp( mxContext, UNO_QUERY ); + if ( xContComp.is() ) + { + Reference< XWindowPeer > xP = xContComp->getPeer(); + if ( xP.is() ) + xPeer.set( xP, UNO_QUERY ); + } + } + return xPeer; +} + +void UnoControl::updateFromModel() +{ + // Read default properties and hand over to peer + if( getPeer().is() ) + { + Reference< XMultiPropertySet > xPropSet( mxModel, UNO_QUERY ); + if( xPropSet.is() ) + { + Sequence< OUString> aNames = lcl_ImplGetPropertyNames( xPropSet ); + xPropSet->firePropertiesChangeEvent( aNames, this ); + } + } +} + + +// XTypeProvider +IMPL_IMPLEMENTATION_ID( UnoControl ) + +void +UnoControl::DisposeAccessibleContext(Reference<XComponent> const& xContextComp) +{ + if (xContextComp.is()) + { + try + { + xContextComp->removeEventListener( this ); + xContextComp->dispose(); + } + catch( const Exception& ) + { + OSL_FAIL( "UnoControl::disposeAccessibleContext: could not dispose my AccessibleContext!" ); + } + } +} + +void UnoControl::dispose( ) +{ + Reference< XVclWindowPeer > xPeer; + Reference<XComponent> xAccessibleComp; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if( mbDisposePeer ) + { + xPeer = mxVclWindowPeer; + } + setPeer( nullptr ); + xAccessibleComp.set(maAccessibleContext, UNO_QUERY); + maAccessibleContext.clear(); + } + if( xPeer.is() ) + { + xPeer->dispose(); + } + + // dispose our AccessibleContext - without Mutex locked + DisposeAccessibleContext(xAccessibleComp); + + EventObject aDisposeEvent; + aDisposeEvent.Source = static_cast< XAggregation* >( this ); + + maDisposeListeners.disposeAndClear( aDisposeEvent ); + maWindowListeners.disposeAndClear( aDisposeEvent ); + maFocusListeners.disposeAndClear( aDisposeEvent ); + maKeyListeners.disposeAndClear( aDisposeEvent ); + maMouseListeners.disposeAndClear( aDisposeEvent ); + maMouseMotionListeners.disposeAndClear( aDisposeEvent ); + maPaintListeners.disposeAndClear( aDisposeEvent ); + maModeChangeListeners.disposeAndClear( aDisposeEvent ); + + // release Model again + setModel( Reference< XControlModel > () ); + setContext( Reference< XInterface > () ); +} + +void UnoControl::addEventListener( const Reference< XEventListener >& rxListener ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + maDisposeListeners.addInterface( rxListener ); +} + +void UnoControl::removeEventListener( const Reference< XEventListener >& rxListener ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + maDisposeListeners.removeInterface( rxListener ); +} + +bool UnoControl::requiresNewPeer( const OUString& /* _rPropertyName */ ) const +{ + return false; +} + +// XPropertiesChangeListener +void UnoControl::propertiesChange( const Sequence< PropertyChangeEvent >& rEvents ) +{ + Sequence< PropertyChangeEvent > aEvents( rEvents ); + { + ::osl::MutexGuard aGuard( GetMutex() ); + + if ( !mpData->aSuspendedPropertyNotifications.empty() ) + { + // strip the property which we are currently updating (somewhere up the stack) + PropertyChangeEvent* pEvents = aEvents.getArray(); + PropertyChangeEvent* pEventsEnd = pEvents + aEvents.getLength(); + for ( ; pEvents < pEventsEnd; ) + if ( mpData->aSuspendedPropertyNotifications.find( pEvents->PropertyName ) != mpData->aSuspendedPropertyNotifications.end() ) + { + std::copy(pEvents + 1, pEventsEnd, pEvents); + --pEventsEnd; + } + else + ++pEvents; + aEvents.realloc( pEventsEnd - aEvents.getConstArray() ); + + if ( !aEvents.hasElements() ) + return; + } + } + + ImplModelPropertiesChanged( aEvents ); +} + +void UnoControl::ImplLockPropertyChangeNotification( const OUString& rPropertyName, bool bLock ) +{ + MapString2Int::iterator pos = mpData->aSuspendedPropertyNotifications.find( rPropertyName ); + if ( bLock ) + { + if ( pos == mpData->aSuspendedPropertyNotifications.end() ) + pos = mpData->aSuspendedPropertyNotifications.emplace( rPropertyName, 0 ).first; + ++pos->second; + } + else + { + OSL_ENSURE( pos != mpData->aSuspendedPropertyNotifications.end(), "UnoControl::ImplLockPropertyChangeNotification: property not locked!" ); + if ( pos != mpData->aSuspendedPropertyNotifications.end() ) + { + OSL_ENSURE( pos->second > 0, "UnoControl::ImplLockPropertyChangeNotification: invalid suspension counter!" ); + if ( 0 == --pos->second ) + mpData->aSuspendedPropertyNotifications.erase( pos ); + } + } +} + +void UnoControl::ImplLockPropertyChangeNotifications( const Sequence< OUString >& rPropertyNames, bool bLock ) +{ + for ( auto const & propertyName : rPropertyNames ) + ImplLockPropertyChangeNotification( propertyName, bLock ); +} + +void UnoControl::ImplModelPropertiesChanged( const Sequence< PropertyChangeEvent >& rEvents ) +{ + ::osl::ClearableGuard< ::osl::Mutex > aGuard( GetMutex() ); + + if( !getPeer().is() ) + return; + + std::vector< PropertyValue > aPeerPropertiesToSet; + sal_Int32 nIndependentPos = 0; + bool bResourceResolverSet( false ); + // position where to insert the independent properties into aPeerPropertiesToSet, + // dependent ones are inserted at the end of the vector + + bool bNeedNewPeer = false; + // some properties require a re-creation of the peer, 'cause they can't be changed on the fly + + Reference< XControlModel > xOwnModel = getModel(); + // our own model for comparison + Reference< XPropertySet > xPS( xOwnModel, UNO_QUERY ); + Reference< XPropertySetInfo > xPSI = xPS->getPropertySetInfo(); + OSL_ENSURE( xPSI.is(), "UnoControl::ImplModelPropertiesChanged: should have property set meta data!" ); + + sal_Int32 nLen = rEvents.getLength(); + aPeerPropertiesToSet.reserve(nLen); + + for( const PropertyChangeEvent& rEvent : rEvents ) + { + Reference< XControlModel > xModel( rEvent.Source, UNO_QUERY ); + bool bOwnModel = xModel.get() == xOwnModel.get(); + if ( !bOwnModel ) + continue; + + // Detect changes on our resource resolver which invalidates + // automatically some language dependent properties. + if ( rEvent.PropertyName == "ResourceResolver" ) + { + Reference< resource::XStringResourceResolver > xStrResolver; + if ( rEvent.NewValue >>= xStrResolver ) + bResourceResolverSet = xStrResolver.is(); + } + + sal_uInt16 nPType = GetPropertyId( rEvent.PropertyName ); + if ( mbDesignMode && mbDisposePeer && !mbRefreshingPeer && !mbCreatingPeer ) + { + // if we're in design mode, then some properties can change which + // require creating a *new* peer (since these properties cannot + // be switched at existing peers) + if ( nPType ) + bNeedNewPeer = ( nPType == BASEPROPERTY_BORDER ) + || ( nPType == BASEPROPERTY_MULTILINE ) + || ( nPType == BASEPROPERTY_DROPDOWN ) + || ( nPType == BASEPROPERTY_HSCROLL ) + || ( nPType == BASEPROPERTY_VSCROLL ) + || ( nPType == BASEPROPERTY_AUTOHSCROLL ) + || ( nPType == BASEPROPERTY_AUTOVSCROLL ) + || ( nPType == BASEPROPERTY_ORIENTATION ) + || ( nPType == BASEPROPERTY_SPIN ) + || ( nPType == BASEPROPERTY_ALIGN ) + || ( nPType == BASEPROPERTY_PAINTTRANSPARENT ); + else + bNeedNewPeer = requiresNewPeer( rEvent.PropertyName ); + + if ( bNeedNewPeer ) + break; + } + + if ( nPType && ( nLen > 1 ) && DoesDependOnOthers( nPType ) ) + { + // Add properties with dependencies on other properties last + // since they're dependent on properties added later (such as + // VALUE dependency on VALUEMIN/MAX) + aPeerPropertiesToSet.emplace_back(rEvent.PropertyName, 0, rEvent.NewValue, PropertyState_DIRECT_VALUE); + } + else + { + if ( bResourceResolverSet ) + { + // The resource resolver property change should be one of the first ones. + // All language dependent properties are dependent on this property. + // As BASEPROPERTY_NATIVE_WIDGET_LOOK is not dependent on resource + // resolver. We don't need to handle a special order for these two props. + aPeerPropertiesToSet.insert( + aPeerPropertiesToSet.begin(), + PropertyValue( rEvent.PropertyName, 0, rEvent.NewValue, PropertyState_DIRECT_VALUE ) ); + ++nIndependentPos; + } + else if ( nPType == BASEPROPERTY_NATIVE_WIDGET_LOOK ) + { + // since *a lot* of other properties might be overruled by this one, we need + // a special handling: + // NativeWidgetLook needs to be set first: If it is set to ON, all other + // properties describing the look (e.g. BackgroundColor) are ignored, anyway. + // If it is switched OFF, then we need to do it first because else it will + // overrule other look-related properties, and re-initialize them from system + // defaults. + aPeerPropertiesToSet.insert( + aPeerPropertiesToSet.begin(), + PropertyValue( rEvent.PropertyName, 0, rEvent.NewValue, PropertyState_DIRECT_VALUE ) ); + ++nIndependentPos; + } + else + { + aPeerPropertiesToSet.insert(aPeerPropertiesToSet.begin() + nIndependentPos, + PropertyValue(rEvent.PropertyName, 0, rEvent.NewValue, PropertyState_DIRECT_VALUE)); + ++nIndependentPos; + } + } + } + + Reference< XWindow > xParent = getParentPeer(); + Reference< XControl > xThis(this); + // call createPeer via an interface got from queryInterface, so the aggregating class can intercept it + + DBG_ASSERT( !bNeedNewPeer || xParent.is(), "Need new peer, but don't have a parent!" ); + + // Check if we have to update language dependent properties + if ( !bNeedNewPeer && bResourceResolverSet ) + { + // Add language dependent properties into the peer property set. + // Our resource resolver has been changed and we must be sure + // that language dependent props use the new resolver. + const LanguageDependentProp* pLangDepProp = aLanguageDependentProp; + while ( pLangDepProp->pPropName != nullptr ) + { + bool bMustBeInserted( true ); + for (const PropertyValue & i : aPeerPropertiesToSet) + { + if ( i.Name.equalsAsciiL( + pLangDepProp->pPropName, pLangDepProp->nPropNameLength )) + { + bMustBeInserted = false; + break; + } + } + + if ( bMustBeInserted ) + { + // Add language dependent props at the end + OUString aPropName( OUString::createFromAscii( pLangDepProp->pPropName )); + if ( xPSI.is() && xPSI->hasPropertyByName( aPropName ) ) + { + aPeerPropertiesToSet.emplace_back( aPropName, 0, xPS->getPropertyValue( aPropName ), PropertyState_DIRECT_VALUE ); + } + } + + ++pLangDepProp; + } + } + aGuard.clear(); + + // clear the guard before creating a new peer - as usual, our peer implementations use the SolarMutex + + if (bNeedNewPeer && xParent.is()) + { + SolarMutexGuard aVclGuard; + // and now this is the final withdrawal: + // I have no other idea than locking the SolarMutex here... + // I really hate the fact that VCL is not threadsafe... + + // Doesn't work for Container! + getPeer()->dispose(); + mxVclWindowPeer.clear(); + mbRefreshingPeer = true; + Reference< XWindowPeer > xP( xParent, UNO_QUERY ); + xThis->createPeer( Reference< XToolkit > (), xP ); + mbRefreshingPeer = false; + aPeerPropertiesToSet.clear(); + } + + // lock the multiplexing of VCL events to our UNO listeners + // this is for compatibility reasons: in OOo 1.0.x, changes which were done at the + // model did not cause the listeners of the controls/peers to be called + // Since the implementations for the listeners changed a lot towards 1.1, this + // would not be the case anymore, if we would not do this listener-lock below + // #i14703# + VCLXWindow* pPeer; + { + SolarMutexGuard g; + VclPtr<vcl::Window> pVclPeer = VCLUnoHelper::GetWindow( getPeer() ); + pPeer = pVclPeer ? pVclPeer->GetWindowPeer() : nullptr; + } + VclListenerLock aNoVclEventMultiplexing( pPeer ); + + // setting peer properties may result in an attempt to acquire the solar mutex, 'cause the peers + // usually don't have an own mutex but use the SolarMutex instead. + // To prevent deadlocks resulting from this, we do this without our own mutex locked + for (const auto& rProp : aPeerPropertiesToSet) + { + ImplSetPeerProperty( rProp.Name, rProp.Value ); + } + +} + +void UnoControl::disposing( const EventObject& rEvt ) +{ + ::osl::ClearableMutexGuard aGuard( GetMutex() ); + // do not compare differing types in case of multiple inheritance + + if ( maAccessibleContext.get() == rEvt.Source ) + { + // just in case the context is disposed, but not released - ensure that we do not re-use it in the future + maAccessibleContext.clear(); + } + else if( mxModel.get() == Reference< XControlModel >(rEvt.Source,UNO_QUERY).get() ) + { + // #62337# if the model dies, it does not make sense for us to live ... + Reference< XControl > xThis = this; + + aGuard.clear(); + xThis->dispose(); + + DBG_ASSERT( !mxModel.is(), "UnoControl::disposing: invalid dispose behaviour!" ); + mxModel.clear(); + } +} + + +void SAL_CALL UnoControl::setOutputSize( const awt::Size& aSize ) +{ + Reference< XWindow2 > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + + if ( xPeerWindow.is() ) + xPeerWindow->setOutputSize( aSize ); +} + +namespace +{ + template < typename RETVALTYPE, typename DEFAULTTYPE > + RETVALTYPE lcl_askPeer( const uno::Reference< awt::XWindowPeer >& _rxPeer, RETVALTYPE (SAL_CALL XWindow2::*_pMethod)(), DEFAULTTYPE _aDefault ) + { + RETVALTYPE aReturn( _aDefault ); + + Reference< XWindow2 > xPeerWindow( _rxPeer, UNO_QUERY ); + if ( xPeerWindow.is() ) + aReturn = (xPeerWindow.get()->*_pMethod)(); + + return aReturn; + } +} + +awt::Size SAL_CALL UnoControl::getOutputSize( ) +{ + return lcl_askPeer( getPeer(), &XWindow2::getOutputSize, awt::Size() ); +} + +sal_Bool SAL_CALL UnoControl::isVisible( ) +{ + return lcl_askPeer( getPeer(), &XWindow2::isVisible, maComponentInfos.bVisible ); +} + +sal_Bool SAL_CALL UnoControl::isActive( ) +{ + return lcl_askPeer( getPeer(), &XWindow2::isActive, false ); +} + +sal_Bool SAL_CALL UnoControl::isEnabled( ) +{ + return lcl_askPeer( getPeer(), &XWindow2::isEnabled, maComponentInfos.bEnable ); +} + +sal_Bool SAL_CALL UnoControl::hasFocus( ) +{ + return lcl_askPeer( getPeer(), &XWindow2::hasFocus, false ); +} + +// XWindow +void UnoControl::setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) +{ + Reference< XWindow > xWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + + if ( Flags & awt::PosSize::X ) + maComponentInfos.nX = X; + if ( Flags & awt::PosSize::Y ) + maComponentInfos.nY = Y; + if ( Flags & awt::PosSize::WIDTH ) + maComponentInfos.nWidth = Width; + if ( Flags & awt::PosSize::HEIGHT ) + maComponentInfos.nHeight = Height; + maComponentInfos.nFlags |= Flags; + + xWindow.set(getPeer(), css::uno::UNO_QUERY); + } + + if( xWindow.is() ) + xWindow->setPosSize( X, Y, Width, Height, Flags ); +} + +awt::Rectangle UnoControl::getPosSize( ) +{ + awt::Rectangle aRect( maComponentInfos.nX, maComponentInfos.nY, maComponentInfos.nWidth, maComponentInfos.nHeight); + Reference< XWindow > xWindow; + + { + ::osl::MutexGuard aGuard( GetMutex() ); + xWindow.set(getPeer(), css::uno::UNO_QUERY); + } + + if( xWindow.is() ) + aRect = xWindow->getPosSize(); + return aRect; +} + +void UnoControl::setVisible( sal_Bool bVisible ) +{ + Reference< XWindow > xWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + + // Visible status is handled by View + maComponentInfos.bVisible = bVisible; + xWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xWindow.is() ) + xWindow->setVisible( bVisible ); +} + +void UnoControl::setEnable( sal_Bool bEnable ) +{ + Reference< XWindow > xWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + + // Enable status is handled by View + maComponentInfos.bEnable = bEnable; + xWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xWindow.is() ) + xWindow->setEnable( bEnable ); +} + +void UnoControl::setFocus( ) +{ + Reference< XWindow > xWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xWindow.is() ) + xWindow->setFocus(); +} + +void UnoControl::addWindowListener( const Reference< XWindowListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maWindowListeners.addInterface( rxListener ); + if ( maWindowListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addWindowListener( &maWindowListeners ); +} + +void UnoControl::removeWindowListener( const Reference< XWindowListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maWindowListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maWindowListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removeWindowListener( &maWindowListeners ); +} + +void UnoControl::addFocusListener( const Reference< XFocusListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maFocusListeners.addInterface( rxListener ); + if ( maFocusListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addFocusListener( &maFocusListeners ); +} + +void UnoControl::removeFocusListener( const Reference< XFocusListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maFocusListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maFocusListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removeFocusListener( &maFocusListeners ); +} + +void UnoControl::addKeyListener( const Reference< XKeyListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maKeyListeners.addInterface( rxListener ); + if ( maKeyListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addKeyListener( &maKeyListeners); +} + +void UnoControl::removeKeyListener( const Reference< XKeyListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maKeyListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maKeyListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removeKeyListener( &maKeyListeners); +} + +void UnoControl::addMouseListener( const Reference< XMouseListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maMouseListeners.addInterface( rxListener ); + if ( maMouseListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addMouseListener( &maMouseListeners); +} + +void UnoControl::removeMouseListener( const Reference< XMouseListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maMouseListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maMouseListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removeMouseListener( &maMouseListeners ); +} + +void UnoControl::addMouseMotionListener( const Reference< XMouseMotionListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maMouseMotionListeners.addInterface( rxListener ); + if ( maMouseMotionListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addMouseMotionListener( &maMouseMotionListeners); +} + +void UnoControl::removeMouseMotionListener( const Reference< XMouseMotionListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maMouseMotionListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maMouseMotionListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removeMouseMotionListener( &maMouseMotionListeners ); +} + +void UnoControl::addPaintListener( const Reference< XPaintListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + maPaintListeners.addInterface( rxListener ); + if ( maPaintListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerWindow.is() ) + xPeerWindow->addPaintListener( &maPaintListeners); +} + +void UnoControl::removePaintListener( const Reference< XPaintListener >& rxListener ) +{ + Reference< XWindow > xPeerWindow; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( maPaintListeners.getLength() == 1 ) + xPeerWindow.set(getPeer(), css::uno::UNO_QUERY); + maPaintListeners.removeInterface( rxListener ); + } + if ( xPeerWindow.is() ) + xPeerWindow->removePaintListener( &maPaintListeners ); +} + +// XView +sal_Bool UnoControl::setGraphics( const Reference< XGraphics >& rDevice ) +{ + Reference< XView > xView; + { + ::osl::MutexGuard aGuard( GetMutex() ); + + mxGraphics = rDevice; + xView.set(getPeer(), css::uno::UNO_QUERY); + } + return !xView.is() || xView->setGraphics( rDevice ); +} + +Reference< XGraphics > UnoControl::getGraphics( ) +{ + return mxGraphics; +} + +awt::Size UnoControl::getSize( ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + return awt::Size( maComponentInfos.nWidth, maComponentInfos.nHeight ); +} + +void UnoControl::draw( sal_Int32 x, sal_Int32 y ) +{ + Reference< XWindowPeer > xDrawPeer; + Reference< XView > xDrawPeerView; + + bool bDisposeDrawPeer( false ); + { + ::osl::MutexGuard aGuard( GetMutex() ); + + xDrawPeer = ImplGetCompatiblePeer(); + bDisposeDrawPeer = xDrawPeer.is() && ( xDrawPeer != getPeer() ); + + xDrawPeerView.set( xDrawPeer, UNO_QUERY ); + DBG_ASSERT( xDrawPeerView.is(), "UnoControl::draw: no peer!" ); + } + + if ( xDrawPeerView.is() ) + { + Reference< XVclWindowPeer > xWindowPeer; + xWindowPeer.set( xDrawPeer, UNO_QUERY ); + if ( xWindowPeer.is() ) + xWindowPeer->setDesignMode( mbDesignMode ); + xDrawPeerView->draw( x, y ); + } + + if ( bDisposeDrawPeer ) + xDrawPeer->dispose(); +} + +void UnoControl::setZoom( float fZoomX, float fZoomY ) +{ + Reference< XView > xView; + { + ::osl::MutexGuard aGuard( GetMutex() ); + + maComponentInfos.nZoomX = fZoomX; + maComponentInfos.nZoomY = fZoomY; + + xView.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xView.is() ) + xView->setZoom( fZoomX, fZoomY ); +} + +// XControl +void UnoControl::setContext( const Reference< XInterface >& rxContext ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + mxContext = rxContext; +} + +Reference< XInterface > UnoControl::getContext( ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + return mxContext; +} + +void UnoControl::peerCreated() +{ + Reference< XWindow > xWindow( getPeer(), UNO_QUERY ); + if ( !xWindow.is() ) + return; + + if ( maWindowListeners.getLength() ) + xWindow->addWindowListener( &maWindowListeners ); + + if ( maFocusListeners.getLength() ) + xWindow->addFocusListener( &maFocusListeners ); + + if ( maKeyListeners.getLength() ) + xWindow->addKeyListener( &maKeyListeners ); + + if ( maMouseListeners.getLength() ) + xWindow->addMouseListener( &maMouseListeners ); + + if ( maMouseMotionListeners.getLength() ) + xWindow->addMouseMotionListener( &maMouseMotionListeners ); + + if ( maPaintListeners.getLength() ) + xWindow->addPaintListener( &maPaintListeners ); +} + +void UnoControl::createPeer( const Reference< XToolkit >& rxToolkit, const Reference< XWindowPeer >& rParentPeer ) +{ + ::osl::ClearableMutexGuard aGuard( GetMutex() ); + if ( !mxModel.is() ) + { + throw RuntimeException("createPeer: no model!", getXWeak()); + } + + if( getPeer().is() ) + return; + + mbCreatingPeer = true; + + WindowClass eType; + Reference< XToolkit > xToolkit = rxToolkit; + if( rParentPeer.is() && mxContext.is() ) + { + // no TopWindow + if ( !xToolkit.is() ) + xToolkit = rParentPeer->getToolkit(); + Any aAny = OWeakAggObject::queryInterface( cppu::UnoType<XControlContainer>::get()); + Reference< XControlContainer > xC; + aAny >>= xC; + if( xC.is() ) + // It's a container + eType = WindowClass_CONTAINER; + else + eType = WindowClass_SIMPLE; + } + else + { // This is only correct for Top Window + if( rParentPeer.is() ) + { + if ( !xToolkit.is() ) + xToolkit = rParentPeer->getToolkit(); + eType = WindowClass_CONTAINER; + } + else + { + if ( !xToolkit.is() ) + xToolkit = VCLUnoHelper::CreateToolkit(); + eType = WindowClass_TOP; + } + } + WindowDescriptor aDescr; + aDescr.Type = eType; + aDescr.WindowServiceName = GetComponentServiceName(); + aDescr.Parent = rParentPeer; + aDescr.Bounds = getPosSize(); + aDescr.WindowAttributes = 0; + + // Border + Reference< XPropertySet > xPSet( mxModel, UNO_QUERY ); + Reference< XPropertySetInfo > xInfo = xPSet->getPropertySetInfo(); + + Any aVal; + OUString aPropName = GetPropertyName( BASEPROPERTY_BORDER ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + sal_Int16 n = sal_Int16(); + if ( aVal >>= n ) + { + if ( n ) + aDescr.WindowAttributes |= WindowAttribute::BORDER; + else + aDescr.WindowAttributes |= VclWindowPeerAttribute::NOBORDER; + } + } + + // DESKTOP_AS_PARENT + if ( aDescr.Type == WindowClass_TOP ) + { + aPropName = GetPropertyName( BASEPROPERTY_DESKTOP_AS_PARENT ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.ParentIndex = -1; + } + } + // Moveable + aPropName = GetPropertyName( BASEPROPERTY_MOVEABLE ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= WindowAttribute::MOVEABLE; + } + + // Sizeable + aPropName = GetPropertyName( BASEPROPERTY_SIZEABLE ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= WindowAttribute::SIZEABLE; + } + + // Closeable + aPropName = GetPropertyName( BASEPROPERTY_CLOSEABLE ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= WindowAttribute::CLOSEABLE; + } + + // Dropdown + aPropName = GetPropertyName( BASEPROPERTY_DROPDOWN ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::DROPDOWN; + } + + // Spin + aPropName = GetPropertyName( BASEPROPERTY_SPIN ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::SPIN; + } + + // HScroll + aPropName = GetPropertyName( BASEPROPERTY_HSCROLL ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::HSCROLL; + } + + // VScroll + aPropName = GetPropertyName( BASEPROPERTY_VSCROLL ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::VSCROLL; + } + + // AutoHScroll + aPropName = GetPropertyName( BASEPROPERTY_AUTOHSCROLL ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::AUTOHSCROLL; + } + + // AutoVScroll + aPropName = GetPropertyName( BASEPROPERTY_AUTOVSCROLL ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>= b ) && b) + aDescr.WindowAttributes |= VclWindowPeerAttribute::AUTOVSCROLL; + } + + //added for issue79712 + //NoLabel + aPropName = GetPropertyName( BASEPROPERTY_NOLABEL ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + bool b = bool(); + if ( ( aVal >>=b ) && b ) + aDescr.WindowAttributes |= VclWindowPeerAttribute::NOLABEL; + } + //issue79712 ends + + // Align + aPropName = GetPropertyName( BASEPROPERTY_ALIGN ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + aVal = xPSet->getPropertyValue( aPropName ); + sal_Int16 n = sal_Int16(); + if ( aVal >>= n ) + { + if ( n == PROPERTY_ALIGN_LEFT ) + aDescr.WindowAttributes |= VclWindowPeerAttribute::LEFT; + else if ( n == PROPERTY_ALIGN_CENTER ) + aDescr.WindowAttributes |= VclWindowPeerAttribute::CENTER; + else + aDescr.WindowAttributes |= VclWindowPeerAttribute::RIGHT; + } + } + + // Allow derivates to manipulate attributes + PrepareWindowDescriptor(aDescr); + + // create the peer + Reference<XWindowPeer> xTemp = xToolkit->createWindow( aDescr ); + mxVclWindowPeer.set(xTemp, UNO_QUERY); + assert(mxVclWindowPeer); + + // release the mutex guard (and work with copies of our members) + // this is necessary as our peer may lock the SolarMutex (actually, all currently known peers do), so calling + // into the peer with our own mutex locked may cause deadlocks + // (We _really_ need peers which do not use the SolarMutex. It's really pissing me off that from time to + // time deadlocks pop up because the low-level components like our peers use a mutex which usually + // is locked at the top of the stack (it protects the global message looping). This is always dangerous, and + // can not always be solved by tampering with other mutexes. + // Unfortunately, the VCL used in the peers is not threadsafe, and by definition needs a locked SolarMutex.) + // 82300 - 12/21/00 - FS + UnoControlComponentInfos aComponentInfos(maComponentInfos); + bool bDesignMode(mbDesignMode); + + Reference< XGraphics > xGraphics( mxGraphics ); + Reference< XView > xView ( getPeer(), UNO_QUERY_THROW ); + Reference< XWindow > xWindow ( getPeer(), UNO_QUERY_THROW ); + + aGuard.clear(); + + // tdf#150886 if false use the same settings for widgets regardless of theme + // for consistency of document across platforms and in pdf/print output + // note: tdf#155029 do this before updateFromModel + if (xInfo->hasPropertyByName("StandardTheme")) + { + aVal = xPSet->getPropertyValue("StandardTheme"); + bool bUseStandardTheme = false; + aVal >>= bUseStandardTheme; + if (bUseStandardTheme) + { + VclPtr<vcl::Window> pVclPeer = VCLUnoHelper::GetWindow(getPeer()); + AllSettings aAllSettings = pVclPeer->GetSettings(); + StyleSettings aStyleSettings = aAllSettings.GetStyleSettings(); + aStyleSettings.SetStandardStyles(); + aAllSettings.SetStyleSettings(aStyleSettings); + pVclPeer->SetSettings(aAllSettings); + } + } + + // the updateFromModel is done without a locked mutex, too. + // The reason is that the only thing this method does is firing property changes, and this in general has + // to be done without locked mutexes (as every notification to external listeners). + // 82300 - 12/21/00 - FS + updateFromModel(); + + xView->setZoom( aComponentInfos.nZoomX, aComponentInfos.nZoomY ); + + setPosSize( aComponentInfos.nX, aComponentInfos.nY, aComponentInfos.nWidth, aComponentInfos.nHeight, aComponentInfos.nFlags ); + + if( aComponentInfos.bVisible && !bDesignMode ) + // Show only after setting the data + xWindow->setVisible( aComponentInfos.bVisible ); + + if( !aComponentInfos.bEnable ) + xWindow->setEnable( aComponentInfos.bEnable ); + + xView->setGraphics( xGraphics ); + + peerCreated(); + + mbCreatingPeer = false; + +} + +Reference< XWindowPeer > UnoControl::getPeer() +{ + ::osl::MutexGuard aGuard( GetMutex() ); + return mxVclWindowPeer; +} + +Reference< XVclWindowPeer > UnoControl::getVclWindowPeer() +{ + ::osl::MutexGuard aGuard( GetMutex() ); + return mxVclWindowPeer; +} + +sal_Bool UnoControl::setModel( const Reference< XControlModel >& rxModel ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + Reference< XMultiPropertySet > xPropSet( mxModel, UNO_QUERY ); + + // query for the XPropertiesChangeListener - our delegator is allowed to overwrite this interface + Reference< XPropertiesChangeListener > xListener; + queryInterface( cppu::UnoType<decltype(xListener)>::get() ) >>= xListener; + + if( xPropSet.is() ) + xPropSet->removePropertiesChangeListener( xListener ); + + mpData->bLocalizationSupport = false; + mxModel = rxModel; + + if( mxModel.is() ) + { + try + { + xPropSet.set( mxModel, UNO_QUERY_THROW ); + Reference< XPropertySetInfo > xPSI( xPropSet->getPropertySetInfo(), UNO_SET_THROW ); + + Sequence< OUString> aNames = lcl_ImplGetPropertyNames( xPropSet ); + xPropSet->addPropertiesChangeListener( aNames, xListener ); + + mpData->bLocalizationSupport = xPSI->hasPropertyByName("ResourceResolver"); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + mxModel.clear(); + } + } + + return mxModel.is(); +} + +Reference< XControlModel > UnoControl::getModel( ) +{ + return mxModel; +} + +Reference< XView > UnoControl::getView( ) +{ + return static_cast< XView* >( this ); +} + +void UnoControl::setDesignMode( sal_Bool bOn ) +{ + ModeChangeEvent aModeChangeEvent; + + Reference< XWindow > xWindow; + Reference<XComponent> xAccessibleComp; + { + ::osl::MutexGuard aGuard( GetMutex() ); + if ( bool(bOn) == mbDesignMode ) + return; + + // remember this + mbDesignMode = bOn; + xWindow.set(getPeer(), css::uno::UNO_QUERY); + + xAccessibleComp.set(maAccessibleContext, UNO_QUERY); + maAccessibleContext.clear(); + + aModeChangeEvent.Source = *this; + aModeChangeEvent.NewMode = mbDesignMode ? std::u16string_view(u"design") : std::u16string_view(u"alive" ); + } + + // dispose current AccessibleContext, if we have one - without Mutex lock + // (changing the design mode implies having a new implementation for this context, + // so the old one must be declared DEFUNC) + DisposeAccessibleContext(xAccessibleComp); + + // adjust the visibility of our window + if ( xWindow.is() ) + xWindow->setVisible( !bOn ); + + // and notify our mode listeners + maModeChangeListeners.notifyEach( &XModeChangeListener::modeChanged, aModeChangeEvent ); +} + +sal_Bool UnoControl::isDesignMode( ) +{ + return mbDesignMode; +} + +sal_Bool UnoControl::isTransparent( ) +{ + return false; +} + +// XServiceInfo +OUString UnoControl::getImplementationName( ) +{ + OSL_FAIL( "This method should be overridden!" ); + return OUString(); +} + +sal_Bool UnoControl::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > UnoControl::getSupportedServiceNames( ) +{ + return { "com.sun.star.awt.UnoControl" }; +} + + +Reference< XAccessibleContext > SAL_CALL UnoControl::getAccessibleContext( ) +{ + // creation of the context will certainly require the SolarMutex ... + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( GetMutex() ); + + Reference< XAccessibleContext > xCurrentContext( maAccessibleContext.get(), UNO_QUERY ); + if ( !xCurrentContext.is() ) + { + if ( !mbDesignMode ) + { // in alive mode, use the AccessibleContext of the peer + Reference< XAccessible > xPeerAcc( getPeer(), UNO_QUERY ); + if ( xPeerAcc.is() ) + xCurrentContext = xPeerAcc->getAccessibleContext( ); + } + else + // in design mode, use a fallback + xCurrentContext = ::toolkit::OAccessibleControlContext::create( this ); + + DBG_ASSERT( xCurrentContext.is(), "UnoControl::getAccessibleContext: invalid context (invalid peer?)!" ); + maAccessibleContext = xCurrentContext; + + // get notified when the context is disposed + Reference< XComponent > xContextComp( xCurrentContext, UNO_QUERY ); + if ( xContextComp.is() ) + xContextComp->addEventListener( this ); + // In an ideal world, this is not necessary - there the object would be released as soon as it has been + // disposed, and thus our weak reference would be empty, too. + // But 'til this ideal world comes (means 'til we do never have any refcount/lifetime bugs anymore), we + // need to listen for disposal and reset our weak reference then. + } + + return xCurrentContext; +} + +void SAL_CALL UnoControl::addModeChangeListener( const Reference< XModeChangeListener >& _rxListener ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + maModeChangeListeners.addInterface( _rxListener ); +} + +void SAL_CALL UnoControl::removeModeChangeListener( const Reference< XModeChangeListener >& _rxListener ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + maModeChangeListeners.removeInterface( _rxListener ); +} + +void SAL_CALL UnoControl::addModeChangeApproveListener( const Reference< XModeChangeApproveListener >& ) +{ + throw NoSupportException( ); +} + +void SAL_CALL UnoControl::removeModeChangeApproveListener( const Reference< XModeChangeApproveListener >& ) +{ + throw NoSupportException( ); +} + + +awt::Point SAL_CALL UnoControl::convertPointToLogic( const awt::Point& i_Point, ::sal_Int16 i_TargetUnit ) +{ + Reference< XUnitConversion > xPeerConversion; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerConversion.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerConversion.is() ) + return xPeerConversion->convertPointToLogic( i_Point, i_TargetUnit ); + return awt::Point( ); +} + + +awt::Point SAL_CALL UnoControl::convertPointToPixel( const awt::Point& i_Point, ::sal_Int16 i_SourceUnit ) +{ + Reference< XUnitConversion > xPeerConversion; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerConversion.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerConversion.is() ) + return xPeerConversion->convertPointToPixel( i_Point, i_SourceUnit ); + return awt::Point( ); +} + + +awt::Size SAL_CALL UnoControl::convertSizeToLogic( const awt::Size& i_Size, ::sal_Int16 i_TargetUnit ) +{ + Reference< XUnitConversion > xPeerConversion; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerConversion.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerConversion.is() ) + return xPeerConversion->convertSizeToLogic( i_Size, i_TargetUnit ); + return awt::Size( ); +} + + +awt::Size SAL_CALL UnoControl::convertSizeToPixel( const awt::Size& i_Size, ::sal_Int16 i_SourceUnit ) +{ + Reference< XUnitConversion > xPeerConversion; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerConversion.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerConversion.is() ) + return xPeerConversion->convertSizeToPixel( i_Size, i_SourceUnit ); + return awt::Size( ); +} + + +uno::Reference< awt::XStyleSettings > SAL_CALL UnoControl::getStyleSettings() +{ + Reference< awt::XStyleSettingsSupplier > xPeerSupplier; + { + ::osl::MutexGuard aGuard( GetMutex() ); + xPeerSupplier.set(getPeer(), css::uno::UNO_QUERY); + } + if ( xPeerSupplier.is() ) + return xPeerSupplier->getStyleSettings(); + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrolbase.cxx b/toolkit/source/controls/unocontrolbase.cxx new file mode 100644 index 0000000000..134e7b181b --- /dev/null +++ b/toolkit/source/controls/unocontrolbase.cxx @@ -0,0 +1,253 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/XLayoutConstrains.hpp> +#include <com/sun/star/awt/XTextLayoutConstrains.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XMultiPropertySet.hpp> + +#include <toolkit/controls/unocontrolbase.hxx> +#include <helper/property.hxx> + +#include <tools/debug.hxx> + +using namespace com::sun::star; + + + + +bool UnoControlBase::ImplHasProperty( sal_uInt16 nPropId ) +{ + const OUString& aPropName( GetPropertyName( nPropId ) ); + return ImplHasProperty( aPropName ); +} + +bool UnoControlBase::ImplHasProperty( const OUString& aPropertyName ) +{ + css::uno::Reference< css::beans::XPropertySet > xPSet( mxModel, css::uno::UNO_QUERY ); + if ( !xPSet.is() ) + return false; + css::uno::Reference< css::beans::XPropertySetInfo > xInfo = xPSet->getPropertySetInfo(); + if ( !xInfo.is() ) + return false; + + return xInfo->hasPropertyByName( aPropertyName ); +} + +void UnoControlBase::ImplSetPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues, bool bUpdateThis ) +{ + css::uno::Reference< css::beans::XMultiPropertySet > xMPS( mxModel, css::uno::UNO_QUERY ); + if ( !mxModel.is() ) + return; + + DBG_ASSERT( xMPS.is(), "UnoControlBase::ImplSetPropertyValues: no multi property set interface!" ); + if ( !xMPS.is() ) + return; + + if ( !bUpdateThis ) + ImplLockPropertyChangeNotifications( aPropertyNames, true ); + + try + { + xMPS->setPropertyValues( aPropertyNames, aValues ); + } + catch( const css::uno::Exception& ) + { + if ( !bUpdateThis ) + ImplLockPropertyChangeNotifications( aPropertyNames, false ); + } + if ( !bUpdateThis ) + ImplLockPropertyChangeNotifications( aPropertyNames, false ); +} + +void UnoControlBase::ImplSetPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue, bool bUpdateThis ) +{ + // Model might be logged off already but an event still fires + if ( !mxModel.is() ) + return; + + css::uno::Reference< css::beans::XPropertySet > xPSet( mxModel, css::uno::UNO_QUERY ); + if ( !bUpdateThis ) + ImplLockPropertyChangeNotification( aPropertyName, true ); + + try + { + xPSet->setPropertyValue( aPropertyName, aValue ); + } + catch( const css::uno::Exception& ) + { + if ( !bUpdateThis ) + ImplLockPropertyChangeNotification( aPropertyName, false ); + throw; + } + if ( !bUpdateThis ) + ImplLockPropertyChangeNotification( aPropertyName, false ); +} + +css::uno::Any UnoControlBase::ImplGetPropertyValue( const OUString& aPropertyName ) const +{ + css::uno::Reference< css::beans::XPropertySet > xPSet( mxModel, css::uno::UNO_QUERY ); + if ( xPSet.is() ) + return xPSet->getPropertyValue( aPropertyName ); + else + return css::uno::Any(); +} + +template <typename T> T UnoControlBase::ImplGetPropertyValuePOD( sal_uInt16 nProp ) +{ + T t(0); + if ( mxModel.is() ) + { + css::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) ); + aVal >>= t; + } + return t; +} + +template <typename T> T UnoControlBase::ImplGetPropertyValueClass( sal_uInt16 nProp ) +{ + T t; + if ( mxModel.is() ) + { + css::uno::Any aVal = ImplGetPropertyValue( GetPropertyName( nProp ) ); + aVal >>= t; + } + return t; +} + +bool UnoControlBase::ImplGetPropertyValue_BOOL( sal_uInt16 nProp ) +{ + return ImplGetPropertyValuePOD<bool>(nProp); +} + +sal_Int16 UnoControlBase::ImplGetPropertyValue_INT16( sal_uInt16 nProp ) +{ + return ImplGetPropertyValuePOD<sal_Int16>(nProp); +} + +sal_Int32 UnoControlBase::ImplGetPropertyValue_INT32( sal_uInt16 nProp ) +{ + return ImplGetPropertyValuePOD<sal_Int32>(nProp); +} + +double UnoControlBase::ImplGetPropertyValue_DOUBLE( sal_uInt16 nProp ) +{ + return ImplGetPropertyValuePOD<double>(nProp); +} + +OUString UnoControlBase::ImplGetPropertyValue_UString( sal_uInt16 nProp ) +{ + return ImplGetPropertyValueClass<OUString>(nProp); +} + +util::Date UnoControlBase::ImplGetPropertyValue_Date( sal_uInt16 nProp ) +{ + return ImplGetPropertyValueClass<util::Date>(nProp); +} + +util::Time UnoControlBase::ImplGetPropertyValue_Time( sal_uInt16 nProp ) +{ + return ImplGetPropertyValueClass<util::Time>(nProp); +} + +css::awt::Size UnoControlBase::Impl_getMinimumSize() +{ + css::awt::Size aSz; + css::uno::Reference< css::awt::XWindowPeer > xP = ImplGetCompatiblePeer(); + DBG_ASSERT( xP.is(), "Layout: No Peer!" ); + if ( xP.is() ) + { + css::uno::Reference< css::awt::XLayoutConstrains > xL( xP, css::uno::UNO_QUERY ); + if ( xL.is() ) + aSz = xL->getMinimumSize(); + + if ( !getPeer().is() || ( getPeer() != xP ) ) + xP->dispose(); + } + return aSz; +} + +css::awt::Size UnoControlBase::Impl_getPreferredSize() +{ + css::awt::Size aSz; + css::uno::Reference< css::awt::XWindowPeer > xP = ImplGetCompatiblePeer(); + DBG_ASSERT( xP.is(), "Layout: No Peer!" ); + if ( xP.is() ) + { + css::uno::Reference< css::awt::XLayoutConstrains > xL( xP, css::uno::UNO_QUERY ); + if ( xL.is() ) + aSz = xL->getPreferredSize(); + + if ( !getPeer().is() || ( getPeer() != xP ) ) + xP->dispose(); + } + return aSz; +} + +css::awt::Size UnoControlBase::Impl_calcAdjustedSize( const css::awt::Size& rNewSize ) +{ + css::awt::Size aSz; + css::uno::Reference< css::awt::XWindowPeer > xP = ImplGetCompatiblePeer(); + DBG_ASSERT( xP.is(), "Layout: No Peer!" ); + if ( xP.is() ) + { + css::uno::Reference< css::awt::XLayoutConstrains > xL( xP, css::uno::UNO_QUERY ); + if ( xL.is() ) + aSz = xL->calcAdjustedSize( rNewSize ); + + if ( !getPeer().is() || ( getPeer() != xP ) ) + xP->dispose(); + } + return aSz; +} + +css::awt::Size UnoControlBase::Impl_getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + css::awt::Size aSz; + css::uno::Reference< css::awt::XWindowPeer > xP = ImplGetCompatiblePeer(); + DBG_ASSERT( xP.is(), "Layout: No Peer!" ); + if ( xP.is() ) + { + css::uno::Reference< css::awt::XTextLayoutConstrains > xL( xP, css::uno::UNO_QUERY ); + if ( xL.is() ) + aSz = xL->getMinimumSize( nCols, nLines ); + + if ( !getPeer().is() || ( getPeer() != xP ) ) + xP->dispose(); + } + return aSz; +} + +void UnoControlBase::Impl_getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + css::uno::Reference< css::awt::XWindowPeer > xP = ImplGetCompatiblePeer(); + DBG_ASSERT( xP.is(), "Layout: No Peer!" ); + if ( xP.is() ) + { + css::uno::Reference< css::awt::XTextLayoutConstrains > xL( xP, css::uno::UNO_QUERY ); + if ( xL.is() ) + xL->getColumnsAndLines( nCols, nLines ); + + if ( !getPeer().is() || ( getPeer() != xP ) ) + xP->dispose(); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrolcontainer.cxx b/toolkit/source/controls/unocontrolcontainer.cxx new file mode 100644 index 0000000000..1610f36629 --- /dev/null +++ b/toolkit/source/controls/unocontrolcontainer.cxx @@ -0,0 +1,823 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/XVclContainerPeer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/implbase.hxx> + +#include <controls/unocontrolcontainer.hxx> +#include <comphelper/sequence.hxx> + +#include <tools/debug.hxx> + +#include <limits> +#include <map> +#include <memory> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> +#include <utility> + +using namespace ::com::sun::star; + + + +namespace { + +struct UnoControlHolder +{ + uno::Reference< awt::XControl > mxControl; + OUString msName; + +public: + UnoControlHolder( OUString aName, uno::Reference< awt::XControl > xControl ) + : mxControl(std::move( xControl )), + msName(std::move( aName )) + { + } + + const OUString& getName() const { return msName; } + const uno::Reference< awt::XControl >& getControl() const { return mxControl; } +}; + +} + +class UnoControlHolderList +{ +public: + typedef sal_Int32 ControlIdentifier; +private: + typedef std::shared_ptr< UnoControlHolder > ControlInfo; + typedef ::std::map< ControlIdentifier, ControlInfo > ControlMap; + +private: + ControlMap maControls; + +public: + UnoControlHolderList(); + + /** adds a control with the given name to the list + @param _rxControl + the control to add. Must not be <NULL/> + @param _pBName + the name of the control, or <NULL/> if an automatic name should be generated + @return + the identifier of the newly added control + */ + ControlIdentifier addControl( const uno::Reference< awt::XControl >& _rxControl, const OUString* _pName ); + + /** determines whether or not the list is empty + */ + bool empty() const { return maControls.empty(); } + + /** retrieves all controls currently in the list + */ + void getControls( uno::Sequence< uno::Reference< awt::XControl > >& _out_rControls ) const; + + /** retrieves all identifiers of all controls currently in the list + */ + void getIdentifiers( uno::Sequence< sal_Int32 >& _out_rIdentifiers ) const; + + /** returns the first control which is registered under the given name + */ + uno::Reference< awt::XControl > + getControlForName( const OUString& _rName ) const; + + /** returns the identifier which a control is registered for, or -1 if the control + isn't registered + */ + ControlIdentifier + getControlIdentifier( const uno::Reference< awt::XControl >& _rxControl ); + + /** retrieves the control for a given id + @param _nIdentifier + the identifier for the control + @param _out_rxControl + takes the XControl upon successful return + @return + <TRUE/> if and only if a control with the given id is part of the list + */ + bool getControlForIdentifier( ControlIdentifier _nIdentifier, uno::Reference< awt::XControl >& _out_rxControl ) const; + + /** removes a control from the list, given by id + @param _nId + The identifier of the control to remove. + */ + void removeControlById( ControlIdentifier _nId ); + + /** replaces a control from the list with another one + @param _nId + The identifier of the control to replace + @param _rxNewControl + the new control to put into the list + */ + void replaceControlById( ControlIdentifier _nId, const uno::Reference< awt::XControl >& _rxNewControl ); + +private: + /** adds a control + @param _rxControl + the control to add to the container + @param _pName + pointer to the name of the control. Might be <NULL/>, in this case, a name is generated. + @return + the identifier of the newly inserted control + */ + ControlIdentifier impl_addControl( + const uno::Reference< awt::XControl >& _rxControl, + const OUString* _pName + ); + + /** finds a free identifier + @throw uno::RuntimeException + if no free identifier can be found + */ + ControlIdentifier impl_getFreeIdentifier_throw(); + + /** finds a free name + @throw uno::RuntimeException + if no free name can be found + */ + OUString impl_getFreeName_throw(); +}; + + +UnoControlHolderList::UnoControlHolderList() +{ +} + + +UnoControlHolderList::ControlIdentifier UnoControlHolderList::addControl( const uno::Reference< awt::XControl >& _rxControl, const OUString* _pName ) +{ + return impl_addControl( _rxControl, _pName ); +} + + +void UnoControlHolderList::getControls( uno::Sequence< uno::Reference< awt::XControl > >& _out_rControls ) const +{ + _out_rControls.realloc( maControls.size() ); + uno::Reference< awt::XControl >* pControls = _out_rControls.getArray(); + for (const auto& rEntry : maControls) + { + *pControls = rEntry.second->getControl(); + ++pControls; + } +} + + +void UnoControlHolderList::getIdentifiers( uno::Sequence< sal_Int32 >& _out_rIdentifiers ) const +{ + _out_rIdentifiers.realloc( maControls.size() ); + sal_Int32* pIdentifiers = _out_rIdentifiers.getArray(); + for (const auto& rEntry : maControls) + { + *pIdentifiers = rEntry.first; + ++pIdentifiers; + } +} + + +uno::Reference< awt::XControl > UnoControlHolderList::getControlForName( const OUString& _rName ) const +{ + auto loop = std::find_if(maControls.begin(), maControls.end(), + [&_rName](const ControlMap::value_type& rEntry) { return rEntry.second->getName() == _rName; }); + if (loop != maControls.end()) + return loop->second->getControl(); + return uno::Reference< awt::XControl >(); +} + + +UnoControlHolderList::ControlIdentifier UnoControlHolderList::getControlIdentifier( const uno::Reference< awt::XControl >& _rxControl ) +{ + auto loop = std::find_if(maControls.begin(), maControls.end(), + [&_rxControl](const ControlMap::value_type& rEntry) { return rEntry.second->getControl().get() == _rxControl.get(); }); + if (loop != maControls.end()) + return loop->first; + return -1; +} + + +bool UnoControlHolderList::getControlForIdentifier( UnoControlHolderList::ControlIdentifier _nIdentifier, uno::Reference< awt::XControl >& _out_rxControl ) const +{ + ControlMap::const_iterator pos = maControls.find( _nIdentifier ); + if ( pos == maControls.end() ) + return false; + _out_rxControl = pos->second->getControl(); + return true; +} + + +void UnoControlHolderList::removeControlById( UnoControlHolderList::ControlIdentifier _nId ) +{ + ControlMap::iterator pos = maControls.find( _nId ); + DBG_ASSERT( pos != maControls.end(), "UnoControlHolderList::removeControlById: invalid id!" ); + if ( pos == maControls.end() ) + return; + + maControls.erase( pos ); +} + + +void UnoControlHolderList::replaceControlById( ControlIdentifier _nId, const uno::Reference< awt::XControl >& _rxNewControl ) +{ + DBG_ASSERT( _rxNewControl.is(), "UnoControlHolderList::replaceControlById: invalid new control!" ); + + ControlMap::iterator pos = maControls.find( _nId ); + DBG_ASSERT( pos != maControls.end(), "UnoControlHolderList::replaceControlById: invalid id!" ); + if ( pos == maControls.end() ) + return; + + pos->second = std::make_shared<UnoControlHolder>( pos->second->getName(), _rxNewControl ); +} + + +UnoControlHolderList::ControlIdentifier UnoControlHolderList::impl_addControl( const uno::Reference< awt::XControl >& _rxControl, const OUString* _pName ) +{ + DBG_ASSERT( _rxControl.is(), "UnoControlHolderList::impl_addControl: invalid control!" ); + + OUString sName = _pName ? *_pName : impl_getFreeName_throw(); + sal_Int32 nId = impl_getFreeIdentifier_throw(); + + maControls[ nId ] = std::make_shared<UnoControlHolder>( sName, _rxControl ); + return nId; +} + + +UnoControlHolderList::ControlIdentifier UnoControlHolderList::impl_getFreeIdentifier_throw() +{ + for ( ControlIdentifier candidateId = 0; candidateId < ::std::numeric_limits< ControlIdentifier >::max(); ++candidateId ) + { + ControlMap::const_iterator existent = maControls.find( candidateId ); + if ( existent == maControls.end() ) + return candidateId; + } + throw uno::RuntimeException("out of identifiers" ); +} + + +OUString UnoControlHolderList::impl_getFreeName_throw() +{ + for ( ControlIdentifier candidateId = 0; candidateId < ::std::numeric_limits< ControlIdentifier >::max(); ++candidateId ) + { + OUString candidateName( "control_" + OUString::number( candidateId ) ); + if ( std::none_of(maControls.begin(), maControls.end(), + [&candidateName](const ControlMap::value_type& rEntry) { return rEntry.second->getName() == candidateName; }) ) + return candidateName; + } + throw uno::RuntimeException("out of identifiers" ); +} + +// Function to set the controls' visibility according +// to the dialog's "Step" property + +static void implUpdateVisibility +( + sal_Int32 nDialogStep, + const uno::Reference< awt::XControlContainer >& xControlContainer +) +{ + const uno::Sequence< uno::Reference< awt::XControl > > + aCtrls = xControlContainer->getControls(); + bool bCompleteVisible = (nDialogStep == 0); + for( const uno::Reference< awt::XControl >& xControl : aCtrls ) + { + bool bVisible = bCompleteVisible; + if( !bVisible ) + { + uno::Reference< awt::XControlModel > xModel( xControl->getModel() ); + uno::Reference< beans::XPropertySet > xPSet + ( xModel, uno::UNO_QUERY ); + uno::Reference< beans::XPropertySetInfo > + xInfo = xPSet->getPropertySetInfo(); + OUString aPropName( "Step" ); + sal_Int32 nControlStep = 0; + if ( xInfo->hasPropertyByName( aPropName ) ) + { + uno::Any aVal = xPSet->getPropertyValue( aPropName ); + aVal >>= nControlStep; + } + bVisible = (nControlStep == 0) || (nControlStep == nDialogStep); + } + + uno::Reference< awt::XWindow> xWindow + ( xControl, uno::UNO_QUERY ); + if( xWindow.is() ) + xWindow->setVisible( bVisible ); + } +} + + + +typedef ::cppu::WeakImplHelper< beans::XPropertyChangeListener > PropertyChangeListenerHelper; + +namespace { + +class DialogStepChangedListener: public PropertyChangeListenerHelper +{ +private: + uno::Reference< awt::XControlContainer > mxControlContainer; + +public: + explicit DialogStepChangedListener( uno::Reference< awt::XControlContainer > xControlContainer ) + : mxControlContainer(std::move( xControlContainer )) {} + + // XEventListener + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const beans::PropertyChangeEvent& evt ) override; + +}; + +} + +void SAL_CALL DialogStepChangedListener::disposing( const lang::EventObject& /*_rSource*/) +{ + mxControlContainer.clear(); +} + +void SAL_CALL DialogStepChangedListener::propertyChange( const beans::PropertyChangeEvent& evt ) +{ + // evt.PropertyName HAS to be "Step" because we only use the listener for that + sal_Int32 nDialogStep = 0; + evt.NewValue >>= nDialogStep; + implUpdateVisibility( nDialogStep, mxControlContainer ); +} + + + +UnoControlContainer::UnoControlContainer() + :maCListeners( *this ) +{ + mpControls.reset(new UnoControlHolderList); +} + +UnoControlContainer::UnoControlContainer(const uno::Reference< awt::XVclWindowPeer >& xP ) + :maCListeners( *this ) +{ + setPeer( xP ); + mbDisposePeer = false; + mpControls.reset(new UnoControlHolderList); +} + +UnoControlContainer::~UnoControlContainer() +{ +} + +void UnoControlContainer::ImplActivateTabControllers() +{ + for ( auto& rTabController : asNonConstRange(maTabControllers) ) + { + rTabController->setContainer( this ); + rTabController->activateTabOrder(); + } +} + +// lang::XComponent +void UnoControlContainer::dispose( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + lang::EventObject aDisposeEvent; + aDisposeEvent.Source = static_cast< uno::XAggregation* >( this ); + + // Notify listeners about disposal of this Container (This is much faster if they + // listen on the controls and the container). + maDisposeListeners.disposeAndClear( aDisposeEvent ); + maCListeners.disposeAndClear( aDisposeEvent ); + + + const uno::Sequence< uno::Reference< awt::XControl > > aCtrls = getControls(); + + for( uno::Reference< awt::XControl > const & control : aCtrls ) + { + removingControl( control ); + // Delete control + control->dispose(); + } + + + // Delete all structures + mpControls.reset(new UnoControlHolderList); + + UnoControlBase::dispose(); +} + +// lang::XEventListener +void UnoControlContainer::disposing( const lang::EventObject& _rEvt ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Reference< awt::XControl > xControl( _rEvt.Source, uno::UNO_QUERY ); + if ( xControl.is() ) + removeControl( xControl ); + + UnoControlBase::disposing( _rEvt ); +} + +// container::XContainer +void UnoControlContainer::addContainerListener( const uno::Reference< container::XContainerListener >& rxListener ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + maCListeners.addInterface( rxListener ); +} + +void UnoControlContainer::removeContainerListener( const uno::Reference< container::XContainerListener >& rxListener ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + maCListeners.removeInterface( rxListener ); +} + + +::sal_Int32 SAL_CALL UnoControlContainer::insert( const uno::Any& _rElement ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Reference< awt::XControl > xControl; + if ( !( _rElement >>= xControl ) || !xControl.is() ) + throw lang::IllegalArgumentException( + "Elements must support the XControl interface.", + *this, + 1 + ); + + return impl_addControl( xControl ); +} + +void SAL_CALL UnoControlContainer::removeByIdentifier( ::sal_Int32 _nIdentifier ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Reference< awt::XControl > xControl; + if ( !mpControls->getControlForIdentifier( _nIdentifier, xControl ) ) + throw container::NoSuchElementException( + "There is no element with the given identifier.", + *this + ); + + impl_removeControl( _nIdentifier, xControl ); +} + +void SAL_CALL UnoControlContainer::replaceByIdentifer( ::sal_Int32 _nIdentifier, const uno::Any& _rElement ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Reference< awt::XControl > xExistentControl; + if ( !mpControls->getControlForIdentifier( _nIdentifier, xExistentControl ) ) + throw container::NoSuchElementException( + "There is no element with the given identifier.", + *this + ); + + uno::Reference< awt::XControl > xNewControl; + if ( !( _rElement >>= xNewControl ) ) + throw lang::IllegalArgumentException( + "Elements must support the XControl interface.", + *this, + 1 + ); + + removingControl( xExistentControl ); + + mpControls->replaceControlById( _nIdentifier, xNewControl ); + + addingControl( xNewControl ); + + impl_createControlPeerIfNecessary( xNewControl ); + + if ( maCListeners.getLength() ) + { + container::ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= _nIdentifier; + aEvent.Element <<= xNewControl; + aEvent.ReplacedElement <<= xExistentControl; + maCListeners.elementReplaced( aEvent ); + } +} + +uno::Any SAL_CALL UnoControlContainer::getByIdentifier( ::sal_Int32 _nIdentifier ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Reference< awt::XControl > xControl; + if ( !mpControls->getControlForIdentifier( _nIdentifier, xControl ) ) + throw container::NoSuchElementException(); + return uno::Any( xControl ); +} + +uno::Sequence< ::sal_Int32 > SAL_CALL UnoControlContainer::getIdentifiers( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + uno::Sequence< ::sal_Int32 > aIdentifiers; + mpControls->getIdentifiers( aIdentifiers ); + return aIdentifiers; +} + +// container::XElementAccess +uno::Type SAL_CALL UnoControlContainer::getElementType( ) +{ + return cppu::UnoType<awt::XControlModel>::get(); +} + +sal_Bool SAL_CALL UnoControlContainer::hasElements( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + return !mpControls->empty(); +} + +// awt::XControlContainer +void UnoControlContainer::setStatusText( const OUString& rStatusText ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + // Descend the parent hierarchy + uno::Reference< awt::XControlContainer > xContainer( mxContext, uno::UNO_QUERY ); + if( xContainer.is() ) + xContainer->setStatusText( rStatusText ); +} + +uno::Sequence< uno::Reference< awt::XControl > > UnoControlContainer::getControls( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + uno::Sequence< uno::Reference< awt::XControl > > aControls; + mpControls->getControls( aControls ); + return aControls; +} + +uno::Reference< awt::XControl > UnoControlContainer::getControl( const OUString& rName ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + return mpControls->getControlForName( rName ); +} + +void UnoControlContainer::addingControl( const uno::Reference< awt::XControl >& _rxControl ) +{ + if ( _rxControl.is() ) + { + uno::Reference< uno::XInterface > xThis; + OWeakAggObject::queryInterface( cppu::UnoType<uno::XInterface>::get() ) >>= xThis; + + _rxControl->setContext( xThis ); + _rxControl->addEventListener( this ); + } +} + +void UnoControlContainer::impl_createControlPeerIfNecessary( const uno::Reference< awt::XControl >& _rxControl ) +{ + OSL_PRECOND( _rxControl.is(), "UnoControlContainer::impl_createControlPeerIfNecessary: invalid control, this will crash!" ); + + // if the container already has a peer, then also create a peer for the control + uno::Reference< awt::XWindowPeer > xMyPeer( getPeer() ); + + if( xMyPeer.is() ) + { + _rxControl->createPeer( nullptr, xMyPeer ); + ImplActivateTabControllers(); + } + +} + +sal_Int32 UnoControlContainer::impl_addControl( const uno::Reference< awt::XControl >& _rxControl, const OUString* _pName ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + UnoControlHolderList::ControlIdentifier id = mpControls->addControl( _rxControl, _pName ); + + addingControl( _rxControl ); + + impl_createControlPeerIfNecessary( _rxControl ); + + if ( maCListeners.getLength() ) + { + container::ContainerEvent aEvent; + aEvent.Source = *this; + if (_pName) + aEvent.Accessor <<= *_pName; + else + aEvent.Accessor <<= static_cast<sal_Int32>(id); + aEvent.Element <<= _rxControl; + maCListeners.elementInserted( aEvent ); + } + + return id; +} + +void UnoControlContainer::addControl( const OUString& rName, const uno::Reference< awt::XControl >& rControl ) +{ + if ( rControl.is() ) + impl_addControl( rControl, &rName ); +} + +void UnoControlContainer::removingControl( const uno::Reference< awt::XControl >& _rxControl ) +{ + if ( _rxControl.is() ) + { + _rxControl->removeEventListener( this ); + _rxControl->setContext( nullptr ); + } +} + +void UnoControlContainer::impl_removeControl( sal_Int32 _nId, const uno::Reference< awt::XControl >& _rxControl ) +{ +#ifdef DBG_UTIL + { + uno::Reference< awt::XControl > xControl; + bool bHas = mpControls->getControlForIdentifier( _nId, xControl ); + DBG_ASSERT( bHas && xControl == _rxControl, "UnoControlContainer::impl_removeControl: inconsistency in the parameters!" ); + } +#endif + removingControl( _rxControl ); + + mpControls->removeControlById( _nId ); + + if ( maCListeners.getLength() ) + { + container::ContainerEvent aEvent; + aEvent.Source = *this; + aEvent.Accessor <<= _nId; + aEvent.Element <<= _rxControl; + maCListeners.elementRemoved( aEvent ); + } +} + +void UnoControlContainer::removeControl( const uno::Reference< awt::XControl >& _rxControl ) +{ + if ( _rxControl.is() ) + { + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + UnoControlHolderList::ControlIdentifier id = mpControls->getControlIdentifier( _rxControl ); + if ( id != -1 ) + impl_removeControl( id, _rxControl ); + } +} + + +// awt::XUnoControlContainer +void UnoControlContainer::setTabControllers( const uno::Sequence< uno::Reference< awt::XTabController > >& TabControllers ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + maTabControllers = TabControllers; +} + +uno::Sequence< uno::Reference< awt::XTabController > > UnoControlContainer::getTabControllers( ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + return maTabControllers; +} + +void UnoControlContainer::addTabController( const uno::Reference< awt::XTabController >& TabController ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + sal_uInt32 nCount = maTabControllers.getLength(); + maTabControllers.realloc( nCount + 1 ); + maTabControllers.getArray()[ nCount ] = TabController; +} + +void UnoControlContainer::removeTabController( const uno::Reference< awt::XTabController >& TabController ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + auto pTabController = std::find_if(std::cbegin(maTabControllers), std::cend(maTabControllers), + [&TabController](const uno::Reference< awt::XTabController >& rTabController) { + return rTabController.get() == TabController.get(); }); + if (pTabController != std::cend(maTabControllers)) + { + auto n = static_cast<sal_Int32>(std::distance(std::cbegin(maTabControllers), pTabController)); + ::comphelper::removeElementAt( maTabControllers, n ); + } +} + +// awt::XControl +void UnoControlContainer::createPeer( const uno::Reference< awt::XToolkit >& rxToolkit, const uno::Reference< awt::XWindowPeer >& rParent ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + if( getPeer().is() ) + return; + + bool bVis = maComponentInfos.bVisible; + if( bVis ) + UnoControl::setVisible( false ); + + // Create a new peer + UnoControl::createPeer( rxToolkit, rParent ); + + // Create all children's peers + if ( !mbCreatingCompatiblePeer ) + { + // Evaluate "Step" property + uno::Reference< awt::XControlModel > xModel( getModel() ); + uno::Reference< beans::XPropertySet > xPSet + ( xModel, uno::UNO_QUERY ); + uno::Reference< beans::XPropertySetInfo > + xInfo = xPSet->getPropertySetInfo(); + OUString aPropName( "Step" ); + if ( xInfo->hasPropertyByName( aPropName ) ) + { + css::uno::Any aVal = xPSet->getPropertyValue( aPropName ); + sal_Int32 nDialogStep = 0; + aVal >>= nDialogStep; + uno::Reference< awt::XControlContainer > xContainer = + static_cast< awt::XControlContainer* >(this); + implUpdateVisibility( nDialogStep, xContainer ); + + uno::Reference< beans::XPropertyChangeListener > xListener = + new DialogStepChangedListener(xContainer); + xPSet->addPropertyChangeListener( aPropName, xListener ); + } + + uno::Sequence< uno::Reference< awt::XControl > > aCtrls = getControls(); + for( auto& rCtrl : asNonConstRange(aCtrls) ) + rCtrl->createPeer( rxToolkit, getPeer() ); + + uno::Reference< awt::XVclContainerPeer > xC( getPeer(), uno::UNO_QUERY ); + if ( xC.is() ) + xC->enableDialogControl( true ); + ImplActivateTabControllers(); + } + + if( bVis && !isDesignMode() ) + UnoControl::setVisible( true ); +} + + +// awt::XWindow +void UnoControlContainer::setVisible( sal_Bool bVisible ) +{ + ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); + + UnoControl::setVisible( bVisible ); + if( !mxContext.is() && bVisible ) + // This is a Topwindow, thus show it automatically + createPeer( uno::Reference< awt::XToolkit > (), uno::Reference< awt::XWindowPeer > () ); +} + +OUString UnoControlContainer::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlContainer"; +} + +css::uno::Sequence<OUString> UnoControlContainer::getSupportedServiceNames() +{ + auto s(UnoControlBase::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlContainer"; + ps[s.getLength() - 1] = "stardiv.vcl.control.ControlContainer"; + return s; +} + +void UnoControlContainer::PrepareWindowDescriptor( css::awt::WindowDescriptor& rDesc ) +{ + // HACK due to the fact that we can't really use VSCROLL & HSCROLL + // for Dialog ( css::awt::VclWindowPeerAttribute::VSCROLL + // has the same value as + // css::awt::WindowAttribute::NODECORATION ) + // For convenience in the PropBrowse using HSCROLL and VSCROLL ensures + // the Correct text. We exchange them here and the control knows + // about this hack ( it sucks badly I know ) + if ( rDesc.WindowAttributes & css::awt::VclWindowPeerAttribute::VSCROLL ) + { + rDesc.WindowAttributes &= ~css::awt::VclWindowPeerAttribute::VSCROLL; + rDesc.WindowAttributes |= css::awt::VclWindowPeerAttribute::AUTOVSCROLL; + } + if ( rDesc.WindowAttributes & css::awt::VclWindowPeerAttribute::HSCROLL ) + { + rDesc.WindowAttributes &= ~css::awt::VclWindowPeerAttribute::HSCROLL; + rDesc.WindowAttributes |= css::awt::VclWindowPeerAttribute::AUTOHSCROLL; + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlContainer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlContainer()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrolcontainermodel.cxx b/toolkit/source/controls/unocontrolcontainermodel.cxx new file mode 100644 index 0000000000..6bfaf4448c --- /dev/null +++ b/toolkit/source/controls/unocontrolcontainermodel.cxx @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <controls/unocontrolcontainermodel.hxx> +#include <helper/property.hxx> + +#include <helper/unopropertyarrayhelper.hxx> + + +UnoControlContainerModel::UnoControlContainerModel( const css::uno::Reference< css::uno::XComponentContext >& i_factory ) + :UnoControlModel( i_factory ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_TEXT ); +} + +OUString UnoControlContainerModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.ControlContainer"; +} + +OUString UnoControlContainerModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlContainerModel"; +} + +css::uno::Sequence<OUString> +UnoControlContainerModel::getSupportedServiceNames() +{ + auto s(UnoControlModel::getSupportedServiceNames()); + s.realloc(s.getLength() + 2); + auto ps = s.getArray(); + ps[s.getLength() - 2] = "com.sun.star.awt.UnoControlContainerModel"; + ps[s.getLength() - 1] = "stardiv.vcl.controlmodel.ControlContainer"; + return s; +} + +css::uno::Any UnoControlContainerModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + css::uno::Any aDefault; + if ( nPropId == BASEPROPERTY_BORDER ) + aDefault <<= sal_Int16(0); + else + aDefault = UnoControlModel::ImplGetDefaultValue( nPropId ); + return aDefault; +} + + +css::uno::Reference< css::beans::XPropertySetInfo > UnoControlContainerModel::getPropertySetInfo( ) +{ + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +::cppu::IPropertyArrayHelper& UnoControlContainerModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlContainerModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlContainerModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrolmodel.cxx b/toolkit/source/controls/unocontrolmodel.cxx new file mode 100644 index 0000000000..0868fc2f6a --- /dev/null +++ b/toolkit/source/controls/unocontrolmodel.cxx @@ -0,0 +1,1357 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/awt/FontWidth.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/MouseWheelBehavior.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <com/sun/star/io/XMarkableStream.hpp> +#include <com/sun/star/i18n/Currency2.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <toolkit/controls/unocontrolmodel.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/debug.hxx> +#include <tools/long.hxx> +#include <helper/property.hxx> +#include <toolkit/helper/emptyfontdescriptor.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/configmgr.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/extract.hxx> +#include <comphelper/servicehelper.hxx> +#include <vcl/unohelp.hxx> + +#include <memory> +#include <o3tl/sorted_vector.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::i18n; +using ::com::sun::star::awt::FontDescriptor; + + +#define UNOCONTROL_STREAMVERSION short(2) + +static void lcl_ImplMergeFontProperty( FontDescriptor& rFD, sal_uInt16 nPropId, const Any& rValue ) +{ + // some props are defined with other types than the matching FontDescriptor members have + // (e.g. FontWidth, FontSlant) + // 78474 - 09/19/2000 - FS + float nExtractFloat = 0; + sal_Int16 nExtractShort = 0; + + switch ( nPropId ) + { + case BASEPROPERTY_FONTDESCRIPTORPART_NAME: rValue >>= rFD.Name; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_STYLENAME: rValue >>= rFD.StyleName; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_FAMILY: rValue >>= rFD.Family; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARSET: rValue >>= rFD.CharSet; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_HEIGHT: rValue >>= nExtractFloat; rFD.Height = static_cast<sal_Int16>(nExtractFloat); + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WEIGHT: rValue >>= rFD.Weight; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_SLANT: if ( rValue >>= nExtractShort ) + rFD.Slant = static_cast<css::awt::FontSlant>(nExtractShort); + else + rValue >>= rFD.Slant; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_UNDERLINE: rValue >>= rFD.Underline; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_STRIKEOUT: rValue >>= rFD.Strikeout; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WIDTH: rValue >>= rFD.Width; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_PITCH: rValue >>= rFD.Pitch; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARWIDTH: rValue >>= rFD.CharacterWidth; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_ORIENTATION: rValue >>= rFD.Orientation; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_KERNING: rValue >>= rFD.Kerning; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WORDLINEMODE: rValue >>= rFD.WordLineMode; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_TYPE: rValue >>= rFD.Type; + break; + default: OSL_FAIL( "FontProperty?!" ); + } +} + + + +UnoControlModel::UnoControlModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel_Base() + ,maDisposeListeners( *this ) + ,m_xContext( rxContext ) +{ + // Insert properties from Model into table, + // only existing properties are valid, even if they're VOID +} + +UnoControlModel::UnoControlModel( const UnoControlModel& rModel ) + : UnoControlModel_Base(), OPropertySetHelper() + , maData( rModel.maData ) + , maDisposeListeners( *this ) + , m_xContext( rModel.m_xContext ) +{ +} + +css::uno::Sequence<sal_Int32> UnoControlModel::ImplGetPropertyIds() const +{ + sal_uInt32 nIDs = maData.size(); + css::uno::Sequence<sal_Int32> aIDs( nIDs ); + sal_Int32* pIDs = aIDs.getArray(); + sal_uInt32 n = 0; + for ( const auto& rData : maData ) + pIDs[n++] = rData.first; + return aIDs; +} + +bool UnoControlModel::ImplHasProperty( sal_uInt16 nPropId ) const +{ + if ( ( nPropId >= BASEPROPERTY_FONTDESCRIPTORPART_START ) && ( nPropId <= BASEPROPERTY_FONTDESCRIPTORPART_END ) ) + nPropId = BASEPROPERTY_FONTDESCRIPTOR; + + return maData.find( nPropId ) != maData.end(); +} + +css::uno::Any UnoControlModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + css::uno::Any aDefault; + + if ( + (nPropId == BASEPROPERTY_FONTDESCRIPTOR) || + ( + (nPropId >= BASEPROPERTY_FONTDESCRIPTORPART_START) && + (nPropId <= BASEPROPERTY_FONTDESCRIPTORPART_END) + ) + ) + { + EmptyFontDescriptor aFD; + switch ( nPropId ) + { + case BASEPROPERTY_FONTDESCRIPTOR: aDefault <<= aFD; break; + case BASEPROPERTY_FONTDESCRIPTORPART_NAME: aDefault <<= aFD.Name; break; + case BASEPROPERTY_FONTDESCRIPTORPART_STYLENAME: aDefault <<= aFD.StyleName; break; + case BASEPROPERTY_FONTDESCRIPTORPART_FAMILY: aDefault <<= aFD.Family; break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARSET: aDefault <<= aFD.CharSet; break; + case BASEPROPERTY_FONTDESCRIPTORPART_HEIGHT: aDefault <<= static_cast<float>(aFD.Height); break; + case BASEPROPERTY_FONTDESCRIPTORPART_WEIGHT: aDefault <<= aFD.Weight; break; + case BASEPROPERTY_FONTDESCRIPTORPART_SLANT: aDefault <<= static_cast<sal_Int16>(aFD.Slant); break; + case BASEPROPERTY_FONTDESCRIPTORPART_UNDERLINE: aDefault <<= aFD.Underline; break; + case BASEPROPERTY_FONTDESCRIPTORPART_STRIKEOUT: aDefault <<= aFD.Strikeout; break; + case BASEPROPERTY_FONTDESCRIPTORPART_WIDTH: aDefault <<= aFD.Width; break; + case BASEPROPERTY_FONTDESCRIPTORPART_PITCH: aDefault <<= aFD.Pitch; break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARWIDTH: aDefault <<= aFD.CharacterWidth; break; + case BASEPROPERTY_FONTDESCRIPTORPART_ORIENTATION: aDefault <<= aFD.Orientation; break; + case BASEPROPERTY_FONTDESCRIPTORPART_KERNING: aDefault <<= aFD.Kerning; break; + case BASEPROPERTY_FONTDESCRIPTORPART_WORDLINEMODE: aDefault <<= aFD.WordLineMode; break; + case BASEPROPERTY_FONTDESCRIPTORPART_TYPE: aDefault <<= aFD.Type; break; + default: OSL_FAIL( "FontProperty?!" ); + } + } + else + { + switch ( nPropId ) + { + case BASEPROPERTY_GRAPHIC: + aDefault <<= Reference< graphic::XGraphic >(); + break; + + case BASEPROPERTY_REFERENCE_DEVICE: + aDefault <<= Reference< awt::XDevice >(); + break; + + case BASEPROPERTY_ITEM_SEPARATOR_POS: + case BASEPROPERTY_VERTICALALIGN: + case BASEPROPERTY_BORDERCOLOR: + case BASEPROPERTY_SYMBOL_COLOR: + case BASEPROPERTY_TABSTOP: + case BASEPROPERTY_TEXTCOLOR: + case BASEPROPERTY_TEXTLINECOLOR: + case BASEPROPERTY_DATE: + case BASEPROPERTY_DATESHOWCENTURY: + case BASEPROPERTY_TIME: + case BASEPROPERTY_VALUE_DOUBLE: + case BASEPROPERTY_PROGRESSVALUE: + case BASEPROPERTY_SCROLLVALUE: + case BASEPROPERTY_VISIBLESIZE: + case BASEPROPERTY_BACKGROUNDCOLOR: + case BASEPROPERTY_FILLCOLOR: + case BASEPROPERTY_HIGHLIGHT_COLOR: + case BASEPROPERTY_HIGHLIGHT_TEXT_COLOR: break; // Void + + case BASEPROPERTY_FONTRELIEF: + case BASEPROPERTY_FONTEMPHASISMARK: + case BASEPROPERTY_MAXTEXTLEN: + case BASEPROPERTY_STATE: + case BASEPROPERTY_EXTDATEFORMAT: + case BASEPROPERTY_EXTTIMEFORMAT: + case BASEPROPERTY_ECHOCHAR: aDefault <<= sal_Int16(0); break; + case BASEPROPERTY_BORDER: aDefault <<= sal_Int16(1); break; + case BASEPROPERTY_DECIMALACCURACY: aDefault <<= sal_Int16(2); break; + case BASEPROPERTY_LINECOUNT: aDefault <<= sal_Int16(5); break; + case BASEPROPERTY_ALIGN: aDefault <<= sal_Int16(PROPERTY_ALIGN_LEFT); break; + case BASEPROPERTY_IMAGEALIGN: aDefault <<= sal_Int16(1) /*ImageAlign::Top*/; break; + case BASEPROPERTY_IMAGEPOSITION: aDefault <<= sal_Int16(12) /*ImagePosition::Centered*/; break; + case BASEPROPERTY_PUSHBUTTONTYPE: aDefault <<= sal_Int16(0) /*PushButtonType::STANDARD*/; break; + case BASEPROPERTY_MOUSE_WHEEL_BEHAVIOUR:aDefault <<= sal_Int16(awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY); break; + + case BASEPROPERTY_DATEMAX: aDefault <<= util::Date( 31, 12, 2200 ); break; + case BASEPROPERTY_DATEMIN: aDefault <<= util::Date( 1, 1, 1900 ); break; + case BASEPROPERTY_TIMEMAX: aDefault <<= util::Time(0, 0, 59, 23, false); break; + case BASEPROPERTY_TIMEMIN: aDefault <<= util::Time(); break; + case BASEPROPERTY_VALUEMAX_DOUBLE: aDefault <<= double(1000000); break; + case BASEPROPERTY_VALUEMIN_DOUBLE: aDefault <<= double(-1000000); break; + case BASEPROPERTY_VALUESTEP_DOUBLE: aDefault <<= double(1); break; + case BASEPROPERTY_PROGRESSVALUE_MAX: aDefault <<= sal_Int32(100); break; + case BASEPROPERTY_PROGRESSVALUE_MIN: aDefault <<= sal_Int32(0); break; + case BASEPROPERTY_SCROLLVALUE_MAX: aDefault <<= sal_Int32(100); break; + case BASEPROPERTY_SCROLLVALUE_MIN: aDefault <<= sal_Int32(0); break; + case BASEPROPERTY_LINEINCREMENT: aDefault <<= sal_Int32(1); break; + case BASEPROPERTY_BLOCKINCREMENT: aDefault <<= sal_Int32(10); break; + case BASEPROPERTY_ORIENTATION: aDefault <<= sal_Int32(0); break; + case BASEPROPERTY_SPINVALUE: aDefault <<= sal_Int32(0); break; + case BASEPROPERTY_SPININCREMENT: aDefault <<= sal_Int32(1); break; + case BASEPROPERTY_SPINVALUE_MIN: aDefault <<= sal_Int32(0); break; + case BASEPROPERTY_SPINVALUE_MAX: aDefault <<= sal_Int32(100); break; + case BASEPROPERTY_REPEAT_DELAY: aDefault <<= sal_Int32(50); break; // 50 milliseconds + case BASEPROPERTY_DEFAULTCONTROL: aDefault <<= const_cast<UnoControlModel*>(this)->getServiceName(); break; + + case BASEPROPERTY_AUTOHSCROLL: + case BASEPROPERTY_AUTOVSCROLL: + case BASEPROPERTY_MOVEABLE: + case BASEPROPERTY_CLOSEABLE: + case BASEPROPERTY_SIZEABLE: + case BASEPROPERTY_HSCROLL: + case BASEPROPERTY_DEFAULTBUTTON: + case BASEPROPERTY_MULTILINE: + case BASEPROPERTY_MULTISELECTION: + case BASEPROPERTY_TRISTATE: + case BASEPROPERTY_DROPDOWN: + case BASEPROPERTY_SPIN: + case BASEPROPERTY_READONLY: + case BASEPROPERTY_VSCROLL: + case BASEPROPERTY_NUMSHOWTHOUSANDSEP: + case BASEPROPERTY_STRICTFORMAT: + case BASEPROPERTY_REPEAT: + case BASEPROPERTY_PAINTTRANSPARENT: + case BASEPROPERTY_DESKTOP_AS_PARENT: + case BASEPROPERTY_HARDLINEBREAKS: + case BASEPROPERTY_NOLABEL: aDefault <<= false; break; + + case BASEPROPERTY_MULTISELECTION_SIMPLEMODE: + case BASEPROPERTY_HIDEINACTIVESELECTION: + case BASEPROPERTY_ENFORCE_FORMAT: + case BASEPROPERTY_AUTOCOMPLETE: + case BASEPROPERTY_SCALEIMAGE: + case BASEPROPERTY_ENABLED: + case BASEPROPERTY_PRINTABLE: + case BASEPROPERTY_ENABLEVISIBLE: + case BASEPROPERTY_DECORATION: aDefault <<= true; break; + + case BASEPROPERTY_GROUPNAME: + case BASEPROPERTY_HELPTEXT: + case BASEPROPERTY_HELPURL: + case BASEPROPERTY_IMAGEURL: + case BASEPROPERTY_DIALOGSOURCEURL: + case BASEPROPERTY_EDITMASK: + case BASEPROPERTY_LITERALMASK: + case BASEPROPERTY_LABEL: + case BASEPROPERTY_TITLE: + case BASEPROPERTY_TEXT: aDefault <<= OUString(); break; + + case BASEPROPERTY_WRITING_MODE: + case BASEPROPERTY_CONTEXT_WRITING_MODE: + aDefault <<= text::WritingMode2::CONTEXT; + break; + + case BASEPROPERTY_STRINGITEMLIST: + { + css::uno::Sequence< OUString> aStringSeq; + aDefault <<= aStringSeq; + + } + break; + case BASEPROPERTY_TYPEDITEMLIST: + { + css::uno::Sequence< css::uno::Any > aAnySeq; + aDefault <<= aAnySeq; + + } + break; + case BASEPROPERTY_SELECTEDITEMS: + { + css::uno::Sequence<sal_Int16> aINT16Seq; + aDefault <<= aINT16Seq; + } + break; + case BASEPROPERTY_CURRENCYSYMBOL: + { + OUString sDefaultCurrency( + utl::ConfigManager::getDefaultCurrency() ); + + // extract the bank symbol + sal_Int32 nSepPos = sDefaultCurrency.indexOf( '-' ); + OUString sBankSymbol; + if ( nSepPos >= 0 ) + { + sBankSymbol = sDefaultCurrency.copy( 0, nSepPos ); + sDefaultCurrency = sDefaultCurrency.copy( nSepPos + 1 ); + } + + // the remaining is the locale + LocaleDataWrapper aLocaleInfo( m_xContext, LanguageTag(sDefaultCurrency) ); + if ( sBankSymbol.isEmpty() ) + sBankSymbol = aLocaleInfo.getCurrBankSymbol(); + + // look for the currency entry (for this language) which has the given bank symbol + const Sequence< Currency2 > aAllCurrencies = aLocaleInfo.getAllCurrencies(); + + OUString sCurrencySymbol = aLocaleInfo.getCurrSymbol(); + if ( sBankSymbol.isEmpty() ) + { + DBG_ASSERT( aAllCurrencies.hasElements(), "UnoControlModel::ImplGetDefaultValue: no currencies at all!" ); + if ( aAllCurrencies.hasElements() ) + { + sBankSymbol = aAllCurrencies[0].BankSymbol; + sCurrencySymbol = aAllCurrencies[0].Symbol; + } + } + + if ( !sBankSymbol.isEmpty() ) + { + bool bLegacy = false; + bool bFound = false; + for ( const Currency2& rCurrency : aAllCurrencies ) + if ( rCurrency.BankSymbol == sBankSymbol ) + { + sCurrencySymbol = rCurrency.Symbol; + if ( rCurrency.LegacyOnly ) + bLegacy = true; + else + { + bFound = true; + break; + } + } + DBG_ASSERT( bLegacy || bFound, "UnoControlModel::ImplGetDefaultValue: did not find the given bank symbol!" ); + } + + aDefault <<= sCurrencySymbol; + } + break; + + default: OSL_FAIL( "ImplGetDefaultValue - unknown Property" ); + } + } + + return aDefault; +} + +void UnoControlModel::ImplRegisterProperty( sal_uInt16 nPropId, const css::uno::Any& rDefault ) +{ + maData[ nPropId ] = rDefault; +} + +void UnoControlModel::ImplRegisterProperty( sal_uInt16 nPropId ) +{ + ImplRegisterProperty( nPropId, ImplGetDefaultValue( nPropId ) ); + + if ( nPropId == BASEPROPERTY_FONTDESCRIPTOR ) + { + // some properties are not included in the FontDescriptor, but every time + // when we have a FontDescriptor we want to have these properties too. + // => Easier to register the here, instead everywhere where I register the FontDescriptor... + + ImplRegisterProperty( BASEPROPERTY_TEXTCOLOR ); + ImplRegisterProperty( BASEPROPERTY_TEXTLINECOLOR ); + ImplRegisterProperty( BASEPROPERTY_FONTRELIEF ); + ImplRegisterProperty( BASEPROPERTY_FONTEMPHASISMARK ); + } +} + +void UnoControlModel::ImplRegisterProperties( const std::vector< sal_uInt16 > &rIds ) +{ + for (const auto& rId : rIds) + { + if( !ImplHasProperty( rId ) ) + ImplRegisterProperty( rId, ImplGetDefaultValue( rId ) ); + } +} + +// css::uno::XInterface +css::uno::Any UnoControlModel::queryAggregation( const css::uno::Type & rType ) +{ + Any aRet = UnoControlModel_Base::queryAggregation( rType ); + if ( !aRet.hasValue() ) + aRet = ::comphelper::OPropertySetHelper::queryInterface( rType ); + return aRet; +} + +// XInterface +IMPLEMENT_FORWARD_REFCOUNT( UnoControlModel, UnoControlModel_Base ) + +// css::lang::XTypeProvider +IMPLEMENT_FORWARD_XTYPEPROVIDER2( UnoControlModel, UnoControlModel_Base, ::comphelper::OPropertySetHelper ) + + +uno::Reference< util::XCloneable > UnoControlModel::createClone() +{ + rtl::Reference<UnoControlModel> pClone = Clone(); + return pClone; +} + +// css::lang::XComponent +void UnoControlModel::dispose( ) +{ + std::unique_lock aGuard( m_aMutex ); + + css::lang::EventObject aEvt; + aEvt.Source = static_cast<css::uno::XAggregation*>(static_cast<cppu::OWeakAggObject*>(this)); + maDisposeListeners.disposeAndClear( aGuard, aEvt ); + + // let the property set helper notify our property listeners + OPropertySetHelper::disposing(aGuard); +} + +void UnoControlModel::addEventListener( const css::uno::Reference< css::lang::XEventListener >& rxListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + maDisposeListeners.addInterface( rxListener ); +} + +void UnoControlModel::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& rxListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + maDisposeListeners.removeInterface( rxListener ); +} + + +// css::beans::XPropertyState +css::beans::PropertyState UnoControlModel::getPropertyState( const OUString& PropertyName ) +{ + std::unique_lock aGuard( m_aMutex ); + return getPropertyStateImpl(aGuard, PropertyName); +} + +css::beans::PropertyState UnoControlModel::getPropertyStateImpl( std::unique_lock<std::mutex>& rGuard, const OUString& PropertyName ) +{ + sal_uInt16 nPropId = GetPropertyId( PropertyName ); + + css::uno::Any aValue = getPropertyValueImpl( rGuard, PropertyName ); + css::uno::Any aDefault = ImplGetDefaultValue( nPropId ); + + return CompareProperties( aValue, aDefault ) ? css::beans::PropertyState_DEFAULT_VALUE : css::beans::PropertyState_DIRECT_VALUE; +} + +css::uno::Sequence< css::beans::PropertyState > UnoControlModel::getPropertyStates( const css::uno::Sequence< OUString >& PropertyNames ) +{ + std::unique_lock aGuard( m_aMutex ); + + sal_Int32 nNames = PropertyNames.getLength(); + + css::uno::Sequence< css::beans::PropertyState > aStates( nNames ); + + std::transform(PropertyNames.begin(), PropertyNames.end(), aStates.getArray(), + [this, &aGuard](const OUString& rName) -> css::beans::PropertyState + { return getPropertyStateImpl(aGuard, rName); }); + + return aStates; +} + +void UnoControlModel::setPropertyToDefault( const OUString& PropertyName ) +{ + Any aDefaultValue; + { + std::unique_lock aGuard( m_aMutex ); + aDefaultValue = ImplGetDefaultValue( GetPropertyId( PropertyName ) ); + } + setPropertyValue( PropertyName, aDefaultValue ); +} + +css::uno::Any UnoControlModel::getPropertyDefault( const OUString& rPropertyName ) +{ + std::unique_lock aGuard( m_aMutex ); + + return ImplGetDefaultValue( GetPropertyId( rPropertyName ) ); +} + + +// css::io::XPersistObjec +OUString UnoControlModel::getServiceName( ) +{ + OSL_FAIL( "ServiceName of UnoControlModel ?!" ); + return OUString(); +} + +void UnoControlModel::write( const css::uno::Reference< css::io::XObjectOutputStream >& OutStream ) +{ + std::unique_lock aGuard( m_aMutex ); + + css::uno::Reference< css::io::XMarkableStream > xMark( OutStream, css::uno::UNO_QUERY ); + DBG_ASSERT( xMark.is(), "write: no css::io::XMarkableStream!" ); + + OutStream->writeShort( UNOCONTROL_STREAMVERSION ); + + o3tl::sorted_vector<sal_uInt16> aProps; + + for (const auto& rData : maData) + { + if ( ( ( GetPropertyAttribs( rData.first ) & css::beans::PropertyAttribute::TRANSIENT ) == 0 ) + && ( getPropertyStateImpl( aGuard, GetPropertyName( rData.first ) ) != css::beans::PropertyState_DEFAULT_VALUE ) ) + { + aProps.insert( rData.first ); + } + } + + sal_uInt32 nProps = aProps.size(); + + // Save FontProperty always in the old format (due to missing distinction + // between 5.0 and 5.1) + OutStream->writeLong( ( aProps.find( BASEPROPERTY_FONTDESCRIPTOR ) != aProps.end() ) ? ( nProps + 3 ) : nProps ); + for ( const auto& rProp : aProps ) + { + sal_Int32 nPropDataBeginMark = xMark->createMark(); + OutStream->writeLong( 0 ); // DataLen + + const css::uno::Any* pProp = &(maData[rProp]); + OutStream->writeShort( rProp ); + + bool bVoid = pProp->getValueType().getTypeClass() == css::uno::TypeClass_VOID; + + OutStream->writeBoolean( bVoid ); + + if ( !bVoid ) + { + const css::uno::Any& rValue = *pProp; + const css::uno::Type& rType = rValue.getValueType(); + + if ( rType == cppu::UnoType< bool >::get() ) + { + bool b = false; + rValue >>= b; + OutStream->writeBoolean( b ); + } + else if ( rType == ::cppu::UnoType< OUString >::get() ) + { + OUString aUString; + rValue >>= aUString; + OutStream->writeUTF( aUString ); + } + else if ( rType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() ) + { + sal_uInt16 n = 0; + rValue >>= n; + OutStream->writeShort( n ); + } + else if ( rType == cppu::UnoType<sal_Int16>::get() ) + { + sal_Int16 n = 0; + rValue >>= n; + OutStream->writeShort( n ); + } + else if ( rType == cppu::UnoType<sal_uInt32>::get() ) + { + sal_uInt32 n = 0; + rValue >>= n; + OutStream->writeLong( n ); + } + else if ( rType == cppu::UnoType<sal_Int32>::get() ) + { + sal_Int32 n = 0; + rValue >>= n; + OutStream->writeLong( n ); + } + else if ( rType == cppu::UnoType<double>::get() ) + { + double n = 0; + rValue >>= n; + OutStream->writeDouble( n ); + } + else if ( rType == cppu::UnoType< css::awt::FontDescriptor >::get() ) + { + css::awt::FontDescriptor aFD; + rValue >>= aFD; + OutStream->writeUTF( aFD.Name ); + OutStream->writeShort( aFD.Height ); + OutStream->writeShort( aFD.Width ); + OutStream->writeUTF( aFD.StyleName ); + OutStream->writeShort( aFD.Family ); + OutStream->writeShort( aFD.CharSet ); + OutStream->writeShort( aFD.Pitch ); + OutStream->writeDouble( aFD.CharacterWidth ); + OutStream->writeDouble( aFD.Weight ); + OutStream->writeShort( + sal::static_int_cast< sal_Int16 >(aFD.Slant) ); + OutStream->writeShort( aFD.Underline ); + OutStream->writeShort( aFD.Strikeout ); + OutStream->writeDouble( aFD.Orientation ); + OutStream->writeBoolean( aFD.Kerning ); + OutStream->writeBoolean( aFD.WordLineMode ); + OutStream->writeShort( aFD.Type ); + } + else if ( rType == cppu::UnoType<css::util::Date>::get() ) + { + css::util::Date d; + rValue >>= d; + OutStream->writeLong(d.Day + 100 * d.Month + 10000 * d.Year); + // YYYYMMDD + } + else if ( rType == cppu::UnoType<css::util::Time>::get() ) + { + css::util::Time t; + rValue >>= t; + OutStream->writeLong( + t.NanoSeconds / 1000000 + 100 * t.Seconds + + 10000 * t.Minutes + 1000000 * t.Hours); // HHMMSShh + } + else if ( rType == cppu::UnoType< css::uno::Sequence< OUString> >::get() ) + { + css::uno::Sequence< OUString> aSeq; + rValue >>= aSeq; + tools::Long nEntries = aSeq.getLength(); + OutStream->writeLong( nEntries ); + for ( const auto& rVal : std::as_const(aSeq) ) + OutStream->writeUTF( rVal ); + } + else if ( rType == cppu::UnoType< cppu::UnoSequenceType<cppu::UnoUnsignedShortType> >::get() ) + { + css::uno::Sequence<sal_uInt16> aSeq; + rValue >>= aSeq; + tools::Long nEntries = aSeq.getLength(); + OutStream->writeLong( nEntries ); + for ( const auto nVal : std::as_const(aSeq) ) + OutStream->writeShort( nVal ); + } + else if ( rType == cppu::UnoType< css::uno::Sequence<sal_Int16> >::get() ) + { + css::uno::Sequence<sal_Int16> aSeq; + rValue >>= aSeq; + tools::Long nEntries = aSeq.getLength(); + OutStream->writeLong( nEntries ); + for ( const auto nVal : std::as_const(aSeq) ) + OutStream->writeShort( nVal ); + } + else if ( rType.getTypeClass() == TypeClass_ENUM ) + { + sal_Int32 nAsInt = 0; + ::cppu::enum2int( nAsInt, rValue ); + OutStream->writeLong( nAsInt ); + } +#if OSL_DEBUG_LEVEL > 0 + else + { + SAL_WARN( "toolkit", "UnoControlModel::write: don't know how to handle a property of type '" + << rType.getTypeName() + << "'.\n(Currently handling property '" + << GetPropertyName( rProp ) + << "'.)"); + } +#endif + } + + sal_Int32 nPropDataLen = xMark->offsetToMark( nPropDataBeginMark ); + xMark->jumpToMark( nPropDataBeginMark ); + OutStream->writeLong( nPropDataLen ); + xMark->jumpToFurthest(); + xMark->deleteMark(nPropDataBeginMark); + } + + if ( aProps.find( BASEPROPERTY_FONTDESCRIPTOR ) == aProps.end() ) + return; + + const css::uno::Any* pProp = &maData[ BASEPROPERTY_FONTDESCRIPTOR ]; + // Until 5.0 export arrives, write old format... + css::awt::FontDescriptor aFD; + (*pProp) >>= aFD; + + for ( sal_uInt16 n = BASEPROPERTY_FONT_TYPE; n <= BASEPROPERTY_FONT_ATTRIBS; n++ ) + { + sal_Int32 nPropDataBeginMark = xMark->createMark(); + OutStream->writeLong( 0 ); // DataLen + OutStream->writeShort( n ); // PropId + OutStream->writeBoolean( false ); // Void + + if ( n == BASEPROPERTY_FONT_TYPE ) + { + OutStream->writeUTF( aFD.Name ); + OutStream->writeUTF( aFD.StyleName ); + OutStream->writeShort( aFD.Family ); + OutStream->writeShort( aFD.CharSet ); + OutStream->writeShort( aFD.Pitch ); + } + else if ( n == BASEPROPERTY_FONT_SIZE ) + { + OutStream->writeLong( aFD.Width ); + OutStream->writeLong( aFD.Height ); + OutStream->writeShort( + sal::static_int_cast< sal_Int16 >( + vcl::unohelper::ConvertFontWidth(aFD.CharacterWidth)) ); + } + else if ( n == BASEPROPERTY_FONT_ATTRIBS ) + { + OutStream->writeShort( + sal::static_int_cast< sal_Int16 >( + vcl::unohelper::ConvertFontWeight(aFD.Weight)) ); + OutStream->writeShort( + sal::static_int_cast< sal_Int16 >(aFD.Slant) ); + OutStream->writeShort( aFD.Underline ); + OutStream->writeShort( aFD.Strikeout ); + OutStream->writeShort( static_cast<short>(aFD.Orientation * 10) ); + OutStream->writeBoolean( aFD.Kerning ); + OutStream->writeBoolean( aFD.WordLineMode ); + } + else + { + OSL_FAIL( "Property?!" ); + } + + sal_Int32 nPropDataLen = xMark->offsetToMark( nPropDataBeginMark ); + xMark->jumpToMark( nPropDataBeginMark ); + OutStream->writeLong( nPropDataLen ); + xMark->jumpToFurthest(); + xMark->deleteMark(nPropDataBeginMark); + } +} + +void UnoControlModel::read( const css::uno::Reference< css::io::XObjectInputStream >& InStream ) +{ + std::unique_lock aGuard( m_aMutex ); + + css::uno::Reference< css::io::XMarkableStream > xMark( InStream, css::uno::UNO_QUERY ); + DBG_ASSERT( xMark.is(), "read: no css::io::XMarkableStream!" ); + + short nVersion = InStream->readShort(); + sal_uInt32 nProps = static_cast<sal_uInt32>(InStream->readLong()); + css::uno::Sequence< OUString> aProps( nProps ); + css::uno::Sequence< css::uno::Any> aValues( nProps ); + bool bInvalidEntries = false; + + // Unfortunately, there's no mark for the whole block, thus only properties may be changed. + // No data for the model may be added following the properties + + // Used for import of old parts in css::awt::FontDescriptor + std::unique_ptr<css::awt::FontDescriptor> pFD; + + for ( sal_uInt32 i = 0; i < nProps; i++ ) + { + sal_Int32 nPropDataBeginMark = xMark->createMark(); + sal_Int32 nPropDataLen = InStream->readLong(); + + sal_uInt16 nPropId = static_cast<sal_uInt16>(InStream->readShort()); + + css::uno::Any aValue; + bool bIsVoid = InStream->readBoolean(); + if ( !bIsVoid ) + { + if ( maData.find( nPropId ) != maData.end() ) + { + const css::uno::Type* pType = GetPropertyType( nPropId ); + if ( *pType == cppu::UnoType<bool>::get() ) + { + bool b = InStream->readBoolean(); + aValue <<= b; + } + else if ( *pType == cppu::UnoType<OUString>::get() ) + { + OUString aUTF = InStream->readUTF(); + aValue <<= aUTF; + } + else if ( *pType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() ) + { + sal_uInt16 n = InStream->readShort(); + aValue <<= n; + } + else if ( *pType == cppu::UnoType<sal_Int16>::get() ) + { + sal_Int16 n = InStream->readShort(); + aValue <<= n; + } + else if ( *pType == cppu::UnoType<sal_uInt32>::get() ) + { + sal_uInt32 n = InStream->readLong(); + aValue <<= n; + } + else if ( *pType == cppu::UnoType<sal_Int32>::get() ) + { + sal_Int32 n = InStream->readLong(); + aValue <<= n; + } + else if ( *pType == cppu::UnoType<double>::get() ) + { + double n = InStream->readDouble(); + aValue <<= n; + } + else if ( *pType == cppu::UnoType< css::awt::FontDescriptor >::get() ) + { + css::awt::FontDescriptor aFD; + aFD.Name = InStream->readUTF(); + aFD.Height = InStream->readShort(); + aFD.Width = InStream->readShort(); + aFD.StyleName = InStream->readUTF(); + aFD.Family = InStream->readShort(); + aFD.CharSet = InStream->readShort(); + aFD.Pitch = InStream->readShort(); + aFD.CharacterWidth = static_cast<float>(InStream->readDouble()); + aFD.Weight = static_cast<float>(InStream->readDouble()); + aFD.Slant = static_cast<css::awt::FontSlant>(InStream->readShort()); + aFD.Underline = InStream->readShort(); + aFD.Strikeout = InStream->readShort(); + aFD.Orientation = static_cast<float>(InStream->readDouble()); + aFD.Kerning = InStream->readBoolean() != 0; + aFD.WordLineMode = InStream->readBoolean() != 0; + aFD.Type = InStream->readShort(); + aValue <<= aFD; + } + else if ( *pType == cppu::UnoType<css::util::Date>::get() ) + { + sal_Int32 n = InStream->readLong(); // YYYYMMDD + aValue <<= css::util::Date( + n % 100, (n / 100) % 100, n / 10000); + } + else if ( *pType == cppu::UnoType<css::util::Time>::get() ) + { + sal_Int32 n = InStream->readLong(); // HHMMSShh + aValue <<= css::util::Time( + (n % 100) * 1000000, (n / 100) % 100, (n / 10000) % 100, + n / 1000000, false); + } + else if ( *pType == cppu::UnoType< css::uno::Sequence< OUString> >::get() ) + { + tools::Long nEntries = InStream->readLong(); + css::uno::Sequence< OUString> aSeq( nEntries ); + for ( tools::Long n = 0; n < nEntries; n++ ) + aSeq.getArray()[n] = InStream->readUTF(); + aValue <<= aSeq; + + } + else if ( *pType == cppu::UnoType< cppu::UnoSequenceType<cppu::UnoUnsignedShortType> >::get() ) + + { + tools::Long nEntries = InStream->readLong(); + css::uno::Sequence<sal_uInt16> aSeq( nEntries ); + for ( tools::Long n = 0; n < nEntries; n++ ) + aSeq.getArray()[n] = static_cast<sal_uInt16>(InStream->readShort()); + aValue <<= aSeq; + } + else if ( *pType == cppu::UnoType< css::uno::Sequence<sal_Int16> >::get() ) + { + tools::Long nEntries = InStream->readLong(); + css::uno::Sequence<sal_Int16> aSeq( nEntries ); + for ( tools::Long n = 0; n < nEntries; n++ ) + aSeq.getArray()[n] = InStream->readShort(); + aValue <<= aSeq; + } + else if ( pType->getTypeClass() == TypeClass_ENUM ) + { + sal_Int32 nAsInt = InStream->readLong(); + aValue = ::cppu::int2enum( nAsInt, *pType ); + } + else + { + SAL_WARN( "toolkit", "UnoControlModel::read: don't know how to handle a property of type '" + << pType->getTypeName() + << "'.\n(Currently handling property '" + << GetPropertyName( nPropId ) + << "'.)"); + } + } + else + { + // Old trash from 5.0 + if ( nPropId == BASEPROPERTY_FONT_TYPE ) + { + // Redundant information for older versions + // is skipped by MarkableStream + if ( nVersion < 2 ) + { + if ( !pFD ) + { + pFD.reset(new css::awt::FontDescriptor); + auto it = maData.find( BASEPROPERTY_FONTDESCRIPTOR ); + if ( it != maData.end() ) // due to defaults... + it->second >>= *pFD; + } + pFD->Name = InStream->readUTF(); + pFD->StyleName = InStream->readUTF(); + pFD->Family = InStream->readShort(); + pFD->CharSet = InStream->readShort(); + pFD->Pitch = InStream->readShort(); + } + } + else if ( nPropId == BASEPROPERTY_FONT_SIZE ) + { + if ( nVersion < 2 ) + { + if ( !pFD ) + { + pFD.reset(new css::awt::FontDescriptor); + auto it = maData.find(BASEPROPERTY_FONTDESCRIPTOR); + if ( it != maData.end() ) // due to defaults... + it->second >>= *pFD; + } + pFD->Width = static_cast<sal_Int16>(InStream->readLong()); + pFD->Height = static_cast<sal_Int16>(InStream->readLong()); + InStream->readShort(); // ignore css::awt::FontWidth - it was + // misspelled and is no longer needed + pFD->CharacterWidth = css::awt::FontWidth::DONTKNOW; + } + } + else if ( nPropId == BASEPROPERTY_FONT_ATTRIBS ) + { + if ( nVersion < 2 ) + { + if ( !pFD ) + { + pFD.reset(new css::awt::FontDescriptor); + auto it = maData.find(BASEPROPERTY_FONTDESCRIPTOR); + if ( it != maData.end() ) // due to defaults... + it->second >>= *pFD; + } + pFD->Weight = vcl::unohelper::ConvertFontWeight(static_cast<FontWeight>(InStream->readShort())); + pFD->Slant = static_cast<css::awt::FontSlant>(InStream->readShort()); + pFD->Underline = InStream->readShort(); + pFD->Strikeout = InStream->readShort(); + pFD->Orientation = static_cast<float>(static_cast<double>(InStream->readShort())) / 10; + pFD->Kerning = InStream->readBoolean() != 0; + pFD->WordLineMode = InStream->readBoolean() != 0; + } + } + else + { + OSL_FAIL( "read: unknown Property!" ); + } + } + } + else // bVoid + { + if ( nPropId == BASEPROPERTY_FONTDESCRIPTOR ) + { + EmptyFontDescriptor aFD; + aValue <<= aFD; + } + } + + if ( maData.find( nPropId ) != maData.end() ) + { + aProps.getArray()[i] = GetPropertyName( nPropId ); + aValues.getArray()[i] = aValue; + } + else + { + bInvalidEntries = true; + } + + // Skip rest of input if there is more data in stream than this version can handle + xMark->jumpToMark( nPropDataBeginMark ); + InStream->skipBytes( nPropDataLen ); + xMark->deleteMark(nPropDataBeginMark); + } + if ( bInvalidEntries ) + { + for ( sal_Int32 i = 0; i < aProps.getLength(); i++ ) + { + if ( aProps.getConstArray()[i].isEmpty() ) + { + ::comphelper::removeElementAt( aProps, i ); + ::comphelper::removeElementAt( aValues, i ); + i--; + } + } + } + + try + { + setPropertyValuesImpl( aGuard, aProps, aValues ); + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + + if ( pFD ) + { + css::uno::Any aValue; + aValue <<= *pFD; + setFastPropertyValueImpl( aGuard, BASEPROPERTY_FONTDESCRIPTOR, aValue ); + } +} + + +// css::lang::XServiceInfo +OUString UnoControlModel::getImplementationName( ) +{ + OSL_FAIL( "This method should be overridden!" ); + return OUString(); + +} + +sal_Bool UnoControlModel::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > UnoControlModel::getSupportedServiceNames( ) +{ + return { "com.sun.star.awt.UnoControlModel" }; +} + +bool UnoControlModel::convertFastPropertyValue( std::unique_lock<std::mutex>& rGuard, Any & rConvertedValue, Any & rOldValue, sal_Int32 nPropId, const Any& rValue ) +{ + bool bVoid = rValue.getValueType().getTypeClass() == css::uno::TypeClass_VOID; + if ( bVoid ) + { + rConvertedValue.clear(); + } + else + { + const css::uno::Type* pDestType = GetPropertyType( static_cast<sal_uInt16>(nPropId) ); + if ( pDestType->getTypeClass() == TypeClass_ANY ) + { + rConvertedValue = rValue; + } + else + { + if ( pDestType->equals( rValue.getValueType() ) ) + { + rConvertedValue = rValue; + } + else + { + bool bConverted = false; + // 13.03.2001 - 84923 - frank.schoenheit@germany.sun.com + + switch (pDestType->getTypeClass()) + { + case TypeClass_DOUBLE: + { + // try as double + double nAsDouble = 0; + bConverted = ( rValue >>= nAsDouble ); + if ( bConverted ) + rConvertedValue <<= nAsDouble; + else + { // try as integer + sal_Int32 nAsInteger = 0; + bConverted = ( rValue >>= nAsInteger ); + if ( bConverted ) + rConvertedValue <<= static_cast<double>(nAsInteger); + } + } + break; + case TypeClass_SHORT: + { + sal_Int16 n; + bConverted = ( rValue >>= n ); + if ( bConverted ) + rConvertedValue <<= n; + } + break; + case TypeClass_UNSIGNED_SHORT: + { + sal_uInt16 n; + bConverted = ( rValue >>= n ); + if ( bConverted ) + rConvertedValue <<= n; + } + break; + case TypeClass_LONG: + { + sal_Int32 n; + bConverted = ( rValue >>= n ); + if ( bConverted ) + rConvertedValue <<= n; + } + break; + case TypeClass_UNSIGNED_LONG: + { + sal_uInt32 n; + bConverted = ( rValue >>= n ); + if ( bConverted ) + rConvertedValue <<= n; + } + break; + case TypeClass_INTERFACE: + { + if ( rValue.getValueType().getTypeClass() == TypeClass_INTERFACE ) + { + Reference< XInterface > xPure( rValue, UNO_QUERY ); + if ( xPure.is() ) + rConvertedValue = xPure->queryInterface( *pDestType ); + else + rConvertedValue.setValue( nullptr, *pDestType ); + bConverted = true; + } + } + break; + case TypeClass_ENUM: + { + sal_Int32 nValue = 0; + bConverted = ( rValue >>= nValue ); + if ( bConverted ) + rConvertedValue = ::cppu::int2enum( nValue, *pDestType ); + } + break; + default: ; // avoid compiler warning + } + + if (!bConverted) + { + throw css::lang::IllegalArgumentException( + "Unable to convert the given value for the property " + + GetPropertyName( static_cast<sal_uInt16>(nPropId) ) + + ".\nExpected type: " + pDestType->getTypeName() + + "\nFound type: " + rValue.getValueType().getTypeName(), + static_cast< css::beans::XPropertySet* >(this), + 1); + } + } + } + } + + // the current value + getFastPropertyValue( rGuard, rOldValue, nPropId ); + return !CompareProperties( rConvertedValue, rOldValue ); +} + +void UnoControlModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& /*rGuard*/, sal_Int32 nPropId, const css::uno::Any& rValue ) +{ + // Missing: the fake solo properties of the FontDescriptor + + ImplPropertyTable::const_iterator it = maData.find( nPropId ); + const css::uno::Any* pProp = it == maData.end() ? nullptr : &(it->second); + ENSURE_OR_RETURN_VOID( pProp, "UnoControlModel::setFastPropertyValue_NoBroadcast: invalid property id!" ); + + DBG_ASSERT( ( rValue.getValueType().getTypeClass() != css::uno::TypeClass_VOID ) || ( GetPropertyAttribs( static_cast<sal_uInt16>(nPropId) ) & css::beans::PropertyAttribute::MAYBEVOID ), "Property should not be VOID!" ); + maData[ nPropId ] = rValue; +} + +void UnoControlModel::getFastPropertyValue( std::unique_lock<std::mutex>& /*rGuard*/, css::uno::Any& rValue, sal_Int32 nPropId ) const +{ + ImplPropertyTable::const_iterator it = maData.find( nPropId ); + const css::uno::Any* pProp = it == maData.end() ? nullptr : &(it->second); + + if ( pProp ) + rValue = *pProp; + else if ( ( nPropId >= BASEPROPERTY_FONTDESCRIPTORPART_START ) && ( nPropId <= BASEPROPERTY_FONTDESCRIPTORPART_END ) ) + { + const auto iter = maData.find( BASEPROPERTY_FONTDESCRIPTOR ); + assert(iter != maData.end()); + pProp = &(iter->second); + css::awt::FontDescriptor aFD; + (*pProp) >>= aFD; + switch ( nPropId ) + { + case BASEPROPERTY_FONTDESCRIPTORPART_NAME: rValue <<= aFD.Name; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_STYLENAME: rValue <<= aFD.StyleName; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_FAMILY: rValue <<= aFD.Family; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARSET: rValue <<= aFD.CharSet; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_HEIGHT: rValue <<= static_cast<float>(aFD.Height); + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WEIGHT: rValue <<= aFD.Weight; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_SLANT: rValue <<= static_cast<sal_Int16>(aFD.Slant); + break; + case BASEPROPERTY_FONTDESCRIPTORPART_UNDERLINE: rValue <<= aFD.Underline; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_STRIKEOUT: rValue <<= aFD.Strikeout; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WIDTH: rValue <<= aFD.Width; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_PITCH: rValue <<= aFD.Pitch; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_CHARWIDTH: rValue <<= aFD.CharacterWidth; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_ORIENTATION: rValue <<= aFD.Orientation; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_KERNING: rValue <<= aFD.Kerning; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_WORDLINEMODE: rValue <<= aFD.WordLineMode; + break; + case BASEPROPERTY_FONTDESCRIPTORPART_TYPE: rValue <<= aFD.Type; + break; + default: OSL_FAIL( "FontProperty?!" ); + } + } + else + { + OSL_FAIL( "getFastPropertyValue - invalid Property!" ); + } +} + +// css::beans::XFastPropertySet +void UnoControlModel::setFastPropertyValueImpl( std::unique_lock<std::mutex>& rGuard, sal_Int32 nPropId, const css::uno::Any& rValue ) +{ + if ( ( nPropId >= BASEPROPERTY_FONTDESCRIPTORPART_START ) && ( nPropId <= BASEPROPERTY_FONTDESCRIPTORPART_END ) ) + { + Any aOldSingleValue; + getFastPropertyValue( rGuard, aOldSingleValue, BASEPROPERTY_FONTDESCRIPTORPART_START ); + + css::uno::Any* pProp = &maData[ BASEPROPERTY_FONTDESCRIPTOR ]; + FontDescriptor aOldFontDescriptor; + (*pProp) >>= aOldFontDescriptor; + + FontDescriptor aNewFontDescriptor( aOldFontDescriptor ); + lcl_ImplMergeFontProperty( aNewFontDescriptor, static_cast<sal_uInt16>(nPropId), rValue ); + + Any aNewValue; + aNewValue <<= aNewFontDescriptor; + sal_Int32 nDescriptorId = BASEPROPERTY_FONTDESCRIPTOR; + + // also, we need fire a propertyChange event for the single property, since with + // the above line, only an event for the FontDescriptor property will be fired + Any aNewSingleValue; + getFastPropertyValue( rGuard, aNewSingleValue, BASEPROPERTY_FONTDESCRIPTORPART_START ); + + setFastPropertyValues( rGuard, 1, &nDescriptorId, &aNewValue, 1 ); + fire( rGuard, &nPropId, &aNewSingleValue, &aOldSingleValue, 1, false ); + } + else + setFastPropertyValues( rGuard, 1, &nPropId, &rValue, 1 ); +} + +// css::beans::XMultiPropertySet +css::uno::Reference< css::beans::XPropertySetInfo > UnoControlModel::getPropertySetInfo( ) +{ + OSL_FAIL( "UnoControlModel::getPropertySetInfo() not possible!" ); + return css::uno::Reference< css::beans::XPropertySetInfo >(); +} + +void UnoControlModel::setPropertyValues( const css::uno::Sequence< OUString >& rPropertyNames, const css::uno::Sequence< css::uno::Any >& Values ) +{ + std::unique_lock aGuard( m_aMutex ); + setPropertyValuesImpl(aGuard, rPropertyNames, Values); +} + +void UnoControlModel::setPropertyValuesImpl( std::unique_lock<std::mutex>& rGuard, const css::uno::Sequence< OUString >& rPropertyNames, const css::uno::Sequence< css::uno::Any >& Values ) +{ + sal_Int32 nProps = rPropertyNames.getLength(); + if (nProps != Values.getLength()) + throw css::lang::IllegalArgumentException("lengths do not match", + getXWeak(), -1); + +// sal_Int32* pHandles = new sal_Int32[nProps]; + // don't do this - it leaks in case of an exception + Sequence< sal_Int32 > aHandles( nProps ); + sal_Int32* pHandles = aHandles.getArray(); + + // may need to change the order in the sequence, for this we need a non-const value sequence + uno::Sequence< uno::Any > aValues( Values ); + uno::Any* pValues = aValues.getArray(); + + sal_Int32 nValidHandles = getInfoHelper().fillHandles( pHandles, rPropertyNames ); + + if ( !nValidHandles ) + return; + + // if somebody sets properties which are single aspects of a font descriptor, + // remove them, and build a font descriptor instead + std::unique_ptr< awt::FontDescriptor > pFD; + for ( sal_Int32 n = 0; n < nProps; ++n ) + { + if ( ( pHandles[n] >= BASEPROPERTY_FONTDESCRIPTORPART_START ) && ( pHandles[n] <= BASEPROPERTY_FONTDESCRIPTORPART_END ) ) + { + if (!pFD) + { + css::uno::Any* pProp = &maData[ BASEPROPERTY_FONTDESCRIPTOR ]; + pFD.reset( new awt::FontDescriptor ); + (*pProp) >>= *pFD; + } + lcl_ImplMergeFontProperty( *pFD, static_cast<sal_uInt16>(pHandles[n]), pValues[n] ); + pHandles[n] = -1; + nValidHandles--; + } + } + + if ( nValidHandles ) + { + ImplNormalizePropertySequence( nProps, pHandles, pValues, &nValidHandles ); + setFastPropertyValues( rGuard, nProps, pHandles, pValues, nValidHandles ); + } + + // Don't merge FD property into array, as it is sorted + if (pFD) + { + css::uno::Any aValue; + aValue <<= *pFD; + sal_Int32 nHandle = BASEPROPERTY_FONTDESCRIPTOR; + setFastPropertyValues( rGuard, 1, &nHandle, &aValue, 1 ); + } +} + + +void UnoControlModel::ImplNormalizePropertySequence( const sal_Int32, sal_Int32*, + uno::Any*, sal_Int32* ) const +{ + // nothing to do here +} + +void UnoControlModel::ImplEnsureHandleOrder( const sal_Int32 _nCount, sal_Int32* _pHandles, + uno::Any* _pValues, sal_Int32 _nFirstHandle, sal_Int32 _nSecondHandle ) +{ + for ( sal_Int32 i=0; i < _nCount; ++_pHandles, ++_pValues, ++i ) + { + if ( _nSecondHandle == *_pHandles ) + { + sal_Int32* pLaterHandles = _pHandles + 1; + uno::Any* pLaterValues = _pValues + 1; + for ( sal_Int32 j = i + 1; j < _nCount; ++j, ++pLaterHandles, ++pLaterValues ) + { + if ( _nFirstHandle == *pLaterHandles ) + { + // indeed it is -> exchange the both places in the sequences + sal_Int32 nHandle( *_pHandles ); + *_pHandles = *pLaterHandles; + *pLaterHandles = nHandle; + + uno::Any aValue( *_pValues ); + *_pValues = *pLaterValues; + *pLaterValues = aValue; + + break; + // this will leave the inner loop, and continue with the outer loop. + // Note that this means we will encounter the _nSecondHandle handle, again, once we reached + // (in the outer loop) the place where we just put it. + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontrols.cxx b/toolkit/source/controls/unocontrols.cxx new file mode 100644 index 0000000000..d9bc55f8cb --- /dev/null +++ b/toolkit/source/controls/unocontrols.cxx @@ -0,0 +1,4757 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/awt/XTextArea.hpp> +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/awt/LineEndFormat.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/awt/ImageScaleMode.hpp> + +#include <o3tl/safeint.hxx> +#include <controls/formattedcontrol.hxx> +#include <toolkit/controls/unocontrols.hxx> +#include <helper/property.hxx> +#include <toolkit/helper/macros.hxx> + +// for introspection +#include <awt/vclxwindows.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <algorithm> + +#include <helper/imagealign.hxx> +#include <helper/unopropertyarrayhelper.hxx> +#include <utility> + +using namespace css; +using namespace css::awt; +using namespace css::lang; +using namespace css::uno; +using ::com::sun::star::graphic::XGraphic; +using ::com::sun::star::uno::Reference; +using namespace ::toolkit; + +uno::Reference< graphic::XGraphic > +ImageHelper::getGraphicAndGraphicObjectFromURL_nothrow( uno::Reference< graphic::XGraphicObject >& xOutGraphicObj, const OUString& _rURL ) +{ + xOutGraphicObj = nullptr; + return ImageHelper::getGraphicFromURL_nothrow( _rURL ); +} + +css::uno::Reference< css::graphic::XGraphic > +ImageHelper::getGraphicFromURL_nothrow( const OUString& _rURL ) +{ + uno::Reference< graphic::XGraphic > xGraphic; + if ( _rURL.isEmpty() ) + return xGraphic; + + try + { + uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference< graphic::XGraphicProvider > xProvider( graphic::GraphicProvider::create(xContext) ); + xGraphic = xProvider->queryGraphic({ comphelper::makePropertyValue("URL", _rURL) }); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + } + + return xGraphic; +} + + +UnoControlEditModel::UnoControlEditModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXEdit>(); +} + +OUString UnoControlEditModel::getServiceName( ) +{ + return "stardiv.vcl.controlmodel.Edit"; +} + +uno::Any UnoControlEditModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + uno::Any aReturn; + + switch ( nPropId ) + { + case BASEPROPERTY_LINE_END_FORMAT: + aReturn <<= sal_Int16(awt::LineEndFormat::LINE_FEED); // LF + break; + case BASEPROPERTY_DEFAULTCONTROL: + aReturn <<= OUString( "stardiv.vcl.control.Edit" ); + break; + default: + aReturn = UnoControlModel::ImplGetDefaultValue( nPropId ); + break; + } + return aReturn; +} + +::cppu::IPropertyArrayHelper& UnoControlEditModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlEditModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlEditModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlEditModel"; +} + +css::uno::Sequence<OUString> UnoControlEditModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlEditModel", "stardiv.vcl.controlmodel.Edit" }; + return comphelper::concatSequences(UnoControlModel::getSupportedServiceNames(), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlEditModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlEditModel(context)); +} + + + +UnoEditControl::UnoEditControl() + :maTextListeners( *this ) + ,mnMaxTextLen( 0 ) + ,mbSetTextInPeer( false ) + ,mbSetMaxTextLenInPeer( false ) + ,mbHasTextProperty( false ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +uno::Any SAL_CALL UnoEditControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aReturn = UnoControlBase::queryAggregation( rType ); + if ( !aReturn.hasValue() ) + aReturn = UnoEditControl_Base::queryInterface( rType ); + return aReturn; +} + +uno::Any SAL_CALL UnoEditControl::queryInterface( const uno::Type & rType ) +{ + return UnoControlBase::queryInterface( rType ); +} + +void SAL_CALL UnoEditControl::acquire( ) noexcept +{ + UnoControlBase::acquire(); +} + +void SAL_CALL UnoEditControl::release( ) noexcept +{ + UnoControlBase::release(); +} + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( UnoEditControl, UnoControlBase, UnoEditControl_Base ) + +OUString UnoEditControl::GetComponentServiceName() const +{ + // by default, we want a simple edit field + OUString sName( "Edit" ); + + // but maybe we are to display multi-line text? + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_MULTILINE ) ); + bool b = bool(); + if ( ( aVal >>= b ) && b ) + sName = "MultiLineEdit"; + + return sName; +} + +sal_Bool SAL_CALL UnoEditControl::setModel(const uno::Reference< awt::XControlModel >& _rModel) +{ + bool bReturn = UnoControlBase::setModel( _rModel ); + mbHasTextProperty = ImplHasProperty( BASEPROPERTY_TEXT ); + return bReturn; +} + +void UnoEditControl::ImplSetPeerProperty( const OUString& rPropName, const uno::Any& rVal ) +{ + bool bDone = false; + if ( GetPropertyId( rPropName ) == BASEPROPERTY_TEXT ) + { + // #96986# use setText(), or text listener will not be called. + uno::Reference < awt::XTextComponent > xTextComponent( getPeer(), uno::UNO_QUERY ); + if ( xTextComponent.is() ) + { + OUString sText; + rVal >>= sText; + ImplCheckLocalize( sText ); + xTextComponent->setText( sText ); + bDone = true; + } + } + + if ( !bDone ) + UnoControlBase::ImplSetPeerProperty( rPropName, rVal ); +} + +void UnoEditControl::dispose() +{ + lang::EventObject aEvt( *this ); + maTextListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + +void UnoEditControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + { + xText->addTextListener( this ); + + if ( mbSetMaxTextLenInPeer ) + xText->setMaxTextLen( mnMaxTextLen ); + if ( mbSetTextInPeer ) + xText->setText( maText ); + } +} + +void UnoEditControl::textChanged(const awt::TextEvent& e) +{ + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + + if ( mbHasTextProperty ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TEXT ), uno::Any(xText->getText()), false ); + } + else + { + maText = xText->getText(); + } + + if ( maTextListeners.getLength() ) + maTextListeners.textChanged( e ); +} + +void UnoEditControl::addTextListener(const uno::Reference< awt::XTextListener > & l) +{ + // tdf#150974 some extensions pass null + if (!l) + { + SAL_WARN("toolkit", "null XTextListener"); + return; + } + maTextListeners.addInterface( l ); +} + +void UnoEditControl::removeTextListener(const uno::Reference< awt::XTextListener > & l) +{ + // tdf#150974 some extensions pass null + if (!l) + { + SAL_WARN("toolkit", "null XTextListener"); + return; + } + maTextListeners.removeInterface( l ); +} + +void UnoEditControl::setText( const OUString& aText ) +{ + if ( mbHasTextProperty ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TEXT ), uno::Any(aText), true ); + } + else + { + maText = aText; + mbSetTextInPeer = true; + uno::Reference < awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + xText->setText( maText ); + } + + // Setting the property to the VCLXWindow doesn't call textChanged + if ( maTextListeners.getLength() ) + { + awt::TextEvent aEvent; + aEvent.Source = *this; + maTextListeners.textChanged( aEvent ); + } +} + +namespace +{ + void lcl_normalize( awt::Selection& _rSel ) + { + if ( _rSel.Min > _rSel.Max ) + ::std::swap( _rSel.Min, _rSel.Max ); + } +} + +void UnoEditControl::insertText( const awt::Selection& rSel, const OUString& rNewText ) +{ + // normalize the selection - OUString::replaceAt has a strange behaviour if the min is greater than the max + awt::Selection aSelection( rSel ); + lcl_normalize( aSelection ); + + OUString aOldText = getText(); + if (aSelection.Min < 0 || aOldText.getLength() < aSelection.Max) + { + throw lang::IllegalArgumentException(); + } + + // preserve the selection resp. cursor position + awt::Selection aNewSelection( getSelection() ); +#ifdef ALSO_PRESERVE_COMPLETE_SELECTION + // (not sure - looks uglier ...) + sal_Int32 nDeletedCharacters = ( aSelection.Max - aSelection.Min ) - rNewText.getLength(); + if ( aNewSelection.Min > aSelection.Min ) + aNewSelection.Min -= nDeletedCharacters; + if ( aNewSelection.Max > aSelection.Max ) + aNewSelection.Max -= nDeletedCharacters; +#else + aNewSelection.Max = ::std::min( aNewSelection.Min, aNewSelection.Max ) + rNewText.getLength(); + aNewSelection.Min = aNewSelection.Max; +#endif + + OUString aNewText = aOldText.replaceAt( aSelection.Min, aSelection.Max - aSelection.Min, rNewText ); + setText( aNewText ); + + setSelection( aNewSelection ); +} + +OUString UnoEditControl::getText() +{ + OUString aText = maText; + + if ( mbHasTextProperty ) + aText = ImplGetPropertyValue_UString( BASEPROPERTY_TEXT ); + else + { + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + aText = xText->getText(); + } + + return aText; +} + +OUString UnoEditControl::getSelectedText() +{ + OUString sSelected; + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + sSelected = xText->getSelectedText(); + + return sSelected; +} + +void UnoEditControl::setSelection( const awt::Selection& aSelection ) +{ + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + xText->setSelection( aSelection ); +} + +awt::Selection UnoEditControl::getSelection() +{ + awt::Selection aSel; + uno::Reference< awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + aSel = xText->getSelection(); + return aSel; +} + +sal_Bool UnoEditControl::isEditable() +{ + return !ImplGetPropertyValue_BOOL( BASEPROPERTY_READONLY ); +} + +void UnoEditControl::setEditable( sal_Bool bEditable ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_READONLY ), uno::Any(!bEditable), true ); +} + +sal_Int16 UnoEditControl::getMaxTextLen() +{ + sal_Int16 nMaxLen = mnMaxTextLen; + + if ( ImplHasProperty( BASEPROPERTY_MAXTEXTLEN ) ) + nMaxLen = ImplGetPropertyValue_INT16( BASEPROPERTY_MAXTEXTLEN ); + + return nMaxLen; +} + +void UnoEditControl::setMaxTextLen( sal_Int16 nLen ) +{ + if ( ImplHasProperty( BASEPROPERTY_MAXTEXTLEN) ) + { + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_MAXTEXTLEN ), uno::Any(nLen), true ); + } + else + { + mnMaxTextLen = nLen; + mbSetMaxTextLenInPeer = true; + uno::Reference < awt::XTextComponent > xText( getPeer(), uno::UNO_QUERY ); + if ( xText.is() ) + xText->setMaxTextLen( mnMaxTextLen ); + } +} + +awt::Size UnoEditControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoEditControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoEditControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +awt::Size UnoEditControl::getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + return Impl_getMinimumSize( nCols, nLines ); +} + +void UnoEditControl::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + Impl_getColumnsAndLines( nCols, nLines ); +} + +OUString UnoEditControl::getImplementationName( ) +{ + return "stardiv.Toolkit.UnoEditControl"; +} + +uno::Sequence< OUString > UnoEditControl::getSupportedServiceNames() +{ + css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlEdit", "stardiv.vcl.control.Edit" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames( ), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoEditControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoEditControl()); +} + + + +UnoControlFileControlModel::UnoControlFileControlModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_ALIGN ); + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_READONLY ); + ImplRegisterProperty( BASEPROPERTY_TABSTOP ); + ImplRegisterProperty( BASEPROPERTY_TEXT ); + ImplRegisterProperty( BASEPROPERTY_VERTICALALIGN ); + ImplRegisterProperty( BASEPROPERTY_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_CONTEXT_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_HIDEINACTIVESELECTION ); +} + +OUString UnoControlFileControlModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.FileControl"; +} + +uno::Any UnoControlFileControlModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.FileControl" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlFileControlModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlFileControlModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlFileControlModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlFileControlModel"; +} + +css::uno::Sequence<OUString> +UnoControlFileControlModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFileControlModel", "stardiv.vcl.controlmodel.FileControl" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlFileControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlFileControlModel(context)); +} + + + +UnoFileControl::UnoFileControl() +{ +} + +OUString UnoFileControl::GetComponentServiceName() const +{ + return "filecontrol"; +} + +OUString UnoFileControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoFileControl"; +} + +css::uno::Sequence<OUString> UnoFileControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFileControl", "stardiv.vcl.control.FileControl" }; + return comphelper::concatSequences( UnoEditControl::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFileControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFileControl()); +} + + + +uno::Any GraphicControlModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_GRAPHIC ) + return uno::Any( uno::Reference< graphic::XGraphic >() ); + + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +void GraphicControlModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const css::uno::Any& rValue ) +{ + UnoControlModel::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + + // - ImageAlign and ImagePosition need to correspond to each other + // - Graphic and ImageURL need to correspond to each other + try + { + switch ( nHandle ) + { + case BASEPROPERTY_IMAGEURL: + if ( !mbAdjustingGraphic && ImplHasProperty( BASEPROPERTY_GRAPHIC ) ) + { + mbAdjustingGraphic = true; + OUString sImageURL; + OSL_VERIFY( rValue >>= sImageURL ); + setDependentFastPropertyValue( rGuard, BASEPROPERTY_GRAPHIC, uno::Any( ImageHelper::getGraphicFromURL_nothrow( sImageURL ) ) ); + mbAdjustingGraphic = false; + } + break; + + case BASEPROPERTY_GRAPHIC: + if ( !mbAdjustingGraphic && ImplHasProperty( BASEPROPERTY_IMAGEURL ) ) + { + mbAdjustingGraphic = true; + setDependentFastPropertyValue( rGuard, BASEPROPERTY_IMAGEURL, uno::Any( OUString() ) ); + mbAdjustingGraphic = false; + } + break; + + case BASEPROPERTY_IMAGEALIGN: + if ( !mbAdjustingImagePosition && ImplHasProperty( BASEPROPERTY_IMAGEPOSITION ) ) + { + mbAdjustingImagePosition = true; + sal_Int16 nUNOValue = 0; + OSL_VERIFY( rValue >>= nUNOValue ); + setDependentFastPropertyValue( rGuard, BASEPROPERTY_IMAGEPOSITION, uno::Any( getExtendedImagePosition( nUNOValue ) ) ); + mbAdjustingImagePosition = false; + } + break; + case BASEPROPERTY_IMAGEPOSITION: + if ( !mbAdjustingImagePosition && ImplHasProperty( BASEPROPERTY_IMAGEALIGN ) ) + { + mbAdjustingImagePosition = true; + sal_Int16 nUNOValue = 0; + OSL_VERIFY( rValue >>= nUNOValue ); + setDependentFastPropertyValue( rGuard, BASEPROPERTY_IMAGEALIGN, uno::Any( getCompatibleImageAlign( translateImagePosition( nUNOValue ) ) ) ); + mbAdjustingImagePosition = false; + } + break; + } + } + catch( const css::uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("toolkit.controls"); + OSL_FAIL( "GraphicControlModel::setFastPropertyValue_NoBroadcast: caught an exception while aligning the ImagePosition/ImageAlign properties!" ); + mbAdjustingImagePosition = false; + } +} + + + +UnoControlButtonModel::UnoControlButtonModel( const Reference< XComponentContext >& rxContext ) + :GraphicControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXButton>(); + + osl_atomic_increment( &m_refCount ); + { + std::unique_lock aGuard(m_aMutex); + setFastPropertyValue_NoBroadcast( aGuard, BASEPROPERTY_IMAGEPOSITION, ImplGetDefaultValue( BASEPROPERTY_IMAGEPOSITION ) ); + // this ensures that our ImagePosition is consistent with our ImageAlign property (since both + // defaults are not per se consistent), since both are coupled in setFastPropertyValue_NoBroadcast + } + osl_atomic_decrement( &m_refCount ); +} + +OUString UnoControlButtonModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.Button"; +} + +uno::Any UnoControlButtonModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString( "stardiv.vcl.control.Button" ) ); + case BASEPROPERTY_TOGGLE: + return uno::Any( false ); + case BASEPROPERTY_ALIGN: + return uno::Any( sal_Int16(PROPERTY_ALIGN_CENTER) ); + case BASEPROPERTY_FOCUSONCLICK: + return uno::Any( true ); + } + + return GraphicControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlButtonModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlButtonModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlButtonModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlButtonModel"; +} + +css::uno::Sequence<OUString> UnoControlButtonModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlButtonModel", "stardiv.vcl.controlmodel.Button" }; + return comphelper::concatSequences( GraphicControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlButtonModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlButtonModel(context)); +} + + + +UnoButtonControl::UnoButtonControl() + :maActionListeners( *this ) + ,maItemListeners( *this ) +{ + maComponentInfos.nWidth = 50; + maComponentInfos.nHeight = 14; +} + +OUString UnoButtonControl::GetComponentServiceName() const +{ + OUString aName( "pushbutton" ); + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_PUSHBUTTONTYPE ) ); + sal_Int16 n = sal_Int16(); + if ( ( aVal >>= n ) && n ) + { + // Use PushButtonType later when available... + switch ( n ) + { + case 1 /*PushButtonType::OK*/: aName = "okbutton"; + break; + case 2 /*PushButtonType::CANCEL*/: aName = "cancelbutton"; + break; + case 3 /*PushButtonType::HELP*/: aName = "helpbutton"; + break; + default: + { + OSL_FAIL( "Unknown Button Type!" ); + } + } + } + return aName; +} + +void UnoButtonControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maActionListeners.disposeAndClear( aEvt ); + maItemListeners.disposeAndClear( aEvt ); + UnoControlBase::dispose(); +} + +void UnoButtonControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( maActionCommand ); + if ( maActionListeners.getLength() ) + xButton->addActionListener( &maActionListeners ); + + uno::Reference< XToggleButton > xPushButton( getPeer(), uno::UNO_QUERY ); + if ( xPushButton.is() ) + xPushButton->addItemListener( this ); +} + +void UnoButtonControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + // tdf#150974 some extensions pass null + if (!l) + { + SAL_WARN("toolkit", "null XActionListener"); + return; + } + + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->addActionListener( &maActionListeners ); + } +} + +void UnoButtonControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + // tdf#150974 some extensions pass null + if (!l) + { + SAL_WARN("toolkit", "null XActionListener"); + return; + } + + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +void UnoButtonControl::addItemListener(const uno::Reference< awt::XItemListener > & l) +{ + maItemListeners.addInterface( l ); +} + +void UnoButtonControl::removeItemListener(const uno::Reference< awt::XItemListener > & l) +{ + maItemListeners.removeInterface( l ); +} + +void SAL_CALL UnoButtonControl::disposing( const lang::EventObject& Source ) +{ + UnoControlBase::disposing( Source ); +} + +void SAL_CALL UnoButtonControl::itemStateChanged( const awt::ItemEvent& rEvent ) +{ + // forward to model + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ), uno::Any(static_cast<sal_Int16>(rEvent.Selected)), false ); + + // multiplex + ItemEvent aEvent( rEvent ); + aEvent.Source = *this; + maItemListeners.itemStateChanged( aEvent ); +} + +void UnoButtonControl::setLabel( const OUString& rLabel ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ), uno::Any(rLabel), true ); +} + +void UnoButtonControl::setActionCommand( const OUString& rCommand ) +{ + maActionCommand = rCommand; + if ( getPeer().is() ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( rCommand ); + } +} + +awt::Size UnoButtonControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoButtonControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoButtonControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +OUString UnoButtonControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoButtonControl"; +} + +css::uno::Sequence<OUString> UnoButtonControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlButton", "stardiv.vcl.control.Button" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoButtonControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoButtonControl()); +} + + + +UnoControlImageControlModel::UnoControlImageControlModel( const Reference< XComponentContext >& rxContext ) + :GraphicControlModel( rxContext ) + ,mbAdjustingImageScaleMode( false ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXImageControl>(); +} + +OUString UnoControlImageControlModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.ImageControl"; +} + +OUString UnoControlImageControlModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlImageControlModel"; +} + +css::uno::Sequence<OUString> +UnoControlImageControlModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { + "com.sun.star.awt.UnoControlImageButtonModel", + "com.sun.star.awt.UnoControlImageControlModel", + "stardiv.vcl.controlmodel.ImageButton", + "stardiv.vcl.controlmodel.ImageControl" + }; + return comphelper::concatSequences( GraphicControlModel::getSupportedServiceNames(), vals); +} + +uno::Any UnoControlImageControlModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + return uno::Any( OUString( "stardiv.vcl.control.ImageControl" ) ); + + if ( nPropId == BASEPROPERTY_IMAGE_SCALE_MODE ) + return Any( awt::ImageScaleMode::ANISOTROPIC ); + + return GraphicControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlImageControlModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlImageControlModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +void UnoControlImageControlModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 _nHandle, const css::uno::Any& _rValue ) +{ + GraphicControlModel::setFastPropertyValue_NoBroadcast( rGuard, _nHandle, _rValue ); + + // ScaleImage is an older (and less powerful) version of ScaleMode, but keep both in sync as far as possible + try + { + switch ( _nHandle ) + { + case BASEPROPERTY_IMAGE_SCALE_MODE: + if ( !mbAdjustingImageScaleMode && ImplHasProperty( BASEPROPERTY_SCALEIMAGE ) ) + { + mbAdjustingImageScaleMode = true; + sal_Int16 nScaleMode( awt::ImageScaleMode::ANISOTROPIC ); + OSL_VERIFY( _rValue >>= nScaleMode ); + setDependentFastPropertyValue( rGuard, BASEPROPERTY_SCALEIMAGE, uno::Any( nScaleMode != awt::ImageScaleMode::NONE ) ); + mbAdjustingImageScaleMode = false; + } + break; + case BASEPROPERTY_SCALEIMAGE: + if ( !mbAdjustingImageScaleMode && ImplHasProperty( BASEPROPERTY_IMAGE_SCALE_MODE ) ) + { + mbAdjustingImageScaleMode = true; + bool bScale = true; + OSL_VERIFY( _rValue >>= bScale ); + setDependentFastPropertyValue( rGuard, BASEPROPERTY_IMAGE_SCALE_MODE, uno::Any( bScale ? awt::ImageScaleMode::ANISOTROPIC : awt::ImageScaleMode::NONE ) ); + mbAdjustingImageScaleMode = false; + } + break; + } + } + catch( const Exception& ) + { + mbAdjustingImageScaleMode = false; + throw; + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlImageControlModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlImageControlModel(context)); +} + + + +UnoImageControlControl::UnoImageControlControl() + :maActionListeners( *this ) +{ + // TODO: Where should I look for defaults? + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 100; +} + +OUString UnoImageControlControl::GetComponentServiceName() const +{ + return "fixedimage"; +} + +void UnoImageControlControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maActionListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + +sal_Bool UnoImageControlControl::isTransparent() +{ + return true; +} + +awt::Size UnoImageControlControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoImageControlControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoImageControlControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +OUString UnoImageControlControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoImageControlControl"; +} + +css::uno::Sequence<OUString> UnoImageControlControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { + "com.sun.star.awt.UnoControlImageButton", + "com.sun.star.awt.UnoControlImageControl", + "stardiv.vcl.control.ImageButton", + "stardiv.vcl.control.ImageControl" + }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoImageControlControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoImageControlControl()); +} + + + +UnoControlRadioButtonModel::UnoControlRadioButtonModel( const Reference< XComponentContext >& rxContext ) + :GraphicControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXRadioButton>(); +} + +OUString UnoControlRadioButtonModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.RadioButton"; +} + +uno::Any UnoControlRadioButtonModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString( "stardiv.vcl.control.RadioButton" ) ); + + case BASEPROPERTY_VISUALEFFECT: + return uno::Any( sal_Int16(awt::VisualEffect::LOOK3D) ); + } + + return GraphicControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlRadioButtonModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlRadioButtonModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlRadioButtonModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlRadioButtonModel"; +} + +css::uno::Sequence<OUString> +UnoControlRadioButtonModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlRadioButtonModel", "stardiv.vcl.controlmodel.RadioButton" }; + return comphelper::concatSequences( GraphicControlModel::getSupportedServiceNames(), vals); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlRadioButtonModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlRadioButtonModel(context)); +} + + + +UnoRadioButtonControl::UnoRadioButtonControl() + :maItemListeners( *this ) + ,maActionListeners( *this ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoRadioButtonControl::GetComponentServiceName() const +{ + return "radiobutton"; +} + +void UnoRadioButtonControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maItemListeners.disposeAndClear( aEvt ); + UnoControlBase::dispose(); +} + + +sal_Bool UnoRadioButtonControl::isTransparent() +{ + return true; +} + +void UnoRadioButtonControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XRadioButton > xRadioButton( getPeer(), uno::UNO_QUERY ); + xRadioButton->addItemListener( this ); + + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( maActionCommand ); + if ( maActionListeners.getLength() ) + xButton->addActionListener( &maActionListeners ); + + // as default, set the "AutoToggle" to true + // (it is set to false in VCLXToolkit::ImplCreateWindow because of #87254#, but we want to + // have it enabled by default because of 85071) + uno::Reference< awt::XVclWindowPeer > xVclWindowPeer( getPeer(), uno::UNO_QUERY ); + if ( xVclWindowPeer.is() ) + xVclWindowPeer->setProperty( GetPropertyName( BASEPROPERTY_AUTOTOGGLE ), css::uno::Any(true) ); +} + +void UnoRadioButtonControl::addItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.addInterface( l ); +} + +void UnoRadioButtonControl::removeItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.removeInterface( l ); +} + +void UnoRadioButtonControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->addActionListener( &maActionListeners ); + } +} + +void UnoRadioButtonControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +void UnoRadioButtonControl::setLabel( const OUString& rLabel ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ), uno::Any(rLabel), true ); +} + +void UnoRadioButtonControl::setActionCommand( const OUString& rCommand ) +{ + maActionCommand = rCommand; + if ( getPeer().is() ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( rCommand ); + } +} + +void UnoRadioButtonControl::setState( sal_Bool bOn ) +{ + sal_Int16 nState = bOn ? 1 : 0; + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ), uno::Any(nState), true ); +} + +sal_Bool UnoRadioButtonControl::getState() +{ + sal_Int16 nState = 0; + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ) ); + aVal >>= nState; + return nState != 0; +} + +void UnoRadioButtonControl::itemStateChanged( const awt::ItemEvent& rEvent ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ), uno::Any(static_cast<sal_Int16>(rEvent.Selected)), false ); + + // compatibility: + // in OOo 1.0.x, when the user clicked a radio button in a group of buttons, this resulted + // in _one_ itemStateChanged call for exactly the radio button which's state changed from + // "0" to "1". + // Nowadays, since the listener handling changed a lot towards 1.1 (the VCLXWindow reacts on more + // basic events from the VCL-windows, not anymore on the Link-based events like in 1.0.x), this + // isn't the case anymore: For instance, this method here gets called for the radio button + // which is being implicitly _de_selected, too. This is pretty bad for compatibility. + // Thus, we suppress all events with a new state other than "1". This is unlogical, and weird, when looking + // from a pure API perspective, but it's _compatible_ with older product versions, and this is + // all which matters here. + // #i14703# + if ( 1 == rEvent.Selected ) + { + if ( maItemListeners.getLength() ) + maItemListeners.itemStateChanged( rEvent ); + } + // note that speaking strictly, this is wrong: When in 1.0.x, the user would have de-selected + // a radio button _without_ selecting another one, this would have caused a notification. + // With the change done here, this today won't cause a notification anymore. + + // Fortunately, it's not possible for the user to de-select a radio button without selecting another on, + // at least not via the regular UI. It _would_ be possible via the Accessibility API, which + // counts as "user input", too. But in 1.0.x, there was no Accessibility API, so there is nothing + // to be inconsistent with. +} + +awt::Size UnoRadioButtonControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoRadioButtonControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoRadioButtonControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +OUString UnoRadioButtonControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoRadioButtonControl"; +} + +css::uno::Sequence<OUString> UnoRadioButtonControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlRadioButton", "stardiv.vcl.control.RadioButton" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoRadioButtonControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoRadioButtonControl()); +} + + + +UnoControlCheckBoxModel::UnoControlCheckBoxModel( const Reference< XComponentContext >& rxContext ) + :GraphicControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXCheckBox>(); +} + +OUString UnoControlCheckBoxModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.CheckBox"; +} + +uno::Any UnoControlCheckBoxModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + switch ( nPropId ) + { + case BASEPROPERTY_DEFAULTCONTROL: + return uno::Any( OUString( "stardiv.vcl.control.CheckBox" ) ); + + case BASEPROPERTY_VISUALEFFECT: + return uno::Any( sal_Int16(awt::VisualEffect::LOOK3D) ); + } + + return GraphicControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlCheckBoxModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlCheckBoxModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlCheckBoxModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlCheckBoxModel"; +} + +css::uno::Sequence<OUString> UnoControlCheckBoxModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlCheckBoxModel", "stardiv.vcl.controlmodel.CheckBox" }; + return comphelper::concatSequences( GraphicControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlCheckBoxModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlCheckBoxModel(context)); +} + + + +UnoCheckBoxControl::UnoCheckBoxControl() + :maItemListeners( *this ), maActionListeners( *this ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoCheckBoxControl::GetComponentServiceName() const +{ + return "checkbox"; +} + +void UnoCheckBoxControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maItemListeners.disposeAndClear( aEvt ); + UnoControlBase::dispose(); +} + +sal_Bool UnoCheckBoxControl::isTransparent() +{ + return true; +} + +void UnoCheckBoxControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XCheckBox > xCheckBox( getPeer(), uno::UNO_QUERY ); + xCheckBox->addItemListener( this ); + + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( maActionCommand ); + if ( maActionListeners.getLength() ) + xButton->addActionListener( &maActionListeners ); +} + +void UnoCheckBoxControl::addItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.addInterface( l ); +} + +void UnoCheckBoxControl::removeItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.removeInterface( l ); +} + +void UnoCheckBoxControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->addActionListener( &maActionListeners ); + } +} + +void UnoCheckBoxControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +void UnoCheckBoxControl::setActionCommand( const OUString& rCommand ) +{ + maActionCommand = rCommand; + if ( getPeer().is() ) + { + uno::Reference < awt::XButton > xButton( getPeer(), uno::UNO_QUERY ); + xButton->setActionCommand( rCommand ); + } +} + + +void UnoCheckBoxControl::setLabel( const OUString& rLabel ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ), uno::Any(rLabel), true ); +} + +void UnoCheckBoxControl::setState( sal_Int16 n ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ), uno::Any(n), true ); +} + +sal_Int16 UnoCheckBoxControl::getState() +{ + sal_Int16 nState = 0; + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ) ); + aVal >>= nState; + return nState; +} + +void UnoCheckBoxControl::enableTriState( sal_Bool b ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TRISTATE ), uno::Any(b), true ); +} + +void UnoCheckBoxControl::itemStateChanged( const awt::ItemEvent& rEvent ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STATE ), uno::Any(static_cast<sal_Int16>(rEvent.Selected)), false ); + + if ( maItemListeners.getLength() ) + maItemListeners.itemStateChanged( rEvent ); +} + +awt::Size UnoCheckBoxControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoCheckBoxControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoCheckBoxControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +OUString UnoCheckBoxControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoCheckBoxControl"; +} + +css::uno::Sequence<OUString> UnoCheckBoxControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlCheckBox", "stardiv.vcl.control.CheckBox" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoCheckBoxControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoCheckBoxControl()); +} + + + +UnoControlFixedHyperlinkModel::UnoControlFixedHyperlinkModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXFixedHyperlink>(); +} + +OUString UnoControlFixedHyperlinkModel::getServiceName() +{ + return "com.sun.star.awt.UnoControlFixedHyperlinkModel"; +} + +uno::Any UnoControlFixedHyperlinkModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "com.sun.star.awt.UnoControlFixedHyperlink" ) ); + } + else if ( nPropId == BASEPROPERTY_BORDER ) + { + return uno::Any(sal_Int16(0)); + } + else if ( nPropId == BASEPROPERTY_URL ) + { + return uno::Any( OUString() ); + } + + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlFixedHyperlinkModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlFixedHyperlinkModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlFixedHyperlinkModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlFixedHyperlinkModel(context)); +} + + + +UnoFixedHyperlinkControl::UnoFixedHyperlinkControl() + :maActionListeners( *this ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoFixedHyperlinkControl::GetComponentServiceName() const +{ + return "fixedhyperlink"; +} + +// uno::XInterface +uno::Any UnoFixedHyperlinkControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XFixedHyperlink* >(this), + static_cast< awt::XLayoutConstrains* >(this) ); + return (aRet.hasValue() ? aRet : UnoControlBase::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoFixedHyperlinkControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoFixedHyperlinkControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XFixedHyperlink>::get(), + cppu::UnoType<awt::XLayoutConstrains>::get(), + UnoControlBase::getTypes() + ); + return aTypeList.getTypes(); +} + +sal_Bool UnoFixedHyperlinkControl::isTransparent() +{ + return true; +} + +void UnoFixedHyperlinkControl::setText( const OUString& Text ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ), uno::Any(Text), true ); +} + +OUString UnoFixedHyperlinkControl::getText() +{ + return ImplGetPropertyValue_UString( BASEPROPERTY_LABEL ); +} + +void UnoFixedHyperlinkControl::setURL( const OUString& URL ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_URL ), uno::Any(URL), true ); +} + +OUString UnoFixedHyperlinkControl::getURL( ) +{ + return ImplGetPropertyValue_UString( BASEPROPERTY_URL ); +} + +void UnoFixedHyperlinkControl::setAlignment( sal_Int16 nAlign ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_ALIGN ), uno::Any(nAlign), true ); +} + +sal_Int16 UnoFixedHyperlinkControl::getAlignment() +{ + sal_Int16 nAlign = 0; + if ( mxModel.is() ) + { + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_ALIGN ) ); + aVal >>= nAlign; + } + return nAlign; +} + +awt::Size UnoFixedHyperlinkControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoFixedHyperlinkControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoFixedHyperlinkControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +void UnoFixedHyperlinkControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maActionListeners.disposeAndClear( aEvt ); + UnoControlBase::dispose(); +} + +void UnoFixedHyperlinkControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControlBase::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XFixedHyperlink > xFixedHyperlink( getPeer(), uno::UNO_QUERY ); + if ( maActionListeners.getLength() ) + xFixedHyperlink->addActionListener( &maActionListeners ); +} + +void UnoFixedHyperlinkControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XFixedHyperlink > xFixedHyperlink( getPeer(), uno::UNO_QUERY ); + xFixedHyperlink->addActionListener( &maActionListeners ); + } +} + +void UnoFixedHyperlinkControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XFixedHyperlink > xFixedHyperlink( getPeer(), uno::UNO_QUERY ); + xFixedHyperlink->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFixedHyperlinkControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFixedHyperlinkControl()); +} + + + +UnoControlFixedTextModel::UnoControlFixedTextModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXFixedText>(); +} + +OUString UnoControlFixedTextModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.FixedText"; +} + +uno::Any UnoControlFixedTextModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.FixedText" ) ); + } + else if ( nPropId == BASEPROPERTY_BORDER ) + { + return uno::Any(sal_Int16(0)); + } + + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlFixedTextModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlFixedTextModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlFixedTextModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlFixedTextModel"; +} + +css::uno::Sequence<OUString> +UnoControlFixedTextModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFixedTextModel", "stardiv.vcl.controlmodel.FixedText" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlFixedTextModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlFixedTextModel(context)); +} + + + +UnoFixedTextControl::UnoFixedTextControl() +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoFixedTextControl::GetComponentServiceName() const +{ + return "fixedtext"; +} + +// uno::XInterface +uno::Any UnoFixedTextControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XFixedText* >(this), + static_cast< awt::XLayoutConstrains* >(this) ); + return (aRet.hasValue() ? aRet : UnoControlBase::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoFixedTextControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoFixedTextControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XFixedText>::get(), + cppu::UnoType<awt::XLayoutConstrains>::get(), + UnoControlBase::getTypes() + ); + return aTypeList.getTypes(); +} + +sal_Bool UnoFixedTextControl::isTransparent() +{ + return true; +} + +void UnoFixedTextControl::setText( const OUString& Text ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LABEL ), uno::Any(Text), true ); +} + +OUString UnoFixedTextControl::getText() +{ + return ImplGetPropertyValue_UString( BASEPROPERTY_LABEL ); +} + +void UnoFixedTextControl::setAlignment( sal_Int16 nAlign ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_ALIGN ), uno::Any(nAlign), true ); +} + +sal_Int16 UnoFixedTextControl::getAlignment() +{ + sal_Int16 nAlign = 0; + if ( mxModel.is() ) + { + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_ALIGN ) ); + aVal >>= nAlign; + } + return nAlign; +} + +awt::Size UnoFixedTextControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoFixedTextControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoFixedTextControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +OUString UnoFixedTextControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoFixedTextControl"; +} + +css::uno::Sequence<OUString> UnoFixedTextControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFixedText", "stardiv.vcl.control.FixedText" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFixedTextControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFixedTextControl()); +} + + + +UnoControlGroupBoxModel::UnoControlGroupBoxModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_LABEL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_WRITING_MODE ); + ImplRegisterProperty( BASEPROPERTY_CONTEXT_WRITING_MODE ); +} + +OUString UnoControlGroupBoxModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.GroupBox"; +} + +uno::Any UnoControlGroupBoxModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any(OUString( "stardiv.vcl.control.GroupBox" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlGroupBoxModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlGroupBoxModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlGroupBoxModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlGroupBoxModel"; +} + +css::uno::Sequence<OUString> UnoControlGroupBoxModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlGroupBoxModel", "stardiv.vcl.controlmodel.GroupBox" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlGroupBoxModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlGroupBoxModel(context)); +} + + + +UnoGroupBoxControl::UnoGroupBoxControl() +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 100; +} + +OUString UnoGroupBoxControl::GetComponentServiceName() const +{ + return "groupbox"; +} + +sal_Bool UnoGroupBoxControl::isTransparent() +{ + return true; +} + +OUString UnoGroupBoxControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoGroupBoxControl"; +} + +css::uno::Sequence<OUString> UnoGroupBoxControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlGroupBox", "stardiv.vcl.control.GroupBox" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoGroupBoxControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoGroupBoxControl()); +} + + +// = UnoControlListBoxModel_Data + +namespace { + +struct ListItem +{ + OUString ItemText; + OUString ItemImageURL; + Any ItemData; + + ListItem() + { + } + + explicit ListItem( OUString i_ItemText ) + :ItemText(std::move( i_ItemText )) + { + } +}; + +} + +typedef beans::Pair< OUString, OUString > UnoListItem; + +namespace { + +struct StripItemData +{ + UnoListItem operator()( const ListItem& i_rItem ) + { + return UnoListItem( i_rItem.ItemText, i_rItem.ItemImageURL ); + } +}; + +} + +struct UnoControlListBoxModel_Data +{ + explicit UnoControlListBoxModel_Data( UnoControlListBoxModel& i_rAntiImpl ) + :m_bSettingLegacyProperty( false ) + ,m_rAntiImpl( i_rAntiImpl ) + { + } + + sal_Int32 getItemCount() const { return sal_Int32( m_aListItems.size() ); } + + const ListItem& getItem( const sal_Int32 i_nIndex ) const + { + if ( ( i_nIndex < 0 ) || ( o3tl::make_unsigned(i_nIndex) >= m_aListItems.size() ) ) + throw IndexOutOfBoundsException( OUString(), m_rAntiImpl ); + return m_aListItems[ i_nIndex ]; + } + + ListItem& getItem( const sal_Int32 i_nIndex ) + { + return const_cast< ListItem& >( static_cast< const UnoControlListBoxModel_Data* >( this )->getItem( i_nIndex ) ); + } + + ListItem& insertItem( const sal_Int32 i_nIndex ) + { + if ( ( i_nIndex < 0 ) || ( o3tl::make_unsigned(i_nIndex) > m_aListItems.size() ) ) + throw IndexOutOfBoundsException( OUString(), m_rAntiImpl ); + return *m_aListItems.insert( m_aListItems.begin() + i_nIndex, ListItem() ); + } + + Sequence< UnoListItem > getAllItems() const + { + Sequence< UnoListItem > aItems( sal_Int32( m_aListItems.size() ) ); + ::std::transform( m_aListItems.begin(), m_aListItems.end(), aItems.getArray(), StripItemData() ); + return aItems; + } + + void copyItems( const UnoControlListBoxModel_Data& i_copySource ) + { + m_aListItems = i_copySource.m_aListItems; + } + + void setAllItems( ::std::vector< ListItem >&& i_rItems ) + { + m_aListItems = std::move(i_rItems); + } + + void removeItem( const sal_Int32 i_nIndex ) + { + if ( ( i_nIndex < 0 ) || ( o3tl::make_unsigned(i_nIndex) >= m_aListItems.size() ) ) + throw IndexOutOfBoundsException( OUString(), m_rAntiImpl ); + m_aListItems.erase( m_aListItems.begin() + i_nIndex ); + } + + void removeAllItems() + { + std::vector<ListItem>().swap(m_aListItems); + } + +public: + bool m_bSettingLegacyProperty; + +private: + UnoControlListBoxModel& m_rAntiImpl; + ::std::vector< ListItem > m_aListItems; +}; + + +// = UnoControlListBoxModel + + +UnoControlListBoxModel::UnoControlListBoxModel( const Reference< XComponentContext >& rxContext, ConstructorMode const i_mode ) + :UnoControlListBoxModel_Base( rxContext ) + ,m_xData( new UnoControlListBoxModel_Data( *this ) ) +{ + if ( i_mode == ConstructDefault ) + { + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXListBox>(); + } +} + +UnoControlListBoxModel::UnoControlListBoxModel( const UnoControlListBoxModel& i_rSource ) + :UnoControlListBoxModel_Base( i_rSource ) + ,m_xData( new UnoControlListBoxModel_Data( *this ) ) +{ + m_xData->copyItems( *i_rSource.m_xData ); +} +UnoControlListBoxModel::~UnoControlListBoxModel() +{ +} + +OUString UnoControlListBoxModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlListBoxModel"; +} + +css::uno::Sequence<OUString> UnoControlListBoxModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlListBoxModel", "stardiv.vcl.controlmodel.ListBox" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +OUString UnoControlListBoxModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.ListBox"; +} + + +uno::Any UnoControlListBoxModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.ListBox" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + + +::cppu::IPropertyArrayHelper& UnoControlListBoxModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlListBoxModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + + +namespace +{ + struct CreateListItem + { + ListItem operator()( const OUString& i_rItemText ) + { + return ListItem( i_rItemText ); + } + }; +} + + +void UnoControlListBoxModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const uno::Any& rValue ) +{ + UnoControlModel::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + + if ( nHandle != BASEPROPERTY_STRINGITEMLIST ) + return; + + // reset selection + uno::Sequence<sal_Int16> aSeq; + setDependentFastPropertyValue( rGuard, BASEPROPERTY_SELECTEDITEMS, uno::Any(aSeq) ); + + if ( m_xData->m_bSettingLegacyProperty ) + return; + + // synchronize the legacy StringItemList property with our list items + Sequence< OUString > aStringItemList; + Any aPropValue; + getFastPropertyValue( rGuard, aPropValue, BASEPROPERTY_STRINGITEMLIST ); + OSL_VERIFY( aPropValue >>= aStringItemList ); + + ::std::vector< ListItem > aItems( aStringItemList.getLength() ); + ::std::transform( + std::cbegin(aStringItemList), + std::cend(aStringItemList), + aItems.begin(), + CreateListItem() + ); + m_xData->setAllItems( std::move(aItems) ); + + // since an XItemListListener does not have a "all items modified" or some such method, + // we simulate this by notifying removal of all items, followed by insertion of all new + // items + lang::EventObject aEvent; + aEvent.Source = *this; + m_aItemListListeners.notifyEach( rGuard, &XItemListListener::itemListChanged, aEvent ); + // TODO: OPropertySetHelper calls into this method with the mutex locked ... + // which is wrong for the above notifications ... +} + + +void UnoControlListBoxModel::ImplNormalizePropertySequence( const sal_Int32 _nCount, sal_Int32* _pHandles, + uno::Any* _pValues, sal_Int32* _pValidHandles ) const +{ + // dependencies we know: + // BASEPROPERTY_STRINGITEMLIST->BASEPROPERTY_SELECTEDITEMS + ImplEnsureHandleOrder( _nCount, _pHandles, _pValues, BASEPROPERTY_STRINGITEMLIST, BASEPROPERTY_SELECTEDITEMS ); + // BASEPROPERTY_STRINGITEMLIST->BASEPROPERTY_TYPEDITEMLIST + ImplEnsureHandleOrder( _nCount, _pHandles, _pValues, BASEPROPERTY_STRINGITEMLIST, BASEPROPERTY_TYPEDITEMLIST ); + + UnoControlModel::ImplNormalizePropertySequence( _nCount, _pHandles, _pValues, _pValidHandles ); +} + + +::sal_Int32 SAL_CALL UnoControlListBoxModel::getItemCount() +{ + std::unique_lock aGuard( m_aMutex ); + return m_xData->getItemCount(); +} + + +void SAL_CALL UnoControlListBoxModel::insertItem( ::sal_Int32 i_nPosition, const OUString& i_rItemText, const OUString& i_rItemImageURL ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->insertItem( i_nPosition ) ); + rItem.ItemText = i_rItemText; + rItem.ItemImageURL = i_rItemImageURL; + + impl_handleInsert( aGuard, i_nPosition, i_rItemText, i_rItemImageURL ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::insertItemText( ::sal_Int32 i_nPosition, const OUString& i_rItemText ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->insertItem( i_nPosition ) ); + rItem.ItemText = i_rItemText; + + impl_handleInsert( aGuard, i_nPosition, i_rItemText, ::std::optional< OUString >() ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::insertItemImage( ::sal_Int32 i_nPosition, const OUString& i_rItemImageURL ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->insertItem( i_nPosition ) ); + rItem.ItemImageURL = i_rItemImageURL; + + impl_handleInsert( aGuard, i_nPosition, ::std::optional< OUString >(), i_rItemImageURL ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::removeItem( ::sal_Int32 i_nPosition ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + m_xData->removeItem( i_nPosition ); + + impl_handleRemove( i_nPosition, aGuard ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::removeAllItems( ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + m_xData->removeAllItems(); + + impl_handleRemove( -1, aGuard ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::setItemText( ::sal_Int32 i_nPosition, const OUString& i_rItemText ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->getItem( i_nPosition ) ); + rItem.ItemText = i_rItemText; + + impl_handleModify( i_nPosition, i_rItemText, ::std::optional< OUString >(), aGuard ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::setItemImage( ::sal_Int32 i_nPosition, const OUString& i_rItemImageURL ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->getItem( i_nPosition ) ); + rItem.ItemImageURL = i_rItemImageURL; + + impl_handleModify( i_nPosition, ::std::optional< OUString >(), i_rItemImageURL, aGuard ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::setItemTextAndImage( ::sal_Int32 i_nPosition, const OUString& i_rItemText, const OUString& i_rItemImageURL ) +{ + std::unique_lock aGuard( m_aMutex ); + // SYNCHRONIZED -----> + ListItem& rItem( m_xData->getItem( i_nPosition ) ); + rItem.ItemText = i_rItemText; + rItem.ItemImageURL = i_rItemImageURL; + + impl_handleModify( i_nPosition, i_rItemText, i_rItemImageURL, aGuard ); + // <----- SYNCHRONIZED +} + + +void SAL_CALL UnoControlListBoxModel::setItemData( ::sal_Int32 i_nPosition, const Any& i_rDataValue ) +{ + std::unique_lock aGuard( m_aMutex ); + ListItem& rItem( m_xData->getItem( i_nPosition ) ); + rItem.ItemData = i_rDataValue; +} + + +OUString SAL_CALL UnoControlListBoxModel::getItemText( ::sal_Int32 i_nPosition ) +{ + std::unique_lock aGuard( m_aMutex ); + const ListItem& rItem( m_xData->getItem( i_nPosition ) ); + return rItem.ItemText; +} + + +OUString SAL_CALL UnoControlListBoxModel::getItemImage( ::sal_Int32 i_nPosition ) +{ + std::unique_lock aGuard( m_aMutex ); + const ListItem& rItem( m_xData->getItem( i_nPosition ) ); + return rItem.ItemImageURL; +} + + +beans::Pair< OUString, OUString > SAL_CALL UnoControlListBoxModel::getItemTextAndImage( ::sal_Int32 i_nPosition ) +{ + std::unique_lock aGuard( m_aMutex ); + const ListItem& rItem( m_xData->getItem( i_nPosition ) ); + return beans::Pair< OUString, OUString >( rItem.ItemText, rItem.ItemImageURL ); +} + + +Any SAL_CALL UnoControlListBoxModel::getItemData( ::sal_Int32 i_nPosition ) +{ + std::unique_lock aGuard( m_aMutex ); + const ListItem& rItem( m_xData->getItem( i_nPosition ) ); + return rItem.ItemData; +} + + +Sequence< beans::Pair< OUString, OUString > > SAL_CALL UnoControlListBoxModel::getAllItems( ) +{ + std::unique_lock aGuard( m_aMutex ); + return m_xData->getAllItems(); +} + + +void SAL_CALL UnoControlListBoxModel::addItemListListener( const uno::Reference< awt::XItemListListener >& i_Listener ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( i_Listener.is() ) + m_aItemListListeners.addInterface( aGuard, i_Listener ); +} + + +void SAL_CALL UnoControlListBoxModel::removeItemListListener( const uno::Reference< awt::XItemListListener >& i_Listener ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( i_Listener.is() ) + m_aItemListListeners.removeInterface( aGuard, i_Listener ); +} + + +void UnoControlListBoxModel::impl_getStringItemList( std::unique_lock<std::mutex>& rGuard, ::std::vector< OUString >& o_rStringItems ) const +{ + Sequence< OUString > aStringItemList; + Any aPropValue; + getFastPropertyValue( rGuard, aPropValue, BASEPROPERTY_STRINGITEMLIST ); + OSL_VERIFY( aPropValue >>= aStringItemList ); + + comphelper::sequenceToContainer(o_rStringItems, aStringItemList); +} + + +void UnoControlListBoxModel::impl_setStringItemList( std::unique_lock<std::mutex>& rGuard, const ::std::vector< OUString >& i_rStringItems ) +{ + Sequence< OUString > aStringItems( comphelper::containerToSequence(i_rStringItems) ); + m_xData->m_bSettingLegacyProperty = true; + try + { + setFastPropertyValueImpl( rGuard, BASEPROPERTY_STRINGITEMLIST, uno::Any( aStringItems ) ); + } + catch( const Exception& ) + { + m_xData->m_bSettingLegacyProperty = false; + throw; + } + m_xData->m_bSettingLegacyProperty = false; +} + + +void UnoControlListBoxModel::impl_handleInsert( std::unique_lock<std::mutex>& rGuard, + const sal_Int32 i_nItemPosition, + const ::std::optional< OUString >& i_rItemText, + const ::std::optional< OUString >& i_rItemImageURL ) +{ + // SYNCHRONIZED -----> + // sync with legacy StringItemList property + ::std::vector< OUString > aStringItems; + impl_getStringItemList( rGuard, aStringItems ); + OSL_ENSURE( o3tl::make_unsigned( i_nItemPosition ) <= aStringItems.size(), "UnoControlListBoxModel::impl_handleInsert" ); + if ( o3tl::make_unsigned( i_nItemPosition ) <= aStringItems.size() ) + { + const OUString sItemText( !!i_rItemText ? *i_rItemText : OUString() ); + aStringItems.insert( aStringItems.begin() + i_nItemPosition, sItemText ); + } + + impl_setStringItemList( rGuard, aStringItems ); + + // notify ItemListListeners + impl_notifyItemListEvent( rGuard, i_nItemPosition, i_rItemText, i_rItemImageURL, &XItemListListener::listItemInserted ); +} + + +void UnoControlListBoxModel::impl_handleRemove( + const sal_Int32 i_nItemPosition, + std::unique_lock<std::mutex>& i_rClearBeforeNotify ) +{ + // SYNCHRONIZED -----> + const bool bAllItems = ( i_nItemPosition < 0 ); + // sync with legacy StringItemList property + ::std::vector< OUString > aStringItems; + impl_getStringItemList( i_rClearBeforeNotify, aStringItems ); + if ( !bAllItems ) + { + OSL_ENSURE( o3tl::make_unsigned( i_nItemPosition ) < aStringItems.size(), "UnoControlListBoxModel::impl_handleRemove" ); + if ( o3tl::make_unsigned( i_nItemPosition ) < aStringItems.size() ) + { + aStringItems.erase( aStringItems.begin() + i_nItemPosition ); + } + } + else + { + aStringItems.resize(0); + } + + impl_setStringItemList( i_rClearBeforeNotify, aStringItems ); + + // notify ItemListListeners + if ( bAllItems ) + { + EventObject aEvent( *this ); + m_aItemListListeners.notifyEach( i_rClearBeforeNotify, &XItemListListener::allItemsRemoved, aEvent ); + } + else + { + impl_notifyItemListEvent( i_rClearBeforeNotify, i_nItemPosition, ::std::optional< OUString >(), ::std::optional< OUString >(), + &XItemListListener::listItemRemoved ); + } +} + + +void UnoControlListBoxModel::impl_handleModify( + const sal_Int32 i_nItemPosition, const ::std::optional< OUString >& i_rItemText, + const ::std::optional< OUString >& i_rItemImageURL, + std::unique_lock<std::mutex>& i_rClearBeforeNotify ) +{ + // SYNCHRONIZED -----> + if ( !!i_rItemText ) + { + // sync with legacy StringItemList property + ::std::vector< OUString > aStringItems; + impl_getStringItemList( i_rClearBeforeNotify, aStringItems ); + OSL_ENSURE( o3tl::make_unsigned( i_nItemPosition ) < aStringItems.size(), "UnoControlListBoxModel::impl_handleModify" ); + if ( o3tl::make_unsigned( i_nItemPosition ) < aStringItems.size() ) + { + aStringItems[ i_nItemPosition] = *i_rItemText; + } + + impl_setStringItemList( i_rClearBeforeNotify, aStringItems ); + } + + // notify ItemListListeners + impl_notifyItemListEvent( i_rClearBeforeNotify, i_nItemPosition, i_rItemText, i_rItemImageURL, &XItemListListener::listItemModified ); +} + + +void UnoControlListBoxModel::impl_notifyItemListEvent( + std::unique_lock<std::mutex>& rGuard, + const sal_Int32 i_nItemPosition, const ::std::optional< OUString >& i_rItemText, + const ::std::optional< OUString >& i_rItemImageURL, + void ( SAL_CALL XItemListListener::*NotificationMethod )( const ItemListEvent& ) ) +{ + ItemListEvent aEvent; + aEvent.Source = *this; + aEvent.ItemPosition = i_nItemPosition; + if ( !!i_rItemText ) + { + aEvent.ItemText.IsPresent = true; + aEvent.ItemText.Value = *i_rItemText; + } + if ( !!i_rItemImageURL ) + { + aEvent.ItemImageURL.IsPresent = true; + aEvent.ItemImageURL.Value = *i_rItemImageURL; + } + + m_aItemListListeners.notifyEach( rGuard, NotificationMethod, aEvent ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlListBoxModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlListBoxModel(context)); +} + + + +UnoListBoxControl::UnoListBoxControl() + :maActionListeners( *this ) + ,maItemListeners( *this ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoListBoxControl::GetComponentServiceName() const +{ + return "listbox"; +} + +OUString UnoListBoxControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoListBoxControl"; +} + +css::uno::Sequence<OUString> UnoListBoxControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlListBox", "stardiv.vcl.control.ListBox" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals); +} + +void UnoListBoxControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maActionListeners.disposeAndClear( aEvt ); + maItemListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} + +void UnoListBoxControl::ImplUpdateSelectedItemsProperty() +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + DBG_ASSERT( xListBox.is(), "XListBox?" ); + + uno::Sequence<sal_Int16> aSeq = xListBox->getSelectedItemsPos(); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_SELECTEDITEMS ), uno::Any(aSeq), false ); + } +} + +void UnoListBoxControl::updateFromModel() +{ + UnoControlBase::updateFromModel(); + + Reference< XItemListListener > xItemListListener( getPeer(), UNO_QUERY ); + ENSURE_OR_RETURN_VOID( xItemListListener.is(), "UnoListBoxControl::updateFromModel: a peer which is no ItemListListener?!" ); + + EventObject aEvent( getModel() ); + xItemListListener->itemListChanged( aEvent ); + + // notify the change of the SelectedItems property, again. While our base class, in updateFromModel, + // already did this, our peer(s) can only legitimately set the selection after they have the string + // item list, which we just notified with the itemListChanged call. + const OUString& sSelectedItemsPropName( GetPropertyName( BASEPROPERTY_SELECTEDITEMS ) ); + ImplSetPeerProperty( sSelectedItemsPropName, ImplGetPropertyValue( sSelectedItemsPropName ) ); +} + +void UnoListBoxControl::ImplSetPeerProperty( const OUString& rPropName, const uno::Any& rVal ) +{ + if ( rPropName == GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ) + // do not forward this to our peer. We are a XItemListListener at our model, and changes in the string item + // list (which is a legacy property) will, later, arrive as changes in the ItemList. Those latter changes + // will be forwarded to the peer, which will update itself accordingly. + return; + + UnoControl::ImplSetPeerProperty( rPropName, rVal ); +} + +void UnoListBoxControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->addItemListener( this ); + + if ( maActionListeners.getLength() ) + xListBox->addActionListener( &maActionListeners ); +} + +void UnoListBoxControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->addActionListener( &maActionListeners ); + } +} + +void UnoListBoxControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +void UnoListBoxControl::addItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.addInterface( l ); +} + +void UnoListBoxControl::removeItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.removeInterface( l ); +} + +void UnoListBoxControl::addItem( const OUString& aItem, sal_Int16 nPos ) +{ + uno::Sequence<OUString> aSeq { aItem }; + addItems( aSeq, nPos ); +} + +void UnoListBoxControl::addItems( const uno::Sequence< OUString>& aItems, sal_Int16 nPos ) +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + sal_uInt16 nNewItems = static_cast<sal_uInt16>(aItems.getLength()); + sal_uInt16 nOldLen = static_cast<sal_uInt16>(aSeq.getLength()); + sal_uInt16 nNewLen = nOldLen + nNewItems; + + uno::Sequence< OUString> aNewSeq( nNewLen ); + + if ( ( nPos < 0 ) || ( nPos > nOldLen ) ) + nPos = nOldLen; + + // Items before the Paste-Position + auto it = std::copy(std::cbegin(aSeq), std::next(std::cbegin(aSeq), nPos), aNewSeq.getArray()); + + // New Items + it = std::copy(aItems.begin(), aItems.end(), it); + + // Rest of old Items + std::copy(std::next(std::cbegin(aSeq), nPos), std::cend(aSeq), it); + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ), uno::Any(aNewSeq), true ); +} + +void UnoListBoxControl::removeItems( sal_Int16 nPos, sal_Int16 nCount ) +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + sal_uInt16 nOldLen = static_cast<sal_uInt16>(aSeq.getLength()); + if ( !(nOldLen && ( nPos < nOldLen )) ) + return; + + if ( nCount > ( nOldLen-nPos ) ) + nCount = nOldLen-nPos; + + sal_uInt16 nNewLen = nOldLen - nCount; + + uno::Sequence< OUString> aNewSeq( nNewLen ); + + // Items before the Remove-Position + auto it = std::copy(std::cbegin(aSeq), std::next(std::cbegin(aSeq), nPos), aNewSeq.getArray()); + + // Rest of Items + std::copy(std::next(std::cbegin(aSeq), nPos + nCount), std::cend(aSeq), it); + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ), uno::Any(aNewSeq), true ); +} + +sal_Int16 UnoListBoxControl::getItemCount() +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + return static_cast<sal_Int16>(aSeq.getLength()); +} + +OUString UnoListBoxControl::getItem( sal_Int16 nPos ) +{ + OUString aItem; + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + if ( nPos < aSeq.getLength() ) + aItem = aSeq[nPos]; + return aItem; +} + +uno::Sequence< OUString> UnoListBoxControl::getItems() +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + return aSeq; +} + +sal_Int16 UnoListBoxControl::getSelectedItemPos() +{ + sal_Int16 n = -1; + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + n = xListBox->getSelectedItemPos(); + } + return n; +} + +uno::Sequence<sal_Int16> UnoListBoxControl::getSelectedItemsPos() +{ + uno::Sequence<sal_Int16> aSeq; + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + aSeq = xListBox->getSelectedItemsPos(); + } + return aSeq; +} + +OUString UnoListBoxControl::getSelectedItem() +{ + OUString aItem; + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + aItem = xListBox->getSelectedItem(); + } + return aItem; +} + +uno::Sequence< OUString> UnoListBoxControl::getSelectedItems() +{ + uno::Sequence< OUString> aSeq; + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + aSeq = xListBox->getSelectedItems(); + } + return aSeq; +} + +void UnoListBoxControl::selectItemPos( sal_Int16 nPos, sal_Bool bSelect ) +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->selectItemPos( nPos, bSelect ); + } + ImplUpdateSelectedItemsProperty(); +} + +void UnoListBoxControl::selectItemsPos( const uno::Sequence<sal_Int16>& aPositions, sal_Bool bSelect ) +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->selectItemsPos( aPositions, bSelect ); + } + ImplUpdateSelectedItemsProperty(); +} + +void UnoListBoxControl::selectItem( const OUString& aItem, sal_Bool bSelect ) +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->selectItem( aItem, bSelect ); + } + ImplUpdateSelectedItemsProperty(); +} + +void UnoListBoxControl::makeVisible( sal_Int16 nEntry ) +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XListBox > xListBox( getPeer(), uno::UNO_QUERY ); + xListBox->makeVisible( nEntry ); + } +} + +void UnoListBoxControl::setDropDownLineCount( sal_Int16 nLines ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LINECOUNT ), uno::Any(nLines), true ); +} + +sal_Int16 UnoListBoxControl::getDropDownLineCount() +{ + return ImplGetPropertyValue_INT16( BASEPROPERTY_LINECOUNT ); +} + +sal_Bool UnoListBoxControl::isMutipleMode() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_MULTISELECTION ); +} + +void UnoListBoxControl::setMultipleMode( sal_Bool bMulti ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_MULTISELECTION ), uno::Any(bMulti), true ); +} + +void UnoListBoxControl::itemStateChanged( const awt::ItemEvent& rEvent ) +{ + ImplUpdateSelectedItemsProperty(); + if ( maItemListeners.getLength() ) + { + try + { + maItemListeners.itemStateChanged( rEvent ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "UnoListBoxControl::itemStateChanged"); + } + } +} + +awt::Size UnoListBoxControl::getMinimumSize( ) +{ + return Impl_getMinimumSize(); +} + +awt::Size UnoListBoxControl::getPreferredSize( ) +{ + return Impl_getPreferredSize(); +} + +awt::Size UnoListBoxControl::calcAdjustedSize( const awt::Size& rNewSize ) +{ + return Impl_calcAdjustedSize( rNewSize ); +} + +awt::Size UnoListBoxControl::getMinimumSize( sal_Int16 nCols, sal_Int16 nLines ) +{ + return Impl_getMinimumSize( nCols, nLines ); +} + +void UnoListBoxControl::getColumnsAndLines( sal_Int16& nCols, sal_Int16& nLines ) +{ + Impl_getColumnsAndLines( nCols, nLines ); +} + +sal_Bool SAL_CALL UnoListBoxControl::setModel( const uno::Reference< awt::XControlModel >& i_rModel ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + const Reference< XItemList > xOldItems( getModel(), UNO_QUERY ); + OSL_ENSURE( xOldItems.is() || !getModel().is(), "UnoListBoxControl::setModel: illegal old model!" ); + const Reference< XItemList > xNewItems( i_rModel, UNO_QUERY ); + OSL_ENSURE( xNewItems.is() || !i_rModel.is(), "UnoListBoxControl::setModel: illegal new model!" ); + + if ( !UnoListBoxControl_Base::setModel( i_rModel ) ) + return false; + + if ( xOldItems.is() ) + xOldItems->removeItemListListener( this ); + if ( xNewItems.is() ) + xNewItems->addItemListListener( this ); + + return true; +} + +void SAL_CALL UnoListBoxControl::listItemInserted( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoListBoxControl::listItemInserted: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemInserted( i_rEvent ); +} + +void SAL_CALL UnoListBoxControl::listItemRemoved( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoListBoxControl::listItemRemoved: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemRemoved( i_rEvent ); +} + +void SAL_CALL UnoListBoxControl::listItemModified( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoListBoxControl::listItemModified: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemModified( i_rEvent ); +} + +void SAL_CALL UnoListBoxControl::allItemsRemoved( const lang::EventObject& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoListBoxControl::allItemsRemoved: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->allItemsRemoved( i_rEvent ); +} + +void SAL_CALL UnoListBoxControl::itemListChanged( const lang::EventObject& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoListBoxControl::itemListChanged: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->itemListChanged( i_rEvent ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoListBoxControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoListBoxControl()); +} + + + +UnoControlComboBoxModel::UnoControlComboBoxModel( const Reference< XComponentContext >& rxContext ) + :UnoControlListBoxModel( rxContext, ConstructWithoutProperties ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXComboBox>(); +} + +OUString UnoControlComboBoxModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlComboBoxModel"; +} + +css::uno::Sequence<OUString> UnoControlComboBoxModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlComboBoxModel", "stardiv.vcl.controlmodel.ComboBox" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals); +} + +uno::Reference< beans::XPropertySetInfo > UnoControlComboBoxModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +::cppu::IPropertyArrayHelper& UnoControlComboBoxModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + + +OUString UnoControlComboBoxModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.ComboBox"; +} + +void UnoControlComboBoxModel::setFastPropertyValue_NoBroadcast( std::unique_lock<std::mutex>& rGuard, sal_Int32 nHandle, const uno::Any& rValue ) +{ + UnoControlModel::setFastPropertyValue_NoBroadcast( rGuard, nHandle, rValue ); + + if (nHandle != BASEPROPERTY_STRINGITEMLIST || m_xData->m_bSettingLegacyProperty) + return; + + // synchronize the legacy StringItemList property with our list items + Sequence< OUString > aStringItemList; + Any aPropValue; + getFastPropertyValue( rGuard, aPropValue, BASEPROPERTY_STRINGITEMLIST ); + OSL_VERIFY( aPropValue >>= aStringItemList ); + + ::std::vector< ListItem > aItems( aStringItemList.getLength() ); + ::std::transform( + std::cbegin(aStringItemList), + std::cend(aStringItemList), + aItems.begin(), + CreateListItem() + ); + m_xData->setAllItems( std::move(aItems) ); + + // since an XItemListListener does not have a "all items modified" or some such method, + // we simulate this by notifying removal of all items, followed by insertion of all new + // items + lang::EventObject aEvent; + aEvent.Source = *this; + m_aItemListListeners.notifyEach( rGuard, &XItemListListener::itemListChanged, aEvent ); +} + +uno::Any UnoControlComboBoxModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.ComboBox" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlComboBoxModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlComboBoxModel(context)); +} + + + +UnoComboBoxControl::UnoComboBoxControl() + :maActionListeners( *this ) + ,maItemListeners( *this ) +{ + maComponentInfos.nWidth = 100; + maComponentInfos.nHeight = 12; +} + +OUString UnoComboBoxControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoComboBoxControl"; +} + +css::uno::Sequence<OUString> UnoComboBoxControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlComboBox", "stardiv.vcl.control.ComboBox" }; + return comphelper::concatSequences( UnoEditControl::getSupportedServiceNames(), vals); +} + +OUString UnoComboBoxControl::GetComponentServiceName() const +{ + return "combobox"; +} + +void UnoComboBoxControl::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = getXWeak(); + maActionListeners.disposeAndClear( aEvt ); + maItemListeners.disposeAndClear( aEvt ); + UnoControl::dispose(); +} +uno::Any UnoComboBoxControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XComboBox* >(this) ); + if ( !aRet.hasValue() ) + { + aRet = ::cppu::queryInterface( rType, + static_cast< awt::XItemListener* >(this) ); + if ( !aRet.hasValue() ) + { + aRet = ::cppu::queryInterface( rType, + static_cast< awt::XItemListListener* >(this) ); + } + } + return (aRet.hasValue() ? aRet : UnoEditControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoComboBoxControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoComboBoxControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<awt::XComboBox>::get(), + cppu::UnoType<awt::XItemListener>::get(), + cppu::UnoType<awt::XItemListListener>::get(), + UnoEditControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoComboBoxControl::updateFromModel() +{ + UnoEditControl::updateFromModel(); + + Reference< XItemListListener > xItemListListener( getPeer(), UNO_QUERY ); + ENSURE_OR_RETURN_VOID( xItemListListener.is(), "UnoComboBoxControl::updateFromModel: a peer which is no ItemListListener?!" ); + + EventObject aEvent( getModel() ); + xItemListListener->itemListChanged( aEvent ); +} +void UnoComboBoxControl::ImplSetPeerProperty( const OUString& rPropName, const uno::Any& rVal ) +{ + if ( rPropName == GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ) + // do not forward this to our peer. We are a XItemListListener at our model, and changes in the string item + // list (which is a legacy property) will, later, arrive as changes in the ItemList. Those latter changes + // will be forwarded to the peer, which will update itself accordingly. + return; + + UnoEditControl::ImplSetPeerProperty( rPropName, rVal ); +} +void UnoComboBoxControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoEditControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XComboBox > xComboBox( getPeer(), uno::UNO_QUERY ); + if ( maActionListeners.getLength() ) + xComboBox->addActionListener( &maActionListeners ); + if ( maItemListeners.getLength() ) + xComboBox->addItemListener( &maItemListeners ); +} + +void UnoComboBoxControl::addActionListener(const uno::Reference< awt::XActionListener > & l) +{ + maActionListeners.addInterface( l ); + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XComboBox > xComboBox( getPeer(), uno::UNO_QUERY ); + xComboBox->addActionListener( &maActionListeners ); + } +} + +void UnoComboBoxControl::removeActionListener(const uno::Reference< awt::XActionListener > & l) +{ + if( getPeer().is() && maActionListeners.getLength() == 1 ) + { + uno::Reference < awt::XComboBox > xComboBox( getPeer(), uno::UNO_QUERY ); + xComboBox->removeActionListener( &maActionListeners ); + } + maActionListeners.removeInterface( l ); +} + +void UnoComboBoxControl::addItemListener(const uno::Reference < awt::XItemListener > & l) +{ + maItemListeners.addInterface( l ); + if( getPeer().is() && maItemListeners.getLength() == 1 ) + { + uno::Reference < awt::XComboBox > xComboBox( getPeer(), uno::UNO_QUERY ); + xComboBox->addItemListener( &maItemListeners ); + } +} + +void UnoComboBoxControl::removeItemListener(const uno::Reference < awt::XItemListener > & l) +{ + if( getPeer().is() && maItemListeners.getLength() == 1 ) + { + // This call is prettier than creating a Ref and calling query + uno::Reference < awt::XComboBox > xComboBox( getPeer(), uno::UNO_QUERY ); + xComboBox->removeItemListener( &maItemListeners ); + } + maItemListeners.removeInterface( l ); +} +void UnoComboBoxControl::itemStateChanged( const awt::ItemEvent& rEvent ) +{ + if ( maItemListeners.getLength() ) + { + try + { + maItemListeners.itemStateChanged( rEvent ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "toolkit", "UnoComboBoxControl::itemStateChanged"); + } + } +} +sal_Bool SAL_CALL UnoComboBoxControl::setModel( const uno::Reference< awt::XControlModel >& i_rModel ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + const Reference< XItemList > xOldItems( getModel(), UNO_QUERY ); + OSL_ENSURE( xOldItems.is() || !getModel().is(), "UnoComboBoxControl::setModel: illegal old model!" ); + const Reference< XItemList > xNewItems( i_rModel, UNO_QUERY ); + OSL_ENSURE( xNewItems.is() || !i_rModel.is(), "UnoComboBoxControl::setModel: illegal new model!" ); + + if ( !UnoEditControl::setModel( i_rModel ) ) + return false; + + if ( xOldItems.is() ) + xOldItems->removeItemListListener( this ); + if ( xNewItems.is() ) + xNewItems->addItemListListener( this ); + + return true; +} + +void SAL_CALL UnoComboBoxControl::listItemInserted( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoComboBoxControl::listItemInserted: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemInserted( i_rEvent ); +} + +void SAL_CALL UnoComboBoxControl::listItemRemoved( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoComboBoxControl::listItemRemoved: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemRemoved( i_rEvent ); +} + +void SAL_CALL UnoComboBoxControl::listItemModified( const awt::ItemListEvent& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoComboBoxControl::listItemModified: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->listItemModified( i_rEvent ); +} + +void SAL_CALL UnoComboBoxControl::allItemsRemoved( const lang::EventObject& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoComboBoxControl::allItemsRemoved: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->allItemsRemoved( i_rEvent ); +} + +void SAL_CALL UnoComboBoxControl::itemListChanged( const lang::EventObject& i_rEvent ) +{ + const Reference< XItemListListener > xPeerListener( getPeer(), UNO_QUERY ); + OSL_ENSURE( xPeerListener.is() || !getPeer().is(), "UnoComboBoxControl::itemListChanged: invalid peer!" ); + if ( xPeerListener.is() ) + xPeerListener->itemListChanged( i_rEvent ); +} + +void UnoComboBoxControl::addItem( const OUString& aItem, sal_Int16 nPos ) +{ + uno::Sequence<OUString> aSeq { aItem }; + addItems( aSeq, nPos ); +} + +void UnoComboBoxControl::addItems( const uno::Sequence< OUString>& aItems, sal_Int16 nPos ) +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + sal_uInt16 nNewItems = static_cast<sal_uInt16>(aItems.getLength()); + sal_uInt16 nOldLen = static_cast<sal_uInt16>(aSeq.getLength()); + sal_uInt16 nNewLen = nOldLen + nNewItems; + + uno::Sequence< OUString> aNewSeq( nNewLen ); + + if ( ( nPos < 0 ) || ( nPos > nOldLen ) ) + nPos = nOldLen; + + // items before the insert position + auto it = std::copy(std::cbegin(aSeq), std::next(std::cbegin(aSeq), nPos), aNewSeq.getArray()); + + // New items + it = std::copy(aItems.begin(), aItems.end(), it); + + // remainder of old items + std::copy(std::next(std::cbegin(aSeq), nPos), std::cend(aSeq), it); + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ), Any(aNewSeq), true ); +} + +void UnoComboBoxControl::removeItems( sal_Int16 nPos, sal_Int16 nCount ) +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + sal_uInt16 nOldLen = static_cast<sal_uInt16>(aSeq.getLength()); + if ( !nOldLen || ( nPos >= nOldLen ) ) + return; + + if ( nCount > ( nOldLen-nPos ) ) + nCount = nOldLen-nPos; + + sal_uInt16 nNewLen = nOldLen - nCount; + + uno::Sequence< OUString> aNewSeq( nNewLen ); + + // items before the deletion position + auto it = std::copy(std::cbegin(aSeq), std::next(std::cbegin(aSeq), nPos), aNewSeq.getArray()); + + // remainder of old items + std::copy(std::next(std::cbegin(aSeq), nPos + nCount), std::cend(aSeq), it); + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ), uno::Any(aNewSeq), true ); +} + +sal_Int16 UnoComboBoxControl::getItemCount() +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + return static_cast<sal_Int16>(aSeq.getLength()); +} + +OUString UnoComboBoxControl::getItem( sal_Int16 nPos ) +{ + OUString aItem; + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + if ( nPos < aSeq.getLength() ) + aItem = aSeq[nPos]; + return aItem; +} + +uno::Sequence< OUString> UnoComboBoxControl::getItems() +{ + uno::Any aVal = ImplGetPropertyValue( GetPropertyName( BASEPROPERTY_STRINGITEMLIST ) ); + uno::Sequence< OUString> aSeq; + aVal >>= aSeq; + return aSeq; +} + +void UnoComboBoxControl::setDropDownLineCount( sal_Int16 nLines ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LINECOUNT ), uno::Any(nLines), true ); +} + +sal_Int16 UnoComboBoxControl::getDropDownLineCount() +{ + return ImplGetPropertyValue_INT16( BASEPROPERTY_LINECOUNT ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoComboBoxControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoComboBoxControl()); +} + + +// UnoSpinFieldControl + +UnoSpinFieldControl::UnoSpinFieldControl() + :maSpinListeners( *this ) +{ + mbRepeat = false; +} + +// uno::XInterface +uno::Any UnoSpinFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XSpinField* >(this) ); + return (aRet.hasValue() ? aRet : UnoEditControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoSpinFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoSpinFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XSpinField>::get(), + UnoEditControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoSpinFieldControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoEditControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + xField->enableRepeat( mbRepeat ); + if ( maSpinListeners.getLength() ) + xField->addSpinListener( &maSpinListeners ); +} + + // css::awt::XSpinField +void UnoSpinFieldControl::addSpinListener( const css::uno::Reference< css::awt::XSpinListener >& l ) +{ + maSpinListeners.addInterface( l ); + if( getPeer().is() && maSpinListeners.getLength() == 1 ) + { + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + xField->addSpinListener( &maSpinListeners ); + } +} + +void UnoSpinFieldControl::removeSpinListener( const css::uno::Reference< css::awt::XSpinListener >& l ) +{ + if( getPeer().is() && maSpinListeners.getLength() == 1 ) + { + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + xField->removeSpinListener( &maSpinListeners ); + } + maSpinListeners.removeInterface( l ); +} + +void UnoSpinFieldControl::up() +{ + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + if ( xField.is() ) + xField->up(); +} + +void UnoSpinFieldControl::down() +{ + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + if ( xField.is() ) + xField->down(); +} + +void UnoSpinFieldControl::first() +{ + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + if ( xField.is() ) + xField->first(); +} + +void UnoSpinFieldControl::last() +{ + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + if ( xField.is() ) + xField->last(); +} + +void UnoSpinFieldControl::enableRepeat( sal_Bool bRepeat ) +{ + mbRepeat = bRepeat; + + uno::Reference < awt::XSpinField > xField( getPeer(), uno::UNO_QUERY ); + if ( xField.is() ) + xField->enableRepeat( bRepeat ); +} + + + +UnoControlDateFieldModel::UnoControlDateFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXDateField>(); +} + +OUString UnoControlDateFieldModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.DateField"; +} + +uno::Any UnoControlDateFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.DateField" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + + +::cppu::IPropertyArrayHelper& UnoControlDateFieldModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlDateFieldModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlDateFieldModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlDateFieldModel"; +} + +css::uno::Sequence<OUString> +UnoControlDateFieldModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlDateFieldModel", "stardiv.vcl.controlmodel.DateField" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlDateFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlDateFieldModel(context)); +} + + + +UnoDateFieldControl::UnoDateFieldControl() +{ + mnFirst = util::Date( 1, 1, 1900 ); + mnLast = util::Date( 31, 12, 2200 ); + mbLongFormat = TRISTATE_INDET; +} + +OUString UnoDateFieldControl::GetComponentServiceName() const +{ + return "datefield"; +} + +// uno::XInterface +uno::Any UnoDateFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XDateField* >(this) ); + return (aRet.hasValue() ? aRet : UnoSpinFieldControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoDateFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoDateFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XDateField>::get(), + UnoSpinFieldControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoDateFieldControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoSpinFieldControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + xField->setLast( mnLast ); + if ( mbLongFormat != TRISTATE_INDET ) + xField->setLongFormat( mbLongFormat != TRISTATE_FALSE); +} + + +void UnoDateFieldControl::textChanged( const awt::TextEvent& e ) +{ + uno::Reference< awt::XVclWindowPeer > xPeer( getPeer(), uno::UNO_QUERY ); + + // also change the text property (#i25106#) + if ( xPeer.is() ) + { + const OUString& sTextPropertyName = GetPropertyName( BASEPROPERTY_TEXT ); + ImplSetPropertyValue( sTextPropertyName, xPeer->getProperty( sTextPropertyName ), false ); + } + + // re-calc the Date property + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + uno::Any aValue; + if ( xField->isEmpty() ) + { + // the field says it's empty + bool bEnforceFormat = true; + if ( xPeer.is() ) + xPeer->getProperty( GetPropertyName( BASEPROPERTY_ENFORCE_FORMAT ) ) >>= bEnforceFormat; + if ( !bEnforceFormat ) + { + // and it also says that it is currently accepting invalid inputs, without + // forcing it to a valid date + uno::Reference< awt::XTextComponent > xText( xPeer, uno::UNO_QUERY ); + if ( xText.is() && xText->getText().getLength() ) + // and in real, the text of the peer is *not* empty + // -> simulate an invalid date, which is different from "no date" + aValue <<= util::Date(); + } + } + else + aValue <<= xField->getDate(); + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DATE ), aValue, false ); + + // multiplex the event + if ( GetTextListeners().getLength() ) + GetTextListeners().textChanged( e ); +} + +void UnoDateFieldControl::setDate( const util::Date& Date ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DATE ), uno::Any(Date), true ); +} + +util::Date UnoDateFieldControl::getDate() +{ + return ImplGetPropertyValue_Date( BASEPROPERTY_DATE ); +} + +void UnoDateFieldControl::setMin( const util::Date& Date ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DATEMIN ), uno::Any(Date), true ); +} + +util::Date UnoDateFieldControl::getMin() +{ + return ImplGetPropertyValue_Date( BASEPROPERTY_DATEMIN ); +} + +void UnoDateFieldControl::setMax( const util::Date& Date ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DATEMAX ), uno::Any(Date), true ); +} + +util::Date UnoDateFieldControl::getMax() +{ + return ImplGetPropertyValue_Date( BASEPROPERTY_DATEMAX ); +} + +void UnoDateFieldControl::setFirst( const util::Date& Date ) +{ + mnFirst = Date; + if ( getPeer().is() ) + { + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( Date ); + } +} + +util::Date UnoDateFieldControl::getFirst() +{ + return mnFirst; +} + +void UnoDateFieldControl::setLast( const util::Date& Date ) +{ + mnLast = Date; + if ( getPeer().is() ) + { + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + xField->setLast( Date ); + } +} + +util::Date UnoDateFieldControl::getLast() +{ + return mnLast; +} + +void UnoDateFieldControl::setLongFormat( sal_Bool bLong ) +{ + mbLongFormat = bLong ? TRISTATE_TRUE : TRISTATE_FALSE; + if ( getPeer().is() ) + { + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + xField->setLongFormat( bLong ); + } +} + +sal_Bool UnoDateFieldControl::isLongFormat() +{ + return mbLongFormat == TRISTATE_TRUE; +} + +void UnoDateFieldControl::setEmpty() +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + xField->setEmpty(); + } +} + +sal_Bool UnoDateFieldControl::isEmpty() +{ + bool bEmpty = false; + if ( getPeer().is() ) + { + uno::Reference < awt::XDateField > xField( getPeer(), uno::UNO_QUERY ); + bEmpty = xField->isEmpty(); + } + return bEmpty; +} + +void UnoDateFieldControl::setStrictFormat( sal_Bool bStrict ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRICTFORMAT ), uno::Any(bStrict), true ); +} + +sal_Bool UnoDateFieldControl::isStrictFormat() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_STRICTFORMAT ); +} + +OUString UnoDateFieldControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoDateFieldControl"; +} + +css::uno::Sequence<OUString> UnoDateFieldControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlDateField", "stardiv.vcl.control.DateField" }; + return comphelper::concatSequences( UnoSpinFieldControl::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoDateFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoDateFieldControl()); +} + + + +UnoControlTimeFieldModel::UnoControlTimeFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXTimeField>(); +} + +OUString UnoControlTimeFieldModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.TimeField"; +} + +uno::Any UnoControlTimeFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.TimeField" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + + +::cppu::IPropertyArrayHelper& UnoControlTimeFieldModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlTimeFieldModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlTimeFieldModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlTimeFieldModel"; +} + +css::uno::Sequence<OUString> +UnoControlTimeFieldModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlTimeFieldModel", "stardiv.vcl.controlmodel.TimeField" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlTimeFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlTimeFieldModel(context)); +} + + + +UnoTimeFieldControl::UnoTimeFieldControl() +{ + mnFirst = util::Time( 0, 0, 0, 0, false ); + mnLast = util::Time( 999999999, 59, 59, 23, false ); +} + +OUString UnoTimeFieldControl::GetComponentServiceName() const +{ + return "timefield"; +} + +// uno::XInterface +uno::Any UnoTimeFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XTimeField* >(this) ); + return (aRet.hasValue() ? aRet : UnoSpinFieldControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoTimeFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoTimeFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XTimeField>::get(), + UnoSpinFieldControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoTimeFieldControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoSpinFieldControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + xField->setLast( mnLast ); +} + +void UnoTimeFieldControl::textChanged( const awt::TextEvent& e ) +{ + // also change the text property (#i25106#) + uno::Reference< awt::XVclWindowPeer > xPeer( getPeer(), uno::UNO_QUERY ); + const OUString& sTextPropertyName = GetPropertyName( BASEPROPERTY_TEXT ); + ImplSetPropertyValue( sTextPropertyName, xPeer->getProperty( sTextPropertyName ), false ); + + // re-calc the Time property + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + uno::Any aValue; + if ( !xField->isEmpty() ) + aValue <<= xField->getTime(); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TIME ), aValue, false ); + + // multiplex the event + if ( GetTextListeners().getLength() ) + GetTextListeners().textChanged( e ); +} + +void UnoTimeFieldControl::setTime( const util::Time& Time ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TIME ), Any(Time), true ); +} + +util::Time UnoTimeFieldControl::getTime() +{ + return ImplGetPropertyValue_Time( BASEPROPERTY_TIME ); +} + +void UnoTimeFieldControl::setMin( const util::Time& Time ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TIMEMIN ), uno::Any(Time), true ); +} + +util::Time UnoTimeFieldControl::getMin() +{ + return ImplGetPropertyValue_Time( BASEPROPERTY_TIMEMIN ); +} + +void UnoTimeFieldControl::setMax( const util::Time& Time ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_TIMEMAX ), uno::Any(Time), true ); +} + +util::Time UnoTimeFieldControl::getMax() +{ + return ImplGetPropertyValue_Time( BASEPROPERTY_TIMEMAX ); +} + +void UnoTimeFieldControl::setFirst( const util::Time& Time ) +{ + mnFirst = Time; + if ( getPeer().is() ) + { + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + } +} + +util::Time UnoTimeFieldControl::getFirst() +{ + return mnFirst; +} + +void UnoTimeFieldControl::setLast( const util::Time& Time ) +{ + mnLast = Time; + if ( getPeer().is() ) + { + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnLast ); + } +} + +util::Time UnoTimeFieldControl::getLast() +{ + return mnLast; +} + +void UnoTimeFieldControl::setEmpty() +{ + if ( getPeer().is() ) + { + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + xField->setEmpty(); + } +} + +sal_Bool UnoTimeFieldControl::isEmpty() +{ + bool bEmpty = false; + if ( getPeer().is() ) + { + uno::Reference < awt::XTimeField > xField( getPeer(), uno::UNO_QUERY ); + bEmpty = xField->isEmpty(); + } + return bEmpty; +} + +void UnoTimeFieldControl::setStrictFormat( sal_Bool bStrict ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRICTFORMAT ), uno::Any(bStrict), true ); +} + +sal_Bool UnoTimeFieldControl::isStrictFormat() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_STRICTFORMAT ); +} + +OUString UnoTimeFieldControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoTimeFieldControl"; +} + +css::uno::Sequence<OUString> UnoTimeFieldControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlTimeField", "stardiv.vcl.control.TimeField" }; + return comphelper::concatSequences( UnoSpinFieldControl::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoTimeFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoTimeFieldControl()); +} + + + +UnoControlNumericFieldModel::UnoControlNumericFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXNumericField>(); +} + +OUString UnoControlNumericFieldModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.NumericField"; +} + +uno::Any UnoControlNumericFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.NumericField" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + + +::cppu::IPropertyArrayHelper& UnoControlNumericFieldModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlNumericFieldModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlNumericFieldModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlNumericFieldModel"; +} + +css::uno::Sequence<OUString> +UnoControlNumericFieldModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "stardiv.vcl.controlmodel.NumericField", "com.sun.star.awt.UnoControlNumericFieldModel" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlNumericFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlNumericFieldModel(context)); +} + + + +UnoNumericFieldControl::UnoNumericFieldControl() +{ + mnFirst = 0; + mnLast = 0x7FFFFFFF; +} + +OUString UnoNumericFieldControl::GetComponentServiceName() const +{ + return "numericfield"; +} + +// uno::XInterface +uno::Any UnoNumericFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XNumericField* >(this) ); + return (aRet.hasValue() ? aRet : UnoSpinFieldControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoNumericFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoNumericFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XNumericField>::get(), + UnoSpinFieldControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoNumericFieldControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoSpinFieldControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XNumericField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + xField->setLast( mnLast ); +} + + +void UnoNumericFieldControl::textChanged( const awt::TextEvent& e ) +{ + uno::Reference < awt::XNumericField > xField( getPeer(), uno::UNO_QUERY ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUE_DOUBLE ), uno::Any(xField->getValue()), false ); + + if ( GetTextListeners().getLength() ) + GetTextListeners().textChanged( e ); +} + +void UnoNumericFieldControl::setValue( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUE_DOUBLE ), uno::Any(Value), true ); +} + +double UnoNumericFieldControl::getValue() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUE_DOUBLE ); +} + +void UnoNumericFieldControl::setMin( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUEMIN_DOUBLE ), uno::Any(Value), true ); +} + +double UnoNumericFieldControl::getMin() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUEMIN_DOUBLE ); +} + +void UnoNumericFieldControl::setMax( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUEMAX_DOUBLE ), uno::Any(Value), true ); +} + +double UnoNumericFieldControl::getMax() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUEMAX_DOUBLE ); +} + +void UnoNumericFieldControl::setFirst( double Value ) +{ + mnFirst = Value; + if ( getPeer().is() ) + { + uno::Reference < awt::XNumericField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + } +} + +double UnoNumericFieldControl::getFirst() +{ + return mnFirst; +} + +void UnoNumericFieldControl::setLast( double Value ) +{ + mnLast = Value; + if ( getPeer().is() ) + { + uno::Reference < awt::XNumericField > xField( getPeer(), uno::UNO_QUERY ); + xField->setLast( mnLast ); + } +} + +double UnoNumericFieldControl::getLast() +{ + return mnLast; +} + +void UnoNumericFieldControl::setStrictFormat( sal_Bool bStrict ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRICTFORMAT ), uno::Any(bStrict), true ); +} + +sal_Bool UnoNumericFieldControl::isStrictFormat() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_STRICTFORMAT ); +} + +OUString UnoNumericFieldControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoNumericFieldControl"; +} + +css::uno::Sequence<OUString> UnoNumericFieldControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlNumericField", "stardiv.vcl.control.NumericField" }; + return comphelper::concatSequences( UnoSpinFieldControl::getSupportedServiceNames(), vals ); +} + +void UnoNumericFieldControl::setSpinSize( double Digits ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUESTEP_DOUBLE ), uno::Any(Digits), true ); +} + +double UnoNumericFieldControl::getSpinSize() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUESTEP_DOUBLE ); +} + +void UnoNumericFieldControl::setDecimalDigits( sal_Int16 Digits ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DECIMALACCURACY ), uno::Any(Digits), true ); +} + +sal_Int16 UnoNumericFieldControl::getDecimalDigits() +{ + return ImplGetPropertyValue_INT16( BASEPROPERTY_DECIMALACCURACY ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoNumericFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoNumericFieldControl()); +} + +UnoControlCurrencyFieldModel::UnoControlCurrencyFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<SVTXCurrencyField>(); +} + +OUString UnoControlCurrencyFieldModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.CurrencyField"; +} + +uno::Any UnoControlCurrencyFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.CurrencyField" ) ); + } + if ( nPropId == BASEPROPERTY_CURSYM_POSITION ) + { + return uno::Any(false); + } + + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlCurrencyFieldModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlCurrencyFieldModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlCurrencyFieldModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlCurrencyFieldModel"; +} + +css::uno::Sequence<OUString> +UnoControlCurrencyFieldModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlCurrencyFieldModel", "stardiv.vcl.controlmodel.CurrencyField" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlCurrencyFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlCurrencyFieldModel(context)); +} + + + +UnoCurrencyFieldControl::UnoCurrencyFieldControl() +{ + mnFirst = 0; + mnLast = 0x7FFFFFFF; +} + +OUString UnoCurrencyFieldControl::GetComponentServiceName() const +{ + return "longcurrencyfield"; +} + +// uno::XInterface +uno::Any UnoCurrencyFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XCurrencyField* >(this) ); + return (aRet.hasValue() ? aRet : UnoSpinFieldControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoCurrencyFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoCurrencyFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XCurrencyField>::get(), + UnoSpinFieldControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoCurrencyFieldControl::createPeer( const uno::Reference< awt::XToolkit > & rxToolkit, const uno::Reference< awt::XWindowPeer > & rParentPeer ) +{ + UnoSpinFieldControl::createPeer( rxToolkit, rParentPeer ); + + uno::Reference < awt::XCurrencyField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + xField->setLast( mnLast ); +} + +void UnoCurrencyFieldControl::textChanged( const awt::TextEvent& e ) +{ + uno::Reference < awt::XCurrencyField > xField( getPeer(), uno::UNO_QUERY ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUE_DOUBLE ), uno::Any(xField->getValue()), false ); + + if ( GetTextListeners().getLength() ) + GetTextListeners().textChanged( e ); +} + +void UnoCurrencyFieldControl::setValue( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUE_DOUBLE ), Any(Value), true ); +} + +double UnoCurrencyFieldControl::getValue() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUE_DOUBLE ); +} + +void UnoCurrencyFieldControl::setMin( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUEMIN_DOUBLE ), uno::Any(Value), true ); +} + +double UnoCurrencyFieldControl::getMin() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUEMIN_DOUBLE ); +} + +void UnoCurrencyFieldControl::setMax( double Value ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUEMAX_DOUBLE ), uno::Any(Value), true ); +} + +double UnoCurrencyFieldControl::getMax() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUEMAX_DOUBLE ); +} + +void UnoCurrencyFieldControl::setFirst( double Value ) +{ + mnFirst = Value; + if ( getPeer().is() ) + { + uno::Reference < awt::XCurrencyField > xField( getPeer(), uno::UNO_QUERY ); + xField->setFirst( mnFirst ); + } +} + +double UnoCurrencyFieldControl::getFirst() +{ + return mnFirst; +} + +void UnoCurrencyFieldControl::setLast( double Value ) +{ + mnLast = Value; + if ( getPeer().is() ) + { + uno::Reference < awt::XCurrencyField > xField( getPeer(), uno::UNO_QUERY ); + xField->setLast( mnLast ); + } +} + +double UnoCurrencyFieldControl::getLast() +{ + return mnLast; +} + +void UnoCurrencyFieldControl::setStrictFormat( sal_Bool bStrict ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRICTFORMAT ), uno::Any(bStrict), true ); +} + +sal_Bool UnoCurrencyFieldControl::isStrictFormat() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_STRICTFORMAT ); +} + +OUString UnoCurrencyFieldControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoCurrencyFieldControl"; +} + +css::uno::Sequence<OUString> +UnoCurrencyFieldControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlCurrencyField", "stardiv.vcl.control.CurrencyField" }; + return comphelper::concatSequences( UnoSpinFieldControl::getSupportedServiceNames(), vals ); +} + +void UnoCurrencyFieldControl::setSpinSize( double Digits ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_VALUESTEP_DOUBLE ), uno::Any(Digits), true ); +} + +double UnoCurrencyFieldControl::getSpinSize() +{ + return ImplGetPropertyValue_DOUBLE( BASEPROPERTY_VALUESTEP_DOUBLE ); +} + +void UnoCurrencyFieldControl::setDecimalDigits( sal_Int16 Digits ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_DECIMALACCURACY ), uno::Any(Digits), true ); +} + +sal_Int16 UnoCurrencyFieldControl::getDecimalDigits() +{ + return ImplGetPropertyValue_INT16( BASEPROPERTY_DECIMALACCURACY ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoCurrencyFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoCurrencyFieldControl()); +} + + + +UnoControlPatternFieldModel::UnoControlPatternFieldModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + UNO_CONTROL_MODEL_REGISTER_PROPERTIES<VCLXPatternField>(); +} + +OUString UnoControlPatternFieldModel::getServiceName() +{ + return "stardiv.vcl.controlmodel.PatternField"; +} + +uno::Any UnoControlPatternFieldModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.PatternField" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlPatternFieldModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlPatternFieldModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlPatternFieldModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlPatternFieldModel"; +} + +css::uno::Sequence<OUString> +UnoControlPatternFieldModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlPatternFieldModel", "stardiv.vcl.controlmodel.PatternField" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlPatternFieldModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlPatternFieldModel(context)); +} + + + +UnoPatternFieldControl::UnoPatternFieldControl() +{ +} + +OUString UnoPatternFieldControl::GetComponentServiceName() const +{ + return "patternfield"; +} + +void UnoPatternFieldControl::ImplSetPeerProperty( const OUString& rPropName, const uno::Any& rVal ) +{ + sal_uInt16 nType = GetPropertyId( rPropName ); + if ( ( nType == BASEPROPERTY_TEXT ) || ( nType == BASEPROPERTY_EDITMASK ) || ( nType == BASEPROPERTY_LITERALMASK ) ) + { + // These masks cannot be set consecutively + OUString Text = ImplGetPropertyValue_UString( BASEPROPERTY_TEXT ); + OUString EditMask = ImplGetPropertyValue_UString( BASEPROPERTY_EDITMASK ); + OUString LiteralMask = ImplGetPropertyValue_UString( BASEPROPERTY_LITERALMASK ); + + uno::Reference < awt::XPatternField > xPF( getPeer(), uno::UNO_QUERY ); + if (xPF.is()) + { + // same comment as in UnoControl::ImplSetPeerProperty - see there + OUString sText( Text ); + ImplCheckLocalize( sText ); + xPF->setString( sText ); + xPF->setMasks( EditMask, LiteralMask ); + } + } + else + UnoSpinFieldControl::ImplSetPeerProperty( rPropName, rVal ); +} + + +// uno::XInterface +uno::Any UnoPatternFieldControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XPatternField* >(this) ); + return (aRet.hasValue() ? aRet : UnoSpinFieldControl::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoPatternFieldControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoPatternFieldControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XPatternField>::get(), + UnoSpinFieldControl::getTypes() + ); + return aTypeList.getTypes(); +} + +void UnoPatternFieldControl::setString( const OUString& rString ) +{ + setText( rString ); +} + +OUString UnoPatternFieldControl::getString() +{ + return getText(); +} + +void UnoPatternFieldControl::setMasks( const OUString& EditMask, const OUString& LiteralMask ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_EDITMASK ), uno::Any(EditMask), true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_LITERALMASK ), uno::Any(LiteralMask), true ); +} + +void UnoPatternFieldControl::getMasks( OUString& EditMask, OUString& LiteralMask ) +{ + EditMask = ImplGetPropertyValue_UString( BASEPROPERTY_EDITMASK ); + LiteralMask = ImplGetPropertyValue_UString( BASEPROPERTY_LITERALMASK ); +} + +void UnoPatternFieldControl::setStrictFormat( sal_Bool bStrict ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_STRICTFORMAT ), uno::Any(bStrict), true ); +} + +sal_Bool UnoPatternFieldControl::isStrictFormat() +{ + return ImplGetPropertyValue_BOOL( BASEPROPERTY_STRICTFORMAT ); +} + +OUString UnoPatternFieldControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoPatternFieldControl"; +} + +css::uno::Sequence<OUString> UnoPatternFieldControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlPatternField", "stardiv.vcl.control.PatternField" }; + return comphelper::concatSequences( UnoSpinFieldControl::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoPatternFieldControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoPatternFieldControl()); +} + + + +UnoControlProgressBarModel::UnoControlProgressBarModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_BORDER ); + ImplRegisterProperty( BASEPROPERTY_BORDERCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FILLCOLOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); + ImplRegisterProperty( BASEPROPERTY_PROGRESSVALUE ); + ImplRegisterProperty( BASEPROPERTY_PROGRESSVALUE_MAX ); + ImplRegisterProperty( BASEPROPERTY_PROGRESSVALUE_MIN ); +} + +OUString UnoControlProgressBarModel::getServiceName( ) +{ + return "stardiv.vcl.controlmodel.ProgressBar"; +} + +uno::Any UnoControlProgressBarModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.ProgressBar" ) ); + } + + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlProgressBarModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlProgressBarModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlProgressBarModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlProgressBarModel"; +} + +css::uno::Sequence<OUString> +UnoControlProgressBarModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlProgressBarModel", "stardiv.vcl.controlmodel.ProgressBar" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlProgressBarModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlProgressBarModel(context)); +} + + + +UnoProgressBarControl::UnoProgressBarControl() +{ +} + +OUString UnoProgressBarControl::GetComponentServiceName() const +{ + return "ProgressBar"; +} + +// uno::XInterface +uno::Any UnoProgressBarControl::queryAggregation( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< awt::XProgressBar* >(this) ); + return (aRet.hasValue() ? aRet : UnoControlBase::queryAggregation( rType )); +} + +IMPL_IMPLEMENTATION_ID( UnoProgressBarControl ) + +// lang::XTypeProvider +css::uno::Sequence< css::uno::Type > UnoProgressBarControl::getTypes() +{ + static const ::cppu::OTypeCollection aTypeList( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<awt::XProgressBar>::get(), + UnoControlBase::getTypes() + ); + return aTypeList.getTypes(); +} + +// css::awt::XProgressBar +void UnoProgressBarControl::setForegroundColor( sal_Int32 nColor ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_FILLCOLOR ), uno::Any(nColor), true ); +} + +void UnoProgressBarControl::setBackgroundColor( sal_Int32 nColor ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_BACKGROUNDCOLOR ), uno::Any(nColor), true ); +} + +void UnoProgressBarControl::setValue( sal_Int32 nValue ) +{ + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_PROGRESSVALUE ), uno::Any(nValue), true ); +} + +void UnoProgressBarControl::setRange( sal_Int32 nMin, sal_Int32 nMax ) +{ + uno::Any aMin; + uno::Any aMax; + + if ( nMin < nMax ) + { + // take correct min and max + aMin <<= nMin; + aMax <<= nMax; + } + else + { + // change min and max + aMin <<= nMax; + aMax <<= nMin; + } + + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_PROGRESSVALUE_MIN ), aMin, true ); + ImplSetPropertyValue( GetPropertyName( BASEPROPERTY_PROGRESSVALUE_MAX ), aMax, true ); +} + +sal_Int32 UnoProgressBarControl::getValue() +{ + return ImplGetPropertyValue_INT32( BASEPROPERTY_PROGRESSVALUE ); +} + +OUString UnoProgressBarControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoProgressBarControl"; +} + +css::uno::Sequence<OUString> UnoProgressBarControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlProgressBar", "stardiv.vcl.control.ProgressBar" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoProgressBarControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoProgressBarControl()); +} + + + +UnoControlFixedLineModel::UnoControlFixedLineModel( const Reference< XComponentContext >& rxContext ) + :UnoControlModel( rxContext ) +{ + ImplRegisterProperty( BASEPROPERTY_BACKGROUNDCOLOR ); + ImplRegisterProperty( BASEPROPERTY_DEFAULTCONTROL ); + ImplRegisterProperty( BASEPROPERTY_ENABLED ); + ImplRegisterProperty( BASEPROPERTY_ENABLEVISIBLE ); + ImplRegisterProperty( BASEPROPERTY_FONTDESCRIPTOR ); + ImplRegisterProperty( BASEPROPERTY_HELPTEXT ); + ImplRegisterProperty( BASEPROPERTY_HELPURL ); + ImplRegisterProperty( BASEPROPERTY_LABEL ); + ImplRegisterProperty( BASEPROPERTY_ORIENTATION ); + ImplRegisterProperty( BASEPROPERTY_PRINTABLE ); +} + +OUString UnoControlFixedLineModel::getServiceName( ) +{ + return "stardiv.vcl.controlmodel.FixedLine"; +} + +uno::Any UnoControlFixedLineModel::ImplGetDefaultValue( sal_uInt16 nPropId ) const +{ + if ( nPropId == BASEPROPERTY_DEFAULTCONTROL ) + { + return uno::Any( OUString( "stardiv.vcl.control.FixedLine" ) ); + } + return UnoControlModel::ImplGetDefaultValue( nPropId ); +} + +::cppu::IPropertyArrayHelper& UnoControlFixedLineModel::getInfoHelper() +{ + static UnoPropertyArrayHelper aHelper( ImplGetPropertyIds() ); + return aHelper; +} + +// beans::XMultiPropertySet +uno::Reference< beans::XPropertySetInfo > UnoControlFixedLineModel::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString UnoControlFixedLineModel::getImplementationName() +{ + return "stardiv.Toolkit.UnoControlFixedLineModel"; +} + +css::uno::Sequence<OUString> +UnoControlFixedLineModel::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFixedLineModel", "stardiv.vcl.controlmodel.FixedLine" }; + return comphelper::concatSequences( UnoControlModel::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoControlFixedLineModel_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoControlFixedLineModel(context)); +} + + + +UnoFixedLineControl::UnoFixedLineControl() +{ + maComponentInfos.nWidth = 100; // ?? + maComponentInfos.nHeight = 100; // ?? +} + +OUString UnoFixedLineControl::GetComponentServiceName() const +{ + return "FixedLine"; +} + +sal_Bool UnoFixedLineControl::isTransparent() +{ + return true; +} + +OUString UnoFixedLineControl::getImplementationName() +{ + return "stardiv.Toolkit.UnoFixedLineControl"; +} + +css::uno::Sequence<OUString> UnoFixedLineControl::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.awt.UnoControlFixedLine", "stardiv.vcl.control.FixedLine" }; + return comphelper::concatSequences( UnoControlBase::getSupportedServiceNames(), vals ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +stardiv_Toolkit_UnoFixedLineControl_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UnoFixedLineControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontroltablemodel.cxx b/toolkit/source/controls/unocontroltablemodel.cxx new file mode 100644 index 0000000000..50f6cac3cf --- /dev/null +++ b/toolkit/source/controls/unocontroltablemodel.cxx @@ -0,0 +1,834 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "unocontroltablemodel.hxx" +#include "unogridcolumnfacade.hxx" + +#include <controls/table/defaultinputhandler.hxx> +#include <controls/table/gridtablerenderer.hxx> + +#include <com/sun/star/awt/grid/XSortableGridData.hpp> +#include <com/sun/star/util/Color.hpp> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <cppuhelper/weakref.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> + + +namespace svt::table +{ + + + using css::uno::Reference; + using css::uno::Sequence; + using css::uno::UNO_QUERY_THROW; + using css::uno::UNO_QUERY; + using css::awt::grid::XGridColumn; + using css::uno::Exception; + using css::awt::grid::XGridDataModel; + using css::awt::grid::XGridColumnModel; + using css::uno::Any; + using css::style::VerticalAlignment_TOP; + using css::style::VerticalAlignment; + using css::awt::grid::GridDataEvent; + using css::awt::grid::XSortableGridData; + using css::beans::Pair; + + + //= UnoControlTableModel +#define DBG_CHECK_ME() \ + DBG_TESTSOLARMUTEX(); \ + + UnoControlTableModel::UnoControlTableModel() + :aColumns ( ) + ,bHasColumnHeaders ( true ) + ,bHasRowHeaders ( false ) + ,eVScrollMode ( ScrollbarShowNever ) + ,eHScrollMode ( ScrollbarShowNever ) + ,pRenderer ( ) + ,pInputHandler ( ) + ,nRowHeight ( 10 ) + ,nColumnHeaderHeight ( 10 ) + ,nRowHeaderWidth ( 10 ) + ,m_aGridLineColor ( ) + ,m_aHeaderBackgroundColor ( ) + ,m_aHeaderTextColor ( ) + ,m_aActiveSelectionBackColor ( ) + ,m_aInactiveSelectionBackColor ( ) + ,m_aActiveSelectionTextColor ( ) + ,m_aInactiveSelectionTextColor ( ) + ,m_aTextColor ( ) + ,m_aTextLineColor ( ) + ,m_aRowColors ( ) + ,m_eVerticalAlign ( VerticalAlignment_TOP ) + ,bEnabled ( true ) + { + pRenderer = std::make_shared<GridTableRenderer>( *this ); + pInputHandler = std::make_shared<DefaultInputHandler>(); + } + + + UnoControlTableModel::~UnoControlTableModel() + { + } + + + TableSize UnoControlTableModel::getColumnCount() const + { + DBG_CHECK_ME(); + return static_cast<TableSize>(aColumns.size()); + } + + + TableSize UnoControlTableModel::getRowCount() const + { + DBG_CHECK_ME(); + + TableSize nRowCount = 0; + try + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_THROW( xDataModel.is(), "no data model anymore!" ); + nRowCount = xDataModel->getRowCount(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return nRowCount; + } + + + bool UnoControlTableModel::hasColumnHeaders() const + { + DBG_CHECK_ME(); + return bHasColumnHeaders; + } + + + bool UnoControlTableModel::hasRowHeaders() const + { + DBG_CHECK_ME(); + return bHasRowHeaders; + } + + + void UnoControlTableModel::setRowHeaders(bool _bRowHeaders) + { + DBG_CHECK_ME(); + if ( bHasRowHeaders == _bRowHeaders ) + return; + + bHasRowHeaders = _bRowHeaders; + impl_notifyTableMetricsChanged(); + } + + + void UnoControlTableModel::setColumnHeaders(bool _bColumnHeaders) + { + DBG_CHECK_ME(); + if ( bHasColumnHeaders == _bColumnHeaders ) + return; + + bHasColumnHeaders = _bColumnHeaders; + impl_notifyTableMetricsChanged(); + } + + + PColumnModel UnoControlTableModel::getColumnModel( ColPos column ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN( ( column >= 0 ) && ( column < getColumnCount() ), + "DefaultTableModel::getColumnModel: invalid index!", PColumnModel() ); + return aColumns[ column ]; + } + + + void UnoControlTableModel::appendColumn( Reference< XGridColumn > const & i_column ) + { + DBG_CHECK_ME(); + insertColumn( aColumns.size(), i_column ); + } + + + void UnoControlTableModel::insertColumn( ColPos const i_position, Reference< XGridColumn > const & i_column ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( ( i_position >= 0 ) && ( o3tl::make_unsigned( i_position ) <= aColumns.size() ), + "UnoControlTableModel::insertColumn: illegal position!" ); + + const PColumnModel pColumn = std::make_shared<UnoGridColumnFacade>( *this, i_column ); + aColumns.insert( aColumns.begin() + i_position, pColumn ); + + // notify listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->columnInserted(); + } + } + + + void UnoControlTableModel::removeColumn( ColPos const i_position ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( ( i_position >= 0 ) && ( o3tl::make_unsigned( i_position ) <= aColumns.size() ), + "UnoControlTableModel::removeColumn: illegal position!" ); + + // remove the column + ColumnModels::iterator pos = aColumns.begin() + i_position; + const PColumnModel pColumn = *pos; + aColumns.erase( pos ); + + // notify listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->columnRemoved(); + } + + // dispose the column + UnoGridColumnFacade* pColumnImpl = dynamic_cast< UnoGridColumnFacade* >( pColumn.get() ); + OSL_ENSURE( pColumnImpl != nullptr, "UnoControlTableModel::removeColumn: illegal column implementation!" ); + if ( pColumnImpl ) + pColumnImpl->dispose(); + } + + + void UnoControlTableModel::removeAllColumns() + { + DBG_CHECK_ME(); + if ( aColumns.empty() ) + return; + + // dispose the column instances + for (auto const& col : aColumns) + { + UnoGridColumnFacade* pColumn = dynamic_cast< UnoGridColumnFacade* >( col.get() ); + if ( !pColumn ) + { + SAL_WARN( "svtools.uno", "UnoControlTableModel::removeAllColumns: illegal column implementation!" ); + continue; + } + + pColumn->dispose(); + } + aColumns.clear(); + + // notify listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->allColumnsRemoved(); + } + } + + + void UnoControlTableModel::impl_notifyTableMetricsChanged() const + { + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->tableMetricsChanged(); + } + } + + + PTableRenderer UnoControlTableModel::getRenderer() const + { + DBG_CHECK_ME(); + return pRenderer; + } + + + PTableInputHandler UnoControlTableModel::getInputHandler() const + { + DBG_CHECK_ME(); + return pInputHandler; + } + + + TableMetrics UnoControlTableModel::getRowHeight() const + { + DBG_CHECK_ME(); + return nRowHeight; + } + + + void UnoControlTableModel::setRowHeight(TableMetrics _nRowHeight) + { + DBG_CHECK_ME(); + if ( nRowHeight == _nRowHeight ) + return; + + nRowHeight = _nRowHeight; + impl_notifyTableMetricsChanged(); + } + + + TableMetrics UnoControlTableModel::getColumnHeaderHeight() const + { + DBG_CHECK_ME(); + DBG_ASSERT( hasColumnHeaders(), "DefaultTableModel::getColumnHeaderHeight: invalid call!" ); + return nColumnHeaderHeight; + } + + + TableMetrics UnoControlTableModel::getRowHeaderWidth() const + { + DBG_CHECK_ME(); + DBG_ASSERT( hasRowHeaders(), "DefaultTableModel::getRowHeaderWidth: invalid call!" ); + return nRowHeaderWidth; + } + + void UnoControlTableModel::setColumnHeaderHeight(TableMetrics _nHeight) + { + DBG_CHECK_ME(); + if ( nColumnHeaderHeight == _nHeight ) + return; + + nColumnHeaderHeight = _nHeight; + impl_notifyTableMetricsChanged(); + } + + + void UnoControlTableModel::setRowHeaderWidth(TableMetrics _nWidth) + { + DBG_CHECK_ME(); + if ( nRowHeaderWidth == _nWidth ) + return; + + nRowHeaderWidth = _nWidth; + impl_notifyTableMetricsChanged(); + } + + + ScrollbarVisibility UnoControlTableModel::getVerticalScrollbarVisibility() const + { + DBG_CHECK_ME(); + return eVScrollMode; + } + + + ScrollbarVisibility UnoControlTableModel::getHorizontalScrollbarVisibility() const + { + DBG_CHECK_ME(); + return eHScrollMode; + } + + + void UnoControlTableModel::addTableModelListener( const PTableModelListener& i_listener ) + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( !!i_listener, "illegal NULL listener" ); + m_aListeners.push_back( i_listener ); + } + + + void UnoControlTableModel::removeTableModelListener( const PTableModelListener& i_listener ) + { + DBG_CHECK_ME(); + auto lookup = std::find(m_aListeners.begin(), m_aListeners.end(), i_listener); + if (lookup != m_aListeners.end()) + { + m_aListeners.erase( lookup ); + return; + } + OSL_ENSURE( false, "UnoControlTableModel::removeTableModelListener: listener is not registered - sure you're doing the right thing here?" ); + } + + + void UnoControlTableModel::setVerticalScrollbarVisibility( ScrollbarVisibility const i_visibility ) + { + DBG_CHECK_ME(); + eVScrollMode = i_visibility; + } + + + void UnoControlTableModel::setHorizontalScrollbarVisibility( ScrollbarVisibility const i_visibility ) + { + DBG_CHECK_ME(); + eHScrollMode = i_visibility; + } + + + void UnoControlTableModel::setDataModel( Reference< XGridDataModel > const & i_gridDataModel ) + { + DBG_CHECK_ME(); + m_aDataModel = i_gridDataModel; + // TODO: register as listener, so we're notified of row/data changes, and can multiplex them to our + // own listeners + } + + + Reference< XGridDataModel > UnoControlTableModel::getDataModel() const + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + return xDataModel; + } + + + bool UnoControlTableModel::hasDataModel() const + { + return getDataModel().is(); + } + + + void UnoControlTableModel::setColumnModel( Reference< XGridColumnModel > const & i_gridColumnModel ) + { + DBG_CHECK_ME(); + m_aColumnModel = i_gridColumnModel; + } + + + Reference< XGridColumnModel > UnoControlTableModel::getColumnModel() const + { + Reference< XGridColumnModel > const xColumnModel( m_aColumnModel ); + return xColumnModel; + } + + + bool UnoControlTableModel::hasColumnModel() const + { + return getColumnModel().is(); + } + + + void UnoControlTableModel::getCellContent( ColPos const i_col, RowPos const i_row, Any& o_cellContent ) + { + DBG_CHECK_ME(); + + o_cellContent.clear(); + try + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_RETURN_VOID( xDataModel.is(), "UnoControlTableModel::getCellContent: no data model anymore!" ); + + PColumnModel const pColumn = getColumnModel( i_col ); + UnoGridColumnFacade* pColumnImpl = dynamic_cast< UnoGridColumnFacade* >( pColumn.get() ); + ENSURE_OR_RETURN_VOID( pColumnImpl != nullptr, "UnoControlTableModel::getCellContent: no (valid) column at this position!" ); + sal_Int32 const nDataColumnIndex = pColumnImpl->getDataColumnIndex() >= 0 ? pColumnImpl->getDataColumnIndex() : i_col; + + if ( nDataColumnIndex >= xDataModel->getColumnCount() ) + { + // this is allowed, in case the column model has been dynamically extended, but the data model does + // not (yet?) know about it. + // So, handle it gracefully. + #if OSL_DEBUG_LEVEL > 0 + Reference< XGridColumnModel > const xColumnModel( m_aColumnModel ); + OSL_ENSURE( xColumnModel.is() && i_col < xColumnModel->getColumnCount(), + "UnoControlTableModel::getCellContent: request a column's value which the ColumnModel doesn't know about!" ); + #endif + } + else + { + o_cellContent = xDataModel->getCellData( nDataColumnIndex, i_row ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + void UnoControlTableModel::getCellToolTip( ColPos const i_col, RowPos const i_row, Any& o_cellToolTip ) + { + DBG_CHECK_ME(); + try + { + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_THROW( xDataModel.is(), "no data model anymore!" ); + + o_cellToolTip = xDataModel->getCellToolTip( i_col, i_row ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + Any UnoControlTableModel::getRowHeading( RowPos const i_rowPos ) const + { + DBG_CHECK_ME(); + + Any aRowHeading; + + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_RETURN( xDataModel.is(), "UnoControlTableModel::getRowHeading: no data model anymore!", aRowHeading ); + + try + { + aRowHeading = xDataModel->getRowHeading( i_rowPos ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return aRowHeading; + } + + + namespace + { + void lcl_setColor( Any const & i_color, ::std::optional< ::Color > & o_convertedColor ) + { + if ( !i_color.hasValue() ) + o_convertedColor.reset(); + else + { + Color nColor = COL_TRANSPARENT; + if ( i_color >>= nColor ) + { + o_convertedColor = nColor; + } + else + { + OSL_ENSURE( false, "lcl_setColor: could not extract color value!" ); + } + } + } + } + + + ::std::optional< ::Color > UnoControlTableModel::getLineColor() const + { + DBG_CHECK_ME(); + return m_aGridLineColor; + } + + + void UnoControlTableModel::setLineColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aGridLineColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getHeaderBackgroundColor() const + { + DBG_CHECK_ME(); + return m_aHeaderBackgroundColor; + } + + + void UnoControlTableModel::setHeaderBackgroundColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aHeaderBackgroundColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getHeaderTextColor() const + { + DBG_CHECK_ME(); + return m_aHeaderTextColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getActiveSelectionBackColor() const + { + DBG_CHECK_ME(); + return m_aActiveSelectionBackColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getInactiveSelectionBackColor() const + { + DBG_CHECK_ME(); + return m_aInactiveSelectionBackColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getActiveSelectionTextColor() const + { + DBG_CHECK_ME(); + return m_aActiveSelectionTextColor; + } + + + ::std::optional< ::Color > UnoControlTableModel::getInactiveSelectionTextColor() const + { + DBG_CHECK_ME(); + return m_aInactiveSelectionTextColor; + } + + + void UnoControlTableModel::setHeaderTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aHeaderTextColor ); + } + + + void UnoControlTableModel::setActiveSelectionBackColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aActiveSelectionBackColor ); + } + + + void UnoControlTableModel::setInactiveSelectionBackColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aInactiveSelectionBackColor ); + } + + + void UnoControlTableModel::setActiveSelectionTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aActiveSelectionTextColor ); + } + + + void UnoControlTableModel::setInactiveSelectionTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aInactiveSelectionTextColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getTextColor() const + { + DBG_CHECK_ME(); + return m_aTextColor; + } + + + void UnoControlTableModel::setTextColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aTextColor ); + } + + + ::std::optional< ::Color > UnoControlTableModel::getTextLineColor() const + { + DBG_CHECK_ME(); + return m_aTextColor; + } + + + void UnoControlTableModel::setTextLineColor( Any const & i_color ) + { + DBG_CHECK_ME(); + lcl_setColor( i_color, m_aTextLineColor ); + } + + + ::std::optional< ::std::vector< ::Color > > UnoControlTableModel::getRowBackgroundColors() const + { + DBG_CHECK_ME(); + return m_aRowColors; + } + + + void UnoControlTableModel::setRowBackgroundColors( css::uno::Any const & i_APIValue ) + { + DBG_CHECK_ME(); + Sequence< css::util::Color > aAPIColors; + if ( !( i_APIValue >>= aAPIColors ) ) + m_aRowColors.reset(); + else + { + ::std::vector< ::Color > aColors( aAPIColors.getLength() ); + std::transform(std::cbegin(aAPIColors), std::cend(aAPIColors), aColors.begin(), + [](const css::util::Color& rAPIColor) -> ::Color { return Color(ColorTransparency, rAPIColor); }); + m_aRowColors = aColors; + } + } + + + VerticalAlignment UnoControlTableModel::getVerticalAlign() const + { + DBG_CHECK_ME(); + return m_eVerticalAlign; + } + + + void UnoControlTableModel::setVerticalAlign( VerticalAlignment _xAlign ) + { + DBG_CHECK_ME(); + m_eVerticalAlign = _xAlign; + } + + + ColPos UnoControlTableModel::getColumnPos( UnoGridColumnFacade const & i_column ) const + { + DBG_CHECK_ME(); + ColPos nPos = 0; + for (auto const& col : aColumns) + { + if ( &i_column == col.get() ) + return nPos; + ++nPos; + } + OSL_ENSURE( false, "UnoControlTableModel::getColumnPos: column not found!" ); + return COL_INVALID; + } + + + ITableDataSort* UnoControlTableModel::getSortAdapter() + { + DBG_CHECK_ME(); + + Reference< XSortableGridData > const xSortAccess( getDataModel(), UNO_QUERY ); + if ( xSortAccess.is() ) + return this; + return nullptr; + } + + + bool UnoControlTableModel::isEnabled() const + { + DBG_CHECK_ME(); + return bEnabled; + } + + + void UnoControlTableModel::setEnabled( bool _bEnabled ) + { + DBG_CHECK_ME(); + bEnabled = _bEnabled; + } + + + void UnoControlTableModel::sortByColumn( ColPos const i_column, ColumnSortDirection const i_sortDirection ) + { + DBG_CHECK_ME(); + + try + { + Reference< XSortableGridData > const xSortAccess( getDataModel(), UNO_QUERY_THROW ); + xSortAccess->sortByColumn( i_column, i_sortDirection == ColumnSortAscending ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + ColumnSort UnoControlTableModel::getCurrentSortOrder() const + { + DBG_CHECK_ME(); + + ColumnSort currentSort; + try + { + Reference< XSortableGridData > const xSortAccess( getDataModel(), UNO_QUERY_THROW ); + Pair< ::sal_Int32, sal_Bool > const aCurrentSortOrder( xSortAccess->getCurrentSortOrder() ); + currentSort.nColumnPos = aCurrentSortOrder.First; + currentSort.eSortDirection = aCurrentSortOrder.Second ? ColumnSortAscending : ColumnSortDescending; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return currentSort; + } + + + void UnoControlTableModel::notifyColumnChange( ColPos const i_columnPos, ColumnAttributeGroup const i_attributeGroup ) const + { + DBG_CHECK_ME(); + ENSURE_OR_RETURN_VOID( ( i_columnPos >= 0 ) && ( i_columnPos < getColumnCount() ), + "UnoControlTableModel::notifyColumnChange: invalid column index!" ); + + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->columnChanged( i_columnPos, i_attributeGroup ); + } + } + + + void UnoControlTableModel::notifyRowsInserted( GridDataEvent const & i_event ) const + { + // check sanity of the event + ENSURE_OR_RETURN_VOID( i_event.FirstRow >= 0, "UnoControlTableModel::notifyRowsInserted: invalid first row!" ); + ENSURE_OR_RETURN_VOID( i_event.LastRow >= i_event.FirstRow, "UnoControlTableModel::notifyRowsInserted: invalid row indexes!" ); + + // check own sanity + Reference< XGridColumnModel > const xColumnModel( m_aColumnModel ); + ENSURE_OR_RETURN_VOID( xColumnModel.is(), "UnoControlTableModel::notifyRowsInserted: no column model anymore!" ); + + Reference< XGridDataModel > const xDataModel( m_aDataModel ); + ENSURE_OR_RETURN_VOID( xDataModel.is(), "UnoControlTableModel::notifyRowsInserted: no data model anymore!" ); + + // implicitly add columns to the column model + // TODO: is this really a good idea? + sal_Int32 const dataColumnCount = xDataModel->getColumnCount(); + OSL_ENSURE( dataColumnCount > 0, "UnoControlTableModel::notifyRowsInserted: no columns at all?" ); + + sal_Int32 const modelColumnCount = xColumnModel->getColumnCount(); + if ( ( modelColumnCount == 0 ) && ( dataColumnCount > 0 ) ) + { + // TODO: shouldn't we clear the mutexes guard for this call? + xColumnModel->setDefaultColumns( dataColumnCount ); + } + + // multiplex the event to our own listeners + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->rowsInserted( i_event.FirstRow, i_event.LastRow ); + } + } + + + void UnoControlTableModel::notifyRowsRemoved( GridDataEvent const & i_event ) const + { + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->rowsRemoved( i_event.FirstRow, i_event.LastRow ); + } + } + + + void UnoControlTableModel::notifyDataChanged( css::awt::grid::GridDataEvent const & i_event ) const + { + RowPos const firstRow = i_event.FirstRow == -1 ? 0 : i_event.FirstRow; + RowPos const lastRow = i_event.FirstRow == -1 ? getRowCount() - 1 : i_event.LastRow; + + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->cellsUpdated( firstRow, lastRow ); + } + } + + + void UnoControlTableModel::notifyAllDataChanged() const + { + ModellListeners aListeners( m_aListeners ); + for (auto const& listener : aListeners) + { + listener->cellsUpdated( 0, getRowCount() - 1 ); + } + } + + +} // svt::table + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unocontroltablemodel.hxx b/toolkit/source/controls/unocontroltablemodel.hxx new file mode 100644 index 0000000000..2619407941 --- /dev/null +++ b/toolkit/source/controls/unocontroltablemodel.hxx @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <controls/table/tablemodel.hxx> +#include <controls/table/tablesort.hxx> +#include <tools/color.hxx> + +#include <com/sun/star/awt/grid/GridDataEvent.hpp> +#include <com/sun/star/awt/grid/XGridColumnModel.hpp> +#include <com/sun/star/awt/grid/XGridDataModel.hpp> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <cppuhelper/weakref.hxx> + + +namespace svt::table +{ + + + //= UnoControlTableModel + + class UnoGridColumnFacade; + class UnoControlTableModel : public ITableModel, public ITableDataSort + { + public: + UnoControlTableModel(); + virtual ~UnoControlTableModel() override; + + public: + // ITableModel overridables + virtual TableSize getColumnCount() const override; + virtual TableSize getRowCount() const override; + virtual bool hasColumnHeaders() const override; + virtual bool hasRowHeaders() const override; + virtual PColumnModel getColumnModel( ColPos column ) override; + virtual PTableRenderer getRenderer() const override; + virtual PTableInputHandler getInputHandler() const override; + virtual TableMetrics getRowHeight() const override; + virtual TableMetrics getColumnHeaderHeight() const override; + virtual TableMetrics getRowHeaderWidth() const override; + virtual ScrollbarVisibility getVerticalScrollbarVisibility() const override; + virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const override; + virtual void addTableModelListener( const PTableModelListener& i_listener ) override; + virtual void removeTableModelListener( const PTableModelListener& i_listener ) override; + virtual void getCellContent( ColPos const i_col, RowPos const i_row, css::uno::Any& o_cellContent ) override; + virtual void getCellToolTip( ColPos const i_col, RowPos const i_row, css::uno::Any & o_cellToolTip ) override; + virtual css::uno::Any getRowHeading( RowPos const i_rowPos ) const override; + virtual ::std::optional< ::Color > getLineColor() const override; + virtual ::std::optional< ::Color > getHeaderBackgroundColor() const override; + virtual ::std::optional< ::Color > getHeaderTextColor() const override; + virtual ::std::optional< ::Color > getActiveSelectionBackColor() const override; + virtual ::std::optional< ::Color > getInactiveSelectionBackColor() const override; + virtual ::std::optional< ::Color > getActiveSelectionTextColor() const override; + virtual ::std::optional< ::Color > getInactiveSelectionTextColor() const override; + virtual ::std::optional< ::Color > getTextColor() const override; + virtual ::std::optional< ::Color > getTextLineColor() const override; + virtual ::std::optional< ::std::vector< ::Color > > + getRowBackgroundColors() const override; + virtual css::style::VerticalAlignment + getVerticalAlign() const override; + virtual ITableDataSort* getSortAdapter() override; + virtual bool isEnabled() const override; + + // ITableDataSort overridables + virtual void sortByColumn( ColPos const i_column, ColumnSortDirection const i_sortDirection ) override; + virtual ColumnSort getCurrentSortOrder() const override; + + // column write access + void appendColumn( css::uno::Reference< css::awt::grid::XGridColumn > const & i_column ); + void insertColumn( ColPos const i_position, css::uno::Reference< css::awt::grid::XGridColumn > const & i_column ); + void removeColumn( ColPos const i_position ); + void removeAllColumns(); + + // other operations + void setVerticalScrollbarVisibility( ScrollbarVisibility const i_visibility ); + void setHorizontalScrollbarVisibility( ScrollbarVisibility const i_visibility ); + + void setDataModel( css::uno::Reference< css::awt::grid::XGridDataModel > const & i_gridDataModel ); + bool hasDataModel() const; + css::uno::Reference< css::awt::grid::XGridDataModel > + getDataModel() const; + void setColumnModel( css::uno::Reference< css::awt::grid::XGridColumnModel > const & i_gridColumnModel ); + bool hasColumnModel() const; + css::uno::Reference< css::awt::grid::XGridColumnModel > + getColumnModel() const; + + void setRowHeaders(bool _bRowHeaders); + void setColumnHeaders(bool _bColumnHeaders); + + void setRowHeight( TableMetrics _nHeight ); + void setRowHeaderWidth( TableMetrics _nWidth ); + void setColumnHeaderHeight( TableMetrics _nHeight ); + + void setLineColor( css::uno::Any const & i_color ); + void setHeaderBackgroundColor( css::uno::Any const & i_color ); + void setHeaderTextColor( css::uno::Any const & i_color ); + void setActiveSelectionBackColor( css::uno::Any const & i_color ); + void setInactiveSelectionBackColor( css::uno::Any const & i_color ); + void setActiveSelectionTextColor( css::uno::Any const & i_color ); + void setInactiveSelectionTextColor( css::uno::Any const & i_color ); + void setTextColor( css::uno::Any const & i_color ); + void setTextLineColor( css::uno::Any const & i_color ); + void setRowBackgroundColors( css::uno::Any const & i_APIValue ); + + void setVerticalAlign(css::style::VerticalAlignment _rAlign); + void setEnabled( bool _bEnabled ); + + // multiplexing of XGridDataListener events + void notifyRowsInserted( css::awt::grid::GridDataEvent const & i_event ) const; + void notifyRowsRemoved( css::awt::grid::GridDataEvent const & i_event ) const; + void notifyDataChanged( css::awt::grid::GridDataEvent const & i_event ) const; + + /// retrieves the index of a column within the model + ColPos getColumnPos( UnoGridColumnFacade const & i_column ) const; + + /// notifies a change in a column belonging to the model + void notifyColumnChange( ColPos const i_columnPos, ColumnAttributeGroup const i_attributeGroup ) const; + + /** notifies a change in all data represented by the model. To be used if you cannot specified the changed data + in more detail. + */ + void notifyAllDataChanged() const; + + private: + void impl_notifyTableMetricsChanged() const; + + typedef ::std::vector< PTableModelListener > ModellListeners; + typedef ::std::vector< PColumnModel > ColumnModels; + + ColumnModels aColumns; + bool bHasColumnHeaders; + bool bHasRowHeaders; + ScrollbarVisibility eVScrollMode; + ScrollbarVisibility eHScrollMode; + PTableRenderer pRenderer; + PTableInputHandler pInputHandler; + TableMetrics nRowHeight; + TableMetrics nColumnHeaderHeight; + TableMetrics nRowHeaderWidth; + ::std::optional< ::Color > m_aGridLineColor; + ::std::optional< ::Color > m_aHeaderBackgroundColor; + ::std::optional< ::Color > m_aHeaderTextColor; + ::std::optional< ::Color > m_aActiveSelectionBackColor; + ::std::optional< ::Color > m_aInactiveSelectionBackColor; + ::std::optional< ::Color > m_aActiveSelectionTextColor; + ::std::optional< ::Color > m_aInactiveSelectionTextColor; + ::std::optional< ::Color > m_aTextColor; + ::std::optional< ::Color > m_aTextLineColor; + ::std::optional< ::std::vector< ::Color > > m_aRowColors; + css::style::VerticalAlignment m_eVerticalAlign; + bool bEnabled; + ModellListeners m_aListeners; + css::uno::WeakReference< css::awt::grid::XGridDataModel > m_aDataModel; + css::uno::WeakReference< css::awt::grid::XGridColumnModel > m_aColumnModel; + }; + + +} // svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unogridcolumnfacade.cxx b/toolkit/source/controls/unogridcolumnfacade.cxx new file mode 100644 index 0000000000..a060c8cf3e --- /dev/null +++ b/toolkit/source/controls/unogridcolumnfacade.cxx @@ -0,0 +1,310 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "unogridcolumnfacade.hxx" +#include "unocontroltablemodel.hxx" + +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/awt/grid/XGridColumnListener.hpp> + +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/svapp.hxx> +#include <cppuhelper/implbase.hxx> + + +namespace svt::table +{ + + + using css::uno::Reference; + using css::awt::grid::XGridColumn; + using css::uno::Exception; + using css::awt::grid::XGridColumnListener; + using css::lang::EventObject; + using css::awt::grid::GridColumnEvent; + using css::style::HorizontalAlignment_LEFT; + using css::style::HorizontalAlignment; + + + namespace + { + template< class T1, class T2 > + void lcl_set( Reference< XGridColumn > const & i_column, void ( SAL_CALL XGridColumn::*i_setter )( T1 ), + T2 i_value ) + { + try + { + (i_column.get()->*i_setter) ( i_value ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + template< class ATTRIBUTE_TYPE > + ATTRIBUTE_TYPE lcl_get( Reference< XGridColumn > const & i_column, ATTRIBUTE_TYPE ( SAL_CALL XGridColumn::*i_getter )() ) + { + ATTRIBUTE_TYPE value = ATTRIBUTE_TYPE(); + try + { + value = (i_column.get()->*i_getter)(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return value; + } + } + + + //= ColumnChangeMultiplexer + + typedef ::cppu::WeakImplHelper < XGridColumnListener + > ColumnChangeMultiplexer_Base; + class ColumnChangeMultiplexer :public ColumnChangeMultiplexer_Base + { + public: + explicit ColumnChangeMultiplexer( UnoGridColumnFacade& i_colImpl ); + ColumnChangeMultiplexer(const ColumnChangeMultiplexer&) = delete; + ColumnChangeMultiplexer& operator=(const ColumnChangeMultiplexer&) = delete; + + void dispose(); + + protected: + virtual ~ColumnChangeMultiplexer() override; + + // XGridColumnListener + virtual void SAL_CALL columnChanged( const GridColumnEvent& i_event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const EventObject& i_event ) override; + + private: + UnoGridColumnFacade* m_pColumnImplementation; + }; + + + ColumnChangeMultiplexer::ColumnChangeMultiplexer( UnoGridColumnFacade& i_colImpl ) + :m_pColumnImplementation( &i_colImpl ) + { + } + + + ColumnChangeMultiplexer::~ColumnChangeMultiplexer() + { + } + + + void ColumnChangeMultiplexer::dispose() + { + DBG_TESTSOLARMUTEX(); + m_pColumnImplementation = nullptr; + } + + + void SAL_CALL ColumnChangeMultiplexer::columnChanged( const GridColumnEvent& i_event ) + { + if ( i_event.AttributeName == "DataColumnIndex" ) + { + SolarMutexGuard aGuard; + if ( m_pColumnImplementation != nullptr ) + m_pColumnImplementation->dataColumnIndexChanged(); + return; + } + + ColumnAttributeGroup nChangedAttributes( ColumnAttributeGroup::NONE ); + + if ( i_event.AttributeName == "HorizontalAlign" ) + nChangedAttributes |= ColumnAttributeGroup::APPEARANCE; + + if ( i_event.AttributeName == "ColumnWidth" + || i_event.AttributeName == "MaxWidth" + || i_event.AttributeName == "MinWidth" + || i_event.AttributeName == "PreferredWidth" + || i_event.AttributeName == "Resizeable" + || i_event.AttributeName == "Flexibility" + ) + nChangedAttributes |= ColumnAttributeGroup::WIDTH; + + OSL_ENSURE( nChangedAttributes != ColumnAttributeGroup::NONE, + "ColumnChangeMultiplexer::columnChanged: unknown column attributed changed!" ); + + SolarMutexGuard aGuard; + if ( m_pColumnImplementation != nullptr ) + m_pColumnImplementation->columnChanged( nChangedAttributes ); + } + + + void SAL_CALL ColumnChangeMultiplexer::disposing( const EventObject& ) + { + } + + + //= UnoGridColumnFacade + + + UnoGridColumnFacade::UnoGridColumnFacade( UnoControlTableModel const & i_owner, Reference< XGridColumn > const & i_gridColumn ) + :m_pOwner( &i_owner ) + ,m_nDataColumnIndex( -1 ) + ,m_xGridColumn( i_gridColumn, css::uno::UNO_SET_THROW ) + ,m_pChangeMultiplexer( new ColumnChangeMultiplexer( *this ) ) + { + m_xGridColumn->addGridColumnListener( m_pChangeMultiplexer ); + impl_updateDataColumnIndex_nothrow(); + } + + + UnoGridColumnFacade::~UnoGridColumnFacade() + { + } + + + void UnoGridColumnFacade::dispose() + { + DBG_TESTSOLARMUTEX(); + ENSURE_OR_RETURN_VOID( m_pOwner != nullptr, "UnoGridColumnFacade::dispose: already disposed!" ); + + m_xGridColumn->removeGridColumnListener( m_pChangeMultiplexer ); + m_pChangeMultiplexer->dispose(); + m_pChangeMultiplexer.clear(); + m_xGridColumn.clear(); + m_pOwner = nullptr; + } + + + void UnoGridColumnFacade::impl_updateDataColumnIndex_nothrow() + { + m_nDataColumnIndex = -1; + ENSURE_OR_RETURN_VOID( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!" ); + try + { + m_nDataColumnIndex = m_xGridColumn->getDataColumnIndex(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + } + + + void UnoGridColumnFacade::dataColumnIndexChanged() + { + DBG_TESTSOLARMUTEX(); + impl_updateDataColumnIndex_nothrow(); + if ( m_pOwner != nullptr ) + m_pOwner->notifyAllDataChanged(); + } + + + void UnoGridColumnFacade::columnChanged( ColumnAttributeGroup const i_attributeGroup ) + { + DBG_TESTSOLARMUTEX(); + if ( m_pOwner != nullptr ) + m_pOwner->notifyColumnChange( m_pOwner->getColumnPos( *this ), i_attributeGroup ); + } + + + OUString UnoGridColumnFacade::getName() const + { + OUString sName; + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", sName ); + try + { + sName = m_xGridColumn->getTitle(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return sName; + } + + + OUString UnoGridColumnFacade::getHelpText() const + { + OUString sHelpText; + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", sHelpText ); + try + { + sHelpText = m_xGridColumn->getHelpText(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.uno"); + } + return sHelpText; + } + + + bool UnoGridColumnFacade::isResizable() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", false ); + return lcl_get( m_xGridColumn, &XGridColumn::getResizeable ); + } + + + sal_Int32 UnoGridColumnFacade::getFlexibility() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 1 ); + return lcl_get( m_xGridColumn, &XGridColumn::getFlexibility ); + } + + + TableMetrics UnoGridColumnFacade::getWidth() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 0 ); + return lcl_get( m_xGridColumn, &XGridColumn::getColumnWidth ); + } + + + void UnoGridColumnFacade::setWidth( TableMetrics _nWidth ) + { + ENSURE_OR_RETURN_VOID( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!" ); + lcl_set( m_xGridColumn, &XGridColumn::setColumnWidth, _nWidth ); + } + + + TableMetrics UnoGridColumnFacade::getMinWidth() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 0 ); + return lcl_get( m_xGridColumn, &XGridColumn::getMinWidth ); + } + + + TableMetrics UnoGridColumnFacade::getMaxWidth() const + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", 0 ); + return lcl_get( m_xGridColumn, &XGridColumn::getMaxWidth ); + } + + + css::style::HorizontalAlignment UnoGridColumnFacade::getHorizontalAlign() + { + ENSURE_OR_RETURN( m_xGridColumn.is(), "UnoGridColumnFacade: already disposed!", HorizontalAlignment_LEFT ); + return lcl_get( m_xGridColumn, &XGridColumn::getHorizontalAlign ); + } + + +} // svt::table + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/controls/unogridcolumnfacade.hxx b/toolkit/source/controls/unogridcolumnfacade.hxx new file mode 100644 index 0000000000..580aee52bc --- /dev/null +++ b/toolkit/source/controls/unogridcolumnfacade.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <controls/table/tablemodel.hxx> + +#include <com/sun/star/awt/grid/XGridColumn.hpp> +#include <com/sun/star/style/HorizontalAlignment.hpp> + +#include <rtl/ref.hxx> + + +namespace svt::table +{ + + + //= UnoGridColumnFacade + + class ColumnChangeMultiplexer; + class UnoControlTableModel; + class UnoGridColumnFacade :public IColumnModel + { + public: + UnoGridColumnFacade( + UnoControlTableModel const & i_owner, + css::uno::Reference< css::awt::grid::XGridColumn > const & i_gridColumn + ); + virtual ~UnoGridColumnFacade() override; + UnoGridColumnFacade(const UnoGridColumnFacade&) = delete; + UnoGridColumnFacade& operator=(const UnoGridColumnFacade&) = delete; + + // IColumnModel overridables + virtual OUString getName() const override; + virtual OUString getHelpText() const override; + virtual bool isResizable() const override; + virtual sal_Int32 getFlexibility() const override; + virtual TableMetrics getWidth() const override; + virtual void setWidth( TableMetrics _nWidth ) override; + virtual TableMetrics getMinWidth() const override; + virtual TableMetrics getMaxWidth() const override; + virtual css::style::HorizontalAlignment getHorizontalAlign() override; + + /** disposes the column wrapper + + Note that the XGridColumn which is wrapped by the instance is <strong>not</strong> disposed, as we + do not own it. + */ + void dispose(); + + sal_Int32 + getDataColumnIndex() const { return m_nDataColumnIndex; } + + // callbacks for the XGridColumnListener + void columnChanged( ColumnAttributeGroup const i_attributeGroup ); + void dataColumnIndexChanged(); + + private: + void impl_updateDataColumnIndex_nothrow(); + + private: + UnoControlTableModel const * m_pOwner; + sal_Int32 m_nDataColumnIndex; + css::uno::Reference< css::awt::grid::XGridColumn > m_xGridColumn; + ::rtl::Reference< ColumnChangeMultiplexer > m_pChangeMultiplexer; + }; + + +} // svt::table + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/documentcloser.cxx b/toolkit/source/hatchwindow/documentcloser.cxx new file mode 100644 index 0000000000..70171d7308 --- /dev/null +++ b/toolkit/source/hatchwindow/documentcloser.cxx @@ -0,0 +1,229 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/dialoghelper.hxx> +#include <vcl/window.hxx> +#include <tools/link.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +using namespace ::com::sun::star; + +namespace { + +// the service is implemented as a wrapper to be able to die by refcount +// the disposing mechanics is required for java related scenarios +class ODocumentCloser : public ::cppu::WeakImplHelper< css::lang::XComponent, + css::lang::XServiceInfo > +{ + std::mutex m_aMutex; + css::uno::Reference< css::frame::XFrame > m_xFrame; + ::comphelper::OInterfaceContainerHelper4<lang::XEventListener> m_aListenersContainer; // list of listeners + + bool m_bDisposed; + +public: + explicit ODocumentCloser(const css::uno::Sequence< css::uno::Any >& aArguments); + +// XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) 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; +}; + +class MainThreadFrameCloserRequest +{ + uno::Reference< frame::XFrame > m_xFrame; + + public: + explicit MainThreadFrameCloserRequest( uno::Reference< frame::XFrame > xFrame ) + : m_xFrame(std::move( xFrame )) + {} + + DECL_STATIC_LINK( MainThreadFrameCloserRequest, worker, void*, void ); + + static void Start( MainThreadFrameCloserRequest* pRequest ); +}; + + +void MainThreadFrameCloserRequest::Start( MainThreadFrameCloserRequest* pMTRequest ) +{ + if ( pMTRequest ) + { + if ( Application::IsMainThread() ) + { + // this is the main thread + worker( nullptr, pMTRequest ); + } + else + Application::PostUserEvent( LINK( nullptr, MainThreadFrameCloserRequest, worker ), pMTRequest ); + } +} + + +IMPL_STATIC_LINK( MainThreadFrameCloserRequest, worker, void*, p, void ) +{ + MainThreadFrameCloserRequest* pMTRequest = static_cast<MainThreadFrameCloserRequest*>(p); + if ( !pMTRequest ) + return; + + if ( pMTRequest->m_xFrame.is() ) + { + // this is the main thread, the solar mutex must be locked + SolarMutexGuard aGuard; + + try + { + uno::Reference< awt::XWindow > xWindow = pMTRequest->m_xFrame->getContainerWindow(); + uno::Reference< awt::XVclWindowPeer > xWinPeer( xWindow, uno::UNO_QUERY_THROW ); + + xWindow->setVisible( false ); + + // reparent the window + xWinPeer->setProperty( "PluginParent", uno::Any( sal_Int64(0) ) ); + + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if (pWindow) + vcl::EndAllDialogs(pWindow); + } + catch( uno::Exception& ) + { + // ignore all the errors + } + + try + { + uno::Reference< util::XCloseable > xCloseable( pMTRequest->m_xFrame, uno::UNO_QUERY_THROW ); + xCloseable->close( true ); + } + catch( uno::Exception& ) + { + // ignore all the errors + } + } + + delete pMTRequest; +} + +ODocumentCloser::ODocumentCloser(const css::uno::Sequence< css::uno::Any >& aArguments) +: m_bDisposed( false ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( !m_refCount ) + throw uno::RuntimeException(); // the object must be refcounted already! + + sal_Int32 nLen = aArguments.getLength(); + if ( nLen != 1 ) + throw lang::IllegalArgumentException( + "Wrong count of parameters!", + uno::Reference< uno::XInterface >(), + 0 ); + + if ( !( aArguments[0] >>= m_xFrame ) || !m_xFrame.is() ) + throw lang::IllegalArgumentException( + "Nonempty reference is expected as the first argument!", + uno::Reference< uno::XInterface >(), + 0 ); +} + + +// XComponent + +void SAL_CALL ODocumentCloser::dispose() +{ + std::unique_lock aGuard( m_aMutex ); + + if ( m_bDisposed ) + return; + + lang::EventObject aSource( getXWeak() ); + m_aListenersContainer.disposeAndClear( aGuard, aSource ); + + // TODO: trigger a main thread execution to close the frame + if ( m_xFrame.is() ) + { + // the created object will be deleted after thread execution + MainThreadFrameCloserRequest* pCloser = new MainThreadFrameCloserRequest( m_xFrame ); + MainThreadFrameCloserRequest::Start( pCloser ); + } + + m_bDisposed = true; +} + + +void SAL_CALL ODocumentCloser::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + m_aListenersContainer.addInterface( aGuard, xListener ); +} + + +void SAL_CALL ODocumentCloser::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + m_aListenersContainer.removeInterface( aGuard, xListener ); +} + +// XServiceInfo +OUString SAL_CALL ODocumentCloser::getImplementationName( ) +{ + return "com.sun.star.comp.embed.DocumentCloser"; +} + +sal_Bool SAL_CALL ODocumentCloser::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL ODocumentCloser::getSupportedServiceNames() +{ + return { "com.sun.star.embed.DocumentCloser" }; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_embed_DocumentCloser_get_implementation( + SAL_UNUSED_PARAMETER css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &arguments) +{ + return cppu::acquire(new ODocumentCloser(arguments)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/hatchwindow.cxx b/toolkit/source/hatchwindow/hatchwindow.cxx new file mode 100644 index 0000000000..4685126b20 --- /dev/null +++ b/toolkit/source/hatchwindow/hatchwindow.cxx @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/XHatchWindowController.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include "hatchwindow.hxx" +#include "ipwin.hxx" + +#include <toolkit/helper/convert.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +VCLXHatchWindow::VCLXHatchWindow() +: pHatchWindow(nullptr) +{ +} + +VCLXHatchWindow::~VCLXHatchWindow() +{ +} + +void VCLXHatchWindow::initializeWindow( const uno::Reference< awt::XWindowPeer >& xParent, + const awt::Rectangle& aBounds, + const awt::Size& aSize ) +{ + SolarMutexGuard aGuard; + + VclPtr<vcl::Window> pParent; + VCLXWindow* pParentComponent = dynamic_cast<VCLXWindow*>( xParent.get() ); + + if ( pParentComponent ) + pParent = pParentComponent->GetWindow(); + + OSL_ENSURE( pParent, "No parent window is provided!" ); + if ( !pParent ) + throw lang::IllegalArgumentException(); // TODO + + pHatchWindow = VclPtr<SvResizeWindow>::Create( pParent, this ); + pHatchWindow->setPosSizePixel( aBounds.X, aBounds.Y, aBounds.Width, aBounds.Height ); + aHatchBorderSize = aSize; + pHatchWindow->SetHatchBorderPixel( Size( aSize.Width, aSize.Height ) ); + + SetWindow( pHatchWindow ); + pHatchWindow->SetComponentInterface( this ); + + //pHatchWindow->Show(); +} + +void VCLXHatchWindow::QueryObjAreaPixel( tools::Rectangle & aRect ) +{ + if ( !m_xController.is() ) + return; + + awt::Rectangle aUnoRequestRect = AWTRectangle( aRect ); + + try { + awt::Rectangle aUnoResultRect = m_xController->calcAdjustedRectangle( aUnoRequestRect ); + aRect = VCLRectangle( aUnoResultRect ); + } + catch( uno::Exception& ) + { + OSL_FAIL( "Can't adjust rectangle size!" ); + } +} + +void VCLXHatchWindow::RequestObjAreaPixel( const tools::Rectangle & aRect ) +{ + if ( m_xController.is() ) + { + awt::Rectangle aUnoRequestRect = AWTRectangle( aRect ); + + try { + m_xController->requestPositioning( aUnoRequestRect ); + } + catch( uno::Exception& ) + { + OSL_FAIL( "Can't request resizing!" ); + } + } +} + +void VCLXHatchWindow::InplaceDeactivate() +{ + if ( m_xController.is() ) + { + // TODO: communicate with controller + } +} + + +css::awt::Size SAL_CALL VCLXHatchWindow::getHatchBorderSize() +{ + return aHatchBorderSize; +} + +void SAL_CALL VCLXHatchWindow::setHatchBorderSize( const css::awt::Size& _hatchbordersize ) +{ + if ( pHatchWindow ) + { + aHatchBorderSize = _hatchbordersize; + pHatchWindow->SetHatchBorderPixel( Size( aHatchBorderSize.Width, aHatchBorderSize.Height ) ); + } +} + +void SAL_CALL VCLXHatchWindow::setController( const uno::Reference< embed::XHatchWindowController >& xController ) +{ + m_xController = xController; +} + +void SAL_CALL VCLXHatchWindow::dispose() +{ + pHatchWindow.clear(); + VCLXWindow::dispose(); +} + +void SAL_CALL VCLXHatchWindow::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + VCLXWindow::addEventListener( xListener ); +} + +void SAL_CALL VCLXHatchWindow::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + VCLXWindow::removeEventListener( xListener ); +} + +void VCLXHatchWindow::Activated() +{ + if ( m_xController.is() ) + m_xController->activated(); +} + +void VCLXHatchWindow::Deactivated() +{ + if ( m_xController.is() ) + m_xController->deactivated(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/hatchwindow.hxx b/toolkit/source/hatchwindow/hatchwindow.hxx new file mode 100644 index 0000000000..128a5198f9 --- /dev/null +++ b/toolkit/source/hatchwindow/hatchwindow.hxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/embed/XHatchWindow.hpp> + +#include <toolkit/awt/vclxwindow.hxx> + +class SvResizeWindow; +typedef cppu::ImplInheritanceHelper< VCLXWindow, css::embed::XHatchWindow> VCLXHatchWindow_Base; +class VCLXHatchWindow : public VCLXHatchWindow_Base +{ + css::uno::Reference< css::embed::XHatchWindowController > m_xController; + css::awt::Size aHatchBorderSize; + VclPtr<SvResizeWindow> pHatchWindow; + +public: + VCLXHatchWindow(); + virtual ~VCLXHatchWindow() override; + + void initializeWindow( const css::uno::Reference< css::awt::XWindowPeer >& xParent, + const css::awt::Rectangle& aBounds, + const css::awt::Size& aSize ); + + void QueryObjAreaPixel( tools::Rectangle & ); + void RequestObjAreaPixel( const tools::Rectangle & ); + void InplaceDeactivate(); + void Activated(); + void Deactivated(); + + // XHatchWindow + virtual void SAL_CALL setController( const css::uno::Reference< css::embed::XHatchWindowController >& xController ) override; + virtual css::awt::Size SAL_CALL getHatchBorderSize() override; + virtual void SAL_CALL setHatchBorderSize( const css::awt::Size& _hatchbordersize ) override; + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/hatchwindowfactory.cxx b/toolkit/source/hatchwindow/hatchwindowfactory.cxx new file mode 100644 index 0000000000..3e50db64ee --- /dev/null +++ b/toolkit/source/hatchwindow/hatchwindowfactory.cxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/XHatchWindowFactory.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <vcl/svapp.hxx> + +#include "hatchwindow.hxx" + +using namespace ::com::sun::star; + +namespace { + +class OHatchWindowFactory : public ::cppu::WeakImplHelper< + embed::XHatchWindowFactory, + lang::XServiceInfo > +{ +public: + OHatchWindowFactory() {} + + // XHatchWindowFactory + virtual uno::Reference< embed::XHatchWindow > SAL_CALL createHatchWindowInstance( const uno::Reference< awt::XWindowPeer >& xParent, const awt::Rectangle& aBounds, const awt::Size& aSize ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +uno::Reference< embed::XHatchWindow > SAL_CALL OHatchWindowFactory::createHatchWindowInstance( + const uno::Reference< awt::XWindowPeer >& xParent, + const awt::Rectangle& aBounds, + const awt::Size& aHandlerSize ) +{ + if ( !xParent.is() ) + throw lang::IllegalArgumentException(); // TODO + + SolarMutexGuard aGuard; + rtl::Reference<VCLXHatchWindow> pResult = new VCLXHatchWindow(); + pResult->initializeWindow( xParent, aBounds, aHandlerSize ); + return pResult; +} + +OUString SAL_CALL OHatchWindowFactory::getImplementationName() +{ + return "com.sun.star.comp.embed.HatchWindowFactory"; +} + +sal_Bool SAL_CALL OHatchWindowFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL OHatchWindowFactory::getSupportedServiceNames() +{ + return { "com.sun.star.embed.HatchWindowFactory", "com.sun.star.comp.embed.HatchWindowFactory" }; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_embed_HatchWindowFactory_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OHatchWindowFactory); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/ipwin.cxx b/toolkit/source/hatchwindow/ipwin.cxx new file mode 100644 index 0000000000..52d3668b7f --- /dev/null +++ b/toolkit/source/hatchwindow/ipwin.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 <com/sun/star/accessibility/AccessibleRole.hpp> + +#include <osl/diagnose.h> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <vcl/ptrstyle.hxx> + +#include "ipwin.hxx" +#include "hatchwindow.hxx" + +/************************************************************************/ +/************************************************************************* +|* SvResizeHelper::SvResizeHelper() +|* +|* Description +*************************************************************************/ +SvResizeHelper::SvResizeHelper() + : aBorder( 5, 5 ) + , nGrab( -1 ) +{ +} + +/************************************************************************* +|* SvResizeHelper::FillHandleRects() +|* +|* Description: the eight handles to magnify +*************************************************************************/ +std::array<tools::Rectangle,8> SvResizeHelper::FillHandleRectsPixel() const +{ + std::array<tools::Rectangle,8> aRects; + + // only because of EMPTY_RECT + Point aBottomRight = aOuter.BottomRight(); + + // upper left + aRects[ 0 ] = tools::Rectangle( aOuter.TopLeft(), aBorder ); + // upper middle + aRects[ 1 ] = tools::Rectangle( Point( aOuter.Center().X() - aBorder.Width() / 2, + aOuter.Top() ), + aBorder ); + // upper right + aRects[ 2 ] = tools::Rectangle( Point( aBottomRight.X() - aBorder.Width() +1, + aOuter.Top() ), + aBorder ); + // middle right + aRects[ 3 ] = tools::Rectangle( Point( aBottomRight.X() - aBorder.Width() +1, + aOuter.Center().Y() - aBorder.Height() / 2 ), + aBorder ); + // lower right + aRects[ 4 ] = tools::Rectangle( Point( aBottomRight.X() - aBorder.Width() +1, + aBottomRight.Y() - aBorder.Height() +1 ), + aBorder ); + // lower middle + aRects[ 5 ] = tools::Rectangle( Point( aOuter.Center().X() - aBorder.Width() / 2, + aBottomRight.Y() - aBorder.Height() +1), + aBorder ); + // lower left + aRects[ 6 ] = tools::Rectangle( Point( aOuter.Left(), + aBottomRight.Y() - aBorder.Height() +1), + aBorder ); + // middle left + aRects[ 7 ] = tools::Rectangle( Point( aOuter.Left(), + aOuter.Center().Y() - aBorder.Height() / 2 ), + aBorder ); + return aRects; +} + +/************************************************************************* +|* SvResizeHelper::FillMoveRectsPixel() +|* +|* Description: the four edges are calculated +*************************************************************************/ +std::array<tools::Rectangle,4> SvResizeHelper::FillMoveRectsPixel() const +{ + std::array<tools::Rectangle,4> aRects; + + // upper + aRects[ 0 ] = aOuter; + aRects[ 0 ].SetBottom( aRects[ 0 ].Top() + aBorder.Height() -1 ); + // right + aRects[ 1 ] = aOuter; + if (!aOuter.IsWidthEmpty()) + aRects[ 1 ].SetLeft( aRects[ 1 ].Right() - aBorder.Width() -1 ); + // lower + aRects[ 2 ] = aOuter; + if (!aOuter.IsHeightEmpty()) + aRects[ 2 ].SetTop( aRects[ 2 ].Bottom() - aBorder.Height() -1 ); + // left + aRects[ 3 ] = aOuter; + aRects[ 3 ].SetRight( aRects[ 3 ].Left() + aBorder.Width() -1 ); + + return aRects; +} + +/************************************************************************* +|* SvResizeHelper::Draw() +|* +|* Description +*************************************************************************/ +void SvResizeHelper::Draw(vcl::RenderContext& rRenderContext) +{ + rRenderContext.Push(); + rRenderContext.SetMapMode( MapMode() ); + + rRenderContext.SetFillColor( COL_LIGHTGRAY ); + rRenderContext.SetLineColor(); + + std::array<tools::Rectangle,4> aMoveRects = FillMoveRectsPixel(); + sal_uInt16 i; + for (i = 0; i < 4; i++) + rRenderContext.DrawRect(aMoveRects[i]); + // draw handles + rRenderContext.SetFillColor(Color()); // black + std::array<tools::Rectangle,8> aRects = FillHandleRectsPixel(); + for (i = 0; i < 8; i++) + rRenderContext.DrawRect( aRects[ i ] ); + rRenderContext.Pop(); +} + +/************************************************************************* +|* SvResizeHelper::InvalidateBorder() +|* +|* Description +*************************************************************************/ +void SvResizeHelper::InvalidateBorder( vcl::Window * pWin ) +{ + std::array<tools::Rectangle,4> aMoveRects = FillMoveRectsPixel(); + for(const auto & rMoveRect : aMoveRects) + pWin->Invalidate( rMoveRect ); +} + +/************************************************************************* +|* SvResizeHelper::SelectBegin() +|* +|* Description +*************************************************************************/ +bool SvResizeHelper::SelectBegin( vcl::Window * pWin, const Point & rPos ) +{ + if( -1 == nGrab ) + { + nGrab = SelectMove( pWin, rPos ); + if( -1 != nGrab ) + { + aSelPos = rPos; // store start position + pWin->CaptureMouse(); + return true; + } + } + return false; +} + +/************************************************************************* +|* SvResizeHelper::SelectMove() +|* +|* Description +*************************************************************************/ +short SvResizeHelper::SelectMove( vcl::Window * pWin, const Point & rPos ) +{ + if( -1 == nGrab ) + { + std::array<tools::Rectangle,8> aRects = FillHandleRectsPixel(); + for( sal_uInt16 i = 0; i < 8; i++ ) + if( aRects[ i ].Contains( rPos ) ) + return i; + // Move-Rect overlaps Handles + std::array<tools::Rectangle,4> aMoveRects = FillMoveRectsPixel(); + for(const auto & rMoveRect : aMoveRects) + if( rMoveRect.Contains( rPos ) ) + return 8; + } + else + { + tools::Rectangle aRect = pWin->PixelToLogic(GetTrackRectPixel( rPos )); + pWin->ShowTracking( aRect ); + } + return nGrab; +} + +Point SvResizeHelper::GetTrackPosPixel( const tools::Rectangle & rRect ) const +{ + // not important how the rectangle is returned, it is important + // which handle has been touched + Point aPos; + tools::Rectangle aRect( rRect ); + aRect.Normalize(); + // only because of EMPTY_RECT + Point aBR = aOuter.BottomRight(); + Point aTR = aOuter.TopRight(); + Point aBL = aOuter.BottomLeft(); + bool bRTL = AllSettings::GetLayoutRTL(); + switch( nGrab ) + { + case 0: + // FIXME: disable it for RTL because it's wrong calculations + if( bRTL ) + break; + aPos = aRect.TopLeft() - aOuter.TopLeft(); + break; + case 1: + aPos.setY( aRect.Top() - aOuter.Top() ); + break; + case 2: + // FIXME: disable it for RTL because it's wrong calculations + if( bRTL ) + break; + aPos = aRect.TopRight() - aTR; + break; + case 3: + if( bRTL ) + aPos.setX( aRect.Left() - aTR.X() ); + else + aPos.setX( aRect.Right() - aTR.X() ); + break; + case 4: + // FIXME: disable it for RTL because it's wrong calculations + if( bRTL ) + break; + aPos = aRect.BottomRight() - aBR; + break; + case 5: + aPos.setY( aRect.Bottom() - aBR.Y() ); + break; + case 6: + // FIXME: disable it for RTL because it's wrong calculations + if( bRTL ) + break; + aPos = aRect.BottomLeft() - aBL; + break; + case 7: + if( bRTL ) + aPos.setX( aRect.Right() + aOuter.Right() - aOuter.Right() ); + else + aPos.setX( aRect.Left() - aOuter.Left() ); + break; + case 8: + aPos = aRect.TopLeft() - aOuter.TopLeft(); + break; + } + return aPos + aSelPos; +} + +/************************************************************************* +|* SvResizeHelper::GetTrackRectPixel() +|* +|* Description +*************************************************************************/ +tools::Rectangle SvResizeHelper::GetTrackRectPixel( const Point & rTrackPos ) const +{ + tools::Rectangle aTrackRect; + if( -1 != nGrab ) + { + Point aDiff = rTrackPos - aSelPos; + aTrackRect = aOuter; + Point aBR = aOuter.BottomRight(); + bool bRTL = AllSettings::GetLayoutRTL(); + switch( nGrab ) + { + case 0: + aTrackRect.AdjustTop(aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.SetRight( aBR.X() - aDiff.X() ); + else + aTrackRect.AdjustLeft(aDiff.X() ); + break; + case 1: + aTrackRect.AdjustTop(aDiff.Y() ); + break; + case 2: + aTrackRect.AdjustTop(aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.AdjustLeft( -(aDiff.X()) ); + else + aTrackRect.SetRight( aBR.X() + aDiff.X() ); + break; + case 3: + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.AdjustLeft( -(aDiff.X()) ); + else + aTrackRect.SetRight( aBR.X() + aDiff.X() ); + break; + case 4: + aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.AdjustLeft( -(aDiff.X()) ); + else + aTrackRect.SetRight( aBR.X() + aDiff.X() ); + break; + case 5: + aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); + break; + case 6: + aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.SetRight( aBR.X() - aDiff.X() ); + else + aTrackRect.AdjustLeft(aDiff.X() ); + break; + case 7: + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.SetRight( aBR.X() - aDiff.X() ); + else + aTrackRect.AdjustLeft(aDiff.X() ); + break; + case 8: + if( bRTL ) + aDiff.setX( -aDiff.X() ); // workaround for move in RTL mode + aTrackRect.SetPos( aTrackRect.TopLeft() + aDiff ); + break; + } + } + return aTrackRect; +} + +void SvResizeHelper::ValidateRect( tools::Rectangle & rValidate ) const +{ + switch( nGrab ) + { + case 0: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetTop( rValidate.Bottom() ); + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetLeft( rValidate.Right() ); + break; + case 1: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetTop( rValidate.Bottom() ); + break; + case 2: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetTop( rValidate.Bottom() ); + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetRight( rValidate.Left() ); + break; + case 3: + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetRight( rValidate.Left() ); + break; + case 4: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetBottom( rValidate.Top() ); + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetRight( rValidate.Left() ); + break; + case 5: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetBottom( rValidate.Top() ); + break; + case 6: + if( rValidate.Top() > rValidate.Bottom() ) + rValidate.SetBottom( rValidate.Top() ); + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetLeft( rValidate.Right() ); + break; + case 7: + if( rValidate.Left() > rValidate.Right() ) + rValidate.SetLeft( rValidate.Right() ); + break; + } + + // Mindestgr"osse 5 x 5 + if( rValidate.Left() + 5 > rValidate.Right() ) + rValidate.SetRight( rValidate.Left() + 5 ); + if( rValidate.Top() + 5 > rValidate.Bottom() ) + rValidate.SetBottom( rValidate.Top() + 5 ); +} + +/************************************************************************* +|* SvResizeHelper::SelectRelease() +|* +|* Description +*************************************************************************/ +bool SvResizeHelper::SelectRelease( vcl::Window * pWin, const Point & rPos, + tools::Rectangle & rOutPosSize ) +{ + if( -1 != nGrab ) + { + rOutPosSize = GetTrackRectPixel( rPos ); + rOutPosSize.Normalize(); + nGrab = -1; + pWin->ReleaseMouse(); + pWin->HideTracking(); + return true; + } + return false; +} + +/************************************************************************* +|* SvResizeHelper::Release() +|* +|* Description +*************************************************************************/ +void SvResizeHelper::Release( vcl::Window * pWin ) +{ + if( nGrab != -1 ) + { + pWin->ReleaseMouse(); + pWin->HideTracking(); + nGrab = -1; + } +} + +/************************************************************************* +|* SvResizeWindow::SvResizeWindow() +|* +|* Description +*************************************************************************/ +SvResizeWindow::SvResizeWindow +( + vcl::Window * pParent, + VCLXHatchWindow* pWrapper +) + : Window( pParent, WB_CLIPCHILDREN ) + , m_aOldPointer(PointerStyle::Arrow) + , m_nMoveGrab( -1 ) + , m_bActive( false ) + , m_pWrapper( pWrapper ) +{ + OSL_ENSURE( pParent != nullptr && pWrapper != nullptr, "Wrong initialization of hatch window!" ); + SetBackground(); + SetAccessibleRole( css::accessibility::AccessibleRole::EMBEDDED_OBJECT ); + m_aResizer.SetOuterRectPixel( tools::Rectangle( Point(), GetOutputSizePixel() ) ); +} + +/************************************************************************* +|* SvResizeWindow::SetHatchBorderPixel() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::SetHatchBorderPixel( const Size & rSize ) +{ + m_aResizer.SetBorderPixel( rSize ); +} + +/************************************************************************* +|* SvResizeWindow::SelectMouse() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::SelectMouse( const Point & rPos ) +{ + short nGrab = m_aResizer.SelectMove( this, rPos ); + if( nGrab >= 4 ) + nGrab -= 4; + if( m_nMoveGrab == nGrab ) + return; + + // Pointer did change + if( -1 == nGrab ) + SetPointer( m_aOldPointer ); + else + { + PointerStyle aStyle = PointerStyle::Move; + if( nGrab == 3 ) + aStyle = PointerStyle::ESize; + else if( nGrab == 2 ) + aStyle = PointerStyle::NESize; + else if( nGrab == 1 ) + aStyle = PointerStyle::SSize; + else if( nGrab == 0 ) + aStyle = PointerStyle::SESize; + if( m_nMoveGrab == -1 ) // the first time + { + m_aOldPointer = GetPointer(); + SetPointer( aStyle ); + } + else + SetPointer( aStyle ); + } + m_nMoveGrab = nGrab; +} + +/************************************************************************* +|* SvResizeWindow::MouseButtonDown() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::MouseButtonDown( const MouseEvent & rEvt ) +{ + if( m_aResizer.SelectBegin( this, rEvt.GetPosPixel() ) ) + SelectMouse( rEvt.GetPosPixel() ); +} + +/************************************************************************* +|* SvResizeWindow::MouseMove() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::MouseMove( const MouseEvent & rEvt ) +{ + if( m_aResizer.GetGrab() == -1 ) + SelectMouse( rEvt.GetPosPixel() ); + else + { + tools::Rectangle aRect( m_aResizer.GetTrackRectPixel( rEvt.GetPosPixel() ) ); + Point aDiff = GetPosPixel(); + aRect.SetPos( aRect.TopLeft() + aDiff ); + m_aResizer.ValidateRect( aRect ); + + m_pWrapper->QueryObjAreaPixel( aRect ); + aRect.SetPos( aRect.TopLeft() - aDiff ); + Point aPos = m_aResizer.GetTrackPosPixel( aRect ); + + SelectMouse( aPos ); + } +} + +/************************************************************************* +|* SvResizeWindow::MouseButtonUp() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::MouseButtonUp( const MouseEvent & rEvt ) +{ + if( m_aResizer.GetGrab() == -1 ) + return; + + tools::Rectangle aRect( m_aResizer.GetTrackRectPixel( rEvt.GetPosPixel() ) ); + Point aDiff = GetPosPixel(); + aRect.SetPos( aRect.TopLeft() + aDiff ); + // aRect -= GetAllBorderPixel(); + m_aResizer.ValidateRect( aRect ); + + m_pWrapper->QueryObjAreaPixel( aRect ); + + tools::Rectangle aOutRect; + if( m_aResizer.SelectRelease( this, rEvt.GetPosPixel(), aOutRect ) ) + { + m_nMoveGrab = -1; + SetPointer( m_aOldPointer ); + m_pWrapper->RequestObjAreaPixel( aRect ); + } +} + +/************************************************************************* +|* SvResizeWindow::KeyEvent() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::KeyInput( const KeyEvent & rEvt ) +{ + if( rEvt.GetKeyCode().GetCode() == KEY_ESCAPE ) + { + m_aResizer.Release( this ); + m_pWrapper->InplaceDeactivate(); + } +} + +/************************************************************************* +|* SvResizeWindow::Resize() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::Resize() +{ + m_aResizer.InvalidateBorder( this ); // old area + m_aResizer.SetOuterRectPixel( tools::Rectangle( Point(), GetOutputSizePixel() ) ); + m_aResizer.InvalidateBorder( this ); // new area +} + +/************************************************************************* +|* SvResizeWindow::Paint() +|* +|* Description +*************************************************************************/ +void SvResizeWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle & /*rRect*/ ) +{ + m_aResizer.Draw(rRenderContext); +} + +bool SvResizeWindow::PreNotify( NotifyEvent& rEvt ) +{ + if ( rEvt.GetType() == NotifyEventType::GETFOCUS && !m_bActive ) + { + m_bActive = true; + m_pWrapper->Activated(); + } + + return Window::PreNotify(rEvt); +} + +bool SvResizeWindow::EventNotify( NotifyEvent& rEvt ) +{ + if ( rEvt.GetType() == NotifyEventType::LOSEFOCUS && m_bActive ) + { + bool bHasFocus = HasChildPathFocus(true); + if ( !bHasFocus ) + { + m_bActive = false; + m_pWrapper->Deactivated(); + } + } + + return Window::EventNotify(rEvt); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/hatchwindow/ipwin.hxx b/toolkit/source/hatchwindow/ipwin.hxx new file mode 100644 index 0000000000..72567ce268 --- /dev/null +++ b/toolkit/source/hatchwindow/ipwin.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <tools/gen.hxx> +#include <vcl/window.hxx> +#include <array> + +/********************** SvResizeHelper *********************************** +*************************************************************************/ +class SvResizeHelper +{ + Size aBorder; + tools::Rectangle aOuter; + short nGrab; // -1 no Grab, 0 - 7, 8 = Move, see FillHandle... + Point aSelPos; +public: + SvResizeHelper(); + + short GetGrab() const + { + return nGrab; + } + void SetBorderPixel(const Size & rBorderP) + { + aBorder = rBorderP; + } + void SetOuterRectPixel(const tools::Rectangle& rRect) + { + aOuter = rRect; + } + // Clockwise, start at upper left + + std::array<tools::Rectangle,8> FillHandleRectsPixel() const; + std::array<tools::Rectangle,4> FillMoveRectsPixel() const; + void Draw(vcl::RenderContext& rRenderContext); + void InvalidateBorder( vcl::Window * ); + bool SelectBegin( vcl::Window *, const Point & rPos ); + short SelectMove( vcl::Window * pWin, const Point & rPos ); + Point GetTrackPosPixel( const tools::Rectangle & rRect ) const; + tools::Rectangle GetTrackRectPixel( const Point & rTrackPos ) const; + void ValidateRect( tools::Rectangle & rValidate ) const; + bool SelectRelease( vcl::Window *, const Point & rPos, tools::Rectangle & rOutPosSize ); + void Release( vcl::Window * pWin ); +}; + +/********************** SvResizeWindow *********************************** +*************************************************************************/ +class VCLXHatchWindow; +class SvResizeWindow : public vcl::Window +{ + PointerStyle m_aOldPointer; + short m_nMoveGrab; // last pointer type + SvResizeHelper m_aResizer; + bool m_bActive; + + VCLXHatchWindow* m_pWrapper; +public: + SvResizeWindow( vcl::Window* pParent, VCLXHatchWindow* pWrapper ); + + void SetHatchBorderPixel( const Size & rSize ); + + void SelectMouse( const Point & rPos ); + virtual void MouseButtonUp( const MouseEvent & rEvt ) override; + virtual void MouseMove( const MouseEvent & rEvt ) override; + virtual void MouseButtonDown( const MouseEvent & rEvt ) override; + virtual void KeyInput( const KeyEvent & rEvt ) override; + virtual void Resize() override; + virtual void Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle & ) override; + virtual bool EventNotify( NotifyEvent& rNEvt ) override; + virtual bool PreNotify( NotifyEvent& rNEvt ) override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/accessibilityclient.cxx b/toolkit/source/helper/accessibilityclient.cxx new file mode 100644 index 0000000000..cad81b3a91 --- /dev/null +++ b/toolkit/source/helper/accessibilityclient.cxx @@ -0,0 +1,235 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_feature_desktop.h> +#include <config_wasm_strip.h> + +#include <sal/config.h> + +#include <toolkit/helper/accessiblefactory.hxx> +#include <osl/module.h> +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <rtl/ref.hxx> +#include <tools/svlibrary.h> + +#include <helper/accessibilityclient.hxx> + +namespace toolkit +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::accessibility; + + namespace + { +#ifndef DISABLE_DYNLOADING + oslModule s_hAccessibleImplementationModule = nullptr; +#endif +#if HAVE_FEATURE_DESKTOP +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + GetStandardAccComponentFactory s_pAccessibleFactoryFunc = nullptr; +#endif +#endif + ::rtl::Reference< IAccessibleFactory > s_pFactory; + } + + + //= AccessibleDummyFactory + + namespace { + + class AccessibleDummyFactory: + public IAccessibleFactory + { + public: + AccessibleDummyFactory(); + AccessibleDummyFactory(const AccessibleDummyFactory&) = delete; + AccessibleDummyFactory& operator=(const AccessibleDummyFactory&) = delete; + + protected: + virtual ~AccessibleDummyFactory() override; + + public: + // IAccessibleFactory + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXButton* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXCheckBox* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXRadioButton* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXListBox* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXFixedHyperlink* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXFixedText* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXScrollBar* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXEdit* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXMultiLineEdit* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXComboBox* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXToolBox* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXHeaderBar* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( SVTXNumericField* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessibleContext > + createAccessibleContext( VCLXWindow* /*_pXWindow*/ ) override + { + return nullptr; + } + css::uno::Reference< css::accessibility::XAccessible > + createAccessible( Menu* /*_pMenu*/, bool /*_bIsMenuBar*/ ) override + { + return nullptr; + } + }; + + } + + AccessibleDummyFactory::AccessibleDummyFactory() + { + } + + + AccessibleDummyFactory::~AccessibleDummyFactory() + { + } + + + //= AccessibilityClient + + + AccessibilityClient::AccessibilityClient() + :m_bInitialized( false ) + { + } + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY +#if HAVE_FEATURE_DESKTOP +#ifndef DISABLE_DYNLOADING + extern "C" { static void thisModule() {} } +#else + extern "C" void *getStandardAccessibleFactory(); +#endif +#endif // HAVE_FEATURE_DESKTOP +#endif // ENABLE_WASM_STRIP_ACCESSIBILITY + + void AccessibilityClient::ensureInitialized() + { + if ( m_bInitialized ) + return; + + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY +#if HAVE_FEATURE_DESKTOP + // load the library implementing the factory + if (!s_pFactory) + { +#ifndef DISABLE_DYNLOADING + s_hAccessibleImplementationModule = osl_loadModuleRelative( &thisModule, u"" SVLIBRARY( "acc" ) ""_ustr.pData, 0 ); + if ( s_hAccessibleImplementationModule != nullptr ) + { + s_pAccessibleFactoryFunc = reinterpret_cast<GetStandardAccComponentFactory>( + osl_getFunctionSymbol( s_hAccessibleImplementationModule, u"getStandardAccessibleFactory"_ustr.pData )); + + } + OSL_ENSURE( s_pAccessibleFactoryFunc, "AccessibilityClient::ensureInitialized: could not load the library, or not retrieve the needed symbol!" ); +#else + s_pAccessibleFactoryFunc = getStandardAccessibleFactory; +#endif // DISABLE_DYNLOADING + + // get a factory instance + if ( s_pAccessibleFactoryFunc ) + { + IAccessibleFactory* pFactory = static_cast< IAccessibleFactory* >( (*s_pAccessibleFactoryFunc)() ); + OSL_ENSURE( pFactory, "AccessibilityClient::ensureInitialized: no factory provided by the A11Y lib!" ); + if ( pFactory ) + { + s_pFactory = pFactory; + pFactory->release(); + } + } + } +#endif // HAVE_FEATURE_DESKTOP +#endif // ENABLE_WASM_STRIP_ACCESSIBILITY + + if (!s_pFactory) + // the attempt to load the lib, or to create the factory, failed + // -> fall back to a dummy factory + s_pFactory = new AccessibleDummyFactory; + + m_bInitialized = true; + } + + IAccessibleFactory& AccessibilityClient::getFactory() + { + ensureInitialized(); + OSL_ENSURE( s_pFactory.is(), "AccessibilityClient::getFactory: at least a dummy factory should have been created!" ); + return *s_pFactory; + } + + +} // namespace toolkit + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/btndlg.cxx b/toolkit/source/helper/btndlg.cxx new file mode 100644 index 0000000000..a2471c3f92 --- /dev/null +++ b/toolkit/source/helper/btndlg.cxx @@ -0,0 +1,304 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcl/toolkit/button.hxx> +#include <vcl/stdtext.hxx> +#include <helper/btndlg.hxx> +#include <sal/log.hxx> +#include <map> +#include <memory> + +struct ImplBtnDlgItem +{ + sal_uInt16 mnId; + bool mbOwnButton; + tools::Long mnSepSize; + VclPtr<PushButton> mpPushButton; + + ImplBtnDlgItem() : mnId(0), mbOwnButton(false), mnSepSize(0) {} +}; + +void ButtonDialog::ImplInitButtonDialogData() +{ + mnButtonSize = 0; + mnCurButtonId = 0; + mnFocusButtonId = BUTTONDIALOG_BUTTON_NOTFOUND; + mbFormat = true; +} + +ButtonDialog::ButtonDialog( WindowType nType ) : + Dialog( nType ) +{ + ImplInitButtonDialogData(); +} + +ButtonDialog::~ButtonDialog() +{ + disposeOnce(); +} + +void ButtonDialog::dispose() +{ + for (auto & it : m_ItemList) + { + if ( it->mbOwnButton ) + it->mpPushButton.disposeAndClear(); + } + m_ItemList.clear(); + Dialog::dispose(); +} + +VclPtr<PushButton> ButtonDialog::ImplCreatePushButton( ButtonDialogFlags nBtnFlags ) +{ + VclPtr<PushButton> pBtn; + WinBits nStyle = 0; + + if ( nBtnFlags & ButtonDialogFlags::Default ) + nStyle |= WB_DEFBUTTON; + if ( nBtnFlags & ButtonDialogFlags::Cancel ) + pBtn = VclPtr<CancelButton>::Create( this, nStyle ); + else if ( nBtnFlags & ButtonDialogFlags::OK ) + pBtn = VclPtr<OKButton>::Create( this, nStyle ); + else if ( nBtnFlags & ButtonDialogFlags::Help ) + pBtn = VclPtr<HelpButton>::Create( this, nStyle ); + else + pBtn = VclPtr<PushButton>::Create( this, nStyle ); + + if ( !(nBtnFlags & ButtonDialogFlags::Help) ) + pBtn->SetClickHdl( LINK( this, ButtonDialog, ImplClickHdl ) ); + + return pBtn; +} + +tools::Long ButtonDialog::ImplGetButtonSize() +{ + if ( !mbFormat ) + return mnButtonSize; + + // Calculate ButtonSize + tools::Long nLastSepSize = 0; + tools::Long nSepSize = 0; + maCtrlSize = Size( IMPL_MINSIZE_BUTTON_WIDTH, IMPL_MINSIZE_BUTTON_HEIGHT ); + + for (const auto & it : m_ItemList) + { + nSepSize += nLastSepSize; + + tools::Long nTxtWidth = it->mpPushButton->GetOutDev()->GetCtrlTextWidth(it->mpPushButton->GetText()); + nTxtWidth += IMPL_EXTRA_BUTTON_WIDTH; + + if ( nTxtWidth > maCtrlSize.Width() ) + maCtrlSize.setWidth( nTxtWidth ); + + tools::Long nTxtHeight = it->mpPushButton->GetTextHeight(); + nTxtHeight += IMPL_EXTRA_BUTTON_HEIGHT; + + if ( nTxtHeight > maCtrlSize.Height() ) + maCtrlSize.setHeight( nTxtHeight ); + + nSepSize += it->mnSepSize; + + if ( GetStyle() & WB_HORZ ) + nLastSepSize = IMPL_SEP_BUTTON_X; + else + nLastSepSize = IMPL_SEP_BUTTON_Y; + } + + size_t const nButtonCount = m_ItemList.size(); + + if ( GetStyle() & WB_HORZ ) + mnButtonSize = nSepSize + (nButtonCount*maCtrlSize.Width()); + else + mnButtonSize = nSepSize + (nButtonCount*maCtrlSize.Height()); + + return mnButtonSize; +} + +void ButtonDialog::ImplPosControls() +{ + if ( !mbFormat ) + return; + + // Create PushButtons and determine Sizes + ImplGetButtonSize(); + + // determine dialog size + Size aDlgSize = maPageSize; + tools::Long nX; + tools::Long nY; + if ( GetStyle() & WB_HORZ ) + { + if ( mnButtonSize+(IMPL_DIALOG_OFFSET*2) > aDlgSize.Width() ) + aDlgSize.setWidth( mnButtonSize+(IMPL_DIALOG_OFFSET*2) ); + if ( GetStyle() & WB_LEFT ) + nX = IMPL_DIALOG_OFFSET; + else if ( GetStyle() & WB_RIGHT ) + nX = aDlgSize.Width()-mnButtonSize-IMPL_DIALOG_OFFSET; + else + nX = (aDlgSize.Width()-mnButtonSize)/2; + + aDlgSize.AdjustHeight(IMPL_DIALOG_OFFSET+maCtrlSize.Height() ); + nY = aDlgSize.Height()-maCtrlSize.Height()-IMPL_DIALOG_OFFSET; + } + else + { + if ( mnButtonSize+(IMPL_DIALOG_OFFSET*2) > aDlgSize.Height() ) + aDlgSize.setHeight( mnButtonSize+(IMPL_DIALOG_OFFSET*2) ); + if ( GetStyle() & WB_BOTTOM ) + nY = aDlgSize.Height()-mnButtonSize-IMPL_DIALOG_OFFSET; + else if ( GetStyle() & WB_VCENTER ) + nY = (aDlgSize.Height()-mnButtonSize)/2; + else + nY = IMPL_DIALOG_OFFSET; + + aDlgSize.AdjustWidth(IMPL_DIALOG_OFFSET+maCtrlSize.Width() ); + nX = aDlgSize.Width()-maCtrlSize.Width()-IMPL_DIALOG_OFFSET; + } + + // Arrange PushButtons + for (auto & it : m_ItemList) + { + if ( GetStyle() & WB_HORZ ) + nX += it->mnSepSize; + else + nY += it->mnSepSize; + + it->mpPushButton->SetPosSizePixel( Point( nX, nY ), maCtrlSize ); + it->mpPushButton->Show(); + + if ( GetStyle() & WB_HORZ ) + nX += maCtrlSize.Width()+IMPL_SEP_BUTTON_X; + else + nY += maCtrlSize.Height()+IMPL_SEP_BUTTON_Y; + } + + SetOutputSizePixel(aDlgSize); + SetMinOutputSizePixel(aDlgSize); + + mbFormat = false; +} + +IMPL_LINK( ButtonDialog, ImplClickHdl, Button*, pBtn, void ) +{ + for (const auto & it : m_ItemList) + { + if ( it->mpPushButton == pBtn ) + { + mnCurButtonId = it->mnId; + if ( IsInExecute() ) + EndDialog( mnCurButtonId ); + break; + } + } +} + +void ButtonDialog::Resize() +{ +} + +void ButtonDialog::StateChanged( StateChangedType nType ) +{ + if ( nType == StateChangedType::InitShow ) + { + ImplPosControls(); + for (auto & it : m_ItemList) + { + if ( it->mpPushButton && it->mbOwnButton ) + it->mpPushButton->SetZOrder(nullptr, ZOrderFlags::Last); + } + + // Set focus on default button. + if ( mnFocusButtonId != BUTTONDIALOG_BUTTON_NOTFOUND ) + { + for (auto & it : m_ItemList) + { + if (it->mnId == mnFocusButtonId ) + { + if (it->mpPushButton->IsVisible()) + it->mpPushButton->GrabFocus(); + + break; + } + } + } + } + + Dialog::StateChanged( nType ); +} + +void ButtonDialog::AddButton( StandardButtonType eType, sal_uInt16 nId, + ButtonDialogFlags nBtnFlags, tools::Long nSepPixel ) +{ + // PageItem anlegen + std::unique_ptr<ImplBtnDlgItem> pItem(new ImplBtnDlgItem); + pItem->mnId = nId; + pItem->mbOwnButton = true; + pItem->mnSepSize = nSepPixel; + + if ( eType == StandardButtonType::OK ) + nBtnFlags |= ButtonDialogFlags::OK; + else if ( eType == StandardButtonType::Help ) + nBtnFlags |= ButtonDialogFlags::Help; + else if ( (eType == StandardButtonType::Cancel) || (eType == StandardButtonType::Close) ) + nBtnFlags |= ButtonDialogFlags::Cancel; + pItem->mpPushButton = ImplCreatePushButton( nBtnFlags ); + + // Standard-Buttons have the right text already + if ( !((eType == StandardButtonType::OK && pItem->mpPushButton->GetType() == WindowType::OKBUTTON) || + (eType == StandardButtonType::Cancel && pItem->mpPushButton->GetType() == WindowType::CANCELBUTTON) || + (eType == StandardButtonType::Help && pItem->mpPushButton->GetType() == WindowType::HELPBUTTON)) ) + { + std::map<StandardButtonType, OUString> mapButtonTypeToID = {{StandardButtonType::Yes, "yes"}, + {StandardButtonType::No, "no"}, {StandardButtonType::Retry, "retry"}, + {StandardButtonType::Close, "close"}, {StandardButtonType::More, "more"}, + {StandardButtonType::Ignore, "ignore"}, {StandardButtonType::Abort, "abort"}, + {StandardButtonType::Less, "less"}, {StandardButtonType::Count, "count"}}; + auto itr = mapButtonTypeToID.find(eType); + if (itr != mapButtonTypeToID.end()) + pItem->mpPushButton->set_id(itr->second); + pItem->mpPushButton->SetText( GetStandardText( eType ) ); + } + + if ( nBtnFlags & ButtonDialogFlags::Focus ) + mnFocusButtonId = nId; + + m_ItemList.push_back(std::move(pItem)); + + mbFormat = true; +} + +void ButtonDialog::RemoveButton( sal_uInt16 nId ) +{ + auto it = std::find_if(m_ItemList.begin(), m_ItemList.end(), + [&nId](const std::unique_ptr<ImplBtnDlgItem>& rItem) { return rItem->mnId == nId; }); + if (it != m_ItemList.end()) + { + (*it)->mpPushButton->Hide(); + if ((*it)->mbOwnButton) + (*it)->mpPushButton.disposeAndClear(); + else + (*it)->mpPushButton.clear(); + m_ItemList.erase(it); + return; + } + + SAL_WARN( "vcl.window", "ButtonDialog::RemoveButton(): ButtonId invalid" ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/imagealign.cxx b/toolkit/source/helper/imagealign.cxx new file mode 100644 index 0000000000..43e7cb012c --- /dev/null +++ b/toolkit/source/helper/imagealign.cxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/awt/ImagePosition.hpp> +#include <com/sun/star/awt/ImageAlign.hpp> + +#include <helper/imagealign.hxx> +#include <osl/diagnose.h> + +namespace toolkit +{ + + + using namespace ::com::sun::star::awt::ImagePosition; + using namespace ::com::sun::star::awt::ImageAlign; + + sal_Int16 translateImagePosition( ImageAlign _eVCLAlign ) + { + sal_Int16 nReturn = AboveCenter; + switch ( _eVCLAlign ) + { + case ImageAlign::Left: nReturn = LeftCenter; break; + case ImageAlign::Top: nReturn = AboveCenter; break; + case ImageAlign::Right: nReturn = RightCenter; break; + case ImageAlign::Bottom: nReturn = BelowCenter; break; + case ImageAlign::LeftTop: nReturn = LeftTop; break; + case ImageAlign::LeftBottom: nReturn = LeftBottom; break; + case ImageAlign::TopLeft: nReturn = AboveLeft; break; + case ImageAlign::TopRight: nReturn = AboveRight; break; + case ImageAlign::RightTop: nReturn = RightTop; break; + case ImageAlign::RightBottom: nReturn = RightBottom; break; + case ImageAlign::BottomLeft: nReturn = BelowLeft; break; + case ImageAlign::BottomRight: nReturn = BelowRight; break; + case ImageAlign::Center: nReturn = Centered; break; + default: + OSL_FAIL( "translateImagePosition: unknown IMAGEALIGN value!" ); + } + return nReturn; + } + + ImageAlign translateImagePosition( sal_Int16 _eUNOAlign ) + { + ImageAlign nReturn = ImageAlign::Top; + switch ( _eUNOAlign ) + { + case LeftCenter: nReturn = ImageAlign::Left; break; + case AboveCenter: nReturn = ImageAlign::Top; break; + case RightCenter: nReturn = ImageAlign::Right; break; + case BelowCenter: nReturn = ImageAlign::Bottom; break; + case LeftTop: nReturn = ImageAlign::LeftTop; break; + case LeftBottom: nReturn = ImageAlign::LeftBottom; break; + case AboveLeft: nReturn = ImageAlign::TopLeft; break; + case AboveRight: nReturn = ImageAlign::TopRight; break; + case RightTop: nReturn = ImageAlign::RightTop; break; + case RightBottom: nReturn = ImageAlign::RightBottom; break; + case BelowLeft: nReturn = ImageAlign::BottomLeft; break; + case BelowRight: nReturn = ImageAlign::BottomRight; break; + case Centered: nReturn = ImageAlign::Center; break; + default: + OSL_FAIL( "translateImagePosition: unknown css.awt.ImagePosition value!" ); + } + return nReturn; + } + + sal_Int16 getCompatibleImageAlign( ImageAlign _eAlign ) + { + sal_Int16 nReturn = TOP; + switch ( _eAlign ) + { + case ImageAlign::LeftTop: + case ImageAlign::Left: + case ImageAlign::LeftBottom: nReturn = LEFT; break; + + case ImageAlign::TopLeft: + case ImageAlign::Top: + case ImageAlign::TopRight: nReturn = TOP; break; + + case ImageAlign::RightTop: + case ImageAlign::Right: + case ImageAlign::RightBottom: nReturn = RIGHT; break; + + case ImageAlign::BottomLeft: + case ImageAlign::Bottom: + case ImageAlign::BottomRight: nReturn = BOTTOM; break; + + case ImageAlign::Center: nReturn = TOP; break; + default: + OSL_FAIL( "getCompatibleImageAlign: unknown IMAGEALIGN value!" ); + } + return nReturn; + } + + sal_Int16 getExtendedImagePosition( sal_Int16 _nImageAlign ) + { + sal_Int16 nReturn = AboveCenter; + switch ( _nImageAlign ) + { + case LEFT: nReturn = LeftCenter; break; + case TOP: nReturn = AboveCenter; break; + case RIGHT: nReturn = RightCenter; break; + case BOTTOM: nReturn = BelowCenter; break; + default: + OSL_FAIL( "getExtendedImagePosition: unknown ImageAlign value!" ); + } + return nReturn; + } + + +} // namespace toolkit + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/listenermultiplexer.cxx b/toolkit/source/helper/listenermultiplexer.cxx new file mode 100644 index 0000000000..1c3ac1de52 --- /dev/null +++ b/toolkit/source/helper/listenermultiplexer.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 <toolkit/helper/listenermultiplexer.hxx> +#include <toolkit/helper/macros.hxx> +#include <com/sun/star/lang/DisposedException.hpp> + +// class EventListenerMultiplexer + +EventListenerMultiplexer::EventListenerMultiplexer( ::cppu::OWeakObject& rSource ) + : ListenerMultiplexerBase( rSource ) +{ +} + +void SAL_CALL EventListenerMultiplexer::acquire() noexcept +{ + return ListenerMultiplexerBase::acquire(); +} + +void SAL_CALL EventListenerMultiplexer::release() noexcept +{ + return ListenerMultiplexerBase::release(); +} + +// css::uno::XInterface +css::uno::Any EventListenerMultiplexer::queryInterface( const css::uno::Type & rType ) +{ + css::uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< css::lang::XEventListener* >(this) ); + return (aRet.hasValue() ? aRet : ListenerMultiplexerBase::queryInterface( rType )); +} + +// css::lang::XEventListener +void EventListenerMultiplexer::disposing( const css::lang::EventObject& ) +{ +} + + +// class FocusListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( FocusListenerMultiplexer, css::awt::XFocusListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( FocusListenerMultiplexer, css::awt::XFocusListener, focusGained, css::awt::FocusEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( FocusListenerMultiplexer, css::awt::XFocusListener, focusLost, css::awt::FocusEvent ) + + +// class WindowListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( WindowListenerMultiplexer, css::awt::XWindowListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( WindowListenerMultiplexer, css::awt::XWindowListener, windowResized, css::awt::WindowEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( WindowListenerMultiplexer, css::awt::XWindowListener, windowMoved, css::awt::WindowEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( WindowListenerMultiplexer, css::awt::XWindowListener, windowShown, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( WindowListenerMultiplexer, css::awt::XWindowListener, windowHidden, css::lang::EventObject ) + + +// class VclContainerListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( VclContainerListenerMultiplexer, css::awt::XVclContainerListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( VclContainerListenerMultiplexer, css::awt::XVclContainerListener, windowAdded, css::awt::VclContainerEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( VclContainerListenerMultiplexer, css::awt::XVclContainerListener, windowRemoved, css::awt::VclContainerEvent ) + + +// class KeyListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( KeyListenerMultiplexer, css::awt::XKeyListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( KeyListenerMultiplexer, css::awt::XKeyListener, keyPressed, css::awt::KeyEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( KeyListenerMultiplexer, css::awt::XKeyListener, keyReleased, css::awt::KeyEvent ) + + +// class MouseListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( MouseListenerMultiplexer, css::awt::XMouseListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseListenerMultiplexer, css::awt::XMouseListener, mousePressed, css::awt::MouseEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseListenerMultiplexer, css::awt::XMouseListener, mouseReleased, css::awt::MouseEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseListenerMultiplexer, css::awt::XMouseListener, mouseEntered, css::awt::MouseEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseListenerMultiplexer, css::awt::XMouseListener, mouseExited, css::awt::MouseEvent ) + + +// class MouseMotionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( MouseMotionListenerMultiplexer, css::awt::XMouseMotionListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseMotionListenerMultiplexer, css::awt::XMouseMotionListener, mouseDragged, css::awt::MouseEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MouseMotionListenerMultiplexer, css::awt::XMouseMotionListener, mouseMoved, css::awt::MouseEvent ) + + +// class PaintListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( PaintListenerMultiplexer, css::awt::XPaintListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( PaintListenerMultiplexer, css::awt::XPaintListener, windowPaint, css::awt::PaintEvent ) + + +// class TopWindowListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TopWindowListenerMultiplexer, css::awt::XTopWindowListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowOpened, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowClosing, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowClosed, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowMinimized, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowNormalized, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowActivated, css::lang::EventObject ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TopWindowListenerMultiplexer, css::awt::XTopWindowListener, windowDeactivated, css::lang::EventObject ) + + +// class TextListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TextListenerMultiplexer, css::awt::XTextListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TextListenerMultiplexer, css::awt::XTextListener, textChanged, css::awt::TextEvent ) + + +// class ActionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( ActionListenerMultiplexer, css::awt::XActionListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( ActionListenerMultiplexer, css::awt::XActionListener, actionPerformed, css::awt::ActionEvent ) + + +// class ItemListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( ItemListenerMultiplexer, css::awt::XItemListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( ItemListenerMultiplexer, css::awt::XItemListener, itemStateChanged, css::awt::ItemEvent ) + + +// class TabListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TabListenerMultiplexer, css::awt::XTabListener ) + +void TabListenerMultiplexer::inserted( sal_Int32 evt ) +IMPL_TABLISTENERMULTIPLEXER_LISTENERMETHOD_BODY_1PARAM( TabListenerMultiplexer, css::awt::XTabListener, inserted, ::sal_Int32 ) + +void TabListenerMultiplexer::removed( sal_Int32 evt ) +IMPL_TABLISTENERMULTIPLEXER_LISTENERMETHOD_BODY_1PARAM( TabListenerMultiplexer, css::awt::XTabListener, removed, ::sal_Int32 ) + +void TabListenerMultiplexer::changed( sal_Int32 evt, const css::uno::Sequence< css::beans::NamedValue >& evt2 ) +{ + sal_Int32 aMulti( evt ); + std::unique_lock g(m_aMutex); + ::comphelper::OInterfaceIteratorHelper4 aIt(g, maListeners); + g.unlock(); + while( aIt.hasMoreElements() ) + { + css::uno::Reference<css::awt::XTabListener> xListener(aIt.next()); + try + { + xListener->changed( aMulti, evt2 ); + } + catch(const css::lang::DisposedException& e) + { + OSL_ENSURE( e.Context.is(), "caught DisposedException with empty Context field" ); + if ( e.Context == xListener || !e.Context.is() ) + { + std::unique_lock g2(m_aMutex); + aIt.remove(g2); + } + } + catch(const css::uno::RuntimeException&) + { + DISPLAY_EXCEPTION( TabListenerMultiplexer, changed ) + } + } +} + + +void TabListenerMultiplexer::activated( sal_Int32 evt ) +IMPL_TABLISTENERMULTIPLEXER_LISTENERMETHOD_BODY_1PARAM( TabListenerMultiplexer, css::awt::XTabListener, activated, ::sal_Int32 ) + +void TabListenerMultiplexer::deactivated( sal_Int32 evt ) +IMPL_TABLISTENERMULTIPLEXER_LISTENERMETHOD_BODY_1PARAM( TabListenerMultiplexer, css::awt::XTabListener, deactivated, ::sal_Int32 ) + + +// class ContainerListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( ContainerListenerMultiplexer, css::container::XContainerListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( ContainerListenerMultiplexer, css::container::XContainerListener, elementInserted, css::container::ContainerEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( ContainerListenerMultiplexer, css::container::XContainerListener, elementRemoved, css::container::ContainerEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( ContainerListenerMultiplexer, css::container::XContainerListener, elementReplaced, css::container::ContainerEvent ) + + +// class SpinListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( SpinListenerMultiplexer, css::awt::XSpinListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( SpinListenerMultiplexer, css::awt::XSpinListener, up, css::awt::SpinEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( SpinListenerMultiplexer, css::awt::XSpinListener, down, css::awt::SpinEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( SpinListenerMultiplexer, css::awt::XSpinListener, first, css::awt::SpinEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( SpinListenerMultiplexer, css::awt::XSpinListener, last, css::awt::SpinEvent ) + + +// class AdjustmentListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( AdjustmentListenerMultiplexer, css::awt::XAdjustmentListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( AdjustmentListenerMultiplexer, css::awt::XAdjustmentListener, adjustmentValueChanged, css::awt::AdjustmentEvent ) + + +// class MenuListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( MenuListenerMultiplexer, css::awt::XMenuListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MenuListenerMultiplexer, css::awt::XMenuListener, itemHighlighted, css::awt::MenuEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MenuListenerMultiplexer, css::awt::XMenuListener, itemSelected, css::awt::MenuEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MenuListenerMultiplexer, css::awt::XMenuListener, itemActivated, css::awt::MenuEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( MenuListenerMultiplexer, css::awt::XMenuListener, itemDeactivated, css::awt::MenuEvent ) + + +// class TreeSelectionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TreeSelectionListenerMultiplexer, css::view::XSelectionChangeListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TreeSelectionListenerMultiplexer, css::view::XSelectionChangeListener, selectionChanged, css::lang::EventObject ) + + +// class TreeSelectionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener, requestChildNodes, css::awt::tree::TreeExpansionEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD_EXCEPTION( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener, treeExpanding, css::awt::tree::TreeExpansionEvent, css::awt::tree::ExpandVetoException ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD_EXCEPTION( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener, treeCollapsing, css::awt::tree::TreeExpansionEvent, css::awt::tree::ExpandVetoException ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener, treeExpanded, css::awt::tree::TreeExpansionEvent ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TreeExpansionListenerMultiplexer, css::awt::tree::XTreeExpansionListener, treeCollapsed, css::awt::tree::TreeExpansionEvent ) + + +// class TreeEditListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TreeEditListenerMultiplexer, css::awt::tree::XTreeEditListener ) + + +// class SelectionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( SelectionListenerMultiplexer, css::awt::grid::XGridSelectionListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( SelectionListenerMultiplexer, css::awt::grid::XGridSelectionListener, selectionChanged, css::awt::grid::GridSelectionEvent ) + + +// class SelectionListenerMultiplexer + +IMPL_LISTENERMULTIPLEXER_BASEMETHODS( TabPageListenerMultiplexer, css::awt::tab::XTabPageContainerListener ) +IMPL_LISTENERMULTIPLEXER_LISTENERMETHOD( TabPageListenerMultiplexer, css::awt::tab::XTabPageContainerListener, tabPageActivated, css::awt::tab::TabPageActivatedEvent ) +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/property.cxx b/toolkit/source/helper/property.cxx new file mode 100644 index 0000000000..945c4b016a --- /dev/null +++ b/toolkit/source/helper/property.cxx @@ -0,0 +1,335 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <helper/property.hxx> + +#include <tools/debug.hxx> +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/awt/tree/XTreeDataModel.hpp> +#include <com/sun/star/awt/grid/XGridDataModel.hpp> +#include <com/sun/star/awt/grid/XGridColumnModel.hpp> +#include <com/sun/star/view/SelectionType.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <algorithm> +#include <string_view> +#include <utility> +#include <unordered_map> + +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::awt::XDevice; +using ::com::sun::star::awt::FontDescriptor; +using ::com::sun::star::style::VerticalAlignment; +using ::com::sun::star::graphic::XGraphic; + +using namespace com::sun::star; + +namespace { + +struct ImplPropertyInfo +{ + css::uno::Type aType; + sal_uInt16 nPropId; + sal_Int16 nAttribs; + bool bDependsOnOthers; // eg. VALUE depends on MIN/MAX and must be set after MIN/MAX. + + ImplPropertyInfo( sal_uInt16 nId, const css::uno::Type& rType, + sal_Int16 nAttrs, bool bDepends = false ) + : aType(rType) + , nPropId(nId) + , nAttribs(nAttrs) + , bDependsOnOthers(bDepends) + { + } + +}; + +} + +#define DECL_PROP_1( asciiname, id, type, attrib1 ) \ + { asciiname, ImplPropertyInfo( BASEPROPERTY_##id, cppu::UnoType<type>::get(), css::beans::PropertyAttribute::attrib1 ) } +#define DECL_PROP_2( asciiname, id, type, attrib1, attrib2 ) \ + { asciiname, ImplPropertyInfo( BASEPROPERTY_##id, cppu::UnoType<type>::get(), css::beans::PropertyAttribute::attrib1 | css::beans::PropertyAttribute::attrib2 ) } +#define DECL_PROP_3( asciiname, id, type, attrib1, attrib2, attrib3 ) \ + { asciiname, ImplPropertyInfo( BASEPROPERTY_##id, cppu::UnoType<type>::get(), css::beans::PropertyAttribute::attrib1 | css::beans::PropertyAttribute::attrib2 | css::beans::PropertyAttribute::attrib3 ) } + +#define DECL_DEP_PROP_2( asciiname, id, type, attrib1, attrib2 ) \ + { asciiname, ImplPropertyInfo( BASEPROPERTY_##id, cppu::UnoType<type>::get(), css::beans::PropertyAttribute::attrib1 | css::beans::PropertyAttribute::attrib2, true ) } +#define DECL_DEP_PROP_3( asciiname, id, type, attrib1, attrib2, attrib3 ) \ + { asciiname, ImplPropertyInfo( BASEPROPERTY_##id, cppu::UnoType<type>::get(), css::beans::PropertyAttribute::attrib1 | css::beans::PropertyAttribute::attrib2 | css::beans::PropertyAttribute::attrib3, true ) } + +typedef std::unordered_map<OUString, ImplPropertyInfo> ImpPropertyInfoMap; +static const ImpPropertyInfoMap & ImplGetPropertyInfos() +{ + static const ImpPropertyInfoMap aImplPropertyInfos { + DECL_PROP_2 ( "AccessibleName", ACCESSIBLENAME, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "Align", ALIGN, sal_Int16, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Autocomplete", AUTOCOMPLETE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "AutoHScroll", AUTOHSCROLL, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_1 ( "AutoMnemonics", AUTOMNEMONICS, bool, BOUND ), + DECL_PROP_2 ( "AutoToggle", AUTOTOGGLE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "AutoVScroll", AUTOVSCROLL, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "BackgroundColor", BACKGROUNDCOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_DEP_PROP_2 ( "BlockIncrement", BLOCKINCREMENT, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "Border", BORDER, sal_Int16, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_DEP_PROP_3 ( "BorderColor", BORDERCOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Closeable", CLOSEABLE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "CurrencySymbol", CURRENCYSYMBOL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "CustomUnitText", CUSTOMUNITTEXT, OUString, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_3 ( "Date", DATE, util::Date, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "DateFormat", EXTDATEFORMAT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DateMax", DATEMAX, util::Date, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DateMin", DATEMIN, util::Date, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "DateShowCentury", DATESHOWCENTURY, bool, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "DecimalAccuracy", DECIMALACCURACY, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DefaultButton", DEFAULTBUTTON, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DefaultControl", DEFAULTCONTROL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DesktopAsParent", DESKTOP_AS_PARENT, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DisplayBackgroundColor", DISPLAYBACKGROUNDCOLOR, sal_Int32, BOUND, MAYBEVOID ), + DECL_PROP_2 ( "Dropdown", DROPDOWN, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "EchoChar", ECHOCHAR, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "EditMask", EDITMASK, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "EffectiveDefault", EFFECTIVE_DEFAULT, Any, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "EffectiveMax", EFFECTIVE_MAX, double, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "EffectiveMin", EFFECTIVE_MIN, double, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_DEP_PROP_3 ( "EffectiveValue", EFFECTIVE_VALUE, Any, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Enabled", ENABLED, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "EnforceFormat", ENFORCE_FORMAT, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "FillColor", FILLCOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "FocusOnClick", FOCUSONCLICK, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontRelief", FONTRELIEF, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontEmphasisMark", FONTEMPHASISMARK, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontDescriptor", FONTDESCRIPTOR, FontDescriptor, BOUND, MAYBEDEFAULT ), + + // parts of css::awt::FontDescriptor + DECL_PROP_2 ( "FontName", FONTDESCRIPTORPART_NAME, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontStyleName", FONTDESCRIPTORPART_STYLENAME, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontFamily", FONTDESCRIPTORPART_FAMILY, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontCharset", FONTDESCRIPTORPART_CHARSET, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontHeight", FONTDESCRIPTORPART_HEIGHT, float, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontWidth", FONTDESCRIPTORPART_WIDTH, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontPitch", FONTDESCRIPTORPART_PITCH, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontWeight", FONTDESCRIPTORPART_WEIGHT, float, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontCharWidth", FONTDESCRIPTORPART_CHARWIDTH, float, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontOrientation", FONTDESCRIPTORPART_ORIENTATION, float, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontSlant", FONTDESCRIPTORPART_SLANT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontUnderline", FONTDESCRIPTORPART_UNDERLINE, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontStrikeout", FONTDESCRIPTORPART_STRIKEOUT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontKerning", FONTDESCRIPTORPART_KERNING, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontWordLineMode", FONTDESCRIPTORPART_WORDLINEMODE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "FontType", FONTDESCRIPTORPART_TYPE, sal_Int16, BOUND, MAYBEDEFAULT ), + + DECL_PROP_3 ( "FormatKey", FORMATKEY, sal_Int32, BOUND, MAYBEVOID, TRANSIENT ), + DECL_PROP_3 ( "FormatsSupplier", FORMATSSUPPLIER, Reference< css::util::XNumberFormatsSupplier >, BOUND, MAYBEVOID, TRANSIENT ), + + DECL_PROP_2 ( "Graphic", GRAPHIC, Reference< XGraphic >, BOUND, TRANSIENT ), + DECL_PROP_2 ( "GroupName", GROUPNAME, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HelpText", HELPTEXT, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HelpURL", HELPURL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HideInactiveSelection", HIDEINACTIVESELECTION, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HighContrastMode", HIGHCONTRASTMODE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HScroll", HSCROLL, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "HardLineBreaks", HARDLINEBREAKS, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "HighlightColor", HIGHLIGHT_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID), + DECL_PROP_3 ( "HighlightTextColor", HIGHLIGHT_TEXT_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID), + DECL_PROP_2 ( "ImageAlign", IMAGEALIGN, sal_Int16, BOUND, MAYBEDEFAULT), + DECL_PROP_2 ( "ImagePosition", IMAGEPOSITION, sal_Int16, BOUND, MAYBEDEFAULT), + DECL_PROP_2 ( "ImageURL", IMAGEURL, css::uno::Any, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "ItemSeparatorPos", ITEM_SEPARATOR_POS, sal_Int16, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Label", LABEL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "LineColor", LINECOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "LineCount", LINECOUNT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "LineEndFormat", LINE_END_FORMAT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_2 ( "LineIncrement", LINEINCREMENT, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "LiteralMask", LITERALMASK, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "LiveScroll", LIVE_SCROLL, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "MaxTextLen", MAXTEXTLEN, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Moveable", MOVEABLE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_1 ( "MouseTransparent", MOUSETRANSPARENT, bool, BOUND ), + DECL_PROP_2 ( "MultiLine", MULTILINE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "MultiSelection", MULTISELECTION, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "MultiSelectionSimpleMode", MULTISELECTION_SIMPLEMODE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "NativeWidgetLook", NATIVE_WIDGET_LOOK, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "NoLabel", NOLABEL, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Orientation", ORIENTATION, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "PaintTransparent", PAINTTRANSPARENT, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "PluginParent", PLUGINPARENT, sal_Int64, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "PrependCurrencySymbol", CURSYM_POSITION, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Printable", PRINTABLE, bool, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_3 ( "ProgressValue", PROGRESSVALUE, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "ProgressValueMax", PROGRESSVALUE_MAX, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ProgressValueMin", PROGRESSVALUE_MIN, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "PushButtonType", PUSHBUTTONTYPE, sal_Int16, BOUND, MAYBEDEFAULT), + DECL_PROP_2 ( "ReadOnly", READONLY, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Repeat", REPEAT, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "AutoRepeat", AUTO_REPEAT, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "RepeatDelay", REPEAT_DELAY, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScaleImage", SCALEIMAGE, bool, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_2 ( "ScaleMode", IMAGE_SCALE_MODE, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_3 ( "ScrollValue", SCROLLVALUE, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "ScrollValueMax", SCROLLVALUE_MAX, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScrollValueMin", SCROLLVALUE_MIN, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScrollWidth", SCROLLWIDTH, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScrollHeight", SCROLLHEIGHT, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScrollTop", SCROLLTOP, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ScrollLeft", SCROLLLEFT, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_2 ( "SelectedItems", SELECTEDITEMS, Sequence<sal_Int16>, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ShowThousandsSeparator", NUMSHOWTHOUSANDSEP, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Sizeable", SIZEABLE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Spin", SPIN, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "SpinIncrement", SPININCREMENT, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_2 ( "SpinValue", SPINVALUE, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "SpinValueMax", SPINVALUE_MAX, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "SpinValueMin", SPINVALUE_MIN, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_2 ( "State", STATE, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "StrictFormat", STRICTFORMAT, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "StringItemList", STRINGITEMLIST, Sequence< OUString >, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "TypedItemList", TYPEDITEMLIST, Sequence< Any >, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "VisualEffect", VISUALEFFECT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "SymbolColor", SYMBOL_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "Tabstop", TABSTOP, bool, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Text", TEXT, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "TextColor", TEXTCOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "TextLineColor", TEXTLINECOLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_DEP_PROP_3 ( "Time", TIME, util::Time, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "TimeFormat", EXTTIMEFORMAT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "TimeMax", TIMEMAX, util::Time, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "TimeMin", TIMEMIN, util::Time, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Title", TITLE, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Toggle", TOGGLE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "TreatAsNumber", TREATASNUMBER, bool, BOUND, MAYBEDEFAULT,TRANSIENT ), + DECL_PROP_2 ( "TriState", TRISTATE, bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Unit", UNIT, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "VScroll", VSCROLL, bool, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_3 ( "Value", VALUE_DOUBLE, double, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "ValueMax", VALUEMAX_DOUBLE, double, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ValueMin", VALUEMIN_DOUBLE, double, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ValueStep", VALUESTEP_DOUBLE, double, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "VerticalAlign", VERTICALALIGN, VerticalAlignment, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_DEP_PROP_3 ( "VisibleSize", VISIBLESIZE, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "Activated", ACTIVATED, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Complete", COMPLETE, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "CurrentItemID", CURRENTITEMID, sal_Int16, BOUND, MAYBEDEFAULT ), + + DECL_PROP_2 ( "MouseWheelBehavior", MOUSE_WHEEL_BEHAVIOUR, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "StepTime", STEP_TIME, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Decoration", DECORATION, sal_Bool, BOUND, MAYBEDEFAULT ), + + DECL_PROP_2 ( "SelectionType", TREE_SELECTIONTYPE, css::view::SelectionType, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "Editable", TREE_EDITABLE, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "DataModel", TREE_DATAMODEL, Reference< css::awt::tree::XTreeDataModel >,BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "RootDisplayed", TREE_ROOTDISPLAYED, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ShowsHandles", TREE_SHOWSHANDLES, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ShowsRootHandles", TREE_SHOWSROOTHANDLES, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "RowHeight", ROW_HEIGHT, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "InvokesStopNodeEditing", TREE_INVOKESSTOPNODEEDITING, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "DialogSourceURL", DIALOGSOURCEURL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "URL", URL, OUString, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "WritingMode", WRITING_MODE, sal_Int16, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "ContextWritingMode", CONTEXT_WRITING_MODE, sal_Int16, BOUND, MAYBEDEFAULT, TRANSIENT ), + DECL_PROP_2 ( "ShowRowHeader", GRID_SHOWROWHEADER, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "RowHeaderWidth", ROW_HEADER_WIDTH, sal_Int32, BOUND, MAYBEDEFAULT ), + DECL_PROP_2 ( "ShowColumnHeader", GRID_SHOWCOLUMNHEADER, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "ColumnHeaderHeight", COLUMN_HEADER_HEIGHT, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_1 ( "GridDataModel", GRID_DATAMODEL, Reference< css::awt::grid::XGridDataModel >, BOUND ), + DECL_PROP_1 ( "ColumnModel", GRID_COLUMNMODEL, Reference< css::awt::grid::XGridColumnModel >, BOUND ), + DECL_PROP_3 ( "SelectionModel", GRID_SELECTIONMODE, css::view::SelectionType, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "EnableVisible", ENABLEVISIBLE, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_PROP_3 ( "ReferenceDevice", REFERENCE_DEVICE, Reference< XDevice >,BOUND, MAYBEDEFAULT, TRANSIENT ), + DECL_PROP_3 ( "HeaderBackgroundColor", GRID_HEADER_BACKGROUND, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "HeaderTextColor", GRID_HEADER_TEXT_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "GridLineColor", GRID_LINE_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "RowBackgroundColors", GRID_ROW_BACKGROUND_COLORS, Sequence< sal_Int32 >, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_2 ( "UseGridLines", USE_GRID_LINES, sal_Bool, BOUND, MAYBEDEFAULT ), + DECL_DEP_PROP_3 ( "MultiPageValue", MULTIPAGEVALUE, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "AllDialogChildren", USERFORMCONTAINEES, Reference< css::container::XNameContainer >, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "ActiveSelectionBackgroundColor", ACTIVE_SEL_BACKGROUND_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "InactiveSelectionBackgroundColor", INACTIVE_SEL_BACKGROUND_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "ActiveSelectionTextColor", ACTIVE_SEL_TEXT_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + DECL_PROP_3 ( "InactiveSelectionTextColor", INACTIVE_SEL_TEXT_COLOR, sal_Int32, BOUND, MAYBEDEFAULT, MAYBEVOID ), + }; + return aImplPropertyInfos; +} + +sal_uInt16 GetPropertyId( const OUString& rPropertyName ) +{ + const ImpPropertyInfoMap & rMap = ImplGetPropertyInfos(); + auto it = rMap.find(rPropertyName); + return it != rMap.end() ? it->second.nPropId : 0; +} + +static const ImplPropertyInfo* ImplGetImplPropertyInfo( sal_uInt16 nPropertyId ) +{ + const ImpPropertyInfoMap & rMap = ImplGetPropertyInfos(); + + for (auto const & rPair : rMap) + if (rPair.second.nPropId == nPropertyId) + return &rPair.second; + return nullptr; +} + +const OUString& GetPropertyName( sal_uInt16 nPropertyId ) +{ + const ImpPropertyInfoMap & rMap = ImplGetPropertyInfos(); + + for (auto const & rPair : rMap) + if (rPair.second.nPropId == nPropertyId) + return rPair.first; + + assert(false && "Invalid PropertyId!"); + static const OUString EMPTY; + return EMPTY; +} + +const css::uno::Type* GetPropertyType( sal_uInt16 nPropertyId ) +{ + const ImplPropertyInfo* pImplPropertyInfo = ImplGetImplPropertyInfo( nPropertyId ); + DBG_ASSERT( pImplPropertyInfo, "Invalid PropertyId!" ); + return pImplPropertyInfo ? &pImplPropertyInfo->aType : nullptr; +} + +sal_Int16 GetPropertyAttribs( sal_uInt16 nPropertyId ) +{ + const ImplPropertyInfo* pImplPropertyInfo = ImplGetImplPropertyInfo( nPropertyId ); + DBG_ASSERT( pImplPropertyInfo, "Invalid PropertyId!" ); + return pImplPropertyInfo ? pImplPropertyInfo->nAttribs : 0; +} + +bool DoesDependOnOthers( sal_uInt16 nPropertyId ) +{ + const ImplPropertyInfo* pImplPropertyInfo = ImplGetImplPropertyInfo( nPropertyId ); + DBG_ASSERT( pImplPropertyInfo, "Invalid PropertyId!" ); + return pImplPropertyInfo && pImplPropertyInfo->bDependsOnOthers; +} + +bool CompareProperties( const css::uno::Any& r1, const css::uno::Any& r2 ) +{ + return r1 == r2; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/servicenames.cxx b/toolkit/source/helper/servicenames.cxx new file mode 100644 index 0000000000..825672fd20 --- /dev/null +++ b/toolkit/source/helper/servicenames.cxx @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <helper/servicenames.hxx> + +const char szServiceName_UnoControlDialog[] = "stardiv.vcl.control.Dialog"; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/tkresmgr.cxx b/toolkit/source/helper/tkresmgr.cxx new file mode 100644 index 0000000000..3a687f5236 --- /dev/null +++ b/toolkit/source/helper/tkresmgr.cxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <comphelper/processfactory.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <comphelper/diagnose_ex.hxx> + +#include <vcl/image.hxx> + +#include <helper/tkresmgr.hxx> + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::graphic::XGraphic; +using ::com::sun::star::graphic::XGraphicProvider; +using namespace ::com::sun::star; + +Image TkResMgr::getImageFromURL(const OUString& i_rImageURL) +{ + if (i_rImageURL.isEmpty()) + return Image(); + + try + { + Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext()); + Reference<XGraphicProvider> xProvider(graphic::GraphicProvider::create(xContext)); + ::comphelper::NamedValueCollection aMediaProperties; + aMediaProperties.put("URL", i_rImageURL); + Reference<XGraphic> xGraphic + = xProvider->queryGraphic(aMediaProperties.getPropertyValues()); + return Image(xGraphic); + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("toolkit"); + } + return Image(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/unopropertyarrayhelper.cxx b/toolkit/source/helper/unopropertyarrayhelper.cxx new file mode 100644 index 0000000000..bc0f996d3d --- /dev/null +++ b/toolkit/source/helper/unopropertyarrayhelper.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 <helper/property.hxx> +#include <map> + +#include <helper/unopropertyarrayhelper.hxx> + + + +UnoPropertyArrayHelper::UnoPropertyArrayHelper( const css::uno::Sequence<sal_Int32>& rIDs ) +{ + for ( const sal_Int32 nID : rIDs ) + maIDs.insert( nID ); +} + +UnoPropertyArrayHelper::UnoPropertyArrayHelper( const std::vector< sal_uInt16 > &rIDs ) +{ + for (const auto& rId : rIDs) + maIDs.insert( rId ); +} + +bool UnoPropertyArrayHelper::ImplHasProperty( sal_uInt16 nPropId ) const +{ + if ( ( nPropId >= BASEPROPERTY_FONTDESCRIPTORPART_START ) && ( nPropId <= BASEPROPERTY_FONTDESCRIPTORPART_END ) ) + nPropId = BASEPROPERTY_FONTDESCRIPTOR; + + return maIDs.find( nPropId ) != maIDs.end(); +} + +// ::cppu::IPropertyArrayHelper +sal_Bool UnoPropertyArrayHelper::fillPropertyMembersByHandle( OUString * pPropName, sal_Int16 * pAttributes, sal_Int32 nPropId ) +{ + sal_uInt16 id = sal::static_int_cast< sal_uInt16 >(nPropId); + bool bValid = ImplHasProperty( id ); + if ( bValid ) + { + if ( pPropName ) + *pPropName = GetPropertyName( id ); + if ( pAttributes ) + *pAttributes = GetPropertyAttribs( id ); + } + return bValid; +} + +css::uno::Sequence< css::beans::Property > UnoPropertyArrayHelper::getProperties() +{ + // Sort by names ... + + std::map<OUString, sal_uInt16> aSortedPropsIds; + for (const auto& rId : maIDs) + { + sal_uInt16 nId = sal::static_int_cast< sal_uInt16 >(rId); + aSortedPropsIds.emplace(GetPropertyName( nId ), nId); + + if ( nId == BASEPROPERTY_FONTDESCRIPTOR ) + { + // single properties ... + for ( sal_uInt16 i = BASEPROPERTY_FONTDESCRIPTORPART_START; i <= BASEPROPERTY_FONTDESCRIPTORPART_END; i++ ) + aSortedPropsIds.emplace(GetPropertyName( i ), i); + } + } + + sal_uInt32 nProps = aSortedPropsIds.size(); // could be more now + css::uno::Sequence< css::beans::Property> aProps( nProps ); + css::beans::Property* pProps = aProps.getArray(); + + sal_uInt32 n = 0; + for ( const auto& rPropIds : aSortedPropsIds ) + { + sal_uInt16 nId = rPropIds.second; + pProps[n].Name = rPropIds.first; + pProps[n].Handle = nId; + pProps[n].Type = *GetPropertyType( nId ); + pProps[n].Attributes = GetPropertyAttribs( nId ); + ++n; + } + + return aProps; +} + +css::beans::Property UnoPropertyArrayHelper::getPropertyByName(const OUString& rPropertyName) +{ + css::beans::Property aProp; + sal_uInt16 nId = GetPropertyId( rPropertyName ); + if ( ImplHasProperty( nId ) ) + { + aProp.Name = rPropertyName; + aProp.Handle = -1; + aProp.Type = *GetPropertyType( nId ); + aProp.Attributes = GetPropertyAttribs( nId ); + } + + return aProp; +} + +sal_Bool UnoPropertyArrayHelper::hasPropertyByName(const OUString& rPropertyName) +{ + return ImplHasProperty( GetPropertyId( rPropertyName ) ); +} + +sal_Int32 UnoPropertyArrayHelper::getHandleByName( const OUString & rPropertyName ) +{ + sal_Int32 nId = static_cast<sal_Int32>(GetPropertyId( rPropertyName )); + return nId ? nId : -1; +} + +sal_Int32 UnoPropertyArrayHelper::fillHandles( sal_Int32* pHandles, const css::uno::Sequence< OUString > & rPropNames ) +{ + const OUString* pNames = rPropNames.getConstArray(); + sal_Int32 nValues = rPropNames.getLength(); + sal_Int32 nValidHandles = 0; + + for ( sal_Int32 n = 0; n < nValues; n++ ) + { + sal_uInt16 nPropId = GetPropertyId( pNames[n] ); + if ( nPropId && ImplHasProperty( nPropId ) ) + { + pHandles[n] = nPropId; + nValidHandles++; + } + else + { + pHandles[n] = -1; + } + } + return nValidHandles; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/unowrapper.cxx b/toolkit/source/helper/unowrapper.cxx new file mode 100644 index 0000000000..82b4dd1733 --- /dev/null +++ b/toolkit/source/helper/unowrapper.cxx @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <toolkit/helper/vclunohelper.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/awt/vclxwindows.hxx> +#include <toolkit/awt/vclxmenu.hxx> +#include <awt/vclxcontainer.hxx> +#include <awt/vclxgraphics.hxx> +#include <awt/vclxtopwindow.hxx> +#include <awt/vclxwindows.hxx> + +#include <toolkit/dllapi.h> +#include <vcl/menu.hxx> + +#include <helper/unowrapper.hxx> + +using namespace ::com::sun::star; + +static rtl::Reference<VCLXWindow> CreateXWindow( vcl::Window const * pWindow ) +{ + switch ( pWindow->GetType() ) + { + case WindowType::IMAGEBUTTON: + case WindowType::SPINBUTTON: + case WindowType::MENUBUTTON: + case WindowType::MOREBUTTON: + case WindowType::PUSHBUTTON: + case WindowType::HELPBUTTON: + case WindowType::OKBUTTON: + case WindowType::CANCELBUTTON: return new VCLXButton; + case WindowType::CHECKBOX: return new VCLXCheckBox; + // #i95042# + // A Window of type <MetricBox> is inherited from type <ComboBox>. + // Thus, it does make more sense to return a <VCLXComboBox> instance + // instead of only a <VCLXWindow> instance, especially regarding its + // corresponding accessibility API. + case WindowType::METRICBOX: + case WindowType::COMBOBOX: return new VCLXComboBox; + case WindowType::FORMATTEDFIELD: return new SVTXNumericField; + case WindowType::SPINFIELD: + case WindowType::CURRENCYFIELD: return new VCLXNumericField; + case WindowType::DATEFIELD: return new VCLXDateField; + case WindowType::MULTILINEEDIT: + case WindowType::EDIT: return new VCLXEdit; + case WindowType::METRICFIELD: return new VCLXSpinField; + case WindowType::MESSBOX: + case WindowType::INFOBOX: + case WindowType::WARNINGBOX: + case WindowType::QUERYBOX: + case WindowType::ERRORBOX: return new VCLXMessageBox; + case WindowType::FIXEDIMAGE: return new VCLXImageControl; + case WindowType::FIXEDTEXT: return new VCLXFixedText; + case WindowType::MULTILISTBOX: + case WindowType::LISTBOX: return new VCLXListBox; + case WindowType::DIALOG: + case WindowType::TABDIALOG: + case WindowType::BUTTONDIALOG: + case WindowType::MODELESSDIALOG: return new VCLXDialog; + case WindowType::PATTERNFIELD: return new VCLXPatternField; + case WindowType::RADIOBUTTON: return new VCLXRadioButton; + case WindowType::SCROLLBAR: return new VCLXScrollBar; + case WindowType::TIMEFIELD: return new VCLXTimeField; + + case WindowType::WORKWINDOW: + case WindowType::DOCKINGWINDOW: + case WindowType::FLOATINGWINDOW: + case WindowType::HELPTEXTWINDOW: return new VCLXTopWindow; + + case WindowType::WINDOW: + case WindowType::TABPAGE: return new VCLXContainer; + + case WindowType::TOOLBOX: return new VCLXToolBox; + case WindowType::TABCONTROL: return new VCLXMultiPage; + + case WindowType::HEADERBAR: return new VCLXHeaderBar; + + case WindowType::BORDERWINDOW: + { + if (pWindow->IsNativeFrame()) + return new VCLXTopWindow; + return new VCLXWindow(true); + } + + // case WindowType::FIXEDLINE: + // case WindowType::FIXEDBITMAP: + // case WindowType::DATEBOX: + // case WindowType::GROUPBOX: + // case WindowType::LONGCURRENCYBOX: + // case WindowType::SPLITTER: + // case WindowType::STATUSBAR: + // case WindowType::TABCONTROL: + // case WindowType::NUMERICBOX: + // case WindowType::TRISTATEBOX: + // case WindowType::TIMEBOX: + // case WindowType::SPLITWINDOW: + // case WindowType::SCROLLBARBOX: + // case WindowType::PATTERNBOX: + // case WindowType::CURRENCYBOX: + default: return new VCLXWindow( true ); + } +} + + + + +extern "C" { + +TOOLKIT_DLLPUBLIC UnoWrapperBase* CreateUnoWrapper() +{ + return new UnoWrapper( nullptr ); +} + +} // extern "C" + + +UnoWrapper::UnoWrapper( const css::uno::Reference< css::awt::XToolkit>& rxToolkit ) +{ + mxToolkit = rxToolkit; +} + +void UnoWrapper::Destroy() +{ + delete this; +} + +UnoWrapper::~UnoWrapper() +{ +} + +css::uno::Reference< css::awt::XToolkit> UnoWrapper::GetVCLToolkit() +{ + if ( !mxToolkit.is() ) + mxToolkit = VCLUnoHelper::CreateToolkit(); + return mxToolkit; +} + +css::uno::Reference< css::awt::XVclWindowPeer> UnoWrapper::GetWindowInterface( vcl::Window* pWindow ) +{ + css::uno::Reference< css::awt::XVclWindowPeer> xPeer = pWindow->GetWindowPeer(); + if ( xPeer ) + return xPeer; + + rtl::Reference<VCLXWindow> xVCLXWindow = CreateXWindow( pWindow ); + xVCLXWindow->SetWindow( pWindow ); + pWindow->SetWindowPeer( xVCLXWindow, xVCLXWindow.get() ); + return xVCLXWindow; +} + +VclPtr<vcl::Window> UnoWrapper::GetWindow(const css::uno::Reference<css::awt::XWindow>& rWindow) +{ + return VCLUnoHelper::GetWindow(rWindow); +} + +void UnoWrapper::SetWindowInterface( vcl::Window* pWindow, const css::uno::Reference< css::awt::XVclWindowPeer> & xIFace ) +{ + VCLXWindow* pVCLXWindow = dynamic_cast<VCLXWindow*>( xIFace.get() ); + + assert( pVCLXWindow && "must be a VCLXWindow subclass" ); + if ( !pVCLXWindow ) + return; + + if (!pWindow) + { + // we are disconnecting a peer from a window + pVCLXWindow->SetWindow( nullptr ); + } + else + { + css::uno::Reference< css::awt::XVclWindowPeer> xPeer = pWindow->GetWindowPeer(); + if( xPeer.is() ) + { + bool bSameInstance( pVCLXWindow == dynamic_cast< VCLXWindow* >( xPeer.get() )); + SAL_WARN_IF( !bSameInstance, "toolkit.helper", "UnoWrapper::SetWindowInterface: there is already a WindowPeer/ComponentInterface for this VCL window" ); + if ( bSameInstance ) + return; + } + pVCLXWindow->SetWindow( pWindow ); + pWindow->SetWindowPeer( xIFace, pVCLXWindow ); + } +} + +css::uno::Reference<css::awt::XPopupMenu> UnoWrapper::CreateMenuInterface( PopupMenu* pPopupMenu ) +{ + return new VCLXPopupMenu(pPopupMenu); +} + +css::uno::Reference< css::awt::XGraphics> UnoWrapper::CreateGraphics( OutputDevice* pOutDev ) +{ + rtl::Reference<VCLXGraphics> pGrf = new VCLXGraphics; + pGrf->Init( pOutDev ); + return pGrf; +} + +void UnoWrapper::ReleaseAllGraphics( OutputDevice* pOutDev ) +{ + std::vector< VCLXGraphics* > *pLst = pOutDev->GetUnoGraphicsList(); + if ( pLst ) + { + for (VCLXGraphics* pGrf : *pLst) + { + pGrf->SetOutputDevice( nullptr ); + } + } + +} + +static bool lcl_ImplIsParent( vcl::Window const * pParentWindow, vcl::Window* pPossibleChild ) +{ + vcl::Window* pWindow = ( pPossibleChild != pParentWindow ) ? pPossibleChild : nullptr; + while ( pWindow && ( pWindow != pParentWindow ) ) + pWindow = pWindow->GetParent(); + + return pWindow != nullptr; +} + +void UnoWrapper::WindowDestroyed( vcl::Window* pWindow ) +{ + // their still might be some children created with css::loader::Java + // that would otherwise not be destroyed until the garbage collector cleans up + VclPtr< vcl::Window > pChild = pWindow->GetWindow( GetWindowType::FirstChild ); + while ( pChild ) + { + VclPtr< vcl::Window > pNextChild = pChild->GetWindow( GetWindowType::Next ); + + VclPtr< vcl::Window > pClient = pChild->GetWindow( GetWindowType::Client ); + if ( pClient && pClient->GetWindowPeer() ) + { + css::uno::Reference< css::lang::XComponent > xComp = pClient->GetComponentInterface( false ); + xComp->dispose(); + } + else + { + // We need it to dispose the child windows properly (even without window peer), + // otherwise the vcl::Window will be leaked. + pClient.disposeAndClear(); + } + + pChild = pNextChild; + } + + // find system windows... + VclPtr< vcl::Window > pOverlap = pWindow->GetWindow( GetWindowType::Overlap ); + if ( pOverlap ) + { + pOverlap = pOverlap->GetWindow( GetWindowType::FirstOverlap ); + while ( pOverlap ) + { + VclPtr< vcl::Window > pNextOverlap = pOverlap->GetWindow( GetWindowType::Next ); + VclPtr< vcl::Window > pClient = pOverlap->GetWindow( GetWindowType::Client ); + + if ( pClient && pClient->GetWindowPeer() && lcl_ImplIsParent( pWindow, pClient ) ) + { + css::uno::Reference< css::lang::XComponent > xComp = pClient->GetComponentInterface( false ); + xComp->dispose(); + } + + pOverlap = pNextOverlap; + } + } + + { + VclPtr< vcl::Window > pParent = pWindow->GetParent(); + if ( pParent && pParent->GetWindowPeer() ) + pParent->GetWindowPeer()->notifyWindowRemoved( *pWindow ); + } + + VCLXWindow* pWindowPeer = pWindow->GetWindowPeer(); + uno::Reference< lang::XComponent > xWindowPeerComp = pWindow->GetComponentInterface( false ); + OSL_ENSURE( ( pWindowPeer != nullptr ) == xWindowPeerComp.is(), + "UnoWrapper::WindowDestroyed: inconsistency in the window's peers!" ); + if ( pWindowPeer ) + { + pWindowPeer->SetWindow( nullptr ); + pWindow->SetWindowPeer( nullptr, nullptr ); + } + if ( xWindowPeerComp.is() ) + xWindowPeerComp->dispose(); + + // #102132# Iterate over frames after setting Window peer to NULL, + // because while destroying other frames, we get into the method again and try + // to destroy this window again... + // #i42462#/#116855# no, don't loop: Instead, just ensure that all our top-window-children + // are disposed, too (which should also be a valid fix for #102132#, but doesn't have the extreme + // performance penalties) + VclPtr< vcl::Window > pTopWindowChild = pWindow->GetWindow( GetWindowType::FirstTopWindowChild ); + while ( pTopWindowChild ) + { + OSL_ENSURE( pTopWindowChild->GetParent() == pWindow, + "UnoWrapper::WindowDestroyed: inconsistency in the SystemWindow relationship!" ); + + VclPtr< vcl::Window > pNextTopChild = pTopWindowChild->GetWindow( GetWindowType::NextTopWindowSibling ); + + pTopWindowChild.disposeAndClear(); + pTopWindowChild = pNextTopChild; + } +} + + +css::uno::Reference< css::accessibility::XAccessible > UnoWrapper::CreateAccessible( Menu* pMenu, bool bIsMenuBar ) +{ + return maAccessibleFactoryAccess.getFactory().createAccessible( pMenu, bIsMenuBar ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/source/helper/vclunohelper.cxx b/toolkit/source/helper/vclunohelper.cxx new file mode 100644 index 0000000000..9f05ae7070 --- /dev/null +++ b/toolkit/source/helper/vclunohelper.cxx @@ -0,0 +1,604 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <tools/stream.hxx> +#include <vcl/dibtools.hxx> +#include <vcl/event.hxx> +#include <vcl/graph.hxx> +#include <vcl/metric.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/unohelp.hxx> +#include <vcl/window.hxx> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/awt/SimpleFontMetric.hpp> +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> +#include <com/sun/star/awt/MouseButton.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/helper/convert.hxx> +#include <awt/vclxbitmap.hxx> +#include <awt/vclxregion.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <awt/vclxgraphics.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <controls/unocontrolcontainer.hxx> +#include <controls/unocontrolcontainermodel.hxx> +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/awt/Point.hpp> + +using namespace ::com::sun::star; + + +uno::Reference< css::awt::XToolkit> VCLUnoHelper::CreateToolkit() +{ + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< awt::XToolkit> xToolkit( awt::Toolkit::create(xContext), uno::UNO_QUERY_THROW ); + return xToolkit; +} + +BitmapEx VCLUnoHelper::GetBitmap( const css::uno::Reference< css::awt::XBitmap>& rxBitmap ) +{ + BitmapEx aBmp; + + css::uno::Reference< css::graphic::XGraphic > xGraphic( rxBitmap, css::uno::UNO_QUERY ); + if( xGraphic.is() ) + { + Graphic aGraphic( xGraphic ); + aBmp = aGraphic.GetBitmapEx(); + } + else if ( rxBitmap.is() ) + { + VCLXBitmap* pVCLBitmap = dynamic_cast<VCLXBitmap*>( rxBitmap.get() ); + if ( pVCLBitmap ) + aBmp = pVCLBitmap->GetBitmap(); + else + { + Bitmap aDIB, aMask; + { + css::uno::Sequence<sal_Int8> aBytes = rxBitmap->getDIB(); + SvMemoryStream aMem( aBytes.getArray(), aBytes.getLength(), StreamMode::READ ); + ReadDIB(aDIB, aMem, true); + } + { + css::uno::Sequence<sal_Int8> aBytes = rxBitmap->getMaskDIB(); + SvMemoryStream aMem( aBytes.getArray(), aBytes.getLength(), StreamMode::READ ); + ReadDIB(aMask, aMem, true); + } + aMask.Invert(); // Convert from transparency to alpha + aBmp = BitmapEx( aDIB, aMask ); + } + } + return aBmp; +} + +css::uno::Reference< css::awt::XBitmap> VCLUnoHelper::CreateBitmap( const BitmapEx& rBitmap ) +{ + Graphic aGraphic( rBitmap ); + css::uno::Reference< css::awt::XBitmap> xBmp( aGraphic.GetXGraphic(), css::uno::UNO_QUERY ); + return xBmp; +} + +vcl::Window* VCLUnoHelper::GetWindow( const css::uno::Reference< css::awt::XWindow>& rxWindow ) +{ + VCLXWindow* pVCLXWindow = dynamic_cast<VCLXWindow*>( rxWindow.get() ); + return pVCLXWindow ? pVCLXWindow->GetWindow() : nullptr; +} + +vcl::Window* VCLUnoHelper::GetWindow( const css::uno::Reference< css::awt::XWindow2>& rxWindow ) +{ + VCLXWindow* pVCLXWindow = dynamic_cast<VCLXWindow*>( rxWindow.get() ); + return pVCLXWindow ? pVCLXWindow->GetWindow() : nullptr; +} + +vcl::Window* VCLUnoHelper::GetWindow( const css::uno::Reference< css::awt::XWindowPeer>& rxWindow ) +{ + VCLXWindow* pVCLXWindow = dynamic_cast<VCLXWindow*>( rxWindow.get() ); + return pVCLXWindow ? pVCLXWindow->GetWindow() : nullptr; +} + +vcl::Region VCLUnoHelper::GetRegion( const css::uno::Reference< css::awt::XRegion >& rxRegion ) +{ + vcl::Region aRegion; + VCLXRegion* pVCLRegion = dynamic_cast<VCLXRegion*>( rxRegion.get() ); + if ( pVCLRegion ) + aRegion = pVCLRegion->GetRegion(); + else + { + const css::uno::Sequence< css::awt::Rectangle > aRects = rxRegion->getRectangles(); + for ( const auto& rRect : aRects ) + aRegion.Union( VCLRectangle( rRect ) ); + } + return aRegion; +} + +css::uno::Reference< css::awt::XWindow> VCLUnoHelper::GetInterface( vcl::Window* pWindow ) +{ + css::uno::Reference< css::awt::XWindow > xWin; + if ( pWindow ) + { + css::uno::Reference< css::awt::XWindowPeer> xPeer = pWindow->GetComponentInterface(); + xWin.set(xPeer, css::uno::UNO_QUERY); + } + return xWin; +} + +OutputDevice* VCLUnoHelper::GetOutputDevice( const css::uno::Reference< css::awt::XDevice>& rxDevice ) +{ + VclPtr<OutputDevice> pOutDev; + VCLXDevice* pDev = dynamic_cast<VCLXDevice*>( rxDevice.get() ); + if ( pDev ) + pOutDev = pDev->GetOutputDevice(); + return pOutDev; +} + +OutputDevice* VCLUnoHelper::GetOutputDevice( const css::uno::Reference< css::awt::XGraphics>& rxGraphics ) +{ + OutputDevice* pOutDev = nullptr; + VCLXGraphics* pGrf = dynamic_cast<VCLXGraphics*>( rxGraphics.get() ); + if ( pGrf ) + pOutDev = pGrf->GetOutputDevice(); + return pOutDev; +} + +tools::Polygon VCLUnoHelper::CreatePolygon( const css::uno::Sequence< sal_Int32 >& DataX, + const css::uno::Sequence< sal_Int32 >& DataY ) +{ + sal_Int32 nLen = DataX.getLength(); + const sal_Int32* pDataX = DataX.getConstArray(); + const sal_Int32* pDataY = DataY.getConstArray(); + tools::Polygon aPoly( static_cast<sal_uInt16>(nLen) ); + for ( sal_Int32 n = 0; n < nLen; n++ ) + { + Point aPnt; + aPnt.setX( pDataX[n] ); + aPnt.setY( pDataY[n] ); + aPoly[n] = aPnt; + } + return aPoly; +} + +css::uno::Reference< css::awt::XControlContainer> VCLUnoHelper::CreateControlContainer( vcl::Window* pWindow ) +{ + rtl::Reference<UnoControlContainer> pContainer = new UnoControlContainer( pWindow->GetComponentInterface() ); + + rtl::Reference<UnoControlModel> pContainerModel = new UnoControlContainerModel( ::comphelper::getProcessComponentContext() ); + pContainer->setModel( pContainerModel ); + + return pContainer; +} + +css::awt::FontDescriptor VCLUnoHelper::CreateFontDescriptor( const vcl::Font& rFont ) +{ + css::awt::FontDescriptor aFD; + aFD.Name = rFont.GetFamilyName(); + aFD.StyleName = rFont.GetStyleName(); + aFD.Height = static_cast<sal_Int16>(rFont.GetFontSize().Height()); + aFD.Width = static_cast<sal_Int16>(rFont.GetFontSize().Width()); + aFD.Family = sal::static_int_cast< sal_Int16 >(rFont.GetFamilyType()); + aFD.CharSet = rFont.GetCharSet(); + aFD.Pitch = sal::static_int_cast< sal_Int16 >(rFont.GetPitch()); + aFD.CharacterWidth = vcl::unohelper::ConvertFontWidth(rFont.GetWidthType()); + aFD.Weight = vcl::unohelper::ConvertFontWeight(rFont.GetWeight()); + aFD.Slant = vcl::unohelper::ConvertFontSlant(rFont.GetItalic()); + aFD.Underline = sal::static_int_cast< sal_Int16 >(rFont.GetUnderline()); + aFD.Strikeout = sal::static_int_cast< sal_Int16 >(rFont.GetStrikeout()); + aFD.Orientation = rFont.GetOrientation().get() / 10.0; + aFD.Kerning = rFont.IsKerning(); + aFD.WordLineMode = rFont.IsWordLineMode(); + aFD.Type = 0; // ??? => Only in Metric... + return aFD; +} + +vcl::Font VCLUnoHelper::CreateFont( const css::awt::FontDescriptor& rDescr, const vcl::Font& rInitFont ) +{ + vcl::Font aFont( rInitFont ); + if ( !rDescr.Name.isEmpty() ) + aFont.SetFamilyName( rDescr.Name ); + if ( !rDescr.StyleName.isEmpty() ) + aFont.SetStyleName( rDescr.StyleName ); + if ( rDescr.Height ) + aFont.SetFontSize( Size( rDescr.Width, rDescr.Height ) ); + if ( static_cast<FontFamily>(rDescr.Family) != FAMILY_DONTKNOW ) + aFont.SetFamily( static_cast<FontFamily>(rDescr.Family) ); + if ( static_cast<rtl_TextEncoding>(rDescr.CharSet) != RTL_TEXTENCODING_DONTKNOW ) + aFont.SetCharSet( static_cast<rtl_TextEncoding>(rDescr.CharSet) ); + if ( static_cast<FontPitch>(rDescr.Pitch) != PITCH_DONTKNOW ) + aFont.SetPitch( static_cast<FontPitch>(rDescr.Pitch) ); + if ( rDescr.CharacterWidth ) + aFont.SetWidthType(vcl::unohelper::ConvertFontWidth(rDescr.CharacterWidth)); + if ( rDescr.Weight ) + aFont.SetWeight(vcl::unohelper::ConvertFontWeight(rDescr.Weight)); + if ( rDescr.Slant != css::awt::FontSlant_DONTKNOW ) + aFont.SetItalic(vcl::unohelper::ConvertFontSlant(rDescr.Slant)); + if ( static_cast<FontLineStyle>(rDescr.Underline) != LINESTYLE_DONTKNOW ) + aFont.SetUnderline( static_cast<FontLineStyle>(rDescr.Underline) ); + if ( static_cast<FontStrikeout>(rDescr.Strikeout) != STRIKEOUT_DONTKNOW ) + aFont.SetStrikeout( static_cast<FontStrikeout>(rDescr.Strikeout) ); + + // Not DONTKNOW + aFont.SetOrientation( Degree10(static_cast<sal_Int16>(rDescr.Orientation * 10)) ); + aFont.SetKerning( static_cast<FontKerning>(rDescr.Kerning) ); + aFont.SetWordLineMode( rDescr.WordLineMode ); + + return aFont; +} + +vcl::Font VCLUnoHelper::CreateFont( const css::uno::Reference< css::awt::XFont >& rxFont ) +{ + vcl::Font aFont; + VCLXFont* pVCLXFont = dynamic_cast<VCLXFont*>( rxFont.get() ); + if ( pVCLXFont ) + aFont = pVCLXFont->GetFont(); + return aFont; +} + + +css::awt::SimpleFontMetric VCLUnoHelper::CreateFontMetric( const FontMetric& rFontMetric ) +{ + css::awt::SimpleFontMetric aFM; + aFM.Ascent = static_cast<sal_Int16>(rFontMetric.GetAscent()); + aFM.Descent = static_cast<sal_Int16>(rFontMetric.GetDescent()); + aFM.Leading = static_cast<sal_Int16>(rFontMetric.GetInternalLeading()); + aFM.Slant = static_cast<sal_Int16>(rFontMetric.GetSlant()); + aFM.FirstChar = 0x0020; + aFM.LastChar = 0xFFFD; + return aFM; +} + +bool VCLUnoHelper::IsZero(const css::awt::Rectangle& rRect) +{ + return ( !rRect.X && !rRect.Y && !rRect.Width && !rRect.Height ); +} + +MapUnit VCLUnoHelper::UnoEmbed2VCLMapUnit( sal_Int32 nUnoEmbedMapUnit ) +{ + switch( nUnoEmbedMapUnit ) + { + case css::embed::EmbedMapUnits::ONE_100TH_MM: + return MapUnit::Map100thMM; + case css::embed::EmbedMapUnits::ONE_10TH_MM: + return MapUnit::Map10thMM; + case css::embed::EmbedMapUnits::ONE_MM: + return MapUnit::MapMM; + case css::embed::EmbedMapUnits::ONE_CM: + return MapUnit::MapCM; + case css::embed::EmbedMapUnits::ONE_1000TH_INCH: + return MapUnit::Map1000thInch; + case css::embed::EmbedMapUnits::ONE_100TH_INCH: + return MapUnit::Map100thInch; + case css::embed::EmbedMapUnits::ONE_10TH_INCH: + return MapUnit::Map10thInch; + case css::embed::EmbedMapUnits::ONE_INCH: + return MapUnit::MapInch; + case css::embed::EmbedMapUnits::POINT: + return MapUnit::MapPoint; + case css::embed::EmbedMapUnits::TWIP: + return MapUnit::MapTwip; + case css::embed::EmbedMapUnits::PIXEL: + return MapUnit::MapPixel; + } + + OSL_FAIL( "Unexpected UNO map mode is provided!" ); + return MapUnit::LASTENUMDUMMY; +} + +sal_Int32 VCLUnoHelper::VCL2UnoEmbedMapUnit( MapUnit nVCLMapUnit ) +{ + switch( nVCLMapUnit ) + { + case MapUnit::Map100thMM: + return css::embed::EmbedMapUnits::ONE_100TH_MM; + case MapUnit::Map10thMM: + return css::embed::EmbedMapUnits::ONE_10TH_MM; + case MapUnit::MapMM: + return css::embed::EmbedMapUnits::ONE_MM; + case MapUnit::MapCM: + return css::embed::EmbedMapUnits::ONE_CM; + case MapUnit::Map1000thInch: + return css::embed::EmbedMapUnits::ONE_1000TH_INCH; + case MapUnit::Map100thInch: + return css::embed::EmbedMapUnits::ONE_100TH_INCH; + case MapUnit::Map10thInch: + return css::embed::EmbedMapUnits::ONE_10TH_INCH; + case MapUnit::MapInch: + return css::embed::EmbedMapUnits::ONE_INCH; + case MapUnit::MapPoint: + return css::embed::EmbedMapUnits::POINT; + case MapUnit::MapTwip: + return css::embed::EmbedMapUnits::TWIP; + case MapUnit::MapPixel: + return css::embed::EmbedMapUnits::PIXEL; + default: ; // avoid compiler warning + } + + OSL_FAIL( "Unexpected VCL map mode is provided!" ); + return -1; +} + +using namespace ::com::sun::star::util; + + +namespace +{ + enum UnitConversionDirection + { + FieldUnitToMeasurementUnit, + MeasurementUnitToFieldUnit + }; + + sal_Int16 convertMeasurementUnit( sal_Int16 _nUnit, UnitConversionDirection eDirection, sal_Int16& _rFieldToUNOValueFactor ) + { + static struct _unit_table + { + FieldUnit eFieldUnit; + sal_Int16 nMeasurementUnit; + sal_Int16 nFieldToMeasureFactor; + } const aUnits[] = { + { FieldUnit::NONE, -1 , -1}, + { FieldUnit::MM, MeasureUnit::MM, 1 }, // must precede MM_10TH + { FieldUnit::MM, MeasureUnit::MM_10TH, 10 }, + { FieldUnit::MM_100TH, MeasureUnit::MM_100TH, 1 }, + { FieldUnit::CM, MeasureUnit::CM, 1 }, + { FieldUnit::M, MeasureUnit::M, 1 }, + { FieldUnit::KM, MeasureUnit::KM, 1 }, + { FieldUnit::TWIP, MeasureUnit::TWIP, 1 }, + { FieldUnit::POINT, MeasureUnit::POINT, 1 }, + { FieldUnit::PICA, MeasureUnit::PICA, 1 }, + { FieldUnit::INCH, MeasureUnit::INCH, 1 }, // must precede INCH_*TH + { FieldUnit::INCH, MeasureUnit::INCH_10TH, 10 }, + { FieldUnit::INCH, MeasureUnit::INCH_100TH, 100 }, + { FieldUnit::INCH, MeasureUnit::INCH_1000TH, 1000 }, + { FieldUnit::FOOT, MeasureUnit::FOOT, 1 }, + { FieldUnit::MILE, MeasureUnit::MILE, 1 }, + }; + for (auto & aUnit : aUnits) + { + if ( eDirection == FieldUnitToMeasurementUnit ) + { + if ( ( aUnit.eFieldUnit == static_cast<FieldUnit>(_nUnit) ) && ( aUnit.nFieldToMeasureFactor == _rFieldToUNOValueFactor ) ) + return aUnit.nMeasurementUnit; + } + else + { + if ( aUnit.nMeasurementUnit == _nUnit ) + { + _rFieldToUNOValueFactor = aUnit.nFieldToMeasureFactor; + return static_cast<sal_Int16>(aUnit.eFieldUnit); + } + } + } + if ( eDirection == FieldUnitToMeasurementUnit ) + return -1; + + _rFieldToUNOValueFactor = 1; + return sal_Int16(FieldUnit::NONE); + } +} + +//= MeasurementUnitConversion + + +sal_Int16 VCLUnoHelper::ConvertToMeasurementUnit( FieldUnit _nFieldUnit, sal_Int16 _nUNOToFieldValueFactor ) +{ + return convertMeasurementUnit( static_cast<sal_Int16>(_nFieldUnit), FieldUnitToMeasurementUnit, _nUNOToFieldValueFactor ); +} + + +FieldUnit VCLUnoHelper::ConvertToFieldUnit( sal_Int16 _nMeasurementUnit, sal_Int16& _rFieldToUNOValueFactor ) +{ + return static_cast<FieldUnit>(convertMeasurementUnit( _nMeasurementUnit, MeasurementUnitToFieldUnit, _rFieldToUNOValueFactor )); +} + + +MapUnit /* MapModeUnit */ VCLUnoHelper::ConvertToMapModeUnit(sal_Int16 /* com.sun.star.util.MeasureUnit.* */ _nMeasureUnit) +{ + MapUnit eMode; + switch(_nMeasureUnit) + { + case css::util::MeasureUnit::MM_100TH: + eMode = MapUnit::Map100thMM; + break; + + case css::util::MeasureUnit::MM_10TH: + eMode = MapUnit::Map10thMM; + break; + + case css::util::MeasureUnit::MM: + eMode = MapUnit::MapMM; + break; + + case css::util::MeasureUnit::CM: + eMode = MapUnit::MapCM; + break; + + case css::util::MeasureUnit::INCH_1000TH: + eMode = MapUnit::Map1000thInch; + break; + + case css::util::MeasureUnit::INCH_100TH: + eMode = MapUnit::Map100thInch; + break; + + case css::util::MeasureUnit::INCH_10TH: + eMode = MapUnit::Map10thInch; + break; + + case css::util::MeasureUnit::INCH: + eMode = MapUnit::MapInch; + break; + + case css::util::MeasureUnit::POINT: + eMode = MapUnit::MapPoint; + break; + + case css::util::MeasureUnit::TWIP: + eMode = MapUnit::MapTwip; + break; + + case css::util::MeasureUnit::PIXEL: + eMode = MapUnit::MapPixel; + break; + + case css::util::MeasureUnit::APPFONT: + eMode = MapUnit::MapAppFont; + break; + + case css::util::MeasureUnit::SYSFONT: + eMode = MapUnit::MapSysFont; + break; + + default: + throw css::lang::IllegalArgumentException("Unsupported measure unit.", nullptr, 1 ); + } + return eMode; +} + +::Size VCLUnoHelper::ConvertToVCLSize(css::awt::Size const& _aSize) +{ + ::Size aVCLSize(_aSize.Width, _aSize.Height); + return aVCLSize; +} + +css::awt::Size VCLUnoHelper::ConvertToAWTSize(::Size /* VCLSize */ const& _aSize) +{ + css::awt::Size aAWTSize(_aSize.Width(), _aSize.Height()); + return aAWTSize; +} + + +::Point VCLUnoHelper::ConvertToVCLPoint(css::awt::Point const& _aPoint) +{ + ::Point aVCLPoint(_aPoint.X, _aPoint.Y); + return aVCLPoint; +} + +css::awt::Point VCLUnoHelper::ConvertToAWTPoint(::Point /* VCLPoint */ const& _aPoint) +{ + css::awt::Point aAWTPoint(_aPoint.X(), _aPoint.Y()); + return aAWTPoint; +} + +::tools::Rectangle VCLUnoHelper::ConvertToVCLRect( css::awt::Rectangle const & _rRect ) +{ + return ::tools::Rectangle( _rRect.X, _rRect.Y, _rRect.X + _rRect.Width - 1, _rRect.Y + _rRect.Height - 1 ); +} + +css::awt::Rectangle VCLUnoHelper::ConvertToAWTRect( ::tools::Rectangle const & _rRect ) +{ + return css::awt::Rectangle( _rRect.Left(), _rRect.Top(), _rRect.GetWidth(), _rRect.GetHeight() ); +} + +awt::MouseEvent VCLUnoHelper::createMouseEvent( const ::MouseEvent& _rVclEvent, const uno::Reference< uno::XInterface >& _rxContext ) +{ + awt::MouseEvent aMouseEvent; + aMouseEvent.Source = _rxContext; + + aMouseEvent.Modifiers = 0; + if ( _rVclEvent.IsShift() ) + aMouseEvent.Modifiers |= css::awt::KeyModifier::SHIFT; + if ( _rVclEvent.IsMod1() ) + aMouseEvent.Modifiers |= css::awt::KeyModifier::MOD1; + if ( _rVclEvent.IsMod2() ) + aMouseEvent.Modifiers |= css::awt::KeyModifier::MOD2; + + aMouseEvent.Buttons = 0; + if ( _rVclEvent.IsLeft() ) + aMouseEvent.Buttons |= css::awt::MouseButton::LEFT; + if ( _rVclEvent.IsRight() ) + aMouseEvent.Buttons |= css::awt::MouseButton::RIGHT; + if ( _rVclEvent.IsMiddle() ) + aMouseEvent.Buttons |= css::awt::MouseButton::MIDDLE; + + aMouseEvent.X = _rVclEvent.GetPosPixel().X(); + aMouseEvent.Y = _rVclEvent.GetPosPixel().Y(); + aMouseEvent.ClickCount = _rVclEvent.GetClicks(); + aMouseEvent.PopupTrigger = false; + + return aMouseEvent; +} + +::MouseEvent VCLUnoHelper::createVCLMouseEvent( const awt::MouseEvent& _rAwtEvent ) +{ + ::MouseEvent aMouseEvent( Point( _rAwtEvent.X, _rAwtEvent.Y ), _rAwtEvent.ClickCount, + ::MouseEventModifiers::NONE, _rAwtEvent.Buttons, _rAwtEvent.Modifiers ); + + return aMouseEvent; +} + +awt::KeyEvent VCLUnoHelper::createKeyEvent( const ::KeyEvent& _rVclEvent, const uno::Reference< uno::XInterface >& _rxContext ) +{ + awt::KeyEvent aKeyEvent; + aKeyEvent.Source = _rxContext; + + aKeyEvent.Modifiers = 0; + if ( _rVclEvent.GetKeyCode().IsShift() ) + aKeyEvent.Modifiers |= awt::KeyModifier::SHIFT; + if ( _rVclEvent.GetKeyCode().IsMod1() ) + aKeyEvent.Modifiers |= awt::KeyModifier::MOD1; + if ( _rVclEvent.GetKeyCode().IsMod2() ) + aKeyEvent.Modifiers |= awt::KeyModifier::MOD2; + if ( _rVclEvent.GetKeyCode().IsMod3() ) + aKeyEvent.Modifiers |= awt::KeyModifier::MOD3; + + aKeyEvent.KeyCode = _rVclEvent.GetKeyCode().GetCode(); + aKeyEvent.KeyChar = _rVclEvent.GetCharCode(); + aKeyEvent.KeyFunc = ::sal::static_int_cast< sal_Int16 >( _rVclEvent.GetKeyCode().GetFunction()); + + return aKeyEvent; +} + +::KeyEvent VCLUnoHelper::createVCLKeyEvent( const awt::KeyEvent& _rAwtEvent ) +{ + sal_Unicode nChar = _rAwtEvent.KeyChar; + vcl::KeyCode aKeyCode( _rAwtEvent.KeyCode, _rAwtEvent.Modifiers & awt::KeyModifier::SHIFT, + _rAwtEvent.Modifiers & awt::KeyModifier::MOD1, + _rAwtEvent.Modifiers & awt::KeyModifier::MOD2, + _rAwtEvent.Modifiers & awt::KeyModifier::MOD3 ); + + return ::KeyEvent (nChar, aKeyCode); + +} + +::PointerStyle VCLUnoHelper::getMousePointer(const css::uno::Reference<css::awt::XWindowPeer>& rWindowPeer) +{ + ::PointerStyle eType = ::PointerStyle::Arrow; // default ? + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(rWindowPeer); + if (pWindow) + eType = pWindow->GetPointer(); + return eType; +} + +void VCLUnoHelper::setMousePointer(const css::uno::Reference<css::awt::XWindowPeer>& rWindowPeer, ::PointerStyle ePointer) +{ + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(rWindowPeer); + if (!pWindow) + return; + pWindow->SetPointer(ePointer); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/toolkit/test/accessibility/AWB.sxw b/toolkit/test/accessibility/AWB.sxw Binary files differnew file mode 100644 index 0000000000..57931aed1d --- /dev/null +++ b/toolkit/test/accessibility/AWB.sxw diff --git a/toolkit/test/accessibility/AccTreeNode.java b/toolkit/test/accessibility/AccTreeNode.java new file mode 100644 index 0000000000..1ce4676842 --- /dev/null +++ b/toolkit/test/accessibility/AccTreeNode.java @@ -0,0 +1,346 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.util.ArrayList; +import java.util.Arrays; + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleComponent; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEditableText; +import com.sun.star.accessibility.XAccessibleSelection; +import com.sun.star.accessibility.XAccessibleTable; +import com.sun.star.accessibility.XAccessibleText; +import com.sun.star.uno.UnoRuntime; + +/** + * The node type for the AccessibleTreeModel. + * This implements all the child-handling based on the appropriate + * NodeHandlers. Trivial nodes can be implemented by any Object + * type. + */ +class AccTreeNode + extends AccessibleTreeNode +{ + private class HandlerDescriptor + { + private HandlerDescriptor (NodeHandler aHandler) + { + maHandler = aHandler; + mnChildCount = -1; + } + private NodeHandler maHandler; + private int mnChildCount; + } + /// NodeHandlers for this node + private ArrayList<HandlerDescriptor> maHandlers; + + // The accessible context of this node. + private XAccessibleContext mxContext; + private XAccessibleComponent mxComponent; + private XAccessibleText mxText; + private XAccessibleTable mxTable; + + public AccTreeNode (XAccessibleContext xContext, Object aDisplay, AccessibleTreeNode aParent) + { + super (aDisplay, aParent); + + maHandlers = new ArrayList<HandlerDescriptor>(5); + mxContext = xContext; + } + + /** Update the internal data extracted from the corresponding accessible + object. This is done by replacing every handler by a new one. An + update method at each handler would be better of course. + */ + @Override + public void update () + { + for (int i=0; i<maHandlers.size(); i++) + { + System.out.println ("replacing handler " + i); + HandlerDescriptor aDescriptor = maHandlers.get(i); + aDescriptor.maHandler = aDescriptor.maHandler.createHandler (mxContext); + aDescriptor.mnChildCount = + aDescriptor.maHandler.getChildCount (); + } + } + + public XAccessibleContext getContext () + { + return mxContext; + } + + public XAccessibleComponent getComponent () + { + if (mxComponent == null && mxContext != null) + mxComponent = UnoRuntime.queryInterface( + XAccessibleComponent.class, mxContext); + return mxComponent; + } + + public XAccessibleText getText () + { + if (mxText == null && mxContext != null) + mxText = UnoRuntime.queryInterface( + XAccessibleText.class, mxContext); + return mxText; + } + + public XAccessibleEditableText getEditText () + { + return UnoRuntime.queryInterface( + XAccessibleEditableText.class, mxContext); + } + + public XAccessibleTable getTable () + { + if (mxTable == null && mxContext != null) + mxTable = UnoRuntime.queryInterface( + XAccessibleTable.class, mxContext); + return mxTable; + } + + + public XAccessibleSelection getSelection () + { + return UnoRuntime.queryInterface( + XAccessibleSelection.class, mxContext); + } + + public void addHandler( NodeHandler aHandler ) + { + if (aHandler != null) + maHandlers.add (new HandlerDescriptor (aHandler)); + } + + + /** iterate over handlers and return child sum */ + private HandlerDescriptor getHandlerDescriptor (int i) + { + HandlerDescriptor aDescriptor = maHandlers.get(i); + if (aDescriptor.mnChildCount < 0) + aDescriptor.mnChildCount = + aDescriptor.maHandler.getChildCount (); + return aDescriptor; + } + + @Override + public int getChildCount() + { + int nChildCount = 0; + for (int i = 0; i < maHandlers.size(); i++) + { + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + nChildCount += aDescriptor.mnChildCount; + } + return nChildCount; + } + + /** iterate over handlers until the child is found */ + @Override + public AccessibleTreeNode getChild (int nIndex) + throws IndexOutOfBoundsException + { + if( nIndex >= 0 ) + { + for(int i = 0; i < maHandlers.size(); i++) + { + // check if this handler has the child, and if not + // search with next handler + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + if (nIndex < aDescriptor.mnChildCount) + return aDescriptor.maHandler.getChild (this, nIndex); + else + nIndex -= aDescriptor.mnChildCount; + } + } + else + throw new IndexOutOfBoundsException(); + + // nothing found? + return null; + } + + @Override + public AccessibleTreeNode getChildNoCreate (int nIndex) + throws IndexOutOfBoundsException + { + if( nIndex >= 0 ) + { + for(int i = 0; i < maHandlers.size(); i++) + { + // check if this handler has the child, and if not + // search with next handler + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + if (nIndex < aDescriptor.mnChildCount) + return aDescriptor.maHandler.getChildNoCreate (nIndex); + else + nIndex -= aDescriptor.mnChildCount; + } + } + else + throw new IndexOutOfBoundsException(); + + // nothing found? + return null; + } + + @Override + public boolean removeChild (int nIndex) + throws IndexOutOfBoundsException + { + boolean bStatus = false; + if (nIndex >= 0) + { + for (int i=0; i<maHandlers.size(); i++) + { + // check if this handler has the child, and if not + // search with next handler + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + if (nIndex < aDescriptor.mnChildCount) + { + bStatus = aDescriptor.maHandler.removeChild (nIndex); + aDescriptor.mnChildCount = aDescriptor.maHandler.getChildCount (); + break; + } + else + nIndex -= aDescriptor.mnChildCount; + } + } + else + throw new IndexOutOfBoundsException(); + + return bStatus; + } + + + @Override + public int indexOf (AccessibleTreeNode aNode) + { + int nBaseIndex = 0; + if (aNode != null) + { + for (int i=0; i<maHandlers.size(); i++) + { + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + int nIndex = aDescriptor.maHandler.indexOf (aNode); + if (nIndex >= 0) + return nBaseIndex + nIndex; + else + nBaseIndex += aDescriptor.mnChildCount; + } + } + + return -1; + } + + /** this node is a leaf if have no handlers, or is those + handlers show no children */ + @Override + public boolean isLeaf() + { + return maHandlers.isEmpty(); + } + + @Override + public boolean equals (Object aOther) + { + return (this == aOther) || (aOther!=null && aOther.equals(mxContext)); + } + + + /** iterate over handlers until the child is found */ + public void getActions(java.util.List<String> aActions) + { + for(int i = 0; i < maHandlers.size(); i++) + { + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + NodeHandler aHandler = aDescriptor.maHandler; + String[] aHandlerActions = aHandler.getActions (this); + aActions.addAll(Arrays.asList(aHandlerActions)); + } + } + + @Override + public void performAction( int nIndex ) + { + if( nIndex >= 0 ) + { + for(int i = 0; i < maHandlers.size(); i++) + { + // check if this handler has the child, and if not + // search with next handler + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + NodeHandler aHandler = aDescriptor.maHandler; + int nCount = aHandler.getActions(this).length; + if( nCount > nIndex ) + { + aHandler.performAction(this, nIndex ); + return; + } + else + nIndex -= nCount; + } + } + } + + /** Try to add the specified accessible object as new accessible child of the + AccessibleTreeHandler. + Note that child is used in another context than + it is used in the other methods of this class. + */ + public AccessibleTreeNode addAccessibleChild (XAccessible xChild) + { + for(int i = 0; i < maHandlers.size(); i++) + { + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + if (aDescriptor.maHandler instanceof AccessibleTreeHandler) + { + AccessibleTreeHandler aHandler = (AccessibleTreeHandler)aDescriptor.maHandler; + AccessibleTreeNode aNode = aHandler.addAccessibleChild (this, xChild); + aDescriptor.mnChildCount = aHandler.getChildCount (); + return aNode; + } + } + return null; + } + + public java.util.List<Integer> updateChildren (java.lang.Class class1, java.lang.Class<AccessibleExtendedComponentHandler> class2) + { + ArrayList<Integer> aChildIndices = new ArrayList<Integer>(); + int nOffset = 0; + for(int i=0; i < maHandlers.size(); i++) + { + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + if ((class1.isInstance(aDescriptor.maHandler)) + || (class2 !=null && class2.isInstance(aDescriptor.maHandler))) + { + aDescriptor.maHandler.update(this); + // Get updated number of children. + int nChildCount = aDescriptor.maHandler.getChildCount (); + aDescriptor.mnChildCount = nChildCount; + // Fill in the indices of the updated children. + for (int j=0; j<nChildCount; j++) + aChildIndices.add(j+nOffset); + } + nOffset += aDescriptor.mnChildCount; + } + return aChildIndices; + } +} diff --git a/toolkit/test/accessibility/AccessibilityTree.java b/toolkit/test/accessibility/AccessibilityTree.java new file mode 100644 index 0000000000..fdf44177a7 --- /dev/null +++ b/toolkit/test/accessibility/AccessibilityTree.java @@ -0,0 +1,379 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.awt.Cursor; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; + +import javax.swing.AbstractAction; +import javax.swing.JPopupMenu; +import javax.swing.JTree; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.event.TreeWillExpandListener; +import javax.swing.tree.TreePath; + +import com.sun.star.accessibility.XAccessibleContext; + + + +/** This is the tree component that is responsible for displaying the + contents of the tree model on the screen. +*/ +public class AccessibilityTree + implements TreeExpansionListener, TreeWillExpandListener +{ + /** Create a new accessibility tree. Use the specified message display + for displaying messages and the specified canvas to draw the + graphical representations of accessible objects on. + */ + public AccessibilityTree () + { + maTree = new JTree (); + + AccessibilityTreeModel aModel = + new AccessibilityTreeModel ( + new StringNode ("Please press Update button", null)); + maTree.setModel (aModel); + + maTree.addMouseListener (new MouseListener (this)); + + // Listen to expansions and collapses to change the mouse cursor. + mnExpandLevel = 0; + maTree.addTreeWillExpandListener (this); + maTree.addTreeExpansionListener (this); + } + + public JTree getComponent () + { + return maTree; + } + + // Change cursor during expansions to show the user that this is a + // lengthy operation. + public void treeWillExpand (TreeExpansionEvent e) + { + if (mnExpandLevel == 0) + { + maTree.setCursor (new Cursor (Cursor.WAIT_CURSOR)); + } + mnExpandLevel += 1; + } + public void treeWillCollapse (TreeExpansionEvent e) + { + if (mnExpandLevel == 0) + { + maTree.setCursor (new Cursor (Cursor.WAIT_CURSOR)); + } + mnExpandLevel += 1; + } + public void treeExpanded (TreeExpansionEvent e) + { + mnExpandLevel -= 1; + if (mnExpandLevel == 0) + { + maTree.setCursor (new Cursor (Cursor.DEFAULT_CURSOR)); + } + } + public void treeCollapsed (TreeExpansionEvent e) + { + mnExpandLevel -= 1; + if (mnExpandLevel == 0) + { + maTree.setCursor (new Cursor (Cursor.DEFAULT_CURSOR)); + } + } + + + + public void SetCanvas (Canvas aCanvas) + { + ((AccessibilityTreeModel)maTree.getModel()).setCanvas(aCanvas); + } + + /** Expand the nodes in the subtree rooted in aNode according to the + specified expander. The tree is locked during the expansion. + */ + private void expandTree (AccessibleTreeNode aNode, Expander aExpander) + { + if (mnExpandLevel == 0) + { + maTree.setEnabled (false); + } + mnExpandLevel += 1; + + ((AccessibilityTreeModel)maTree.getModel()).lock (); + + try + { + expandTree (new TreePath (aNode.createPath()), aExpander); + } + catch (Exception e) + { + // Ignore + } + + mnExpandLevel -= 1; + if (mnExpandLevel == 0) + { + maTree.setEnabled (true); + ((AccessibilityTreeModel)maTree.getModel()).unlock (aNode); + } + } + + private TreePath expandTree( TreePath aPath, Expander aExpander ) + { + // return first expanded object + TreePath aFirst = null; + + try + { + // get 'our' object + Object aObj = aPath.getLastPathComponent(); + + // expand this object, if the Expander tells us so + if( aExpander.expand( aObj ) ) + { + maTree.expandPath (aPath); + aFirst = aPath; + } + + // visit all children + if (aObj instanceof AccessibleTreeNode) + { + AccessibleTreeNode aNode = (AccessibleTreeNode)aObj; + int nLength = aNode.getChildCount(); + for( int i = 0; i < nLength; i++ ) + { + TreePath aRet = expandTree( + aPath.pathByAddingChild( aNode.getChild( i ) ), + aExpander ); + if( aFirst == null ) + aFirst = aRet; + } + } + } + catch (Exception e) + { + System.out.println ("caught exception while expanding tree path " + + aPath + ": " + e); + e.printStackTrace (); + } + + return aFirst; + } + + + /** Expand all nodes and their subtrees that represent shapes. Call + * this method from the outside. */ + public void expandShapes () + { + expandShapes ((AccessibleTreeNode)maTree.getModel().getRoot()); + } + private void expandShapes (AccessibleTreeNode aNode) + { + expandTree (aNode, new ShapeExpander()); + } + + /** Expand all nodes */ + public void expandAll () + { + expandAll ((AccessibleTreeNode)maTree.getModel().getRoot()); + } + private void expandAll (AccessibleTreeNode aNode) + { + expandTree (aNode, new AllExpander()); + } + + + + /* + public Dimension getPreferredSize () + { + Dimension aPreferredSize = super.getPreferredSize(); + Dimension aMinimumSize = super.getMinimumSize(); + if (aPreferredSize.width < aMinimumSize.width) + aPreferredSize.width = aMinimumSize.width; + return aPreferredSize; + } + */ + + private class MouseListener extends MouseAdapter + { + public MouseListener (AccessibilityTree aTree) + { + maTree=aTree; + } + @Override + public void mousePressed(MouseEvent e) { popupTrigger(e); } + @Override + public void mouseClicked(MouseEvent e) { popupTrigger(e); } + @Override + public void mouseEntered(MouseEvent e) { popupTrigger(e); } + @Override + public void mouseExited(MouseEvent e) { popupTrigger(e); } + @Override + public void mouseReleased(MouseEvent e) { popupTrigger(e); } + + private boolean popupTrigger( MouseEvent e ) + { + boolean bIsPopup = e.isPopupTrigger(); + if( !bIsPopup ) + return false; + + int selRow = maTree.getComponent().getRowForLocation(e.getX(), e.getY()); + if (selRow == -1) + return bIsPopup; + + TreePath aPath = maTree.getComponent().getPathForLocation(e.getX(), e.getY()); + + // check for actions + Object aObject = aPath.getLastPathComponent(); + JPopupMenu aMenu = new JPopupMenu(); + if( aObject instanceof AccTreeNode ) + { + AccTreeNode aNode = (AccTreeNode)aObject; + + ArrayList<String> aActions = new ArrayList<String>(); + aMenu.add (new AccessibilityTree.ShapeExpandAction(maTree, aNode)); + aMenu.add (new AccessibilityTree.SubtreeExpandAction(maTree, aNode)); + + aNode.getActions(aActions); + for( int i = 0; i < aActions.size(); i++ ) + { + aMenu.add( new NodeAction( + aActions.get(i), + aNode, i ) ); + } + } + else if (aObject instanceof AccessibleTreeNode) + { + AccessibleTreeNode aNode = (AccessibleTreeNode)aObject; + String[] aActionNames = aNode.getActions(); + int nCount=aActionNames.length; + if (nCount > 0) + { + for (int i=0; i<nCount; i++) + aMenu.add( new NodeAction( + aActionNames[i], + aNode, + i)); + } + else + aMenu = null; + } + if (aMenu != null) + aMenu.show (maTree.getComponent(), + e.getX(), e.getY()); + + return bIsPopup; + } + + private final AccessibilityTree maTree; + } + + private class NodeAction extends AbstractAction + { + private final int mnIndex; + private final AccessibleTreeNode maNode; + + private NodeAction( String aName, AccessibleTreeNode aNode, int nIndex ) + { + super( aName ); + maNode = aNode; + mnIndex = nIndex; + } + + public void actionPerformed(ActionEvent e) + { + maNode.performAction(mnIndex); + } + } + + // This action expands all shapes in the subtree rooted in the specified node. + private class ShapeExpandAction extends AbstractAction + { + private final AccessibilityTree maTree; + private final AccTreeNode maNode; + public ShapeExpandAction (AccessibilityTree aTree, AccTreeNode aNode) + { + super ("Expand Shapes"); + maTree = aTree; + maNode = aNode; + } + public void actionPerformed (ActionEvent e) + { + maTree.expandShapes (maNode); + } + } + + // This action expands all nodes in the subtree rooted in the specified node. + private class SubtreeExpandAction extends AbstractAction + { + private final AccessibilityTree maTree; + private final AccTreeNode maNode; + public SubtreeExpandAction (AccessibilityTree aTree, AccTreeNode aNode) + { + super ("Expand Subtree"); + maTree = aTree; + maNode = aNode; + } + public void actionPerformed (ActionEvent e) + { + maTree.expandAll (maNode); + } + } + + /** Predicate class to determine whether a node should be expanded + * For use with expandTree method */ + private abstract class Expander + { + abstract public boolean expand (Object aObject); + } + + /** expand all nodes */ + private class AllExpander extends Expander + { + @Override + public boolean expand(Object aObject) { return true; } + } + + /** expand all nodes with accessibility roles > 100 */ + private class ShapeExpander extends Expander + { + @Override + public boolean expand (Object aObject) + { + if (aObject instanceof AccTreeNode) + { + AccTreeNode aNode = (AccTreeNode)aObject; + XAccessibleContext xContext = aNode.getContext(); + if (xContext != null && xContext.getAccessibleRole() >= 100) + return true; + } + return false; + } + } + + + + private final JTree maTree; + private int mnExpandLevel; +} diff --git a/toolkit/test/accessibility/AccessibilityTreeModel.java b/toolkit/test/accessibility/AccessibilityTreeModel.java new file mode 100644 index 0000000000..b021746bc2 --- /dev/null +++ b/toolkit/test/accessibility/AccessibilityTreeModel.java @@ -0,0 +1,471 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.util.ArrayList; + +import javax.swing.event.TreeModelEvent; +import javax.swing.tree.TreePath; + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEventBroadcaster; +import com.sun.star.accessibility.XAccessibleEventListener; +import com.sun.star.uno.UnoRuntime; + +public class AccessibilityTreeModel + extends AccessibilityTreeModelBase +{ + private boolean mbVerbose = false; + + public AccessibilityTreeModel (AccessibleTreeNode aRoot) + { + // create default node (unless we have a 'proper' node) + setRoot (aRoot); + + maNodeMap = new NodeMap(); + + mxListener = new QueuedListener(new EventListener (this)); + } + + public void clear () + { + maNodeMap.Clear(); + } + + /** Lock the tree. While the tree is locked, events from the outside are + not processed. Lock the tree when you change its internal structure. + */ + public void lock () + { + mnLockCount += 1; + } + + /** Unlock the tree. After unlocking the tree as many times as locking + it, a treeStructureChange event is sent to the event listeners. + @param aNodeHint + If not null and treeStructureChange events are thrown then this + node is used as root of the modified subtree. + */ + public void unlock (AccessibleTreeNode aNodeHint) + { + mnLockCount -= 1; + if (mnLockCount == 0) + fireTreeStructureChanged ( + new TreeModelEvent (this, + new TreePath (aNodeHint.createPath()))); + } + + + @Override + public synchronized void setRoot (AccessibleTreeNode aRoot) + { + if (getRoot() == null) + super.setRoot (aRoot); + else + { + lock (); + maNodeMap.ForEach (new NodeMapCallback () { + @Override + public void Apply (AccTreeNode aNode) + { + if (maCanvas != null) + maCanvas.removeNode (aNode); + removeAccListener (aNode); + } + }); + maNodeMap.Clear (); + + setRoot (aRoot); + unlock (aRoot); + } + } + + + + // child management: + + + + + /** Delegate the request to the parent and then register listeners at + the child and add the child to the canvas. + */ + @Override + public synchronized Object getChild (Object aParent, int nIndex) + { + AccessibleTreeNode aChild = (AccessibleTreeNode)super.getChild (aParent, nIndex); + + if (aChild == null) + System.out.println ("getChild: child not found"); + else + // Keep translation table up-to-date. + addNode (aChild); + + return aChild; + } + + /** Remove a node (and all children) from the tree model. + */ + private boolean removeChild (AccessibleTreeNode aNode) + { + try + { + if( aNode == null ) + { + System.out.println ("can't remove null node"); + return false; + } + else + { + // depth-first removal of children + while (aNode.getChildCount() > 0) { + if ( ! removeChild (aNode.getChildNoCreate (0))) + break; + } + + // Remove node from its parent. + AccessibleTreeNode aParent = aNode.getParent(); + if (aParent != null) + { + int nIndex = aParent.indexOf(aNode); + aParent.removeChild (nIndex); + } + + maNodeMap.RemoveNode (aNode); + } + } + catch (Exception e) + { + System.out.println ("caught exception while removing child " + + aNode + " : " + e); + e.printStackTrace (); + return false; + } + return true; + } + + public void removeNode (XAccessibleContext xNode) + { + if (xNode != null) + { + AccessibleTreeNode aNode = maNodeMap.GetNode (xNode); + AccessibleTreeNode aRootNode = (AccessibleTreeNode)getRoot(); + TreeModelEvent aEvent = createEvent (aRootNode, aNode); + removeChild (aNode); + if (mbVerbose) + System.out.println (aNode); + fireTreeNodesRemoved (aEvent); + maCanvas.repaint (); + } + } + + + /** Add a new child to a parent. + @return + Returns the new or existing representation of the specified + accessible object. + */ + private AccessibleTreeNode addChild (AccTreeNode aParentNode, XAccessible xNewChild) + { + AccessibleTreeNode aChildNode = null; + try + { + // First make sure that the accessible object does not already have + // a representation. + aChildNode = maNodeMap.GetNode(xNewChild); + if (aChildNode == null) + aChildNode = aParentNode.addAccessibleChild (xNewChild); + else + System.out.println ("node already present"); + } + catch (Exception e) + { + System.out.println ("caught exception while adding child " + + xNewChild + " to parent " + aParentNode + ": " + e); + e.printStackTrace (); + } + return aChildNode; + } + + public void addChild (XAccessibleContext xParent, XAccessible xChild) + { + AccessibleTreeNode aParentNode = maNodeMap.GetNode (xParent); + if (aParentNode instanceof AccTreeNode) + { + AccessibleTreeNode aChild = addChild ((AccTreeNode)aParentNode, xChild); + if (addNode (aChild)) + { + if (maCanvas != null) + maCanvas.updateNode ((AccTreeNode)aParentNode); + + // A call to fireTreeNodesInserted for xNew + // should be sufficient but at least the + // StringNode object that contains the number of + // children also changes and we do not know its + // index relative to its parent. Therefore the + // more expensive fireTreeStructureChanged is + // necessary. + fireTreeNodesInserted (createChildUpdateEvent (xParent)); + updateNode (xParent, AccessibleTreeHandler.class); + } + maCanvas.repaint (); + } + } + + + /** Add the child node to the internal tree structure. + @param aNode + The node to insert into the internal tree structure. + */ + private boolean addNode (AccessibleTreeNode aNode) + { + boolean bRet = false; + try + { + if ( ! maNodeMap.ValueIsMember (aNode)) + { + if (aNode instanceof AccTreeNode) + { + AccTreeNode aChild = (AccTreeNode)aNode; + XAccessibleContext xChild = aChild.getContext(); + registerAccListener (aChild); + if (maCanvas != null) + maCanvas.addNode (aChild); + maNodeMap.InsertNode (xChild, aChild); + } + bRet = true; + } + + } + catch (Exception e) + { + System.out.println ("caught exception while adding node " + + aNode + ": " + e); + e.printStackTrace (); + } + return bRet; + } + + + + + /** create path to node, suitable for TreeModelEvent constructor + * @see javax.swing.event.TreeModelEvent#TreeModelEvent + */ + private Object[] createPath (AccessibleTreeNode aNode) + { + ArrayList<AccessibleTreeNode> aPath = new ArrayList<AccessibleTreeNode>(); + aNode.createPath (aPath); + return aPath.toArray(); + } + + + // listeners (and helper methods) + + // We are registered with listeners as soon as objects are in the + // tree cache, and we should get removed as soon as they are out. + + + private void fireTreeNodesChanged(TreeModelEvent e) + { + for(int i = 0; i < maTMListeners.size(); i++) + { + maTMListeners.get(i).treeNodesChanged(e); + } + } + + protected void fireTreeNodesInserted(final TreeModelEvent e) + { + for(int i = 0; i < maTMListeners.size(); i++) + { + maTMListeners.get(i).treeNodesInserted(e); + } + } + + private void fireTreeNodesRemoved(final TreeModelEvent e) + { + for(int i = 0; i < maTMListeners.size(); i++) + { + maTMListeners.get(i).treeNodesRemoved(e); + } + } + + private void fireTreeStructureChanged(final TreeModelEvent e) + { + for(int i = 0; i < maTMListeners.size(); i++) + { + maTMListeners.get(i).treeStructureChanged(e); + } + } + + /** Create a TreeModelEvent object that informs listeners that one child + has been removed from or inserted into its parent. + */ + private TreeModelEvent createChildUpdateEvent (XAccessibleContext xParent) + { + AccessibleTreeNode aParentNode = maNodeMap.GetNode (xParent); + return createEvent (aParentNode, xParent); + } + + private TreeModelEvent createEvent (AccessibleTreeNode aParentNode, XAccessibleContext xChild) + { + AccessibleTreeNode aChildNode = null; + if (xChild != null) + aChildNode = maNodeMap.GetNode (xChild); + return createEvent (aParentNode, aChildNode); + } + + + + protected TreeModelEvent createEvent ( + AccessibleTreeNode aParentNode, + AccessibleTreeNode aChildNode) + { + Object[] aPathToParent = createPath (aParentNode); + + int nIndexInParent = -1; + if (aChildNode != null) + nIndexInParent = aParentNode.indexOf (aChildNode); + if (mbVerbose) + System.out.println (aChildNode + " " + nIndexInParent); + + if (nIndexInParent == -1) + // This event may be passed only to treeStructureChanged of the listeners. + return new TreeModelEvent (this, + aPathToParent); + else + // General purpose event for removing or inserting known nodes. + return new TreeModelEvent (this, + aPathToParent, + new int[] {nIndexInParent}, + new Object[] {aChildNode} ); + } + + + + + /** Create a TreeModelEvent that indicates changes at those children of + the specified node with the specified indices. + */ + private TreeModelEvent createChangeEvent (AccTreeNode aNode, java.util.List<Integer> aChildIndices) + { + // Build a list of child objects that are indicated by the given indices. + int nCount = aChildIndices.size(); + Object aChildObjects[] = new Object[nCount]; + int nChildIndices[] = new int[nCount]; + for (int i=0; i<nCount; i++) + { + int nIndex = aChildIndices.get(i); + aChildObjects[i] = aNode.getChild (nIndex); + nChildIndices[i] = nIndex; + } + + return new TreeModelEvent (this, + createPath(aNode), + nChildIndices, + aChildObjects); + } + + + + private XAccessibleEventBroadcaster getBroadcaster (Object aObject) + { + if (aObject instanceof AccTreeNode) + return UnoRuntime.queryInterface ( + XAccessibleEventBroadcaster.class, ((AccTreeNode)aObject).getContext()); + else + return null; + } + + private void registerAccListener( Object aObject ) + { + // register this as listener for XAccessibleEventBroadcaster + // implementations + XAccessibleEventBroadcaster xBroadcaster = getBroadcaster( aObject ); + if (xBroadcaster != null) + { + xBroadcaster.addAccessibleEventListener( mxListener ); + } + } + + private void removeAccListener( Object aObject ) + { + XAccessibleEventBroadcaster xBroadcaster = getBroadcaster( aObject ); + if (xBroadcaster != null) + { + xBroadcaster.removeAccessibleEventListener( mxListener ); + } + } + + + + public void setCanvas (Canvas aCanvas) + { + maCanvas = aCanvas; + } + + public Canvas getCanvas () + { + return maCanvas; + } + + public void updateNode (XAccessibleContext xSource, java.lang.Class class1) + { + updateNode (xSource, class1,null); + } + + /** Get a list of children of the node associated with xSource that are + affected by the given handlers. Fire events that these children may + have changed in the tree view. Update the canvas representation of + xSource. + */ + public AccTreeNode updateNode (XAccessibleContext xSource, + java.lang.Class class1, java.lang.Class<AccessibleExtendedComponentHandler> class2) + { + AccessibleTreeNode aTreeNode = maNodeMap.GetNode (xSource); + AccTreeNode aNode = null; + if (mbVerbose) + System.out.println ("updating node " + xSource + " " + aTreeNode); + if (aTreeNode instanceof AccTreeNode) + { + aNode = (AccTreeNode) aTreeNode; + // Get list of affected children. + java.util.List<Integer> aChildIndices = aNode.updateChildren ( + class1, class2); + // Fire events that these children may have changed. + fireTreeNodesChanged ( + createChangeEvent (aNode, aChildIndices)); + } + return aNode; + } + + /** The listener to be registered with the accessible objects. + * Could be set to 'this' for same-thread event delivery, or to an + * instance of QueuedListener for multi-threaded delivery. May + * not be changed, since this would trip the + * register/removeAccListener logic. */ + private final XAccessibleEventListener mxListener; + + // Map to translate from accessible object to corresponding tree node. + private final NodeMap maNodeMap; + + // If the lock count is higher then zero, then no events are processed. + private int mnLockCount; + + private Canvas maCanvas; +} diff --git a/toolkit/test/accessibility/AccessibilityTreeModelBase.java b/toolkit/test/accessibility/AccessibilityTreeModelBase.java new file mode 100644 index 0000000000..5ff2d96c9e --- /dev/null +++ b/toolkit/test/accessibility/AccessibilityTreeModelBase.java @@ -0,0 +1,140 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.util.ArrayList; + +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +public class AccessibilityTreeModelBase + implements TreeModel +{ + public AccessibilityTreeModelBase () + { + setRoot (null); + maTMListeners = new ArrayList<TreeModelListener>(); + } + + public synchronized void addTreeModelListener(TreeModelListener l) + { + maTMListeners.add(l); + } + + public synchronized void removeTreeModelListener(TreeModelListener l) + { + maTMListeners.remove(l); + } + + public synchronized int getChildCount(Object aParent) + { + return (aParent instanceof AccessibleTreeNode) ? + ((AccessibleTreeNode)aParent).getChildCount() : 0; + } + + public synchronized Object getChild (Object aParent, int nIndex) + { + Object aChild = null; + try + { + if (aParent instanceof AccessibleTreeNode) + aChild = ((AccessibleTreeNode)aParent).getChild(nIndex); + else + System.out.println ("getChild called for unknown parent node"); + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) + { + aChild = ("no child " + nIndex + " from " + aParent + ": " + e); + } + return aChild; + } + + public synchronized Object getChildNoCreate (Object aParent, int nIndex) + { + Object aChild = null; + try + { + if (aParent instanceof AccessibleTreeNode) + aChild = ((AccessibleTreeNode)aParent).getChildNoCreate(nIndex); + else + System.out.println ("getChild called for unknown parent node"); + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) + { } + return aChild; + } + + /** iterate over all children and look for child */ + public synchronized int getIndexOfChild (Object aParent, Object aChild) + { + int nIndex = -1; + try + { + if ((aParent instanceof AccessibleTreeNode) && (aChild instanceof AccessibleTreeNode)) + { + AccessibleTreeNode aParentNode = (AccessibleTreeNode) aParent; + AccessibleTreeNode aChildNode = (AccessibleTreeNode) aChild; + + int nChildCount = aParentNode.getChildCount(); + for( int i = 0; i < nChildCount; i++ ) + { + if (aChildNode.equals (aParentNode.getChild (i))) + { + nIndex = i; + break; + } + } + } + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) + { + // Return -1 by falling through. + } + + // not found? + return nIndex; + } + + public boolean isLeaf (Object aNode) + { + return (aNode instanceof AccessibleTreeNode) ? + ((AccessibleTreeNode)aNode).isLeaf() : true; + } + + + + public synchronized Object getRoot() + { + return maRoot; + } + + public void valueForPathChanged(TreePath path, Object newValue) + { } + + protected synchronized void setRoot (AccessibleTreeNode aRoot) + { + maRoot = aRoot; + } + + + // The list of TreeModelListener objects. + protected ArrayList<TreeModelListener> maTMListeners; + + // The root node of the tree. Use setRoot to change it. + private AccessibleTreeNode maRoot = null; +} diff --git a/toolkit/test/accessibility/AccessibilityWorkBench.java b/toolkit/test/accessibility/AccessibilityWorkBench.java new file mode 100644 index 0000000000..918b8cf6dd --- /dev/null +++ b/toolkit/test/accessibility/AccessibilityWorkBench.java @@ -0,0 +1,591 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.frame.XFrame; +import com.sun.star.frame.XTerminateListener; +import com.sun.star.uno.UnoRuntime; + +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.awt.XExtendedToolkit; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.tree.*; +import javax.swing.event.TreeSelectionListener; +import javax.swing.event.TreeSelectionEvent; +import ov.ObjectViewContainer; + +/** This class manages the GUI of the work bench. + @see AccessibilityTreeModel + for the implementation of the tree view on the left side which also + manages the registration of accessibility listeners. + @see Canvas + for the graphical view of the accessible objects. +*/ +public class AccessibilityWorkBench + extends JFrame + implements ActionListener, XTerminateListener, TreeSelectionListener + +{ + private static final String msVersion = "v1.7.2"; + private String msOptionsFileName = ".AWBrc"; + + public static void main (String args[]) + { + int nPortNumber = 5678; + + for (int i=0; i<args.length; i++) + { + if (args[i].equals ("-h") || args[i].equals ("--help") || args[i].equals ("-?")) + { + System.out.println ("usage: AccessibilityWorkBench <option>*"); + System.out.println ("options:"); + System.out.println (" -p <port-number> Port on which to connect to StarOffice."); + System.out.println (" Defaults to 5678."); + System.exit (0); + } + else if (args[i].equals ("-p")) + { + nPortNumber = Integer.parseInt (args[++i]); + } + } + + saWorkBench = new AccessibilityWorkBench (nPortNumber); + } + + + + + /** Return the one instance of the AccessibilityWorkBench + @return + Returns null when the AccessibilityWorkBench could not be + created successfully. + */ + public static AccessibilityWorkBench Instance () + { + return saWorkBench; + } + + + + /** Create an accessibility work bench that listens at the specified + port to Office applications. + */ + private AccessibilityWorkBench (int nPortNumber) + { + mbInitialized = false; + + Layout (); + + MessageArea.println (System.getProperty ("os.name") + " / " + + System.getProperty ("os.arch") + " / " + + System.getProperty ("os.version")); + MessageArea.println ("Using port " + nPortNumber); + office = new SimpleOffice (nPortNumber); + + maAccessibilityTree.getComponent().addTreeSelectionListener (this); + + addWindowListener (new WindowAdapter () + { @Override + public void windowClosing (WindowEvent e) + { System.exit(0); } + }); + + initialize (); + } + + + + + /** Create and arrange the widgets of the GUI. + */ + private void Layout () + { + setSize (new Dimension (8000,600)); + + // Create new layout. + GridBagLayout aLayout = new GridBagLayout (); + getContentPane().setLayout (aLayout); + + // Accessible Tree. + maAccessibilityTree = new AccessibilityTree (); + JScrollPane aTreeScrollPane = new JScrollPane( + maAccessibilityTree.getComponent(), + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + aTreeScrollPane.setPreferredSize (new Dimension (400,300)); + + // Object view shows details about the currently selected accessible + // object. + maObjectViewContainer = new ObjectViewContainer (); + JScrollPane aObjectViewContainerScrollPane = new JScrollPane( + maObjectViewContainer, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + aObjectViewContainerScrollPane.setPreferredSize (new Dimension (400,300)); + + // Split pane for tree view and object view. + JSplitPane aLeftViewSplitPane = new JSplitPane ( + JSplitPane.VERTICAL_SPLIT, + aTreeScrollPane, + aObjectViewContainerScrollPane + ); + aLeftViewSplitPane.setDividerLocation (300); + + // Canvas. + maCanvas = new Canvas (); + maCanvas.setTree (maAccessibilityTree.getComponent()); + maAccessibilityTree.SetCanvas (maCanvas); + JScrollPane aScrolledCanvas = new JScrollPane(maCanvas, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + aScrolledCanvas.getViewport().setBackground (Color.RED); + aScrolledCanvas.setPreferredSize (new Dimension(600,400)); + + // Split pane for tree view and canvas. + JSplitPane aViewSplitPane = new JSplitPane ( + JSplitPane.HORIZONTAL_SPLIT, + aLeftViewSplitPane, + aScrolledCanvas + ); + aViewSplitPane.setOneTouchExpandable(true); + aViewSplitPane.setDividerLocation (400); + + // Text output area. + MessageArea aMessageArea = MessageArea.Instance (); + + // Split pane for the two views and the message area. + JSplitPane aSplitPane = new JSplitPane (JSplitPane.VERTICAL_SPLIT, + aViewSplitPane, aMessageArea); + aSplitPane.setOneTouchExpandable(true); + addGridElement (aViewSplitPane, 0,0, 2,1, 3,3, + GridBagConstraints.CENTER, GridBagConstraints.BOTH); + + // Button bar. + maButtonBar = new JPanel(); + maButtonBar.setLayout (new FlowLayout()); + addGridElement (maButtonBar, 0,3, 2,1, 1,0, + GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL); + + // Buttons. + aConnectButton = createButton ("Connect", "connect"); + aUpdateButton = createButton ("Update", "update"); + aShapesButton = createButton ("Expand Shapes", "shapes"); + aExpandButton = createButton ("Expand All", "expand"); + aQuitButton = createButton ("Quit", "quit"); + UpdateButtonStates (); + + Options.Instance().Load (msOptionsFileName); + + setJMenuBar (CreateMenuBar ()); + + setTitle("Accessibility Workbench " + msVersion); + + pack (); + setVisible (true); + validate (); + repaint(); + } + + + + + /** Shortcut method for adding an object to a GridBagLayout. + */ + private void addGridElement (JComponent object, + int x, int y, int width, int height, int weightx, int weighty, + int anchor, int fill) + { + GridBagConstraints constraints = new GridBagConstraints (); + constraints.gridx = x; + constraints.gridy = y; + constraints.gridwidth = width; + constraints.gridheight = height; + constraints.weightx = weightx; + constraints.weighty = weighty; + constraints.anchor = anchor; + constraints.fill = fill; + getContentPane().add (object, constraints); + } + + + + + /** Create a new button and place at the right most position into the + button bar. + */ + private JButton createButton (String title, String command) + { + JButton aButton = new JButton (title); + aButton.setEnabled (false); + aButton.setActionCommand (command); + aButton.addActionListener (this); + + maButtonBar.add (aButton); + return aButton; + } + + + + + /** Create a menu bar for the application. + @return + Returns the new menu bar. The returned reference is also + remembered in the data member <member>maMenuBar</member>. + */ + private JMenuBar CreateMenuBar () + { + // Menu bar. + JMenuBar aMenuBar = new JMenuBar (); + + // File menu. + JMenu aFileMenu = new JMenu ("File"); + aMenuBar.add (aFileMenu); + JMenuItem aItem; + aItem = new JMenuItem ("Quit"); + aFileMenu.add (aItem); + aItem.addActionListener (this); + + // View menu. + JMenu aViewMenu = new JMenu ("View"); + aMenuBar.add (aViewMenu); + ButtonGroup aGroup = new ButtonGroup (); + JRadioButtonMenuItem aRadioButton = new JRadioButtonMenuItem ("Whole Screen"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + aRadioButton = new JRadioButtonMenuItem ("200%"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + aRadioButton = new JRadioButtonMenuItem ("100%"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + aRadioButton = new JRadioButtonMenuItem ("50%"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + aRadioButton = new JRadioButtonMenuItem ("25%"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + aRadioButton = new JRadioButtonMenuItem ("10%"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + + // Options menu. + JMenu aOptionsMenu = new JMenu ("Options"); + aMenuBar.add (aOptionsMenu); + JCheckBoxMenuItem aCBItem; + aCBItem = new JCheckBoxMenuItem ("Show Descriptions", maCanvas.getShowDescriptions()); + aOptionsMenu.add (aCBItem); + aCBItem.addActionListener (this); + + aCBItem = new JCheckBoxMenuItem ("Show Names", maCanvas.getShowNames()); + aOptionsMenu.add (aCBItem); + aCBItem.addActionListener (this); + + aCBItem = new JCheckBoxMenuItem ("Show Text", maCanvas.getShowText()); + aOptionsMenu.add (aCBItem); + aCBItem.addActionListener (this); + + aCBItem = new JCheckBoxMenuItem ("Antialiased Rendering", maCanvas.getAntialiasing()); + aOptionsMenu.add (aCBItem); + aCBItem.addActionListener (this); + + // Help menu. + JMenu aHelpMenu = new JMenu ("Help"); + aMenuBar.add (aHelpMenu); + + aItem = new JMenuItem ("Help"); + aHelpMenu.add (aItem); + aItem.addActionListener (this); + + aItem = new JMenuItem ("News"); + aHelpMenu.add (aItem); + aItem.addActionListener (this); + + aItem = new JMenuItem ("About"); + aHelpMenu.add (aItem); + aItem.addActionListener (this); + + return aMenuBar; + } + + + + + /** Initialize the AWB. This includes clearing the canvas, add + listeners, creation of a new tree model for the tree list box and + the update of the button states. + + This method may be called any number of times. Note that all + actions will be carried out every time. The main purpose of a + second call is that of a re-initialization after a reconnect. + */ + private void initialize () + { + maCanvas.clear(); + + AccessibilityTreeModel aModel = null; + aModel = new AccessibilityTreeModel (createTreeModelRoot()); + + aModel.setCanvas (maCanvas); + maAccessibilityTree.getComponent().setModel (aModel); + + if (office != null) + { + // Add terminate listener. + if (office.getDesktop() != null) + office.getDesktop().addTerminateListener (this); + + XExtendedToolkit xToolkit = office.getExtendedToolkit(); + // Remove old top window listener. + if (maTopWindowListener != null) + xToolkit.removeTopWindowListener (maQueuedTopWindowListener); + // Add top window listener. + if (xToolkit != null) + { + MessageArea.println ("registering at extended toolkit"); + maTopWindowListener = new TopWindowListener (aModel, office); + maQueuedTopWindowListener = new QueuedTopWindowListener (maTopWindowListener); + xToolkit.addTopWindowListener (maQueuedTopWindowListener); + maTopWindowListener.Initialize (); + } + else + maTopWindowListener = null; + } + + mbInitialized = true; + UpdateButtonStates (); + } + + + + + /** Update the states of the buttons according to the internal state of + the AWB. + */ + private void UpdateButtonStates () + { + aConnectButton.setEnabled (mbInitialized); + aQuitButton.setEnabled (mbInitialized); + aUpdateButton.setEnabled (mbInitialized); + aExpandButton.setEnabled (mbInitialized); + aShapesButton.setEnabled (mbInitialized); + } + + + + /** Callback for GUI actions from the buttons. + */ + public void actionPerformed (java.awt.event.ActionEvent e) + { + if (e.getActionCommand().equals("connect")) + { + office.connect(); + initialize (); + } + else if (e.getActionCommand().equals("quit")) + { + AccessibilityTreeModel aModel = (AccessibilityTreeModel)maAccessibilityTree.getComponent().getModel(); + aModel.clear(); + System.exit (0); + } + else if (e.getActionCommand().equals("update")) + { + initialize (); + } + else if (e.getActionCommand().equals("shapes")) + { + Cursor aCursor = getCursor(); + setCursor (new Cursor (Cursor.WAIT_CURSOR)); + maAccessibilityTree.expandShapes(); + setCursor (aCursor); + } + else if (e.getActionCommand().equals("expand")) + { + Cursor aCursor = getCursor(); + setCursor (new Cursor (Cursor.WAIT_CURSOR)); + maAccessibilityTree.expandAll(); + setCursor (aCursor); + } + else if (e.getActionCommand().equals ("Quit")) + { + System.out.println ("exiting"); + System.exit (0); + } + else if (e.getActionCommand().equals ("Show Descriptions")) + { + maCanvas.setShowDescriptions ( ! maCanvas.getShowDescriptions()); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("Show Names")) + { + maCanvas.setShowNames ( ! maCanvas.getShowNames()); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("Antialiased Rendering")) + { + maCanvas.setAntialiasing ( ! maCanvas.getAntialiasing()); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("Help")) + { + HelpWindow.Instance().loadFile ("help.html"); + } + else if (e.getActionCommand().equals ("News")) + { + try{ + HelpWindow.Instance().loadFile ("news.html"); + } catch (Exception ex) {} + } + else if (e.getActionCommand().equals ("About")) + { + HelpWindow.Instance().loadFile ("about.html"); + } + else if (e.getActionCommand().equals ("Whole Screen")) + { + maCanvas.setZoomMode (Canvas.WHOLE_SCREEN); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("200%")) + { + maCanvas.setZoomMode (200); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("100%")) + { + maCanvas.setZoomMode (100); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("50%")) + { + maCanvas.setZoomMode (50); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("25%")) + { + maCanvas.setZoomMode (25); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("10%")) + { + maCanvas.setZoomMode (10); + Options.Instance().Save (msOptionsFileName); + } + else + { + System.err.println("unknown command " + e.getActionCommand()); + } + } + + + + + /** Create an AccessibilityTreeModel root which contains the documents + (top windows) that are present at the moment. + */ + private AccessibleTreeNode createTreeModelRoot() + { + // create root node + VectorNode aRoot = new VectorNode ("Accessibility Tree", null); + if (maTopWindowListener != null) + maTopWindowListener.Initialize (); + return aRoot; + } + + + // TreeSelectionListener + public void valueChanged (TreeSelectionEvent aEvent) + { + TreePath aPath = aEvent.getPath(); + Object aObject = aPath.getLastPathComponent(); + if (aObject instanceof AccTreeNode) + { + AccTreeNode aNode = (AccTreeNode) aObject; + XAccessibleContext xContext = aNode.getContext(); + maObjectViewContainer.SetObject (xContext); + } + } + + + + + // XEventListener + public void disposing( com.sun.star.lang.EventObject aSourceObj ) + { + XFrame xFrame = UnoRuntime.queryInterface(XFrame.class, aSourceObj.Source); + + if( xFrame != null ) + System.out.println("frame disposed"); + else + System.out.println("controller disposed"); + } + + + + + // XTerminateListener + public void queryTermination (final com.sun.star.lang.EventObject aEvent) throws RuntimeException + { + System.out.println ("Terminate Event : " + aEvent); + } + + + + + // XTerminateListener + public void notifyTermination (final com.sun.star.lang.EventObject aEvent) throws RuntimeException + { + System.out.println ("Notify Termination Event : " + aEvent); + } + + + + /// The Singleton Workbench object. + private static AccessibilityWorkBench + saWorkBench = null; + + private SimpleOffice + office; + + private JPanel + maButtonBar; + private Canvas + maCanvas; + private AccessibilityTree + maAccessibilityTree; + private ObjectViewContainer + maObjectViewContainer; + private JButton + aConnectButton, + aQuitButton, + aUpdateButton, + aExpandButton, + aShapesButton; + private boolean + mbInitialized; + private TopWindowListener + maTopWindowListener; + private QueuedTopWindowListener + maQueuedTopWindowListener; +} diff --git a/toolkit/test/accessibility/AccessibleActionHandler.java b/toolkit/test/accessibility/AccessibleActionHandler.java new file mode 100644 index 0000000000..868ea1105c --- /dev/null +++ b/toolkit/test/accessibility/AccessibleActionHandler.java @@ -0,0 +1,91 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleAction; +import com.sun.star.lang.IndexOutOfBoundsException; + +class AccessibleActionHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleAction xEComponent = + UnoRuntime.queryInterface ( + XAccessibleAction.class, xContext); + if (xEComponent != null) + return new AccessibleActionHandler (xEComponent); + else + return null; + } + + public AccessibleActionHandler () + { + } + + private AccessibleActionHandler (XAccessibleAction xAction) + { + if (xAction != null) + maChildList.setSize (1 + xAction.getAccessibleActionCount()); + } + + protected static XAccessibleAction getAction (AccTreeNode aParent) + { + return UnoRuntime.queryInterface ( + XAccessibleAction.class, aParent.getContext()); + } + + @Override + public AccessibleTreeNode createChild ( + AccessibleTreeNode aParent, + int nIndex) + { + AccessibleTreeNode aChild = null; + + if (aParent instanceof AccTreeNode) + { + XAccessibleAction xAction = getAction ((AccTreeNode)aParent); + if( xAction != null ) + { + if (nIndex == 0) + aChild = new StringNode ("Number of actions: " + xAction.getAccessibleActionCount(), + aParent); + else + { + nIndex -= 1; + try + { + aChild = new AccessibleActionNode ( + "Action " + nIndex + " : " + + xAction.getAccessibleActionDescription (nIndex), + aParent, + nIndex); + } + catch( IndexOutOfBoundsException e ) + { + aChild = new StringNode ("ERROR", aParent); + } + } + } + } + + return aChild; + } +} diff --git a/toolkit/test/accessibility/AccessibleActionNode.java b/toolkit/test/accessibility/AccessibleActionNode.java new file mode 100644 index 0000000000..c1c927574c --- /dev/null +++ b/toolkit/test/accessibility/AccessibleActionNode.java @@ -0,0 +1,67 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import javax.swing.JOptionPane; + +/** + Base class for all tree nodes. + */ +class AccessibleActionNode + extends StringNode +{ + public AccessibleActionNode (String aDisplayObject, + AccessibleTreeNode aParent, + int nActionIndex) + { + super (aDisplayObject, aParent); + mnActionIndex = nActionIndex; + } + + @Override + public String[] getActions () + { + return new String[] {"Perform Action"}; + } + + /** perform action */ + @Override + public void performAction (int nIndex) + { + if (nIndex != 0) + return; + boolean bResult = false; + if (getParent() instanceof AccTreeNode) + try + { + bResult = AccessibleActionHandler.getAction( + (AccTreeNode)getParent()).doAccessibleAction ( + mnActionIndex); + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) + { + } + + JOptionPane.showMessageDialog (null, + "performed action " + mnActionIndex + + (bResult?" with":" without") + " success", + "Action " + mnActionIndex, + JOptionPane.INFORMATION_MESSAGE); + } + + private final int mnActionIndex; +} diff --git a/toolkit/test/accessibility/AccessibleCellHandler.java b/toolkit/test/accessibility/AccessibleCellHandler.java new file mode 100644 index 0000000000..716e1374ef --- /dev/null +++ b/toolkit/test/accessibility/AccessibleCellHandler.java @@ -0,0 +1,168 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleTable; +import com.sun.star.accessibility.XAccessible; + + +class AccessibleCellHandler extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + if (xContext == null) + return null; + + AccessibleCellHandler aCellHandler = null; + XAccessible xParent = xContext.getAccessibleParent(); + if (xParent != null) + { + XAccessibleTable xTable = + UnoRuntime.queryInterface ( + XAccessibleTable.class, xParent.getAccessibleContext()); + if (xTable != null) + aCellHandler = new AccessibleCellHandler (xTable); + } + return aCellHandler; + } + + public AccessibleCellHandler () + { + } + + private AccessibleCellHandler (XAccessibleTable xTable) + { + if (xTable != null) + maChildList.setSize (8); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + XAccessibleTable xTable = null; + XAccessibleContext xContext = null; + AccessibleTreeNode aGrandParent = aParent.getParent(); + if (aGrandParent instanceof AccTreeNode) + { + xTable = ((AccTreeNode)aGrandParent).getTable(); + xContext = ((AccTreeNode)aGrandParent).getContext(); + } + if (aParent instanceof AccTreeNode) + { + xContext = ((AccTreeNode)aParent).getContext(); + } + try + { + if( xTable != null && xContext != null ) + { + switch( nIndex ) + { + case 0: + { + long nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + + aChild = new StringNode ("# table row: " + nRow, aParent); + } + break; + case 1: + { + long nChild = xContext.getAccessibleIndexInParent(); + int nCol = xTable.getAccessibleColumn( nChild ); + + aChild = new StringNode ("# table column: " + nCol, aParent); + } + break; + case 2: + { + long nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + int nCol = xTable.getAccessibleColumn( nChild ); + int nExt = xTable.getAccessibleRowExtentAt( nRow, nCol ); + + aChild = new StringNode ("# table row extend: " + nExt, aParent); + } + break; + case 3: + { + long nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + int nCol = xTable.getAccessibleColumn( nChild ); + int nExt = xTable.getAccessibleColumnExtentAt( nRow, nCol ); + + aChild = new StringNode ("# table column extend: " + nExt, aParent); + } + break; + case 4: + { + long nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + int nCol = xTable.getAccessibleColumn( nChild ); + XAccessible xChild = + xTable.getAccessibleCellAt( nRow, nCol ); + + aChild = new StringNode ("# cell name retrieved from table: " + xChild.getAccessibleContext().getAccessibleName(), aParent); + } + break; + case 5: + { + int nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + int nCol = xTable.getAccessibleColumn( nChild ); + boolean bSelected = + xTable.isAccessibleSelected( nRow, nCol ); + + aChild = new StringNode ("cell is selected: " + bSelected, aParent); + } + break; + case 6: + { + long nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + boolean bSelected = + xTable.isAccessibleRowSelected( nRow ); + + aChild = new StringNode ("table row is selected: " + bSelected, aParent); + } + break; + case 7: + { + long nChild = xContext.getAccessibleIndexInParent(); + int nCol = xTable.getAccessibleColumn( nChild ); + boolean bSelected = + xTable.isAccessibleColumnSelected( nCol ); + + aChild = new StringNode ("table column is selected: " + bSelected, aParent); + } + break; + default: + aChild = new StringNode ("unknown child index " + nIndex, aParent); + } + } + } + catch (Exception e) + { + // Return empty child. + } + + return aChild; + } +} diff --git a/toolkit/test/accessibility/AccessibleComponentHandler.java b/toolkit/test/accessibility/AccessibleComponentHandler.java new file mode 100644 index 0000000000..229612b8dd --- /dev/null +++ b/toolkit/test/accessibility/AccessibleComponentHandler.java @@ -0,0 +1,121 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleComponent; + + +class AccessibleComponentHandler + extends NodeHandler +{ + + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleComponent xComponent = + UnoRuntime.queryInterface ( + XAccessibleComponent.class, xContext); + if (xComponent != null) + return new AccessibleComponentHandler (xComponent); + else + return null; + + } + + public AccessibleComponentHandler () + { + } + + private AccessibleComponentHandler (XAccessibleComponent xComponent) + { + if (xComponent != null) + maChildList.setSize (6); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + if (aParent instanceof AccTreeNode) + { + XAccessibleComponent xComponent = + ((AccTreeNode)aParent).getComponent(); + + if (xComponent != null) + { + int nColor; + switch (nIndex) + { + case 0: + com.sun.star.awt.Point aLocation = xComponent.getLocation(); + aChild = new StringNode ( + "Location: " + aLocation.X + ", " + aLocation.Y, + aParent); + break; + case 1: + com.sun.star.awt.Point aScreenLocation = xComponent.getLocationOnScreen(); + aChild = new StringNode ( + "Location on Screen: " + aScreenLocation.X + ", " + aScreenLocation.Y, + aParent); + break; + case 2: + com.sun.star.awt.Size aSize = xComponent.getSize(); + aChild = new StringNode ( + "Size: "+ aSize.Width + ", " + aSize.Height, + aParent); + break; + case 3: + com.sun.star.awt.Rectangle aBBox = xComponent.getBounds(); + aChild = new StringNode ( + "Bounding Box: "+ aBBox.X + ", " + aBBox.Y + "," + + aBBox.Width + ", " + aBBox.Height, + aParent); + break; + case 4: + nColor = xComponent.getForeground(); + aChild = new StringNode ("Foreground color: R" + + (nColor>>16&0xff) + + "G" + (nColor>>8&0xff) + + "B" + (nColor&0xff) + + "A" + (nColor>>24&0xff), + aParent); + break; + case 5: + nColor = xComponent.getBackground(); + aChild = new StringNode ("Background color: R" + + (nColor>>16&0xff) + + "G" + (nColor>>8&0xff) + + "B" + (nColor&0xff) + + "A" + (nColor>>24&0xff), + aParent); + break; + } + } + } + return aChild; + } + + @Override + public void update (AccessibleTreeNode aNode) + { + maChildList.clear(); + if (aNode instanceof AccTreeNode && ((AccTreeNode)aNode).getComponent() != null) + maChildList.setSize (4); + } +} diff --git a/toolkit/test/accessibility/AccessibleContextHandler.java b/toolkit/test/accessibility/AccessibleContextHandler.java new file mode 100644 index 0000000000..62bd4b8132 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleContextHandler.java @@ -0,0 +1,88 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import tools.NameProvider; + +class AccessibleContextHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + if (xContext != null) + return new AccessibleContextHandler (xContext); + else + return null; + } + + public AccessibleContextHandler () + { + super (); + } + + private AccessibleContextHandler (XAccessibleContext xContext) + { + super(); + if (xContext != null) + maChildList.setSize (4); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + XAccessibleContext xContext = null; + if (aParent instanceof AccTreeNode) + xContext = ((AccTreeNode)aParent).getContext(); + + String sChild = ""; + if (xContext != null) + { + switch( nIndex ) + { + case 0: + sChild = "Description: " + + xContext.getAccessibleDescription(); + break; + case 1: + int nRole = xContext.getAccessibleRole(); + sChild = "Role: " + nRole + " (" + NameProvider.getRoleName(nRole) + ")"; + break; + case 2: + XAccessible xParent = xContext.getAccessibleParent(); + sChild = "Has parent: " + (xParent!=null ? "yes" : "no"); + break; + case 3: + sChild = ""; + long xStateSet = xContext.getAccessibleStateSet(); + for (short i=0; i<=30; i++) + { + if ((xStateSet & (1<<i)) != 0) + { + if (sChild.compareTo ("") != 0) + sChild += ", "; + sChild += NameProvider.getStateName(1<<i); + } + } + sChild = "State set: " + sChild; + } + } + return new StringNode (sChild, aParent); + } +} diff --git a/toolkit/test/accessibility/AccessibleEditableTextHandler.java b/toolkit/test/accessibility/AccessibleEditableTextHandler.java new file mode 100644 index 0000000000..ae5df14618 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleEditableTextHandler.java @@ -0,0 +1,53 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEditableText; + + +class AccessibleEditableTextHandler extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleEditableText xText = + UnoRuntime.queryInterface ( + XAccessibleEditableText.class, xContext); + if (xText != null) + return new AccessibleEditableTextHandler (xText); + else + return null; + } + + public AccessibleEditableTextHandler () + { + } + + private AccessibleEditableTextHandler (XAccessibleEditableText xText) + { + if (xText != null) + maChildList.setSize (1); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + return new StringNode ("XAccessibleEditableText is supported", aParent); + } +} diff --git a/toolkit/test/accessibility/AccessibleExtendedComponentHandler.java b/toolkit/test/accessibility/AccessibleExtendedComponentHandler.java new file mode 100644 index 0000000000..f34ed1b5fc --- /dev/null +++ b/toolkit/test/accessibility/AccessibleExtendedComponentHandler.java @@ -0,0 +1,93 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleExtendedComponent; + + +class AccessibleExtendedComponentHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleExtendedComponent xEComponent = + UnoRuntime.queryInterface ( + XAccessibleExtendedComponent.class, xContext); + if (xEComponent != null) + return new AccessibleExtendedComponentHandler (xEComponent); + else + return null; + } + + public AccessibleExtendedComponentHandler () + { + } + + private AccessibleExtendedComponentHandler (XAccessibleExtendedComponent xEComponent) + { + if (xEComponent != null) + maChildList.setSize (0); + } + + private static XAccessibleExtendedComponent getComponent (AccTreeNode aNode) + { + return UnoRuntime.queryInterface ( + XAccessibleExtendedComponent.class, + aNode.getContext()); + } + + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + if (aParent instanceof AccTreeNode) + { + XAccessibleExtendedComponent xEComponent = getComponent ((AccTreeNode)aParent); + + if (xEComponent != null) + { + int nColor; + switch( nIndex ) + { + case 0: + nColor = xEComponent.getForeground(); + aChild = new StringNode ("Deprecated Foreground color: R" + + (nColor>>16&0xff) + + "G" + (nColor>>8&0xff) + + "B" + (nColor&0xff) + + "A" + (nColor>>24&0xff), + aParent); + break; + case 1: + nColor = xEComponent.getBackground(); + aChild = new StringNode ("Deprecated Background color: R" + + (nColor>>16&0xff) + + "G" + (nColor>>8&0xff) + + "B" + (nColor&0xff) + + "A" + (nColor>>24&0xff), + aParent); + break; + } + } + } + return aChild; + } +} diff --git a/toolkit/test/accessibility/AccessibleHyperlinkHandler.java b/toolkit/test/accessibility/AccessibleHyperlinkHandler.java new file mode 100644 index 0000000000..41a161ac5a --- /dev/null +++ b/toolkit/test/accessibility/AccessibleHyperlinkHandler.java @@ -0,0 +1,53 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleHyperlink; + + +class AccessibleHyperlinkHandler extends AccessibleTreeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleHyperlink xLink = + UnoRuntime.queryInterface ( + XAccessibleHyperlink.class, xContext); + if (xLink != null) + return new AccessibleHyperlinkHandler (xLink); + else + return null; + } + + public AccessibleHyperlinkHandler () + { + } + + private AccessibleHyperlinkHandler (XAccessibleHyperlink xLink) + { + if (xLink != null) + maChildList.setSize (1); + } + + @Override + public AccessibleTreeNode getChild (AccessibleTreeNode aParent, int nIndex) + { + return new StringNode ("interface XAccessibleHyperlink is supported", aParent); + } +} diff --git a/toolkit/test/accessibility/AccessibleHypertextHandler.java b/toolkit/test/accessibility/AccessibleHypertextHandler.java new file mode 100644 index 0000000000..603d68997d --- /dev/null +++ b/toolkit/test/accessibility/AccessibleHypertextHandler.java @@ -0,0 +1,53 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleHypertext; + + +class AccessibleHypertextHandler extends AccessibleTreeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleHypertext xText = + UnoRuntime.queryInterface ( + XAccessibleHypertext.class, xContext); + if (xText != null) + return new AccessibleHypertextHandler (xText); + else + return null; + } + + public AccessibleHypertextHandler () + { + } + + private AccessibleHypertextHandler (XAccessibleHypertext xText) + { + if (xText != null) + maChildList.setSize (1); + } + + @Override + public AccessibleTreeNode getChild (AccessibleTreeNode aParent, int nIndex) + { + return new StringNode ("interface XAccessibleHypertext is supported", aParent); + } +} diff --git a/toolkit/test/accessibility/AccessibleImageHandler.java b/toolkit/test/accessibility/AccessibleImageHandler.java new file mode 100644 index 0000000000..a02077b7d2 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleImageHandler.java @@ -0,0 +1,70 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleImage; + + +class AccessibleImageHandler extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleImage xImage = + UnoRuntime.queryInterface ( + XAccessibleImage.class, xContext); + if (xImage != null) + return new AccessibleImageHandler (xImage); + else + return null; + } + + public AccessibleImageHandler () + { + } + + private AccessibleImageHandler (XAccessibleImage xImage) + { + if (xImage != null) + maChildList.setSize (1); + } + + private static XAccessibleImage getImage (AccTreeNode aNode) + { + return UnoRuntime.queryInterface ( + XAccessibleImage.class, aNode.getContext()); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + if (aParent instanceof AccTreeNode) + { + XAccessibleImage xImage = getImage ((AccTreeNode)aParent); + if (xImage != null) + return new StringNode ( + "Image: " + + xImage.getAccessibleImageDescription() + " (" + + xImage.getAccessibleImageWidth() + "x" + + xImage.getAccessibleImageHeight() + ")", + aParent); + } + return null; + } +} diff --git a/toolkit/test/accessibility/AccessibleRelationHandler.java b/toolkit/test/accessibility/AccessibleRelationHandler.java new file mode 100644 index 0000000000..1f7eebada2 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleRelationHandler.java @@ -0,0 +1,115 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.AccessibleRelation; +import com.sun.star.accessibility.XAccessibleRelationSet; +import com.sun.star.lang.IndexOutOfBoundsException; + +import tools.NameProvider; + +class AccessibleRelationHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler( XAccessibleContext xContext ) + { + AccessibleRelationHandler aHandler = null; + if (xContext != null) + { + XAccessibleRelationSet xRelation = xContext.getAccessibleRelationSet(); + if (xRelation != null) + aHandler = new AccessibleRelationHandler(xContext); + } + return aHandler; + } + + public AccessibleRelationHandler() + { + } + + private AccessibleRelationHandler( XAccessibleContext xContext ) + { + XAccessibleRelationSet xRelation = xContext.getAccessibleRelationSet(); + if (xRelation != null) + maChildList.setSize( 1 ); + } + + @Override + public AccessibleTreeNode createChild( AccessibleTreeNode aParent, + int nIndex ) + { + XAccessibleRelationSet xRelation = null; + AccessibleTreeNode aChild = null; + + if( aParent instanceof AccTreeNode ) + { + xRelation = + ((AccTreeNode)aParent).getContext().getAccessibleRelationSet(); + } + if( xRelation == null ) + return aChild; + + + VectorNode aVNode = new VectorNode( "RelationSet", aParent); + int nCount = xRelation.getRelationCount(); + try + { + for( int i = 0; i < nCount; i++ ) + { + AccessibleRelation aRelation = xRelation.getRelation( i ); + + StringBuffer aBuffer = new StringBuffer(); + aBuffer.append (NameProvider.getRelationName (aRelation.RelationType)); + aBuffer.append( ": " ); + + for( int j = 0; j < aRelation.TargetSet.length; j++ ) + { + Object aTarget = aRelation.TargetSet[j]; + XAccessible xAccTarget = + UnoRuntime.queryInterface( + XAccessible.class, aTarget ); + if( xAccTarget == null ) + { + aBuffer.append( aTarget.toString() ); + } + else + { + aBuffer.append( xAccTarget.getAccessibleContext(). + getAccessibleName() ); + } + aBuffer.append( ", " ); + } + aBuffer.delete( aBuffer.length() - 2, aBuffer.length() ); + + aVNode.addChild( new StringNode( aBuffer.toString(), + aParent ) ); + } + + aChild = aVNode; + } + catch( IndexOutOfBoundsException e ) + { + aChild = new StringNode( "IndexOutOfBounds", aParent ); + } + + return aChild; + } +} diff --git a/toolkit/test/accessibility/AccessibleSelectionHandler.java b/toolkit/test/accessibility/AccessibleSelectionHandler.java new file mode 100644 index 0000000000..41eae0d980 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleSelectionHandler.java @@ -0,0 +1,145 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleSelection; +import com.sun.star.lang.IndexOutOfBoundsException; + + + +class AccessibleSelectionHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler( XAccessibleContext xContext ) + { + XAccessibleSelection xSelection = + UnoRuntime.queryInterface( + XAccessibleSelection.class, xContext); + return (xSelection == null) ? null : + new AccessibleSelectionHandler(xSelection); + } + + public AccessibleSelectionHandler() + { + } + + private AccessibleSelectionHandler( XAccessibleSelection xSelection ) + { + if (xSelection != null) + maChildList.setSize( 2 ); + } + + @Override + public AccessibleTreeNode createChild( AccessibleTreeNode aParent, + int nIndex ) + { + if( !(aParent instanceof AccTreeNode) ) + return null; + + XAccessibleSelection xSelection = ((AccTreeNode)aParent).getSelection(); + if( xSelection == null ) + return null; + + AccessibleTreeNode aChild = null; + + switch( nIndex ) + { + case 0: + aChild = new StringNode( + "getSelectedAccessibleChildCount: " + + xSelection.getSelectedAccessibleChildCount(), + aParent ); + break; + case 1: + { + VectorNode aVNode = + new VectorNode( "Selected Children", aParent); + long nSelected = 0; + long nCount = ((AccTreeNode)aParent).getContext(). + getAccessibleChildCount(); + try + { + for( long i = 0; i < nCount; i++ ) + { + try + { + if( xSelection.isAccessibleChildSelected( i ) ) + { + XAccessible xSelChild = xSelection. + getSelectedAccessibleChild(nSelected); + XAccessible xNChild = + ((AccTreeNode)aParent). + getContext().getAccessibleChild( i ); + aVNode.addChild( new StringNode( + i + ": " + + xNChild.getAccessibleContext(). + getAccessibleDescription() + " (" + + (xSelChild.equals(xNChild) ? "OK" : "XXX") + + ")", aParent ) ); + } + } + catch (com.sun.star.lang.DisposedException e) + { + aVNode.addChild( new StringNode( + i + ": caught DisposedException while creating", + aParent )); + } + } + aChild = aVNode; + } + catch( IndexOutOfBoundsException e ) + { + aChild = new StringNode( "IndexOutOfBounds", + aParent ); + } + } + break; + default: + aChild = new StringNode( "ERROR", aParent ); + break; + } + + return aChild; + } + + + @Override + public String[] getActions (AccessibleTreeNode aNode) + { + if( aNode instanceof AccTreeNode ) + { + XAccessibleSelection xSelection = + ((AccTreeNode)aNode).getSelection(); + if( xSelection != null ) + { + return new String[] { "Select..." }; + } + } + return new String[0]; + } + + @Override + public void performAction (AccessibleTreeNode aNode, int nIndex) + { + SelectionDialog selectionDialog = new SelectionDialog( (AccTreeNode)aNode ); + selectionDialog.setVisible(true); + } +} diff --git a/toolkit/test/accessibility/AccessibleTableHandler.java b/toolkit/test/accessibility/AccessibleTableHandler.java new file mode 100644 index 0000000000..a72fe61a35 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleTableHandler.java @@ -0,0 +1,103 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleTable; + + +class AccessibleTableHandler extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleTable xTable = + UnoRuntime.queryInterface ( + XAccessibleTable.class, xContext); + if (xTable != null) + return new AccessibleTableHandler (xTable); + else + return null; + } + + public AccessibleTableHandler () + { + } + + private AccessibleTableHandler (XAccessibleTable xTable) + { + if (xTable != null) + maChildList.setSize (4); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + XAccessibleTable xTable = null; + if (aParent instanceof AccTreeNode) + xTable = ((AccTreeNode)aParent).getTable(); + try + { + if( xTable != null ) + { + switch( nIndex ) + { + case 0: + aChild = new StringNode ("# table rows: " + xTable.getAccessibleRowCount(), aParent); + break; + case 1: + aChild = new StringNode ("# table columns: " + xTable.getAccessibleColumnCount(), aParent); + break; + case 2: + { + String sText = "selected rows: "; + int[] aSelected = xTable.getSelectedAccessibleRows(); + for( int i=0; i < aSelected.length; i++ ) + { + sText += aSelected[i]; + sText += " "; + } + aChild = new StringNode (sText, aParent); + } + break; + case 3: + { + String sText = "selected columns: "; + int[] aSelected = xTable.getSelectedAccessibleColumns(); + for( int i=0; i < aSelected.length; i++ ) + { + sText += aSelected[i]; + sText += " "; + } + aChild = new StringNode (sText, aParent); + } + break; + default: + aChild = new StringNode ("unknown child index " + nIndex, aParent); + } + } + } + catch (Exception e) + { + // Return empty child. + } + + return aChild; + } +} diff --git a/toolkit/test/accessibility/AccessibleTextHandler.java b/toolkit/test/accessibility/AccessibleTextHandler.java new file mode 100644 index 0000000000..714bcb5910 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleTextHandler.java @@ -0,0 +1,821 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.text.JTextComponent; + +import com.sun.star.accessibility.AccessibleTextType; +import com.sun.star.accessibility.TextSegment; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEditableText; +import com.sun.star.accessibility.XAccessibleText; +import com.sun.star.awt.Point; +import com.sun.star.awt.Rectangle; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.UnknownPropertyException; +import com.sun.star.lang.IndexOutOfBoundsException; +import com.sun.star.uno.UnoRuntime; + + +class AccessibleTextHandler extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleText xText = UnoRuntime.queryInterface ( + XAccessibleText.class, xContext); + if (xText != null) + return new AccessibleTextHandler (xText); + else + return null; + } + + public AccessibleTextHandler () + { + } + + private AccessibleTextHandler (XAccessibleText xText) + { + if (xText != null) + maChildList.setSize (8); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + XAccessibleText xText = null; + if (aParent instanceof AccTreeNode) + xText = ((AccTreeNode)aParent).getText(); + + try + { + if( xText != null ) + { + switch( nIndex ) + { + case 0: + aChild = new StringNode (xText.getText(), aParent); + break; + case 1: + aChild = new StringNode ("# chars: " + xText.getCharacterCount(), aParent); + break; + case 2: + aChild = new StringNode (characters( xText ), aParent); + break; + case 3: + aChild = new StringNode ("selection: " + + "[" + xText.getSelectionStart() + + "," + xText.getSelectionEnd() + + "] \"" + xText.getSelectedText() + "\"", + aParent); + break; + case 4: + aChild = new StringNode ("getCaretPosition: " + xText.getCaretPosition(), aParent); + break; + case 5: + { + VectorNode aVec = new VectorNode("portions", aParent); + aChild = aVec; + aVec.addChild( + textAtIndexNode( xText, "Character", + AccessibleTextType.CHARACTER, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Word", + AccessibleTextType.WORD, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Sentence", + AccessibleTextType.SENTENCE, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Paragraph", + AccessibleTextType.PARAGRAPH, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Line", + AccessibleTextType.LINE, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Attribute", + AccessibleTextType.ATTRIBUTE_RUN, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Glyph", + AccessibleTextType.GLYPH, + aParent ) ); + } + break; + case 6: + aChild = new StringNode (bounds( xText ), aParent); + break; + case 7: + aChild = getAttributes( xText, aParent ); + break; + default: + aChild = new StringNode ("unknown child index " + nIndex, aParent); + } + } + } + catch (Exception e) + { + // Return empty child. + } + + return aChild; + } + + + private String textAtIndexNodeString( + int nStart, int nEnd, + String sWord, String sBefore, String sBehind) + { + return "[" + nStart + "," + nEnd + "] " + + "\"" + sWord + "\" \t" + + "(" + sBefore + "," + sBehind + ")"; + } + + /** Create a text node that lists all strings of a particular text type + */ + private AccessibleTreeNode textAtIndexNode( + XAccessibleText xText, + String sName, + short nTextType, + AccessibleTreeNode aParent) + { + VectorNode aNode = new VectorNode (sName, aParent); + + // get word at all positions; + // for nicer display, compare current word to previous one and + // make a new node for every interval, not for every word + int nLength = xText.getCharacterCount(); + if( nLength > 0 ) + { + try + { + // sWord + nStart mark the current word + // make a node as soon as a new one is found; close the last + // one at the end + TextSegment sWord = xText.getTextAtIndex(0, nTextType); + TextSegment sBefore = xText.getTextBeforeIndex(0, nTextType); + TextSegment sBehind = xText.getTextBehindIndex(0, nTextType); + int nStart = 0; + for(int i = 1; i < nLength; i++) + { + TextSegment sTmp = xText.getTextAtIndex(i, nTextType); + TextSegment sTBef = xText.getTextBeforeIndex(i, nTextType); + TextSegment sTBeh = xText.getTextBehindIndex(i, nTextType); + if( ! ( sTmp.equals( sWord ) && sTBef.equals( sBefore ) && + sTBeh.equals( sBehind ) ) ) + { + aNode.addChild (new StringNode (textAtIndexNodeString( + nStart, i, + sWord.SegmentText, sBefore.SegmentText, sBehind.SegmentText), aNode)); + sWord = sTmp; + sBefore = sTBef; + sBehind = sTBeh; + nStart = i; + } + + // don't generate more than 50 children. + if (aNode.getChildCount() > 50) + { + sWord.SegmentText = "..."; + break; + } + } + aNode.addChild (new StringNode (textAtIndexNodeString( + nStart, nLength, + sWord.SegmentText, sBefore.SegmentText, sBehind.SegmentText), aNode)); + } + catch( IndexOutOfBoundsException e ) + { + aNode.addChild (new StringNode (e.toString(), aNode)); + } + catch (com.sun.star.lang.IllegalArgumentException e) + { + aNode.addChild (new StringNode (e.toString(), aNode)); + } + } + + return aNode; + } + + + + /** getCharacter (display as array string) */ + private String characters(XAccessibleText xText) + { + // get count (max. 30) + int nChars = xText.getCharacterCount(); + if( nChars > 30 ) + nChars = 30; + + // build up string + StringBuffer aChars = new StringBuffer(); + try + { + aChars.append( '[' ); + for( int i = 0; i < nChars; i++) + { + aChars.append( xText.getCharacter(i) ); + aChars.append( ',' ); + } + if( nChars > 0) + { + if( nChars == xText.getCharacterCount() ) + aChars.deleteCharAt( aChars.length() - 1 ); + else + aChars.append( "..." ); + } + aChars.append( ']' ); + } + catch( IndexOutOfBoundsException e ) + { + aChars.append( " ERROR " ); + } + + // return result + return "getCharacters: " + aChars; + } + + + /** iterate over characters, and translate their positions + * back and forth */ + private String bounds( XAccessibleText xText ) + { + StringBuffer aBuffer = new StringBuffer( "bounds: " ); + try + { + // iterate over characters + int nCount = xText.getCharacterCount(); + for(int i = 0; i < nCount; i++ ) + { + // get bounds for this character + Rectangle aRect = xText.getCharacterBounds( i ); + + // get the character by 'clicking' into the middle of + // the bounds + Point aMiddle = new Point(); + aMiddle.X = aRect.X + (aRect.Width / 2) - 1; + aMiddle.Y = aRect.Y + (aRect.Height / 2 ) - 1; + int nIndex = xText.getIndexAtPoint( aMiddle ); + + // get the character, or a '#' for an illegal index + if( (nIndex >= 0) && (nIndex < xText.getCharacter(i)) ) + aBuffer.append( xText.getCharacter(nIndex) ); + else + aBuffer.append( '#' ); + } + } + catch( IndexOutOfBoundsException e ) + { } // ignore errors + + return aBuffer.toString(); + } + + + private AccessibleTreeNode getAttributes( XAccessibleText xText, + AccessibleTreeNode aParent) + { + String[] aAttributeList = new String[] { + "CharBackColor", + "CharColor", + "CharEscapement", + "CharHeight", + "CharPosture", + "CharStrikeout", + "CharUnderline", + "CharWeight", + "ParaAdjust", + "ParaBottomMargin", + "ParaFirstLineIndent", + "ParaLeftMargin", + "ParaLineSpacing", + "ParaRightMargin", + "ParaTabStops"}; + + AccessibleTreeNode aRet; + + try + { + VectorNode aPortions = new VectorNode ("getAttributes", aParent); + + int nIndex = 0; + int nLength = xText.getCharacterCount(); + while( nIndex < nLength ) + { + // get attribute run + String aPortion = null; + try + { + aPortion = xText.getTextAtIndex( + nIndex, AccessibleTextType.ATTRIBUTE_RUN).SegmentText; + } + catch(com.sun.star.lang.IllegalArgumentException e) + { + aPortion = ""; + } + + // get attributes and make node with attribute children + PropertyValue[] aValues = xText.getCharacterAttributes(nIndex, aAttributeList); + VectorNode aAttrs = new VectorNode (aPortion, aPortions); + for( int i = 0; i < aValues.length; i++ ) + { + new StringNode( aValues[i].Name + ": " + aValues[i].Value, + aAttrs ); + } + + // get next portion, but advance at least one + nIndex += (aPortion.length() > 0) ? aPortion.length() : 1; + } + + aRet = aPortions; + } + catch( UnknownPropertyException e ) + { + aRet = new StringNode( "Exception caught:" + e, aParent ); + } + catch( IndexOutOfBoundsException e ) + { + aRet = new StringNode( "Exception caught:" + e, aParent ); + } + + return aRet; + } + + + private static String[] aTextActions = + new String[] { "select...", "copy..." }; + private static String[] aEditableTextActions = + new String[] { "select...", "copy...", + "cut...", "paste...", "edit...", "format..." }; + + @Override + public String[] getActions (AccessibleTreeNode aNode) + { + XAccessibleEditableText xEText = null; + if (aNode instanceof AccTreeNode) + xEText = ((AccTreeNode)aNode).getEditText (); + + return (xEText == null) ? aTextActions : aEditableTextActions; + } + + @Override + public void performAction (AccessibleTreeNode aNode, int nIndex) + { + if ( ! (aNode instanceof AccTreeNode)) + return; + + AccTreeNode aATNode = (AccTreeNode)aNode; + TextActionDialog aDialog = null; + + // create proper dialog + switch( nIndex ) + { + case 0: + aDialog = new TextActionDialog( aATNode, + "Select range:", + "select" ) + { + @Override + boolean action( + JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException + { + return aNode.getText().setSelection( + getSelectionStart(), + getSelectionEnd() ); + } + }; + break; + case 1: + aDialog = new TextActionDialog( aATNode, + "Select range and copy:", + "copy" ) + { + @Override + boolean action( + JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException + { + return aNode.getText().copyText( + getSelectionStart(), + getSelectionEnd() ); + } + }; + break; + case 2: + aDialog = new TextActionDialog( aATNode, + "Select range and cut:", + "cut" ) + { + @Override + boolean action( + JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException + { + return aNode.getEditText().cutText( + getSelectionStart(), + getSelectionEnd() ); + } + }; + break; + case 3: + aDialog = new TextActionDialog( aATNode, + "Place Caret and paste:", + "paste" ) + { + @Override + boolean action( + JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException + { + return aNode.getEditText().pasteText( + aText.getCaretPosition() ); + } + }; + break; + case 4: + aDialog = new TextEditDialog( aATNode, "Edit text:", + "edit" ); + break; + case 5: + aDialog = new TextAttributeDialog( aATNode ); + break; + } + + if( aDialog != null ) + aDialog.setVisible(true); + } + +} + +/** + * Display a dialog with a text field and a pair of cancel/do-it buttons + */ +abstract class TextActionDialog extends JDialog + implements ActionListener +{ + private AccTreeNode aNode; + JTextArea aText; + private String sName; + private JCheckBox aIndexToggle; + + public TextActionDialog( AccTreeNode aNd, + String sExplanation, + String sButtonText ) + { + super( AccessibilityWorkBench.Instance() ); + + aNode = aNd; + sName = sButtonText; + init( sExplanation, aNode.getText().getText(), sButtonText ); + setSize( 350, 225 ); + } + + /** build dialog */ + protected void init( String sExplanation, + String sText, + String sButtonText ) + { + setTitle( sName ); + + // vertical stacking of the elements + Container aContent = getContentPane(); + + // label with explanation + if( sExplanation.length() > 0 ) + aContent.add( new JLabel( sExplanation ), BorderLayout.NORTH ); + + // the text field + aText = new JTextArea(); + aText.setText( sText ); + aText.setColumns( Math.min( Math.max( 40, sText.length() ), 20 ) ); + aText.setRows( sText.length() / 40 + 1 ); + aText.setLineWrap( true ); + aText.setEditable( false ); + aContent.add( aText, BorderLayout.CENTER ); + + JPanel aButtons = new JPanel(); + aButtons.setLayout( new FlowLayout() ); + aIndexToggle = new JCheckBox( "reverse selection" ); + aButtons.add( aIndexToggle ); + JButton aActionButton = new JButton( sButtonText ); + aActionButton.setActionCommand( "Action" ); + aActionButton.addActionListener( this ); + aButtons.add( aActionButton ); + JButton aCancelButton = new JButton( "cancel" ); + aCancelButton.setActionCommand( "Cancel" ); + aCancelButton.addActionListener( this ); + aButtons.add( aCancelButton ); + + // add Panel with buttons + aContent.add( aButtons, BorderLayout.SOUTH ); + } + + private void cancel() + { + setVisible(false); + dispose(); + } + + private void action() + { + String sError = null; + try + { + boolean bSuccess = action( aText, aNode ); + if( !bSuccess ) + sError = "Can't execute"; + } + catch( IndexOutOfBoundsException e ) + { + sError = "Index out of bounds"; + } + + if( sError != null ) + JOptionPane.showMessageDialog( AccessibilityWorkBench.Instance(), + sError, sName, + JOptionPane.ERROR_MESSAGE); + + cancel(); + } + + public void actionPerformed(ActionEvent e) + { + String sCommand = e.getActionCommand(); + + if( "Cancel".equals( sCommand ) ) + cancel(); + else if( "Action".equals( sCommand ) ) + action(); + } + + + int getSelectionStart() { return getSelection(true); } + int getSelectionEnd() { return getSelection(false); } + private int getSelection(boolean bStart) + { + return ( bStart ^ aIndexToggle.isSelected() ) + ? aText.getSelectionStart() : aText.getSelectionEnd(); + } + + + + /** override this for dialog-specific action */ + abstract boolean action( JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException; +} + + +class TextEditDialog extends TextActionDialog +{ + public TextEditDialog( AccTreeNode aNode, + String sExplanation, + String sButtonText ) + { + super( aNode, sExplanation, sButtonText ); + } + + @Override + protected void init( String sExplanation, + String sText, + String sButtonText ) + { + super.init( sExplanation, sText, sButtonText ); + aText.setEditable( true ); + } + + + /** edit the text */ + @Override + boolean action( JTextComponent aText, AccTreeNode aNode ) + { + // is this text editable? if not, fudge you and return + XAccessibleEditableText xEdit = aNode.getEditText(); + return ( xEdit == null ) ? false : + updateText( xEdit, aText.getText() ); + } + + + /** update the text */ + private boolean updateText( XAccessibleEditableText xEdit, String sNew ) + { + String sOld = xEdit.getText(); + + // false alarm? Early out if no change was done! + if( sOld.equals( sNew ) ) + return false; + + // get the minimum length of both strings + int nMinLength = sOld.length(); + if( sNew.length() < nMinLength ) + nMinLength = sNew.length(); + + // count equal characters from front and end + int nFront = 0; + while( (nFront < nMinLength) && + (sNew.charAt(nFront) == sOld.charAt(nFront)) ) { + nFront++; + } + int nBack = 0; + while( (nBack < nMinLength) && + ( sNew.charAt(sNew.length()-nBack-1) == + sOld.charAt(sOld.length()-nBack-1) ) ) { + nBack++; + } + if( nFront + nBack > nMinLength ) + nBack = nMinLength - nFront; + + // so... the first nFront and the last nBack characters + // are the same. Change the others! + String sDel = sOld.substring( nFront, sOld.length() - nBack ); + String sIns = sNew.substring( nFront, sNew.length() - nBack ); + + System.out.println("edit text: " + + sOld.substring(0, nFront) + + " [ " + sDel + " -> " + sIns + " ] " + + sOld.substring(sOld.length() - nBack) ); + + boolean bRet = false; + try + { + // edit the text, and use + // (set|insert|delete|replace)Text as needed + if( nFront+nBack == 0 ) + bRet = xEdit.setText( sIns ); + else if( sDel.length() == 0 ) + bRet = xEdit.insertText( sIns, nFront ); + else if( sIns.length() == 0 ) + bRet = xEdit.deleteText( nFront, sOld.length()-nBack ); + else + bRet = xEdit.replaceText(nFront, sOld.length()-nBack,sIns); + } + catch( IndexOutOfBoundsException e ) + { + bRet = false; + } + + return bRet; + } +} + + +class TextAttributeDialog extends TextActionDialog +{ + public TextAttributeDialog( + AccTreeNode aNode ) + { + super( aNode, "Choose attributes, select text, and press 'Set':", + "set" ); + } + + private JCheckBox aBold, aUnderline, aItalics; + private Color aForeground, aBackground; + + @Override + protected void init( String sExplanation, + String sText, + String sButtonText ) + { + super.init( sExplanation, sText, sButtonText ); + + aForeground = Color.black; + aBackground = Color.white; + + JPanel aAttr = new JPanel(); + aAttr.setLayout( new BoxLayout( aAttr, BoxLayout.Y_AXIS ) ); + + aBold = new JCheckBox( "bold" ); + aUnderline = new JCheckBox( "underline" ); + aItalics = new JCheckBox( "italics" ); + + JButton aForeButton = new JButton("Foreground", new ColorIcon(true)); + aForeButton.addActionListener( new ActionListener() { + public void actionPerformed(ActionEvent e) + { + aForeground = JColorChooser.showDialog( + TextAttributeDialog.this, + "Select Foreground Color", + aForeground); + } + } ); + + JButton aBackButton = new JButton("Background", new ColorIcon(false)); + aBackButton.addActionListener( new ActionListener() { + public void actionPerformed(ActionEvent e) + { + aBackground = JColorChooser.showDialog( + TextAttributeDialog.this, + "Select Background Color", + aBackground); + } + } ); + + aAttr.add( aBold ); + aAttr.add( aUnderline ); + aAttr.add( aItalics ); + aAttr.add( aForeButton ); + aAttr.add( aBackButton ); + + getContentPane().add( aAttr, BorderLayout.WEST ); + } + + + private class ColorIcon implements Icon + { + private boolean bForeground; + private static final int nHeight = 16; + private static final int nWidth = 16; + + public ColorIcon(boolean bWhich) { bForeground = bWhich; } + public int getIconHeight() { return nHeight; } + public int getIconWidth() { return nWidth; } + public void paintIcon(Component c, Graphics g, int x, int y) + { + g.setColor( getColor() ); + g.fillRect( x, y, nHeight, nWidth ); + g.setColor( c.getForeground() ); + g.drawRect( x, y, nHeight, nWidth ); + } + Color getColor() + { + return bForeground ? aForeground : aBackground; + } + } + + + + /** edit the text */ + @Override + boolean action( JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException + { + // is this text editable? if not, fudge you and return + XAccessibleEditableText xEdit = aNode.getEditText(); + boolean bSuccess = false; + if( xEdit != null ) + { + PropertyValue[] aSequence = new PropertyValue[6]; + aSequence[0] = new PropertyValue(); + aSequence[0].Name = "CharWeight"; + aSequence[0].Value = Integer.valueOf( aBold.isSelected() ? 150 : 100 ); + aSequence[1] = new PropertyValue(); + aSequence[1].Name = "CharUnderline"; + aSequence[1].Value = Integer.valueOf( aUnderline.isSelected() ? 1 : 0 ); + aSequence[2] = new PropertyValue(); + aSequence[2].Name = "CharBackColor"; + aSequence[2].Value = Integer.valueOf( aBackground.getRGB() ); + aSequence[3] = new PropertyValue(); + aSequence[3].Name = "CharColor"; + aSequence[3].Value = Integer.valueOf( aForeground.getRGB() ); + aSequence[4] = new PropertyValue(); + aSequence[4].Name = "CharPosture"; + aSequence[4].Value = Integer.valueOf( aItalics.isSelected() ? 1 : 0 ); + aSequence[5] = new PropertyValue(); + aSequence[5].Name = "CharBackTransparent"; + aSequence[5].Value = Boolean.FALSE; + + bSuccess = xEdit.setAttributes( getSelectionStart(), + getSelectionEnd(), + aSequence ); + } + return bSuccess; + } + +} diff --git a/toolkit/test/accessibility/AccessibleTreeHandler.java b/toolkit/test/accessibility/AccessibleTreeHandler.java new file mode 100644 index 0000000000..09fe790251 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleTreeHandler.java @@ -0,0 +1,130 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.lang.IndexOutOfBoundsException; + + +/** + * Map the tree of accessibility objects into their + * AccessibilityTreeModel counterparts. + */ +class AccessibleTreeHandler + extends NodeHandler +{ + private XAccessibleContext mxContext; + + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + if (xContext != null) + return new AccessibleTreeHandler (xContext); + else + return null; + } + + public AccessibleTreeHandler () + { + super(); + mxContext = null; + } + + private AccessibleTreeHandler (XAccessibleContext xContext) + { + super(); + mxContext = xContext; + if (mxContext != null) + // Add one to the number of children to include the string node + // that tells you how many children there are. + synchronized (maChildList) + { + maChildList.setSize (1 + mxContext.getAccessibleChildCount()); + } + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + if (mxContext != null) + { + if (nIndex == 0) + aChild = new StringNode ("Child count: " + mxContext.getAccessibleChildCount(), + aParent); + else + { + // Lower index to skip the string node. + nIndex -= 1; + try + { + XAccessible xChild = mxContext.getAccessibleChild (nIndex); + aChild = NodeFactory.Instance().createDefaultNode ( + xChild, aParent); + } + catch( IndexOutOfBoundsException e ) + { + aChild = new StringNode ("ERROR: no child with index " + nIndex, aParent); + } + } + } + else + aChild = new StringNode ("XAccessibleContext interface not supported", aParent); + return aChild; + } + + /** Try to add the specified accessible child into the lists of + children. The insertion position is determined from the + getIndexInParent method of the child. + */ + public AccessibleTreeNode addAccessibleChild (AccessibleTreeNode aParent, XAccessible xChild) + { + AccessibleTreeNode aChild = null; + + if (xChild != null) + { + XAccessibleContext xContext = xChild.getAccessibleContext(); + if (xContext != null) + { + long nIndex = xContext.getAccessibleIndexInParent() + 1; + synchronized (maChildList) + { + if ((nIndex >= 0) || (nIndex <= maChildList.size())) + { + aChild = NodeFactory.Instance().createDefaultNode (xChild, aParent); + maChildList.add (nIndex, aChild); + } + } + } + } + return aChild; + } + + + /** Update only the child count node. Trust on other ways to update the + accessible children. + */ + @Override + public void update (AccessibleTreeNode aNode) + { + synchronized (maChildList) + { + maChildList.set (0, null); + } + } +} diff --git a/toolkit/test/accessibility/AccessibleTreeNode.java b/toolkit/test/accessibility/AccessibleTreeNode.java new file mode 100644 index 0000000000..22fa035f95 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleTreeNode.java @@ -0,0 +1,113 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.util.ArrayList; + +import com.sun.star.lang.IndexOutOfBoundsException; + +/** + Base class for all tree nodes. + */ +abstract class AccessibleTreeNode +{ + /// The parent node. It is null for the root node. + private AccessibleTreeNode maParent; + + /// The object to be displayed. + private final Object maDisplayObject; + + public AccessibleTreeNode (Object aDisplayObject, AccessibleTreeNode aParent) + { + maDisplayObject = aDisplayObject; + maParent = aParent; + } + + public void update () + { + // Empty + } + + public AccessibleTreeNode getParent () + { + return maParent; + } + + public int getChildCount () + { + return 0; + } + + public AccessibleTreeNode getChild (int nIndex) + throws IndexOutOfBoundsException + { + throw new IndexOutOfBoundsException(); + } + + public AccessibleTreeNode getChildNoCreate (int nIndex) + throws IndexOutOfBoundsException + { + throw new IndexOutOfBoundsException(); + } + + public boolean removeChild (int nIndex) + throws IndexOutOfBoundsException + { + throw new IndexOutOfBoundsException(); + } + + public abstract int indexOf (AccessibleTreeNode aNode); + + /** Create a path to this node by first asking the parent for its path + and then appending this object. + */ + public void createPath (java.util.List<AccessibleTreeNode> aPath) + { + if (maParent != null) + maParent.createPath (aPath); + aPath.add (this); + } + + public Object[] createPath () + { + ArrayList<AccessibleTreeNode> aPath = new ArrayList<AccessibleTreeNode> (1); + createPath (aPath); + return aPath.toArray(); + } + + public boolean isLeaf() + { + return true; + } + + @Override + public String toString() + { + return maDisplayObject.toString(); + } + + /** get names of supported actions */ + public String[] getActions () + { + return new String[] {}; + } + + /** perform action */ + public void performAction (int nIndex) + { + } +} diff --git a/toolkit/test/accessibility/AccessibleUNOHandler.java b/toolkit/test/accessibility/AccessibleUNOHandler.java new file mode 100644 index 0000000000..1ee522b277 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleUNOHandler.java @@ -0,0 +1,126 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.uno.Type; + + +/** This handler displays lower level UNO information. These are the + supported services, interfaces, and the implementation name. +*/ +class AccessibleUNOHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + if (xContext == null) + return null; + else + { + AccessibleUNOHandler h = new AccessibleUNOHandler(); + h.maChildList.setSize (3); + return h; + } + } + + private XServiceInfo GetServiceInfo (AccessibleTreeNode aNode) + { + XServiceInfo xServiceInfo = null; + if (aNode instanceof AccTreeNode) + xServiceInfo = UnoRuntime.queryInterface( + XServiceInfo.class, ((AccTreeNode)aNode).getContext()); + return xServiceInfo; + } + private XTypeProvider GetTypeProvider (AccessibleTreeNode aNode) + { + XTypeProvider xTypeProvider = null; + if (aNode instanceof AccTreeNode) + xTypeProvider = UnoRuntime.queryInterface( + XTypeProvider.class, ((AccTreeNode)aNode).getContext()); + return xTypeProvider; + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, + int nIndex) + { + AccessibleTreeNode aChild = null; + XServiceInfo xServiceInfo; + switch (nIndex) + { + case 0 : // Implementation name. + xServiceInfo = GetServiceInfo (aParent); + aChild = new StringNode ("Implementation name: " + + (xServiceInfo!=null ? xServiceInfo.getImplementationName() + : "<XServiceInfo not supported>"), + aParent); + break; + case 1 : + xServiceInfo = GetServiceInfo (aParent); + if (xServiceInfo == null) + aChild = new StringNode ( + "Supported services: <XServiceInfo not supported>", + aParent); + else + aChild = CreateServiceTree (aParent, xServiceInfo); + break; + case 2 : + XTypeProvider xTypeProvider = GetTypeProvider (aParent); + if (xTypeProvider == null) + aChild = new StringNode ( + "Supported interfaces: <XTypeProvider not supported>", + aParent); + else + aChild = CreateInterfaceTree (aParent, xTypeProvider); + break; + } + + return aChild; + } + + + private AccessibleTreeNode CreateServiceTree (AccessibleTreeNode aParent, + XServiceInfo xServiceInfo) + { + String[] aServiceNames = xServiceInfo.getSupportedServiceNames(); + VectorNode aNode = new VectorNode ("Supported Services", aParent); + + int nCount = aServiceNames.length; + for (int i=0; i<nCount; i++) + aNode.addChild (new StringNode (aServiceNames[i], aParent)); + + return aNode; + } + + private AccessibleTreeNode CreateInterfaceTree (AccessibleTreeNode aParent, + XTypeProvider xTypeProvider) + { + Type[] aTypes = xTypeProvider.getTypes(); + VectorNode aNode = new VectorNode ("Supported Interfaces", aParent); + + int nCount = aTypes.length; + for (int i=0; i<nCount; i++) + aNode.addChild (new StringNode (aTypes[i].getTypeName(), aParent)); + + return aNode; + } +} diff --git a/toolkit/test/accessibility/Canvas.java b/toolkit/test/accessibility/Canvas.java new file mode 100644 index 0000000000..e9149ff82e --- /dev/null +++ b/toolkit/test/accessibility/Canvas.java @@ -0,0 +1,463 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Toolkit; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.JViewport; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.TreePath; + +/** This canvas displays accessible objects graphically. Each accessible + object with graphical representation is represented by an + CanvasShape object and has to be added by the + <member>addAccessible</member> member function. + + <p>The canvas listens to selection events of the associated JTree and + highlights the first selected node of that tree.</p> +*/ +class Canvas + extends JPanel + implements MouseListener, MouseMotionListener, TreeSelectionListener//, Scrollable +{ + // This constant can be passed to SetZoomMode to always show the whole screen. + public static final int WHOLE_SCREEN = -1; + + public Canvas () + { + super (true); + maObjects = new java.util.HashMap<AccTreeNode, CanvasShape> (); + maNodes = new ArrayList<AccTreeNode> (); + maObjectList = new ArrayList<CanvasShape> (); + addMouseListener (this); + addMouseMotionListener (this); + maBoundingBox = new Rectangle (0,0,100,100); + maTree = null; + mnHOffset = 0; + mnVOffset = 0; + mnScale = 1; + setShowText(false); + setShowDescriptions (true); + setShowNames (true); + setAntialiasing (true); + } + + /** Tell the canvas which tree view to use to highlight accessible + objects. + */ + public void setTree (JTree aTree) + { + if (maTree != null) + maTree.removeTreeSelectionListener (this); + maTree = aTree; + if (maTree != null) + maTree.addTreeSelectionListener (this); + } + + + + + public void addNode (AccTreeNode aNode) + { + if (maNodes.indexOf (aNode) == -1) + { + maNodes.add (aNode); + + CanvasShape aObject = maObjects.get (aNode); + if (aObject == null) + { + aObject = new CanvasShape (aNode); + // Update bounding box that includes all objects. + if (maObjects.isEmpty()) + maBoundingBox = aObject.getBBox(); + else + maBoundingBox = maBoundingBox.union (aObject.getBBox()); + + maObjects.put (aNode, aObject); + maObjectList.add (aObject); + + } + repaint (); + } + } + + public void removeNode (AccTreeNode aNode) + { + int i = maNodes.indexOf (aNode); + if( i != -1 ) + { + CanvasShape aObject = maObjects.get(aNode); + maObjectList.remove (aObject); + maObjects.remove (aNode); + maNodes.remove (aNode); + repaint (); + } + } + + public void updateNode (AccTreeNode aNode) + { + int i = maNodes.indexOf (aNode); + if (i != -1) + { + CanvasShape aObject = maObjects.get(aNode); + if (aObject != null) + aObject.update(); + } + } + + public void updateNodeGeometry (AccTreeNode aNode) + { + CanvasShape aObject = maObjects.get(aNode); + if (aObject != null) + aObject.updateGeometry(); + } + + public void clear () + { + while (maNodes.size() > 0) { + removeNode (maNodes.get(0)); + } + maNodes.clear(); + maObjects.clear(); + maObjectList.clear(); + } + + public boolean getShowDescriptions () + { + return Options.GetBoolean ("ShowDescriptions"); + } + + public void setShowDescriptions (boolean bNewValue) + { + Options.SetBoolean ("ShowDescriptions", bNewValue); + repaint (); + } + + public boolean getShowNames () + { + return Options.GetBoolean ("ShowNames"); + } + + public void setShowNames (boolean bNewValue) + { + Options.SetBoolean ("ShowNames", bNewValue); + repaint (); + } + + public boolean getAntialiasing () + { + return Options.GetBoolean ("Antialiasing"); + } + + public void setAntialiasing (boolean bNewValue) + { + Options.SetBoolean ("Antialiasing", bNewValue); + repaint (); + } + + public boolean getShowText () + { + return Options.GetBoolean ("ShowText"); + } + + private void setShowText (boolean bNewValue) + { + Options.SetBoolean ("ShowText", bNewValue); + repaint (); + } + + public void setZoomMode (int nZoomMode) + { + Options.SetInteger ("ZoomMode", nZoomMode); + repaint (); + } + + private int getZoomMode () + { + return Options.GetInteger ("ZoomMode", WHOLE_SCREEN); + } + + + @Override + public void paintComponent (Graphics g) + { + synchronized (g) + { + super.paintComponent (g); + + Graphics2D g2 = (Graphics2D)g; + if (getAntialiasing()) + g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + else + g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + + setupTransformation (); + + // Draw the screen representation to give a hint of the location of the + // accessible object on the screen. + Dimension aScreenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Rectangle2D.Double aScreen = new Rectangle2D.Double ( + mnHOffset, + mnVOffset, + mnScale*aScreenSize.getWidth(), + mnScale*aScreenSize.getHeight()); + // Fill the screen rectangle and draw a frame around it to increase its visibility. + g2.setColor (new Color (250,240,230)); + g2.fill (aScreen); + g2.setColor (Color.BLACK); + g2.draw (aScreen); + + synchronized (maObjectList) + { + int nCount = maObjectList.size(); + boolean bShowDescriptions = getShowDescriptions(); + boolean bShowNames = getShowNames(); + boolean bShowText = getShowText(); + for (int i=0; i<nCount; i++) + { + CanvasShape aCanvasShape = maObjectList.get(i); + aCanvasShape.paint ( + g2, + mnHOffset, mnVOffset, mnScale, + bShowDescriptions, bShowNames, bShowText); + } + } + + // Paint highlighted frame around active object as the last thing. + if (maActiveObject != null) + maActiveObject.paint_highlight ( + g2); + } + } + + + + + /** Set up the transformation so that the graphical display can show a + centered representation of the whole screen. + */ + private void setupTransformation () + { + // Turn off scrollbars when showing the whole screen. Otherwise show them when needed. + JViewport aViewport = (JViewport)getParent(); + JScrollPane aScrollPane = (JScrollPane)aViewport.getParent(); + int nZoomMode = getZoomMode(); + if (nZoomMode == WHOLE_SCREEN) + { + if (aScrollPane.getHorizontalScrollBarPolicy() + != JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) + aScrollPane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + if (aScrollPane.getVerticalScrollBarPolicy() + != JScrollPane.VERTICAL_SCROLLBAR_NEVER) + aScrollPane.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_NEVER); + } + else + { + if (aScrollPane.getHorizontalScrollBarPolicy() + != JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED) + aScrollPane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + if (aScrollPane.getVerticalScrollBarPolicy() + != JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED) + aScrollPane.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + } + + Dimension aScreenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension aWidgetSize = aViewport.getSize(); + { + if ((aScreenSize.getWidth() > 0) && (aScreenSize.getHeight() > 0)) + { + if (nZoomMode == WHOLE_SCREEN) + { + // Calculate the scales that would map the screen onto the + // widget in both of the coordinate axes and select the + // smaller + // of the two: it maps the screen onto the widget in both + // axes at the same time. + double nHScale = (aWidgetSize.getWidth() - 10) / aScreenSize.getWidth(); + double nVScale = (aWidgetSize.getHeight() - 10) / aScreenSize.getHeight(); + if (nHScale < nVScale) + mnScale = nHScale; + else + mnScale = nVScale; + } + else + { + mnScale = nZoomMode / 100.0; + } + + // Calculate offsets that center the scaled screen inside the widget. + mnHOffset = (aWidgetSize.getWidth() - mnScale*aScreenSize.getWidth()) / 2.0; + mnVOffset = (aWidgetSize.getHeight() - mnScale*aScreenSize.getHeight()) / 2.0; + if (mnHOffset < 0) + mnHOffset = 0; + if (mnVOffset < 0) + mnVOffset = 0; + + setPreferredSize (new Dimension ( + (int)(2*mnHOffset + mnScale * aScreenSize.getWidth()), + (int)(2*mnVOffset + mnScale * aScreenSize.getHeight()))); + revalidate (); + } + else + { + // In case of a degenerate (not yet initialized?) screen size + // use some meaningless default values. + mnScale = 1; + mnHOffset = 0; + mnVOffset = 0; + } + } + } + + + + /** Call getAccessibleAt to determine accessible object under mouse. + */ + public void mouseClicked (MouseEvent e) + { + } + + public void mousePressed (MouseEvent e) + { + CanvasShape aObjectUnderMouse = FindCanvasShapeUnderMouse (e); + highlightObject (aObjectUnderMouse); + if ((e.getModifiers() & InputEvent.CTRL_MASK) != 0) + { + maTree.expandPath (aObjectUnderMouse.getPath()); + } + } + + public void mouseReleased (MouseEvent e) + { + } + + public void mouseEntered (MouseEvent e) + { + } + + public void mouseExited (MouseEvent e) + { + // Deselect currently active object. + if (maActiveObject != null) + { + maActiveObject.unhighlight (); + maActiveObject = null; + repaint (); + } + } + + public void mouseDragged (MouseEvent e) + { + } + + public void mouseMoved (MouseEvent e) + { + if ((e.getModifiers() & InputEvent.SHIFT_MASK) != 0) + highlightObject (FindCanvasShapeUnderMouse (e)); + } + + private CanvasShape FindCanvasShapeUnderMouse (MouseEvent e) + { + CanvasShape aObjectUnderMouse = null; + int nCount = maObjectList.size(); + for (int i=nCount-1; i>=0; --i) + { + CanvasShape aObject = maObjectList.get(i); + if (aObject != null && aObject.contains (e.getX(),e.getY())) + { + aObjectUnderMouse = aObject; + break; + } + } + return aObjectUnderMouse; + } + + private boolean highlightObject (CanvasShape aNewActiveObject) + { + if (aNewActiveObject != maActiveObject) + { + if (maActiveObject != null) + maActiveObject.unhighlight(); + + maActiveObject = aNewActiveObject; + if (maActiveObject != null) + { + if (maTree != null) + { + maTree.scrollPathToVisible (maActiveObject.getPath()); + maTree.setSelectionPath (maActiveObject.getPath()); + maTree.repaint (); + } + maActiveObject.highlight (); + repaint (); + } + return true; + } + else + return false; + } + + /** Called when the selection of the tree changes. Highlight the + corresponding graphical representation of the first selected object. + */ + public void valueChanged (javax.swing.event.TreeSelectionEvent event) + { + TreePath aPath = event.getPath(); + Object aObject = aPath.getLastPathComponent(); + if (aObject instanceof AccTreeNode) + { + CanvasShape aCanvasShape = maObjects.get (aObject); + if (highlightObject (aCanvasShape)) + repaint(); + } + } + + private double + mnHOffset, + mnVOffset, + mnScale; + private CanvasShape + maActiveObject; + private final java.util.HashMap<AccTreeNode, CanvasShape> + maObjects; + private final List<CanvasShape> + maObjectList; + private final List<AccTreeNode> + maNodes; + private Rectangle + maBoundingBox; + private JTree + maTree; +} diff --git a/toolkit/test/accessibility/CanvasShape.java b/toolkit/test/accessibility/CanvasShape.java new file mode 100644 index 0000000000..71ae17d688 --- /dev/null +++ b/toolkit/test/accessibility/CanvasShape.java @@ -0,0 +1,286 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.awt.*; +import javax.swing.tree.*; +import java.awt.geom.Rectangle2D; + +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleComponent; +import com.sun.star.accessibility.XAccessibleText; +import com.sun.star.accessibility.AccessibleStateType; + +class CanvasShape +{ + private final Color maHighlightColor = Color.red; + private final Color maSelectionColor = Color.green; + private final Color maFocusColor = Color.blue; + + public CanvasShape (AccTreeNode aNode) + { + maNode = aNode; + mxContext = aNode.getContext(); + msName = "name unknown"; + msDescription = "description unknown"; + maShape = new Rectangle2D.Double (-10,-10,10,10); + maPosition = new Point (-10,-10); + maSize = new Dimension (10,10); + maFgColor = java.awt.Color.black; + maBgColor = Color.blue; + mbHighlighted = false; + mbSelected = false; + mbFocused = false; + mxComponent = aNode.getComponent(); + + update (); + } + + + + /** Update the data obtained from the xAccessible. + */ + public void update () + { + if (mxContext != null) + { + msName = mxContext.getAccessibleName(); + msDescription = mxContext.getAccessibleDescription(); + + // Extract the selected and focused flag. + long nStateSet = mxContext.getAccessibleStateSet (); + mbSelected = (nStateSet & AccessibleStateType.SELECTED) != 0; + mbFocused = (nStateSet & AccessibleStateType.FOCUSED) != 0; + } + + updateGeometry (); + if (mxComponent != null) + { + // Note: alpha values in office 0..255 have to be mapped to + // 255..0 in Java + Color aCol = new Color (mxComponent.getForeground(), true); + maFgColor = new Color (aCol.getRed (), + aCol.getGreen (), + aCol.getBlue (), + 0xff - aCol.getAlpha ()); + aCol = new Color (mxComponent.getBackground(), true); + maBgColor = new Color (aCol.getRed (), + aCol.getGreen (), + aCol.getBlue (), + 0xff - aCol.getAlpha ()); + } + } + + public void updateGeometry () + { + if (mxComponent != null) + { + com.sun.star.awt.Point aLocationOnScreen = mxComponent.getLocationOnScreen(); + com.sun.star.awt.Size aSizeOnScreen = mxComponent.getSize(); + maPosition = new Point ( + aLocationOnScreen.X, + aLocationOnScreen.Y); + maSize = new Dimension ( + aSizeOnScreen.Width, + aSizeOnScreen.Height); + } + } + + + /** Paint the object into the specified canvas. It is transformed + according to the specified offset and scale. + */ + public void paint (Graphics2D g, + double nXOffset, double nYOffset, double nScaleFactor, + boolean bShowDescription, boolean bShowName, boolean bShowText) + { + try{ + // Transform the object's position and size according to the + // specified offset and scale. + maShape = new Rectangle2D.Double ( + maPosition.x * nScaleFactor + nXOffset, + maPosition.y * nScaleFactor + nYOffset, + maSize.width * nScaleFactor, + maSize.height * nScaleFactor); + + // Fill the object's bounding box with its background color if it + // has no children. + if (mxContext.getAccessibleChildCount() == 0) + { + g.setColor (maBgColor); + g.fill (maShape); + } + + // Remove alpha channel from color before drawing the frame. + Color color = maFgColor; + if (maFgColor.getAlpha()<128) + color = new Color (maFgColor.getRed(), maFgColor.getGreen(), maFgColor.getBlue()); + g.setColor (color); + g.draw (maShape); + + if (mbFocused) + { + g.setColor (maFocusColor); + for (int x=0; x<=2; x++) + for (int y=0; y<=2; y++) + g.fill ( + new Rectangle2D.Double ( + maShape.x + x/2.0 * maShape.width-3, + maShape.y + y/2.0 * maShape.height-3, + 6, + 6)); + } + if (mbSelected) + { + g.setColor (maSelectionColor); + for (int x=0; x<=2; x++) + for (int y=0; y<=2; y++) + g.draw ( + new Rectangle2D.Double ( + maShape.x + x/2.0 * maShape.width-2, + maShape.y + y/2.0 * maShape.height-2, + 4, + 4)); + } + + // Write the object's text OR name and description. + g.setColor (maFgColor); + if (bShowName) + paintName (g); + if (bShowDescription) + paintDescription (g); + if (bShowText) + paintText (g); + } + catch (Exception e) + { // don't care + } + } + + public void paint_highlight (Graphics2D g) + { + if (mbHighlighted) + g.setColor (maHighlightColor); + else + g.setColor (maFgColor); + g.draw (maShape); + } + + + + + private void paintName (Graphics2D g) + { + g.drawString ("Name: " + msName, + (float)maShape.x+5, + (float)maShape.y+15); + } + + + + private void paintDescription (Graphics2D g) + { + g.drawString ("Description: " + msDescription, + (float)maShape.x+5, + (float)maShape.y+35); + } + + + + + private void paintText (Graphics2D g) + { + XAccessibleText xText = null; + // get XAccessibleText + xText = maNode.getText(); + + // Draw every character in the text string. + if (xText != null) + { + String sText = xText.getText(); + try + { + for(int i = 0; i < sText.length(); i++) + { + com.sun.star.awt.Rectangle aRect = + xText.getCharacterBounds(i); + + double x = maShape.x + aRect.X; + double y = maShape.y + aRect.Y + aRect.Height; + + g.drawString(sText.substring(i, i+1), (float)x, (float)y); + } + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) + {} + } + } + + + + + /** Compute whether the specified point lies inside the object's + bounding box. + */ + public boolean contains (int x, int y) + { + return maShape.contains (x,y); + } + + public void highlight () + { + mbHighlighted = true; + } + + public void unhighlight () + { + mbHighlighted = false; + } + + public Rectangle getBBox () + { + return new Rectangle (maPosition, maSize); + } + + public TreePath getPath () + { + return new TreePath (maNode.createPath()); + } + + @Override + public String toString () + { + return ">"+msName+", "+msDescription+" +"+maPosition.x+"+"+maPosition.y + +"x"+maSize.width+"x"+maSize.height+"<"; + } + + private final AccTreeNode maNode; + private final XAccessibleContext mxContext; + private final XAccessibleComponent mxComponent; + private String msDescription, msName; + private Rectangle2D.Double maShape; + private Point maPosition; + private Dimension maSize; + private Color maFgColor, maBgColor; + private boolean + // Highlighting objects is an internal concept. Corresponds to selection in the tree view. + mbHighlighted, + // Set when the accessible object is selected. + mbSelected, + // Set when the accessible object is focused. + mbFocused; +} diff --git a/toolkit/test/accessibility/ChildEventHandler.java b/toolkit/test/accessibility/ChildEventHandler.java new file mode 100644 index 0000000000..e36f3502ad --- /dev/null +++ b/toolkit/test/accessibility/ChildEventHandler.java @@ -0,0 +1,66 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.uno.UnoRuntime; + +import java.io.PrintStream; + +class ChildEventHandler + extends EventHandler +{ + public ChildEventHandler (AccessibleEventObject aEvent, AccessibilityTreeModel aTreeModel) + { + super (aEvent, aTreeModel); + mxOldChild = UnoRuntime.queryInterface( + XAccessible.class, aEvent.OldValue); + mxNewChild = UnoRuntime.queryInterface( + XAccessible.class, aEvent.NewValue); + } + + @Override + public void PrintOldAndNew (PrintStream out) + { + if (mxOldChild != null) + out.println (" removing child " + mxOldChild); + if (mxNewChild != null) + out.println (" adding child " + mxNewChild); + } + + @Override + public void Process () + { + // Insertion and removal of children should be mutually exclusive. + // But this is a test tool and should take everything into account. + if (mxOldChild != null) + { + maTreeModel.removeNode (mxOldChild.getAccessibleContext()); + maTreeModel.updateNode (mxEventSource, AccessibleTreeHandler.class); + } + + if (mxNewChild != null) + { + maTreeModel.addChild (mxEventSource, mxNewChild); + } + } + + + private final XAccessible mxOldChild; + private final XAccessible mxNewChild; +} diff --git a/toolkit/test/accessibility/ContextEventHandler.java b/toolkit/test/accessibility/ContextEventHandler.java new file mode 100644 index 0000000000..12ffd0648c --- /dev/null +++ b/toolkit/test/accessibility/ContextEventHandler.java @@ -0,0 +1,70 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.uno.AnyConverter; + +import java.io.PrintStream; + +import tools.NameProvider; + +class ContextEventHandler + extends EventHandler +{ + public ContextEventHandler (AccessibleEventObject aEvent, AccessibilityTreeModel aTreeModel) + { + super (aEvent, aTreeModel); + } + + @Override + public void PrintOldAndNew (PrintStream out) + { + switch (mnEventId) + { + case AccessibleEventId.STATE_CHANGED: + try + { + int nOldValue = AnyConverter.toInt (maEvent.OldValue); + out.println (" turning off state " + nOldValue + " (" + + NameProvider.getStateName (nOldValue) + ")"); + } + catch (com.sun.star.lang.IllegalArgumentException e) + {} + try + { + int nNewValue = AnyConverter.toInt (maEvent.NewValue); + out.println (" turning on state " + nNewValue + " (" + + NameProvider.getStateName (nNewValue) + ")"); + } + catch (com.sun.star.lang.IllegalArgumentException e) + {} + break; + + default: + super.PrintOldAndNew (out); + } + + } + + @Override + public void Process () + { + maTreeModel.updateNode (mxEventSource, AccessibleContextHandler.class); + } +} diff --git a/toolkit/test/accessibility/EventHandler.java b/toolkit/test/accessibility/EventHandler.java new file mode 100644 index 0000000000..ed1a7adb4a --- /dev/null +++ b/toolkit/test/accessibility/EventHandler.java @@ -0,0 +1,75 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.uno.UnoRuntime; + +import java.io.PrintStream; + +import tools.NameProvider; + +/** Base class for handling of accessibility events. +*/ +class EventHandler +{ + public EventHandler (AccessibleEventObject aEvent, AccessibilityTreeModel aTreeModel) + { + maEvent = aEvent; + maTreeModel = aTreeModel; + + mnEventId = aEvent.EventId; + + mxEventSource = UnoRuntime.queryInterface( + XAccessibleContext.class, aEvent.Source); + if (mxEventSource == null) + { + XAccessible xAccessible = UnoRuntime.queryInterface( + XAccessible.class, aEvent.Source); + if (xAccessible != null) + mxEventSource = xAccessible.getAccessibleContext(); + } + } + + public void Print (PrintStream out) + { + out.println ("Event id is " + mnEventId + + " (" + NameProvider.getEventName(mnEventId)+")" + + " for " + mxEventSource.getAccessibleName() + " / " + + NameProvider.getRoleName (mxEventSource.getAccessibleRole())); + PrintOldAndNew (out); + } + + public void PrintOldAndNew (PrintStream out) + { + out.println (" old value is " + maEvent.OldValue); + out.println (" new value is " + maEvent.NewValue); + } + + public void Process () + { + System.out.println ("processing of event " + maEvent + " not implemented"); + } + + protected AccessibleEventObject maEvent; + protected AccessibilityTreeModel maTreeModel; + + protected int mnEventId; + protected XAccessibleContext mxEventSource; +} diff --git a/toolkit/test/accessibility/EventListener.java b/toolkit/test/accessibility/EventListener.java new file mode 100644 index 0000000000..5d9141953d --- /dev/null +++ b/toolkit/test/accessibility/EventListener.java @@ -0,0 +1,108 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.*; + +/** Objects of this class (usually one, singleton?) listen to accessible + events of all objects in all trees. +*/ +public class EventListener +{ + private boolean mbVerbose = false; + + public EventListener (AccessibilityTreeModel aTreeModel) + { + maTreeModel = aTreeModel; + } + + + /** This method handles accessibility objects that are being disposed. + */ + public void disposing (XAccessibleContext xContext) + { + if (mbVerbose) + System.out.println("disposing " + xContext); + maTreeModel.removeNode (xContext); + } + + /** This method is called from accessible objects that broadcast + modifications of themselves or from their children. The event is + processed only, except printing some messages, if the tree is not + locked. It should be locked during changes to its internal + structure like expanding nodes. + */ + public void notifyEvent (AccessibleEventObject aEvent) + { + EventHandler aHandler; + + switch (aEvent.EventId) + { + case AccessibleEventId.CHILD: + aHandler = new ChildEventHandler (aEvent, maTreeModel); + break; + + case AccessibleEventId.BOUNDRECT_CHANGED: + case AccessibleEventId.VISIBLE_DATA_CHANGED: + aHandler = new GeometryEventHandler (aEvent, maTreeModel); + break; + + + case AccessibleEventId.NAME_CHANGED: + case AccessibleEventId.DESCRIPTION_CHANGED: + case AccessibleEventId.STATE_CHANGED: + case AccessibleEventId.SELECTION_CHANGED: + aHandler = new ContextEventHandler (aEvent, maTreeModel); + break; + + case AccessibleEventId.TABLE_MODEL_CHANGED: + case AccessibleEventId.TABLE_CAPTION_CHANGED: + case AccessibleEventId.TABLE_COLUMN_DESCRIPTION_CHANGED: + case AccessibleEventId.TABLE_COLUMN_HEADER_CHANGED: + case AccessibleEventId.TABLE_ROW_DESCRIPTION_CHANGED: + case AccessibleEventId.TABLE_ROW_HEADER_CHANGED: + case AccessibleEventId.TABLE_SUMMARY_CHANGED: + aHandler = new TableEventHandler (aEvent, maTreeModel); + break; + + case AccessibleEventId.ACTION_CHANGED: + case AccessibleEventId.HYPERTEXT_CHANGED: + case AccessibleEventId.ACTIVE_DESCENDANT_CHANGED: + case AccessibleEventId.CARET_CHANGED: + case AccessibleEventId.TEXT_CHANGED: + case AccessibleEventId.VALUE_CHANGED: + aHandler = new EventHandler (aEvent, maTreeModel); + break; + + default: + aHandler = null; + break; + } + + if (aHandler == null) + System.out.println (" unhandled event"); + else + { + if (mbVerbose) + aHandler.Print (System.out); + aHandler.Process (); + } + } + + + private final AccessibilityTreeModel maTreeModel; +} diff --git a/toolkit/test/accessibility/EventQueue.java b/toolkit/test/accessibility/EventQueue.java new file mode 100644 index 0000000000..b6f6c6415e --- /dev/null +++ b/toolkit/test/accessibility/EventQueue.java @@ -0,0 +1,140 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.util.LinkedList; + +/** The event queue singleton dispatches events received from OpenOffice.org + applications in a thread separate from the AWB main thread. + + The queue of event objects, LinkedList<Runnable> The queue object will + also serve as lock for the consumer/producer type synchronization. +*/ +class EventQueue + implements Runnable +{ + private boolean mbVerbose = false; + private boolean mbHandleDisposingEventsSynchronous = true; + + public synchronized static EventQueue Instance () + { + if (maInstance == null) + maInstance = new EventQueue (); + return maInstance; + } + + public void addEvent (Runnable aEvent) + { + synchronized (maMonitor) + { + if (mbVerbose) + System.out.println ("queueing regular event " + aEvent); + maRegularQueue.addLast (aEvent); + maMonitor.notify (); + } + } + + + public void addDisposingEvent (Runnable aEvent) + { + if (mbHandleDisposingEventsSynchronous) + aEvent.run (); + else + synchronized (maMonitor) + { + if (mbVerbose) + System.out.println ("queueing disposing event " + aEvent); + maDisposingQueue.addLast (aEvent); + maMonitor.notify (); + } + } + + + private EventQueue () + { + maRegularQueue = new LinkedList<Runnable>(); + maDisposingQueue = new LinkedList<Runnable>(); + new Thread(this, "AWB.EventQueue").start(); + } + + + /// This thread's main method: deliver all events + public void run() + { + // in an infinite loop, check for events to deliver, then + // wait on lock (which will be notified when new events arrive) + while( true ) + { + Runnable aEvent = null; + do + { + synchronized (maMonitor) + { + if (maDisposingQueue.size() > 0) + { + aEvent = maDisposingQueue.removeFirst(); + if (mbVerbose) + System.out.println ("delivering disposing event " + aEvent); + } + else if (maRegularQueue.size() > 0) + { + aEvent = maRegularQueue.removeFirst(); + if (mbVerbose) + System.out.println ("delivering regular event " + aEvent); + } + else + aEvent = null; + } + if (aEvent != null) + { + try + { + aEvent.run(); + } + catch( Throwable e ) + { + System.out.println( + "caught exception during event delivery: " + e ); + e.printStackTrace(); + } + } + } + while( aEvent != null ); + + try + { + synchronized (maMonitor) + { + maMonitor.wait(); + } + } + catch (Exception e) + { + // can't wait? odd! + System.err.println("Can't wait!"); + e.printStackTrace(); + } + } + } + + private static EventQueue maInstance = null; + private final Object maMonitor = new Object(); + private final LinkedList<Runnable> maRegularQueue; + private final LinkedList<Runnable> maDisposingQueue; +} + + diff --git a/toolkit/test/accessibility/GeometryEventHandler.java b/toolkit/test/accessibility/GeometryEventHandler.java new file mode 100644 index 0000000000..c55ee3acc4 --- /dev/null +++ b/toolkit/test/accessibility/GeometryEventHandler.java @@ -0,0 +1,71 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.AccessibleEventObject; +import java.io.PrintStream; +import java.util.LinkedList; + +class GeometryEventHandler + extends EventHandler +{ + public GeometryEventHandler (AccessibleEventObject aEvent, AccessibilityTreeModel aTreeModel) + { + super (aEvent, aTreeModel); + } + + @Override + public void PrintOldAndNew (PrintStream out) + { + out.println (" children not relevant"); + } + + @Override + public void Process () + { + AccTreeNode aNode = maTreeModel.updateNode (mxEventSource, + AccessibleComponentHandler.class, + AccessibleExtendedComponentHandler.class); + + // Update the graphical representation of aNode in the Canvas. + Canvas aCanvas = maTreeModel.getCanvas(); + if (aCanvas != null) + { + // Iterate over all nodes in the sub-tree rooted in aNode. + LinkedList<Object> aShapeQueue = new LinkedList<Object>(); + aShapeQueue.addLast (aNode); + while (aShapeQueue.size() > 0) + { + // Remove the first node from the queue and update its + // graphical representation. + AccTreeNode aShapeNode = (AccTreeNode) aShapeQueue.getFirst(); + aShapeQueue.removeFirst(); + aCanvas.updateNodeGeometry (aShapeNode); + + // Add the node's children to the queue. + int nChildCount = maTreeModel.getChildCount (aShapeNode); + for (int i=0; i<nChildCount; i++) + { + Object aTreeNode = maTreeModel.getChildNoCreate (aShapeNode, i); + if (aTreeNode instanceof AccTreeNode) + aShapeQueue.addLast (aTreeNode); + } + } + aCanvas.repaint (); + } + } +} diff --git a/toolkit/test/accessibility/HelpWindow.java b/toolkit/test/accessibility/HelpWindow.java new file mode 100644 index 0000000000..00c9340676 --- /dev/null +++ b/toolkit/test/accessibility/HelpWindow.java @@ -0,0 +1,189 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JEditorPane; +import javax.swing.JButton; +import java.net.URL; +import javax.swing.event.HyperlinkListener; +import javax.swing.event.HyperlinkEvent; +import java.net.MalformedURLException; +import java.io.File; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; +import java.awt.event.ActionListener; +import java.util.LinkedList; + +class HelpWindow + implements ActionListener +{ + public static synchronized HelpWindow Instance () + { + if (maInstance == null) + maInstance = new HelpWindow(); + return maInstance; + } + + public void loadFile (String sFilename) + { + File aFile = new File (sFilename); + try + { + loadURL (aFile.toURI().toURL()); + } + catch (MalformedURLException e) + { + e.printStackTrace (System.err); + } + } + + private void loadURL (URL aURL) + { + maHistory.addLast (aURL); + selectHistoryPage (maHistory.size()-1); + maFrame.toFront (); + } + + + + + private HelpWindow () + { + try + { + maCurrentHistoryEntry = -1; + maHistory = new LinkedList<URL>(); + + maFrame = new JFrame (); + maFrame.addWindowListener (new WindowAdapter () + { + @Override + public void windowClosing (WindowEvent e) + { + maInstance = null; + } + }); + maContent = createContentWidget(); + + maFrame.getContentPane().setLayout (new GridBagLayout()); + GridBagConstraints aConstraints = new GridBagConstraints (); + aConstraints.gridx = 0; + aConstraints.gridy = 0; + aConstraints.gridwidth = 3; + aConstraints.weightx = 1; + aConstraints.weighty = 1; + aConstraints.fill = GridBagConstraints.BOTH; + maFrame.getContentPane().add (new JScrollPane (maContent), aConstraints); + + aConstraints = new GridBagConstraints(); + aConstraints.gridx = 0; + aConstraints.gridy = 1; + maPrevButton = new JButton ("Prev"); + maFrame.getContentPane().add (maPrevButton, aConstraints); + maPrevButton.addActionListener (this); + + aConstraints = new GridBagConstraints(); + aConstraints.gridx = 1; + aConstraints.gridy = 1; + maNextButton = new JButton ("Next"); + maFrame.getContentPane().add (maNextButton, aConstraints); + maNextButton.addActionListener (this); + + aConstraints = new GridBagConstraints(); + aConstraints.gridx = 2; + aConstraints.gridy = 1; + aConstraints.anchor = GridBagConstraints.EAST; + JButton aButton = new JButton ("Close"); + maFrame.getContentPane().add (aButton, aConstraints); + aButton.addActionListener (this); + + maFrame.setSize (600,400); + maFrame.setVisible (true); + } + catch (Exception e) + {} + } + + public void actionPerformed (java.awt.event.ActionEvent e) + { + if (e.getActionCommand().equals("Prev")) + { + selectHistoryPage (maCurrentHistoryEntry - 1); + } + else if (e.getActionCommand().equals("Next")) + { + selectHistoryPage (maCurrentHistoryEntry + 1); + } + else if (e.getActionCommand().equals("Close")) + { + maFrame.dispose (); + maInstance = null; + } + } + + private JEditorPane createContentWidget () + { + JEditorPane aContent = new JEditorPane (); + aContent.setEditable (false); + aContent.addHyperlinkListener (new HyperlinkListener() + { + public void hyperlinkUpdate (HyperlinkEvent e) + { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) + HelpWindow.Instance().loadURL (e.getURL()); + } + }); + return aContent; + } + + private void selectHistoryPage (int i) + { + if (i < 0) + i = 0; + else if (i >= maHistory.size()-1) + i = maHistory.size()-1; + if (i != maCurrentHistoryEntry) + { + URL aURL = maHistory.get (i); + try + { + maContent.setPage (aURL); + } + catch (java.io.IOException ex) + { + ex.printStackTrace(System.err); + } + + maCurrentHistoryEntry = i; + } + + maPrevButton.setEnabled (maCurrentHistoryEntry > 0); + maNextButton.setEnabled (maCurrentHistoryEntry < maHistory.size()-1); + } + + private static HelpWindow maInstance = null; + private JFrame maFrame; + private JEditorPane maContent; + private LinkedList<URL> maHistory; + private int maCurrentHistoryEntry; + private JButton maPrevButton; + private JButton maNextButton; +} diff --git a/toolkit/test/accessibility/MessageArea.java b/toolkit/test/accessibility/MessageArea.java new file mode 100644 index 0000000000..512cfaf733 --- /dev/null +++ b/toolkit/test/accessibility/MessageArea.java @@ -0,0 +1,131 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.awt.Font; +import java.awt.Color; +import java.awt.Graphics; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JScrollBar; + + + +/** A message area displays text in a scrollable text widget. It is a + singleton. Other objects can access it directly to display messages. +*/ +public class MessageArea + extends JScrollPane +{ + public static synchronized MessageArea Instance () + { + if (saInstance == null) + saInstance = new MessageArea (); + return saInstance; + } + + + + + /** Create a new message area. This method is private because the class is + a singleton and may therefore not be instantiated from the outside. + */ + private MessageArea () + { + maText = new JTextArea(); + maText.setBackground (new Color (255,250,240)); + maText.setFont (new Font ("Helvetica", Font.PLAIN, 9)); + setViewportView (maText); + setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + + printMessage ( + "class path is " + System.getProperty ("java.class.path") + "\n"); + } + + + + + /** Show the given string at the end of the message area and scroll to make + it visible. Indent the string as requested. + */ + private static synchronized void print (int nIndentation, String aMessage) + { + while (nIndentation-- > 0) { + aMessage = " " + aMessage; + } + Instance().printMessage(aMessage); + } + + + + + /** Show the given string at the end of the message area and scroll to make + it visible. + */ + public static void println (String aMessage) + { + println (0, aMessage); + } + + + + + /** Show the given string at the end of the message area and scroll to make + it visible. + */ + private static void println (int nIndentation, String aMessage) + { + print (nIndentation, aMessage+"\n"); + } + + + + + @Override + public void paintComponent (Graphics g) + { + synchronized (g) + { + JScrollBar sb = getVerticalScrollBar(); + if (sb != null) + { + int nScrollBarValue = sb.getMaximum() - sb.getVisibleAmount() - 1; + sb.setValue (nScrollBarValue); + } + super.paintComponent (g); + } + } + + + + + /** Append the given string to the end of the text and scroll so that it + becomes visible. This is an internal method. Use one of the static + and public ones. + */ + private synchronized void printMessage (String aMessage) + { + maText.append (aMessage); + } + + + + + private static MessageArea saInstance = null; + private final JTextArea maText; +} diff --git a/toolkit/test/accessibility/NodeFactory.java b/toolkit/test/accessibility/NodeFactory.java new file mode 100644 index 0000000000..7e1d9f585c --- /dev/null +++ b/toolkit/test/accessibility/NodeFactory.java @@ -0,0 +1,142 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.*; +import tools.NameProvider; + +/** This singleton class creates nodes for given accessible objects. +*/ +class NodeFactory +{ + public synchronized static NodeFactory Instance () + { + if (maInstance == null) + { + maInstance = new NodeFactory(); + } + return maInstance; + } + + private NodeFactory () + { + } + + + /** add default handlers based on the supported interfaces */ + private void addDefaultHandlers (AccTreeNode aNode, XAccessibleContext xContext) + { + if (false) + { + // Slow but complete version: try each handler type separately. + aNode.addHandler (maContextHandler.createHandler (xContext)); + aNode.addHandler (maTextHandler.createHandler (xContext)); + aNode.addHandler (maEditableTextHandler.createHandler (xContext)); + aNode.addHandler (maComponentHandler.createHandler (xContext)); + aNode.addHandler (maExtendedComponentHandler.createHandler (xContext)); + aNode.addHandler (maActionHandler.createHandler (xContext)); + aNode.addHandler (maImageHandler.createHandler (xContext)); + aNode.addHandler (maTableHandler.createHandler (xContext)); + aNode.addHandler (maCellHandler.createHandler (xContext)); + aNode.addHandler (maHypertextHandler.createHandler (xContext)); + aNode.addHandler (maHyperlinkHandler.createHandler (xContext)); + aNode.addHandler (maSelectionHandler.createHandler (xContext)); + aNode.addHandler (maRelationHandler.createHandler (xContext)); + aNode.addHandler (maUNOHandler.createHandler (xContext)); + aNode.addHandler (maTreeHandler.createHandler (xContext)); + } + else + { + // Exploit dependencies between interfaces. + NodeHandler aHandler; + aNode.addHandler (maContextHandler.createHandler (xContext)); + + aHandler = maTextHandler.createHandler (xContext); + if (aHandler != null) + { + aNode.addHandler (aHandler); + aNode.addHandler (maEditableTextHandler.createHandler (xContext)); + aNode.addHandler (maHypertextHandler.createHandler (xContext)); + aNode.addHandler (maHyperlinkHandler.createHandler (xContext)); + } + aHandler = maComponentHandler.createHandler (xContext); + if (aHandler != null) + { + aNode.addHandler (aHandler); + aNode.addHandler (maExtendedComponentHandler.createHandler (xContext)); + } + aNode.addHandler (maActionHandler.createHandler (xContext)); + aNode.addHandler (maImageHandler.createHandler (xContext)); + aNode.addHandler (maTableHandler.createHandler (xContext)); + aNode.addHandler (maRelationHandler.createHandler (xContext)); + aNode.addHandler (maCellHandler.createHandler (xContext)); + aNode.addHandler (maSelectionHandler.createHandler (xContext)); + aNode.addHandler (maUNOHandler.createHandler (xContext)); + aNode.addHandler (maTreeHandler.createHandler (xContext)); + } + } + + /** create a node with the default handlers */ + public AccTreeNode createDefaultNode (XAccessible xAccessible, AccessibleTreeNode aParent) + { + // default: aObject + aDisplay + String sDisplay; + + // if we are accessible, we use the context + name instead + XAccessibleContext xContext = null; + if (xAccessible != null) + xContext = xAccessible.getAccessibleContext(); + if (xContext != null) + { + sDisplay = xContext.getAccessibleName(); + if (sDisplay.length()==0) + { + sDisplay = "<no name> Role: " + + NameProvider.getRoleName ( + xContext.getAccessibleRole()); + } + } + else + sDisplay = "not accessible"; + + + // create node, and add default handlers + AccTreeNode aNode = new AccTreeNode (xContext, sDisplay, aParent); + addDefaultHandlers (aNode, xContext); + + return aNode; + } + + private static NodeFactory maInstance = null; + + // default handlers + private final NodeHandler maContextHandler = new AccessibleContextHandler(); + private final NodeHandler maTextHandler = new AccessibleTextHandler(); + private final NodeHandler maEditableTextHandler = new AccessibleEditableTextHandler(); + private final NodeHandler maComponentHandler = new AccessibleComponentHandler(); + private final NodeHandler maExtendedComponentHandler = new AccessibleExtendedComponentHandler(); + private final NodeHandler maActionHandler = new AccessibleActionHandler(); + private final NodeHandler maImageHandler = new AccessibleImageHandler(); + private final NodeHandler maTableHandler = new AccessibleTableHandler(); + private final NodeHandler maCellHandler = new AccessibleCellHandler(); + private final NodeHandler maHypertextHandler = new AccessibleHypertextHandler(); + private final NodeHandler maHyperlinkHandler = new AccessibleHyperlinkHandler(); + private final NodeHandler maSelectionHandler = new AccessibleSelectionHandler(); + private final NodeHandler maRelationHandler = new AccessibleRelationHandler(); + private final NodeHandler maTreeHandler = new AccessibleTreeHandler(); + private final NodeHandler maUNOHandler = new AccessibleUNOHandler(); +} diff --git a/toolkit/test/accessibility/NodeHandler.java b/toolkit/test/accessibility/NodeHandler.java new file mode 100644 index 0000000000..69cff2c13e --- /dev/null +++ b/toolkit/test/accessibility/NodeHandler.java @@ -0,0 +1,133 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.util.Vector; + + +/** + * Map an arbitrary object into parts of a tree node. + */ +abstract class NodeHandler +{ + /** This vector is used as cache for the child objects. + */ + protected Vector<AccessibleTreeNode> maChildList; + + + public abstract NodeHandler createHandler ( + com.sun.star.accessibility.XAccessibleContext xContext); + + public NodeHandler () + { + maChildList = new Vector<AccessibleTreeNode> (); + } + + /** return the number of children this object has */ + public int getChildCount() + { + synchronized (maChildList) + { + return maChildList.size(); + } + } + + /** + * return a child object. Complex + * children have to be AccTreeNode instances. + * @see AccTreeNode + */ + public AccessibleTreeNode getChild (AccessibleTreeNode aParent, int nIndex) + { + synchronized (maChildList) + { + AccessibleTreeNode aChild = maChildList.get(nIndex); + if (aChild == null) + { + aChild = createChild (aParent, nIndex); + if (aChild == null) + aChild = new StringNode ("could not create child", aParent); + maChildList.set (nIndex, aChild); + } + return aChild; + } + } + + public AccessibleTreeNode getChildNoCreate (int nIndex) + { + synchronized (maChildList) + { + return maChildList.get(nIndex); + } + } + + /** Remove the specified child from the list of children. + */ + public boolean removeChild (int nIndex) + { + try + { + synchronized (maChildList) + { + System.out.println (" removing child at position " + nIndex + ": " + + maChildList.get (nIndex)); + maChildList.remove (nIndex); + } + } + catch (Exception e) + { + return false; + } + return true; + } + + public int indexOf (AccessibleTreeNode aNode) + { + synchronized (maChildList) + { + return maChildList.indexOf (aNode); + } + } + + /** Create a child object for the specified data. This method is called + usually from getChild and put there into the cache. + */ + public abstract AccessibleTreeNode createChild ( + AccessibleTreeNode aParent, int nIndex); + + + // The following methods support editing of children and actions. + // They have default implementations for no actions and read-only. + + + /** get names of supported actions */ + public String[] getActions (AccessibleTreeNode aNode) + { + return new String[] {}; + } + + /** perform action */ + public void performAction (AccessibleTreeNode aNode, int nIndex) + { + } + + /** Update all children. + */ + public void update (AccessibleTreeNode aNode) + { + } +} diff --git a/toolkit/test/accessibility/NodeMap.java b/toolkit/test/accessibility/NodeMap.java new file mode 100644 index 0000000000..045c933713 --- /dev/null +++ b/toolkit/test/accessibility/NodeMap.java @@ -0,0 +1,117 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.XAccessibleContext; + +import java.util.HashMap; + +abstract class NodeMapCallback +{ + public abstract void Apply (AccTreeNode aNode); +} + +/** This map translates from XAccessible objects to our internal + representations. +*/ +class NodeMap +{ + public NodeMap () + { + maXAccessibleToNode = new HashMap<XAccessibleContext, AccessibleTreeNode> (); + } + + /** Clear the whole map. + */ + public void Clear () + { + maXAccessibleToNode.clear(); + } + + /** @return + whether the new node was different from a previous one + respectively was the first one set. + */ + public boolean InsertNode (XAccessibleContext xContext, AccessibleTreeNode aNode) + { + AccessibleTreeNode aPreviousNode = maXAccessibleToNode.put ( + xContext, + aNode); + return aPreviousNode != aNode; + } + + protected void RemoveNode (AccessibleTreeNode aNode) + { + try + { + if ((aNode != null) && (aNode instanceof AccTreeNode)) + { + maXAccessibleToNode.remove (((AccTreeNode)aNode).getContext()); + } + } + catch (Exception e) + { + System.out.println ("caught exception while removing node " + + aNode + " : " + e); + e.printStackTrace(); + } + } + + + public void ForEach (NodeMapCallback aFunctor) + { + Object[] aNodes = maXAccessibleToNode.values().toArray(); + for (int i=0; i<aNodes.length; i++) + { + if (aNodes[i] != null && (aNodes[i] instanceof AccTreeNode)) + { + try + { + aFunctor.Apply ((AccTreeNode)aNodes[i]); + } + catch (Exception e) + { + System.out.println ("caught exception applying functor to " + + i + "th node " + aNodes[i] + " : " + e); + e.printStackTrace(); + } + } + } + } + + AccessibleTreeNode GetNode (XAccessibleContext xContext) + { + return maXAccessibleToNode.get (xContext); + } + + AccessibleTreeNode GetNode (Object aObject) + { + if (aObject instanceof XAccessibleContext) + return GetNode ((XAccessibleContext)aObject); + else + return null; + } + + boolean ValueIsMember (AccessibleTreeNode aNode) + { + return maXAccessibleToNode.containsValue(aNode); + } + + + + private final HashMap<XAccessibleContext, AccessibleTreeNode> maXAccessibleToNode; +} diff --git a/toolkit/test/accessibility/OfficeConnection.java b/toolkit/test/accessibility/OfficeConnection.java new file mode 100644 index 0000000000..357c7c6d18 --- /dev/null +++ b/toolkit/test/accessibility/OfficeConnection.java @@ -0,0 +1,98 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +// base classes +import com.sun.star.uno.UnoRuntime; + +import com.sun.star.bridge.XUnoUrlResolver; +import com.sun.star.lang.XMultiServiceFactory; + + +/** @descr This class establishes a connection to a LibreOffice application. + */ +public class OfficeConnection +{ + public OfficeConnection (int nPortNumber) + { + mnDefaultPort = nPortNumber; + connect (); + } + + /** @descr Return the service manager that represents the connected + LibreOffice application + */ + public XMultiServiceFactory getServiceManager () + { + if ( ! mbInitialized) + connect (); + return maServiceManager; + } + + /** @descr Connect to an already running LibreOffice application. + */ + private void connect () + { + connect (msDefaultHost, mnDefaultPort); + } + + /** @descr Connect to an already running LibreOffice application that has + been started with a command line argument like + "--accept=socket,host=localhost,port=5678;urp;" + */ + private void connect (String hostname, int portnumber) + { + mbInitialized = true; + // Set up connection string. + String sConnectString = "uno:socket,host=" + hostname + ",port=" + portnumber + + ";urp;StarOffice.ServiceManager"; + + + // connect to a running office and get the ServiceManager + try + { + // Create a URL Resolver. + XMultiServiceFactory aLocalServiceManager = + com.sun.star.comp.helper.Bootstrap.createSimpleServiceManager(); + XUnoUrlResolver aURLResolver = UnoRuntime.queryInterface ( + XUnoUrlResolver.class, + aLocalServiceManager.createInstance ("com.sun.star.bridge.UnoUrlResolver") + ); + + maServiceManager = UnoRuntime.queryInterface ( + XMultiServiceFactory.class, + aURLResolver.resolve (sConnectString) + ); + } + + catch (Exception e) + { + MessageArea.println ("Could not connect with " + sConnectString + " : " + e); + MessageArea.println ("Please start LibreOffice with " + + "\"--accept=socket,host=localhost,port=5678;urp;\""); + } + } + + private final int mnDefaultPort; + private static final String msDefaultHost = "localhost"; + private XMultiServiceFactory maServiceManager = null; + + /** A value of true just indicates that it has been tried to establish a connection, + not that has been successful. + */ + private boolean mbInitialized = false; +} diff --git a/toolkit/test/accessibility/Options.java b/toolkit/test/accessibility/Options.java new file mode 100644 index 0000000000..6cf6e48f2a --- /dev/null +++ b/toolkit/test/accessibility/Options.java @@ -0,0 +1,122 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +/** Load from and save options into a file. +*/ +class Options + extends Properties +{ + public static Options Instance () + { + if (saOptions == null) + saOptions = new Options (); + return saOptions; + } + + public static void SetBoolean (String sName, boolean bValue) + { + Instance().setProperty (sName, Boolean.toString(bValue)); + } + + public static boolean GetBoolean (String sName) + { + return Boolean.getBoolean(Instance().getProperty (sName)); + } + + public static void SetInteger (String sName, int nValue) + { + Instance().setProperty (sName, Integer.toString(nValue)); + } + + public static int GetInteger (String sName, int nDefault) + { + String sValue = Instance().getProperty (sName); + if (sValue == null) + return nDefault; + else + return Integer.parseInt (sValue); + } + + public void Load (String sBaseName) + { + FileInputStream fis = null; + try + { + fis = new FileInputStream (ProvideFile(sBaseName)); + load (fis); + } + catch (IOException e) + { + // Ignore a non-existing options file. + } + finally + { + try + { + if (fis != null) + fis.close(); + } + catch (IOException ex) + { + } + } + } + + public void Save (String sBaseName) + { + FileOutputStream fos = null; + try + { + fos = new FileOutputStream (ProvideFile(sBaseName)); + store (fos, null); + } + catch (IOException e) + { + } + finally + { + try + { + if (fos != null) + fos.close(); + } + catch (IOException ex) + { + } + } + } + + private Options () + { + } + + private File ProvideFile (String sBaseName) + { + return new File ( + System.getProperty ("user.home"), + sBaseName); + } + + private static Options saOptions = null; +} diff --git a/toolkit/test/accessibility/QueuedListener.java b/toolkit/test/accessibility/QueuedListener.java new file mode 100644 index 0000000000..a34b335497 --- /dev/null +++ b/toolkit/test/accessibility/QueuedListener.java @@ -0,0 +1,70 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.*; +import com.sun.star.lang.EventObject; +import com.sun.star.uno.*; + +class QueuedListener + implements XAccessibleEventListener +{ + public QueuedListener (EventListener aListener) + { + maListener = aListener; + } + + + public void disposing( final EventObject aEvent) + { + XAccessibleContext xContext = UnoRuntime.queryInterface( + XAccessibleContext.class, aEvent.Source); + if (xContext == null) + { + XAccessible xAccessible = UnoRuntime.queryInterface( + XAccessible.class, aEvent.Source); + if (xAccessible != null) + xContext = xAccessible.getAccessibleContext(); + } + final XAccessibleContext xSource = xContext; + EventQueue.Instance().addDisposingEvent (new Runnable() + { + public void run() + { + if (QueuedListener.this.maListener != null) + QueuedListener.this.maListener.disposing (xSource); + } + } + ); + } + + public void notifyEvent( final AccessibleEventObject aEvent ) + { + EventQueue.Instance().addEvent (new Runnable() + { + public void run() + { + QueuedListener.this.maListener.notifyEvent( aEvent ); + } + } + ); + } + + private final EventListener maListener; +} + + diff --git a/toolkit/test/accessibility/QueuedTopWindowListener.java b/toolkit/test/accessibility/QueuedTopWindowListener.java new file mode 100644 index 0000000000..2ac5c7849d --- /dev/null +++ b/toolkit/test/accessibility/QueuedTopWindowListener.java @@ -0,0 +1,97 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.awt.XTopWindowListener; +import com.sun.star.lang.EventObject; + +class QueuedTopWindowListener + implements XTopWindowListener +{ + public QueuedTopWindowListener (TopWindowListener aListener) + { + maListener = aListener; + } + + public void windowOpened (final com.sun.star.lang.EventObject aEvent) throws RuntimeException + { + EventQueue.Instance().addEvent (new Runnable() + { + public void run() + { + QueuedTopWindowListener.this.maListener.windowOpened (aEvent); + } + } + ); + } + + public void windowClosing (final com.sun.star.lang.EventObject aEvent) throws RuntimeException + { + // Ignored. + } + + public void windowClosed (final com.sun.star.lang.EventObject aEvent) throws RuntimeException + { + EventQueue.Instance().addEvent (new Runnable() + { + public void run() + { + QueuedTopWindowListener.this.maListener.windowClosed (aEvent); + } + } + ); + } + + public void windowMinimized (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + System.out.println ("QueuedTopWindowListener: Top window minimized: " + aEvent); + } + + public void windowNormalized (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + System.out.println ("QueuedTopWindowListener: Top window normalized: " + aEvent); + } + + public void windowActivated (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + System.out.println ("QueuedTopWindowListener: Top window activated: " + aEvent); + } + + public void windowDeactivated (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + System.out.println ("QueuedTopWindowListener: Top window deactivated: " + aEvent); + } + + public void disposing( final EventObject aEvent) + { + EventQueue.Instance().addDisposingEvent (new Runnable() + { + public void run() + { + if (QueuedTopWindowListener.this.maListener != null) + QueuedTopWindowListener.this.maListener.disposing (aEvent); + } + } + ); + } + + private final TopWindowListener maListener; +} diff --git a/toolkit/test/accessibility/SelectionDialog.java b/toolkit/test/accessibility/SelectionDialog.java new file mode 100644 index 0000000000..2f0c6d5434 --- /dev/null +++ b/toolkit/test/accessibility/SelectionDialog.java @@ -0,0 +1,203 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Vector; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleSelection; +import com.sun.star.lang.IndexOutOfBoundsException; + +/** + * Display a dialog with a list-box of children and select/deselect buttons + */ +class SelectionDialog extends JDialog + implements ActionListener +{ + public SelectionDialog (AccTreeNode aNode) + { + super (AccessibilityWorkBench.Instance()); + + maNode = aNode; + + Layout(); + } + + /** build dialog */ + private void Layout () + { + setTitle( "Select" ); + + // vertical stacking of the elements + Container aContent = getContentPane(); + + // label with explanation + aContent.add( new JLabel( "Select/Deselect child elements" ), + BorderLayout.NORTH ); + + // the JListBox + maChildrenSelector = new JList (GetChildrenList()); + maChildrenSelector.setPreferredSize (new Dimension (500,300)); + aContent.add (maChildrenSelector, BorderLayout.CENTER); + maChildrenSelector.setSelectionMode (ListSelectionModel.SINGLE_SELECTION); + + JPanel aButtons = new JPanel(); + aButtons.setLayout( new FlowLayout() ); + + JButton aButton; + + aButton = new JButton( "Select" ); + aButton.setActionCommand( "Select" ); + aButton.addActionListener( this ); + aButtons.add( aButton ); + + aButton = new JButton( "Deselect" ); + aButton.setActionCommand( "Deselect" ); + aButton.addActionListener( this ); + aButtons.add( aButton ); + + aButton = new JButton( "Select all" ); + aButton.setActionCommand( "Select all" ); + aButton.addActionListener( this ); + aButtons.add( aButton ); + + aButton = new JButton( "Clear Selection" ); + aButton.setActionCommand( "Clear Selection" ); + aButton.addActionListener( this ); + aButtons.add( aButton ); + + aButton = new JButton( "Close" ); + aButton.setActionCommand( "Close" ); + aButton.addActionListener( this ); + aButtons.add( aButton ); + + // add Panel with buttons + aContent.add( aButtons, BorderLayout.SOUTH ); + + setSize( getPreferredSize() ); + } + + /** Get a list of all children + */ + private Vector<String> GetChildrenList () + { + mxSelection = maNode.getSelection(); + + XAccessibleContext xContext = maNode.getContext(); + int nCount = xContext.getAccessibleChildCount(); + Vector<String> aChildVector = new Vector<String>(); + for(int i = 0; i < nCount; i++) + { + try + { + XAccessible xChild = xContext.getAccessibleChild(i); + XAccessibleContext xChildContext = xChild.getAccessibleContext(); + aChildVector.add( i + " " + xChildContext.getAccessibleName()); + } + catch( IndexOutOfBoundsException e ) + { + aChildVector.add( "ERROR: IndexOutOfBoundsException" ); + } + } + return aChildVector; + } + + + private void close () + { + setVisible(false); + dispose(); + } + + private void select() + { + try + { + mxSelection.selectAccessibleChild (maChildrenSelector.getSelectedIndex()); + } + catch( IndexOutOfBoundsException e ) + { + JOptionPane.showMessageDialog( AccessibilityWorkBench.Instance(), + "Can't select: IndexOutofBounds", + "Error in selectAccessibleChild", + JOptionPane.ERROR_MESSAGE); + } + } + + private void deselect() + { + try + { + mxSelection.deselectAccessibleChild( + maChildrenSelector.getSelectedIndex()); + } + catch( IndexOutOfBoundsException e ) + { + JOptionPane.showMessageDialog( AccessibilityWorkBench.Instance(), + "Can't deselect: IndexOutofBounds", + "Error in deselectAccessibleChild", + JOptionPane.ERROR_MESSAGE); + } + } + + private void selectAll() + { + mxSelection.selectAllAccessibleChildren(); + } + + private void clearSelection() + { + mxSelection.clearAccessibleSelection(); + } + + + + public void actionPerformed(ActionEvent e) + { + String sCommand = e.getActionCommand(); + + if( "Close".equals( sCommand ) ) + close(); + else if ( "Select".equals( sCommand ) ) + select(); + else if ( "Deselect".equals( sCommand ) ) + deselect(); + else if ( "Clear Selection".equals( sCommand ) ) + clearSelection(); + else if ( "Select all".equals( sCommand ) ) + selectAll(); + } + + private JList maChildrenSelector; + private XAccessibleSelection mxSelection; + private final AccTreeNode maNode; +} diff --git a/toolkit/test/accessibility/SimpleOffice.java b/toolkit/test/accessibility/SimpleOffice.java new file mode 100644 index 0000000000..ecb16a6e38 --- /dev/null +++ b/toolkit/test/accessibility/SimpleOffice.java @@ -0,0 +1,139 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.frame.XDesktop; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.awt.XExtendedToolkit; + + +/** This class tries to simplify some tasks like loading a document or + getting various objects. +*/ +public class SimpleOffice +{ + private XDesktop mxDesktop = null; + private OfficeConnection aConnection; + private int mnPortNumber; + + public SimpleOffice (int nPortNumber) + { + mnPortNumber = nPortNumber; + connect (); + getDesktop (); + } + + public void connect () + { + aConnection = new OfficeConnection (mnPortNumber); + mxDesktop = null; + getDesktop (); + } + + + + + + + + + + + + public XDesktop getDesktop () + { + if (mxDesktop != null) + return mxDesktop; + try + { + // Get the factory of the connected office. + XMultiServiceFactory xMSF = aConnection.getServiceManager (); + if (xMSF == null) + { + MessageArea.println ("can't connect to office"); + return null; + } + else + MessageArea.println ("Connected successfully."); + + // Create a new desktop. + mxDesktop = UnoRuntime.queryInterface( + XDesktop.class, + xMSF.createInstance ("com.sun.star.frame.Desktop") + ); + } + catch (Exception e) + { + MessageArea.println ("caught exception while creating desktop: " + + e); + } + + return mxDesktop; + } + + + /** Return a reference to the extended toolkit which is a broadcaster of + top window, key, and focus events. + */ + public XExtendedToolkit getExtendedToolkit () + { + XExtendedToolkit xToolkit = null; + try + { + // Get the factory of the connected office. + XMultiServiceFactory xMSF = aConnection.getServiceManager (); + if (xMSF != null) + { + xToolkit = UnoRuntime.queryInterface( + XExtendedToolkit.class, + xMSF.createInstance ("stardiv.Toolkit.VCLXToolkit") + ); + } + } + catch (Exception e) + { + MessageArea.println ("caught exception while creating extended toolkit: " + e); + } + + return xToolkit; + } + + + + public XAccessible getAccessibleObject (XInterface xObject) + { + XAccessible xAccessible = null; + try + { + xAccessible = UnoRuntime.queryInterface( + XAccessible.class, xObject); + } + catch (Exception e) + { + MessageArea.println ( + "caught exception while getting accessible object" + e); + e.printStackTrace(); + } + return xAccessible; + } + + + +} diff --git a/toolkit/test/accessibility/StringNode.java b/toolkit/test/accessibility/StringNode.java new file mode 100644 index 0000000000..f6660e44a9 --- /dev/null +++ b/toolkit/test/accessibility/StringNode.java @@ -0,0 +1,36 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + + +/** + Base class for all tree nodes. + */ +class StringNode + extends AccessibleTreeNode +{ + public StringNode (String aDisplayObject, AccessibleTreeNode aParent) + { + super (aDisplayObject, aParent); + } + + @Override + public int indexOf(AccessibleTreeNode aNode) { + return -1; + } +} diff --git a/toolkit/test/accessibility/TableEventHandler.java b/toolkit/test/accessibility/TableEventHandler.java new file mode 100644 index 0000000000..09a3adbbc3 --- /dev/null +++ b/toolkit/test/accessibility/TableEventHandler.java @@ -0,0 +1,58 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.accessibility.AccessibleTableModelChange; +import java.io.PrintStream; + +class TableEventHandler + extends EventHandler +{ + public TableEventHandler (AccessibleEventObject aEvent, AccessibilityTreeModel aTreeModel) + { + super (aEvent, aTreeModel); + } + + @Override + public void PrintOldAndNew (PrintStream out) + { + switch (mnEventId) + { + case AccessibleEventId.TABLE_MODEL_CHANGED: + AccessibleTableModelChange aModelChange = + (AccessibleTableModelChange)maEvent.NewValue; + out.println( "Range: StartRow " + aModelChange.FirstRow + + " StartColumn " + aModelChange.FirstColumn + + " EndRow " + aModelChange.LastRow + + " EndColumn " + aModelChange.LastColumn + + " Id " + aModelChange.Type); + break; + default: + super.PrintOldAndNew (out); + } + } + + @Override + public void Process () + { + maTreeModel.updateNode (mxEventSource, AccessibleTableHandler.class); + } + + +} diff --git a/toolkit/test/accessibility/TopWindowListener.java b/toolkit/test/accessibility/TopWindowListener.java new file mode 100644 index 0000000000..a35b51a5ff --- /dev/null +++ b/toolkit/test/accessibility/TopWindowListener.java @@ -0,0 +1,175 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.awt.XWindow; +import com.sun.star.awt.XExtendedToolkit; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.uno.UnoRuntime; + +/** Listen for top window events and create or delete children of the tree + model accordingly. +*/ +class TopWindowListener +{ + TopWindowListener (AccessibilityTreeModel aModel, SimpleOffice aOffice) + { + maModel = aModel; + maOffice = aOffice; + } + + + + + /** Use this function to initially fill the accessibility object tree + view with nodes for top level windows. + */ + public void Initialize () + { + XExtendedToolkit xToolkit = maOffice.getExtendedToolkit(); + if (xToolkit != null) + { + maModel.lock (); + int nTopWindowCount = xToolkit.getTopWindowCount(); + MessageArea.println ("There are " + nTopWindowCount + " top windows."); + for (int i=0; i<nTopWindowCount; i++) + { + try + { + XAccessible xAccessible = maOffice.getAccessibleObject( + xToolkit.getTopWindow (i)); + // Uncomment the following line to get the real root of + // the accessible tree that xAccessible belongs to. + // xAccessible = maOffice.getAccessibleRoot(xAccessible); + AddTopLevelNode (xAccessible); + } + catch (Exception e) + { + System.out.println ("caught exception: " + e); + e.printStackTrace(); + } + } + maModel.unlock ((AccessibleTreeNode)maModel.getRoot()); + } + } + + + + /** Add a new top level node which, to be exact, will be placed on the + second layer of the tree. + @param xNewTopLevelObject + The accessible object of the new top level window. + */ + private void AddTopLevelNode (XAccessible xNewTopLevelObject) + { + System.out.println ("adding top level window"); + if (xNewTopLevelObject != null) + { + XAccessibleContext xContext = xNewTopLevelObject.getAccessibleContext(); + if (xContext == null) + System.out.println ("top level window not accessible"); + else + { + Object aRootObject = maModel.getRoot(); + if (aRootObject instanceof VectorNode) + { + VectorNode aRoot = (VectorNode) aRootObject; + AccessibleTreeNode aNode = + NodeFactory.Instance().createDefaultNode (xNewTopLevelObject, aRoot); + aRoot.addChild (aNode); + maModel.fireTreeNodesInserted (maModel.createEvent (aRoot, aNode)); + } + } + } + } + + /** Remove an existing top level node from the tree. + @param xNewTopLevelObject + The accessible object to remove. + */ + private void RemoveTopLevelNode (XAccessible xTopLevelObject) + { + Object aObject = maModel.getRoot(); + if (aObject instanceof VectorNode && xTopLevelObject != null) + { + System.out.println ("removing node " + xTopLevelObject); + maModel.removeNode (xTopLevelObject.getAccessibleContext()); + } + } + + + + + + // XTopWindowListener + public void windowOpened (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + if (maModel != null) + { + XWindow xWindow = UnoRuntime.queryInterface( + XWindow.class, aEvent.Source); + if (xWindow == null) + System.out.println ("event source is no XWindow"); + else + { + XAccessible xAccessible = maOffice.getAccessibleObject(xWindow); + if (xAccessible == null) + System.out.println ("event source is no XAccessible"); + else + AddTopLevelNode (xAccessible); + } + } + } + + + + + public void windowClosed (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + if (maModel != null) + { + XWindow xWindow = UnoRuntime.queryInterface( + XWindow.class, aEvent.Source); + if (xWindow == null) + System.out.println ("event source is no XWindow"); + else + { + XAccessible xAccessible = maOffice.getAccessibleObject(xWindow); + if (xAccessible == null) + System.out.println ("event source is no XAccessible"); + else + RemoveTopLevelNode (xAccessible); + } + } + } + + public void disposing (final com.sun.star.lang.EventObject aEvent) + { + System.out.println ("Top window disposed: " + aEvent); + } + + + + + private final AccessibilityTreeModel + maModel; + private final SimpleOffice + maOffice; +} diff --git a/toolkit/test/accessibility/VectorNode.java b/toolkit/test/accessibility/VectorNode.java new file mode 100644 index 0000000000..c1ffcd6504 --- /dev/null +++ b/toolkit/test/accessibility/VectorNode.java @@ -0,0 +1,74 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.util.ArrayList; + +import com.sun.star.lang.IndexOutOfBoundsException; + +/** The VectorNode class is a simple container whose list of children is + managed entirely by its owner. +*/ +class VectorNode + extends StringNode +{ + private final ArrayList<AccessibleTreeNode> maChildren; + + public VectorNode (String sDisplayObject, AccessibleTreeNode aParent) + { + super (sDisplayObject, aParent); + + maChildren = new ArrayList<AccessibleTreeNode> (); + } + + public void addChild (AccessibleTreeNode aChild) + { + maChildren.add (aChild); + } + + @Override + public int getChildCount () + { + return maChildren.size(); + } + + @Override + public AccessibleTreeNode getChild (int nIndex) + throws IndexOutOfBoundsException + { + return maChildren.get(nIndex); + } + + @Override + public boolean removeChild (int nIndex) + throws IndexOutOfBoundsException + { + return maChildren.remove (nIndex) != null; + } + + @Override + public int indexOf (AccessibleTreeNode aNode) + { + return maChildren.indexOf (aNode); + } + + @Override + public boolean isLeaf() + { + return maChildren.isEmpty(); + } +} diff --git a/toolkit/test/accessibility/about.html b/toolkit/test/accessibility/about.html new file mode 100644 index 0000000000..ff24ed0586 --- /dev/null +++ b/toolkit/test/accessibility/about.html @@ -0,0 +1,8 @@ +<html> +<body> +<center><h1>About AWB</h1> +<p>AWB, the <em>A</em>ccessibility <em>W</em>ork <em>B</em>ench.</p> +<p>Version 1.7</p> +</center> +</body> +</html>
\ No newline at end of file diff --git a/toolkit/test/accessibility/help.html b/toolkit/test/accessibility/help.html new file mode 100644 index 0000000000..681fcead46 --- /dev/null +++ b/toolkit/test/accessibility/help.html @@ -0,0 +1,91 @@ +<html><body bgcolor="#fffaf0"> +<h1>Help for the AWB v1.7</h1> + +<p>The AWB, or <em>A</em>ccessibility <em>W</em>ork <em>B</em>ench, is a tool +for testing the implementation UNO Accessibility API.</p> + +<p>The main window is roughly divided into three areas: +<ul> +<li>The <a href="#treeview">tree view</a> on the left shows a part of the +accessibility tree of one or more StarOffice/OpenOffice applications.</li> +<li>The <a href="#graphicalview">graphical view</a> on the right side shows +a graphical representation of the accessibility objects in the tree. To +make objects missing in the graphical view visible expand the corresponding +nodes in the tree view.</li> + +<li>The text window at the bottom logs important messages.</li> +</ul> +</p> + +<h2><a name="treeview">Tree View</a></h2> +<p>The tree view has a top-level node for every open + document window of StarOffice/OpenOffice. Expand those nodes to make them + visible in the <a href="#graphicalview">graphical view</a>.</p> + +<h3>Nodes</h3> +<p>The nodes in the tree view belong to different classes, some of which + have children others do not: +<ul> +<li><b>Accessible Object</b><br> + The node represents an accessible object and has corresponding shape in + the graphical view. Only this kind of node gets highlighted by clicking + on those shapes.</li> +<li><b>Simple Property</b><br> + These leaves represent simple properties of their parent nodes. Examples + are the position, size, and color of an accessible object.</li> +<li><b>Complex Properties</b><br> + These nodes have children that are not accessible objects. Examples are + the lists of interfaces or services supported by an accessible object.</li> +</ul> +</p> + +<h3>Actions</h3> +<p>The tree view supports the following actions: +<ul><li>Left double click expands the node under the mouse pointer.</li> +<li>Right click shows a context menu. Entries, when supported, are: +<ul> +<li><b>Expand Shapes</b><br> +Expands all nodes in the sub-tree that lie on a path from the root to a shape.</li> +<li><b>Expand Subtree</b><br> +Expands all nodes in the sub-tree.</li> +<li><b>Select...</b><br> +Show a dialog that gives access to the XAccessibleSelection interface.</li> +<li><b>select...</b>, <b>copy...</b>, <b>cut...</b>, <b>paste...</b>, + <b>edit...</b>, <b>format...</b><br> +Show dialogs that give access to the XAccessibleText and +XAccessibleEditableText interfaces. +</ul></li> +</ul> +</p> + + + +<h2><a name="graphicalview">Graphical View</a></h2> +<p>The graphical view shows several properties of accessibility objects: +<ul> +<li>The bounding box is represented as a rectangle.</li> +<li>The background color of the accessible object is taken to draw the + bounding box rectangle.</li> +<li>The foreground color is used to fill the bounding box.</li> +<li>The accessible name is shown when the menu checkbox Options->Show Name + is check.</li> +<li>The accessible description is shown when the menu checkbox Options->Show + Descriptions is checked.</li> +<li>If the XAccessibleText interface is supported and the menu checkbox + Options->Show Text is checked then the text of the accessible object is + shown.</li> +</ul> +</p> + +<h3>Actions</h3> +<p>The graphical view supports three the following actions: +<ul> +<li>Left click (no modifiers) highlights the object under the mouse as well + as the corresponding node in the tree view.</li> +<li>Left click with Control modifier expands the object under the + mouse.</li> +<li>Mouse motion with Shift modifier highlights the object under the mouse + as well as the corresponding node in the tree view.</li> +</ul> + +</body></html> diff --git a/toolkit/test/accessibility/jawb.mf b/toolkit/test/accessibility/jawb.mf new file mode 100644 index 0000000000..88aa2cc765 --- /dev/null +++ b/toolkit/test/accessibility/jawb.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: AccessibilityWorkBench +Class-Path: classes.jar libreoffice.jar java_uno.jar xt.jar xml-apis.jar diff --git a/toolkit/test/accessibility/news.html b/toolkit/test/accessibility/news.html new file mode 100644 index 0000000000..7b5336ea45 --- /dev/null +++ b/toolkit/test/accessibility/news.html @@ -0,0 +1,36 @@ +<html><body bgcolor="#fffaf0"> +<h1>News for AWB v1.7.2</h1> +<p>Adaptation to recent UAA changes: +<ul> +<li>Introduction of +com.sun.star.accessibility.TextSegment structure that is returned by some +functions of the XAccessibleText interface.</li> +<li>Removal of the COLLAPSED state.</li> +<li>Renaming of XAccessibleSelectioni::selectAllAccessible to +selectAllAccessibleChildren.</li> +</ul> + +<h1>News for AWB v1.7</h1> + +<ul> + +<li>This help window.</li> + +<li>Integrated relocation of UAA files from drafts to final.</li> +<li>Control left click in the graphical view expands the object that has + been clicked at.</li> + +<li>Introduction of split panes for easily changing the size of the three +main widgets.</li> + +<li>The graphical view visualizes the screen size.</li> + +<li>Removed the "Load" button.</li> +<li>Removed the status line.</li> +<li>The tree view shows top level nodes only for document windows.</li> +<li>New "views" menu that allows you to select the zoom scale of the + graphical view.</li> + +</ul> + +</body></html>
\ No newline at end of file diff --git a/toolkit/test/accessibility/ov/ContextView.java b/toolkit/test/accessibility/ov/ContextView.java new file mode 100644 index 0000000000..0dc97a67a7 --- /dev/null +++ b/toolkit/test/accessibility/ov/ContextView.java @@ -0,0 +1,140 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package ov; + +import java.awt.Color; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; + +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import javax.swing.JLabel; +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.XAccessibleContext; + +import tools.NameProvider; + +public class ContextView + extends ListeningObjectView + implements ActionListener +{ + public static ObjectView Create ( + ObjectViewContainer aContainer, + XAccessibleContext xContext) + { + System.out.println ("ContextView.CreateView"); + if (xContext != null) + return new ContextView (aContainer); + else + return null; + } + + private ContextView (ObjectViewContainer aContainer) + { + super (aContainer); + JLabel aNameLabel = new JLabel ("Name: "); + maName = new JLabel (""); + JLabel aDescriptionLabel = new JLabel ("Description: "); + maDescription = new JLabel (""); + JLabel maRoleLabel = new JLabel ("Role: "); + maRole = new JLabel (""); + + // Make the background of name and description white and opaque so + // that leading and trailing spaces become visible. + maName.setOpaque (true); + maName.setBackground (Color.WHITE); + maDescription.setOpaque (true); + maDescription.setBackground (Color.WHITE); + maRole.setOpaque (true); + maRole.setBackground (Color.WHITE); + + GridBagLayout aLayout = new GridBagLayout(); + setLayout (aLayout); + GridBagConstraints constraints = new GridBagConstraints (); + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.weightx = 0; + constraints.weighty = 1; + constraints.anchor = GridBagConstraints.WEST; + constraints.fill = GridBagConstraints.NONE; + add (aNameLabel, constraints); + constraints.gridy = 1; + add (aDescriptionLabel, constraints); + constraints.gridy = 2; + add (maRoleLabel, constraints); + constraints.gridy = 0; + constraints.gridx = 1; + constraints.weightx = 2; + add (maName, constraints); + constraints.gridy = 1; + add (maDescription, constraints); + constraints.gridy = 2; + add (maRole, constraints); + } + + @Override + public void Update () + { + if (mxContext == null) + { + maName.setText ("<null object>"); + maDescription.setText ("<null object>"); + maRole.setText ("<null object>"); + } + else + { + maName.setText (mxContext.getAccessibleName()); + maDescription.setText (mxContext.getAccessibleDescription()); + maRole.setText (NameProvider.getRoleName (mxContext.getAccessibleRole())); + } + } + + @Override + public String GetTitle () + { + return "Context"; + } + + /** Listen for changes regarding displayed values. + */ + @Override + public void notifyEvent (AccessibleEventObject aEvent) + { + switch (aEvent.EventId) + { + case AccessibleEventId.NAME_CHANGED : + case AccessibleEventId.DESCRIPTION_CHANGED : + Update (); + } + } + + public void actionPerformed (ActionEvent aEvent) + { + } + + + private final JLabel + maName, + maDescription, + maRole; +} diff --git a/toolkit/test/accessibility/ov/FocusView.java b/toolkit/test/accessibility/ov/FocusView.java new file mode 100644 index 0000000000..0428403f80 --- /dev/null +++ b/toolkit/test/accessibility/ov/FocusView.java @@ -0,0 +1,140 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package ov; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import javax.swing.JButton; +import javax.swing.JLabel; + +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.AccessibleStateType; +import com.sun.star.accessibility.XAccessibleComponent; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.uno.UnoRuntime; + +public class FocusView + extends ListeningObjectView + implements ActionListener +{ + /** Create a FocusView when the given object supports the + XAccessibleComponent interface. + */ + public static ObjectView Create ( + ObjectViewContainer aContainer, + XAccessibleContext xContext) + { + XAccessibleComponent xComponent = UnoRuntime.queryInterface( + XAccessibleComponent.class, xContext); + if (xComponent != null) + return new FocusView (aContainer); + else + return null; + } + + private FocusView (ObjectViewContainer aContainer) + { + super (aContainer); + + setLayout (new GridBagLayout()); + GridBagConstraints aConstraints = new GridBagConstraints (); + + maFocused = new JLabel (); + aConstraints.gridy = 0; + aConstraints.weightx = 1; + aConstraints.fill = GridBagConstraints.HORIZONTAL; + add (maFocused, aConstraints); + + maGrabFocus = new JButton ("grabFocus"); + aConstraints.gridy = 1; + aConstraints.fill = GridBagConstraints.NONE; + aConstraints.anchor = GridBagConstraints.WEST; + add (maGrabFocus, aConstraints); + + maGrabFocus.addActionListener (this); + } + + /** Additionally to the context store a reference to the + XAccessibleComponent interface. + */ + @Override + public void SetObject (XAccessibleContext xObject) + { + mxComponent = UnoRuntime.queryInterface( + XAccessibleComponent.class, xObject); + super.SetObject (xObject); + } + + @Override + synchronized public void Destroy () + { + super.Destroy(); + maGrabFocus.removeActionListener (this); + } + + @Override + synchronized public void Update () + { + if (mxContext == null) + { + maFocused.setText ("<null object>"); + maGrabFocus.setEnabled (false); + } + else + { + long aStateSet = mxContext.getAccessibleStateSet(); + if (aStateSet & AccessibleStateType.FOCUSED) + maFocused.setText ("focused"); + else + maFocused.setText ("not focused"); + if (maGrabFocus != null) + maGrabFocus.setEnabled (true); + } + } + + @Override + public String GetTitle () + { + return "Focus"; + } + + synchronized public void actionPerformed (ActionEvent aEvent) + { + if (aEvent.getActionCommand().equals("grabFocus")) + { + mxComponent.grabFocus(); + } + } + + @Override + public void notifyEvent (AccessibleEventObject aEvent) + { + System.out.println (aEvent); + if (aEvent.EventId == AccessibleEventId.STATE_CHANGED) + Update (); + } + + private final JLabel maFocused; + private final JButton maGrabFocus; + private XAccessibleComponent mxComponent; +} diff --git a/toolkit/test/accessibility/ov/ListeningObjectView.java b/toolkit/test/accessibility/ov/ListeningObjectView.java new file mode 100644 index 0000000000..63b5f8ff7a --- /dev/null +++ b/toolkit/test/accessibility/ov/ListeningObjectView.java @@ -0,0 +1,80 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package ov; + +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEventBroadcaster; +import com.sun.star.accessibility.XAccessibleEventListener; +import com.sun.star.lang.EventObject; +import com.sun.star.uno.UnoRuntime; + +/** Base class for object views that registers as accessibility event + listener. +*/ +abstract class ListeningObjectView + extends ObjectView + implements XAccessibleEventListener +{ + public ListeningObjectView (ObjectViewContainer aContainer) + { + super (aContainer); + } + + /** Add this object as event listener at the broadcasting + accessible object. + */ + @Override + public void SetObject (XAccessibleContext xContext) + { + super.SetObject (xContext); + XAccessibleEventBroadcaster xBroadcaster = + UnoRuntime.queryInterface( + XAccessibleEventBroadcaster.class, xContext); + if (xBroadcaster != null) + xBroadcaster.addAccessibleEventListener (this); + } + + + /** Remove this object as event listener from the broadcasting + accessible object. + */ + @Override + public void Destroy () + { + super.Destroy (); + XAccessibleEventBroadcaster xBroadcaster = + UnoRuntime.queryInterface( + XAccessibleEventBroadcaster.class, mxContext); + if (xBroadcaster != null) + xBroadcaster.removeAccessibleEventListener (this); + } + + /** Derived classes have to implement this method to handle incoming + events. + */ + abstract public void notifyEvent (AccessibleEventObject aEvent); + + /** The disposing event is ignored per default. If a derived class is + interested it can overwrite this method. + */ + public void disposing (EventObject aEvent) + { + } +} diff --git a/toolkit/test/accessibility/ov/ObjectView.java b/toolkit/test/accessibility/ov/ObjectView.java new file mode 100644 index 0000000000..7e1570b62b --- /dev/null +++ b/toolkit/test/accessibility/ov/ObjectView.java @@ -0,0 +1,80 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package ov; + +import javax.swing.JPanel; + +import com.sun.star.accessibility.XAccessibleContext; + +/** This is the base class for all object views that can be placed inside an + object view container. + + <p>When provided with a new accessible object the container will call + the Create method to create a new instance when certain conditions are + met. It then calls SetObject to pass the object to the instance. + Finally it calls Update.</p> + + <p>The SetObject and Update methods may be called for a new object + without calling Create first. In this way an existing instance is + recycled.</p> +*/ +abstract public class ObjectView + extends JPanel +{ + public ObjectView (ObjectViewContainer aContainer) + { + mxContext = null; + } + + /** Call this when you want the object to be destroyed. Release all + resources when called. + */ + public void Destroy () + { + } + + /** Tell the view to display information for a new accessible object. + @param xContext + The given object may be null. A typical behaviour in this case + would be to display a blank area. But is also possible to show + information about the last object. + */ + public void SetObject (XAccessibleContext xContext) + { + mxContext = xContext; + Update (); + } + + + /** This is a request of a repaint with the current state of the current + object. The current object may or may not be the same as the one + when Update() was called the last time. + */ + public void Update () + { + } + + + /** Return a string that is used as a title of an enclosing frame. + */ + abstract public String GetTitle (); + + /// Reference to the current object to display information about. + protected XAccessibleContext mxContext; +} diff --git a/toolkit/test/accessibility/ov/ObjectViewContainer.java b/toolkit/test/accessibility/ov/ObjectViewContainer.java new file mode 100644 index 0000000000..c7e647d673 --- /dev/null +++ b/toolkit/test/accessibility/ov/ObjectViewContainer.java @@ -0,0 +1,152 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package ov; + +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.util.ArrayList; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.border.BevelBorder; +import javax.swing.border.Border; + +import com.sun.star.accessibility.XAccessibleContext; + + +public class ObjectViewContainer + extends JPanel +{ + private static interface IViewFactory { + ObjectView Create ( + ObjectViewContainer aContainer, + XAccessibleContext xContext); + } + + public ObjectViewContainer () + { + maViewTemplates = new ArrayList<IViewFactory>(); + maViewBorder = BorderFactory.createBevelBorder (BevelBorder.RAISED); + setLayout (new GridBagLayout ()); + + maViewTemplates.add(new IViewFactory() { + public ObjectView Create(ObjectViewContainer aContainer, + XAccessibleContext xContext) { + return ContextView.Create(aContainer, xContext); + } + }); + maViewTemplates.add(new IViewFactory() { + public ObjectView Create(ObjectViewContainer aContainer, + XAccessibleContext xContext) { + return FocusView.Create(aContainer, xContext); + } + }); + maViewTemplates.add(new IViewFactory() { + public ObjectView Create(ObjectViewContainer aContainer, + XAccessibleContext xContext) { + return TextView.Create(aContainer, xContext); + } + }); + } + + /** Remove all existing views and create new ones according to the + interfaces supported by the given object. + */ + public void SetObject (XAccessibleContext xContext) + { + // Call Destroy at all views to give them a chance to release their + // resources. + int n = getComponentCount(); + for (int i=0; i<n; i++) + ((ObjectView)getComponent(i)).Destroy(); + // Remove existing views. + removeAll (); + + // Add new views. + for (int i=0; i<maViewTemplates.size(); i++) + { + IViewFactory aViewFactory = maViewTemplates.get(i); + ObjectView aView = aViewFactory.Create(this, xContext); + Add (aView); + } + + UpdateLayoutManager (); + + // Now set the object at all views. + n = getComponentCount(); + for (int i=0; i<n; i++) + ((ObjectView)getComponent(i)).SetObject (xContext); + + setPreferredSize (getLayout().preferredLayoutSize (this)); + } + + + /** Add an object view and place it below all previously added views. + @param aView + This argument may be null. In this case nothing happens. + */ + private void Add (ObjectView aView) + { + if (aView != null) + { + GridBagConstraints constraints = new GridBagConstraints (); + constraints.gridx = 0; + constraints.gridy = getComponentCount(); + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.weightx = 1; + constraints.weighty = 0; + constraints.ipadx = 2; + constraints.ipady = 5; + constraints.insets = new Insets (5,5,5,5); + constraints.anchor = GridBagConstraints.NORTH; + constraints.fill = GridBagConstraints.HORIZONTAL; + + aView.setBorder ( + BorderFactory.createTitledBorder ( + maViewBorder, aView.GetTitle())); + + add (aView, constraints); + } + } + + /** Update the layout manager by setting the vertical weight of the + bottom entry to 1 and so make it stretch to over the available + space. + + */ + private void UpdateLayoutManager () + { + // Adapt the layout manager. + if (getComponentCount() > 0) + { + Component aComponent = getComponent (getComponentCount()-1); + GridBagLayout aLayout = (GridBagLayout)getLayout(); + GridBagConstraints aConstraints = aLayout.getConstraints (aComponent); + aConstraints.weighty = 1; + aLayout.setConstraints (aComponent, aConstraints); + } + } + + private final Border maViewBorder; + /// List of view templates which are instantiated when new object is set. + private final ArrayList<IViewFactory> maViewTemplates; +} diff --git a/toolkit/test/accessibility/ov/TextView.java b/toolkit/test/accessibility/ov/TextView.java new file mode 100644 index 0000000000..518d5ff507 --- /dev/null +++ b/toolkit/test/accessibility/ov/TextView.java @@ -0,0 +1,133 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package ov; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import javax.swing.JLabel; + +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.XAccessibleText; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.uno.UnoRuntime; + +public class TextView + extends ListeningObjectView +{ + /** Create a TextView when the given object supports the + XAccessibleText interface. + */ + public static ObjectView Create ( + ObjectViewContainer aContainer, + XAccessibleContext xContext) + { + XAccessibleText xText = UnoRuntime.queryInterface( + XAccessibleText.class, xContext); + if (xText != null) + return new TextView (aContainer); + else + return null; + } + + + private TextView (ObjectViewContainer aContainer) + { + super (aContainer); + + setLayout (new GridBagLayout()); + GridBagConstraints aConstraints = new GridBagConstraints (); + + JLabel aLabel = new JLabel ("Text:"); + aConstraints.gridy = 0; + aConstraints.weightx = 1; + aConstraints.fill = GridBagConstraints.HORIZONTAL; + add (aLabel, aConstraints); + + maTextLabel = new JLabel (""); + aConstraints.gridx = 1; + aConstraints.fill = GridBagConstraints.NONE; + aConstraints.anchor = GridBagConstraints.WEST; + add (maTextLabel, aConstraints); + + aLabel = new JLabel ("Caret position:"); + aConstraints.gridx = 0; + aConstraints.gridy = 1; + aConstraints.weightx = 1; + aConstraints.fill = GridBagConstraints.HORIZONTAL; + add (aLabel, aConstraints); + + maCaretPositionLabel = new JLabel (""); + aConstraints.gridx = 1; + aConstraints.fill = GridBagConstraints.NONE; + aConstraints.anchor = GridBagConstraints.WEST; + add (maCaretPositionLabel, aConstraints); + } + + + /** Additionally to the context store a reference to the + XAccessibleText interface. + */ + @Override + public void SetObject (XAccessibleContext xObject) + { + mxText = UnoRuntime.queryInterface( + XAccessibleText.class, xObject); + super.SetObject (xObject); + } + + @Override + synchronized public void Update () + { + if (mxText == null) + { + maTextLabel.setText ("<null object>"); + maCaretPositionLabel.setText ("<null object>"); + } + else + { + maTextLabel.setText (mxText.getText()); + maCaretPositionLabel.setText (Integer.toString(mxText.getCaretPosition())); + } + } + + @Override + public String GetTitle () + { + return "Text"; + } + + @Override + public void notifyEvent (AccessibleEventObject aEvent) + { + System.out.println (aEvent); + switch (aEvent.EventId) + { + case AccessibleEventId.TEXT_CHANGED : + case AccessibleEventId.CARET_CHANGED : + Update (); + break; + } + } + + private final JLabel + maTextLabel, + maCaretPositionLabel; + private XAccessibleText mxText; +} diff --git a/toolkit/test/accessibility/tools/NameProvider.java b/toolkit/test/accessibility/tools/NameProvider.java new file mode 100644 index 0000000000..7a859fcea6 --- /dev/null +++ b/toolkit/test/accessibility/tools/NameProvider.java @@ -0,0 +1,277 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package tools; + +import java.util.HashMap; +import com.sun.star.accessibility.AccessibleStateType; +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.accessibility.AccessibleRole; +import com.sun.star.accessibility.AccessibleRelationType; + + +/** Provide names for several accessibility constants groups. +*/ +public class NameProvider +{ + /** Return the name of the specified state. + @param nStateId + Id of the state for which to return its name. This is one of + the ids listed in the <type>AccessibleStateType</const> + constants group. + @return + Returns the name of the specified state or an empty string if an + invalid / unknown state id was given. + */ + public static String getStateName (int nStateId) + { + return maStateMap.get (Integer.valueOf(nStateId)); + } + + + /** Return the name of the specified event. + @param nEventId + Id of the event type for which to return its name. This is one + of the ids listed in the <type>AccessibleEventId</const> + constants group. + @return + Returns the name of the specified event type or an empty string + if an invalid / unknown event id was given. + */ + public static String getEventName (int nEventId) + { + return maEventMap.get (Integer.valueOf(nEventId)); + } + + + /** Return the name of the specified role. + @param nRole + Id of the role for which to return its name. This is one of + the ids listed in the <type>AccessibleRole</const> + constants group. + @return + Returns the name of the specified role or an empty string if an + invalid / unknown role id was given. + */ + public static String getRoleName (int nRole) + { + return maRoleMap.get (Integer.valueOf(nRole)); + } + + + /** Return the name of the specified relation. + @param nRelation + Id of the relation for which to return its name. This is one of + the ids listed in the <type>AccessibleRelationType</const> + constants group. + @return + Returns the name of the specified relation type or an empty + string if an invalid / unknown role id was given. + */ + public static String getRelationName (int nRelation) + { + return maRelationMap.get (Integer.valueOf(nRelation)); + } + + + private static HashMap<Integer, String> maStateMap = new HashMap<Integer, String>(); + private static HashMap<Integer, String> maEventMap = new HashMap<Integer, String>(); + private static HashMap<Integer, String> maRoleMap = new HashMap<Integer, String>(); + private static HashMap<Integer, String> maRelationMap = new HashMap<Integer, String>(); + + static { + maStateMap.put (Integer.valueOf(AccessibleStateType.INVALID), "INVALID"); + maStateMap.put (Integer.valueOf(AccessibleStateType.ACTIVE), "ACTIVE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.ARMED), "ARMED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.BUSY), "BUSY"); + maStateMap.put (Integer.valueOf(AccessibleStateType.CHECKABLE), "CHECKABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.CHECKED), "CHECKED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.DEFUNC), "DEFUNC"); + maStateMap.put (Integer.valueOf(AccessibleStateType.EDITABLE), "EDITABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.ENABLED), "ENABLED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.EXPANDABLE), "EXPANDABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.EXPANDED), "EXPANDED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.FOCUSABLE), "FOCUSABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.FOCUSED), "FOCUSED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.HORIZONTAL), "HORIZONTAL"); + maStateMap.put (Integer.valueOf(AccessibleStateType.ICONIFIED), "ICONIFIED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.MODAL), "MODAL"); + maStateMap.put (Integer.valueOf(AccessibleStateType.MULTI_LINE), "MULTI_LINE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.MULTI_SELECTABLE), "MULTI_SELECTABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.OPAQUE), "OPAQUE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.PRESSED), "PRESSED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.RESIZABLE), "RESIZABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.SELECTABLE), "SELECTABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.SELECTED), "SELECTED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.SENSITIVE), "SENSITIVE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.SHOWING), "SHOWING"); + maStateMap.put (Integer.valueOf(AccessibleStateType.SINGLE_LINE), "SINGLE_LINE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.STALE), "STALE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.TRANSIENT), "TRANSIENT"); + maStateMap.put (Integer.valueOf(AccessibleStateType.VERTICAL), "VERTICAL"); + maStateMap.put (Integer.valueOf(AccessibleStateType.VISIBLE), "VISIBLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.MANAGES_DESCENDANTS), + "MANAGES_DESCENDANTS"); + + + maEventMap.put (Integer.valueOf(0), + "[UNKNOWN]"); + maEventMap.put (Integer.valueOf(AccessibleEventId.NAME_CHANGED), + "NAME_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.DESCRIPTION_CHANGED), + "DESCRIPTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.ACTION_CHANGED), + "ACTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.STATE_CHANGED), + "STATE_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.ACTIVE_DESCENDANT_CHANGED), + "ACTIVE_DESCENDANT_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.BOUNDRECT_CHANGED), + "BOUNDRECT_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CHILD), + "CHILD"); + maEventMap.put (Integer.valueOf(AccessibleEventId.INVALIDATE_ALL_CHILDREN), + "INVALIDATE_ALL_CHILDREN"); + maEventMap.put (Integer.valueOf(AccessibleEventId.SELECTION_CHANGED), + "SELECTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.VISIBLE_DATA_CHANGED), + "VISIBLE_DATA_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.VALUE_CHANGED), + "VALUE_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CONTENT_FLOWS_FROM_RELATION_CHANGED), + "CONTENT_FLOWS_FROM_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CONTENT_FLOWS_TO_RELATION_CHANGED), + "CONTENT_FLOWS_TO_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CONTROLLED_BY_RELATION_CHANGED), + "CONTROLLED_BY_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CONTROLLER_FOR_RELATION_CHANGED), + "CONTROLLER_FOR_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.LABEL_FOR_RELATION_CHANGED), + "LABEL_FOR_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.LABELED_BY_RELATION_CHANGED), + "LABELED_BY_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.MEMBER_OF_RELATION_CHANGED), + "MEMBER_OF_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.SUB_WINDOW_OF_RELATION_CHANGED), + "SUB_WINDOW_OF_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CARET_CHANGED), + "CARET_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TEXT_SELECTION_CHANGED), + "TEXT_SELECTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TEXT_CHANGED), + "TEXT_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TEXT_ATTRIBUTE_CHANGED), + "TEXT_ATTRIBUTE_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.HYPERTEXT_CHANGED), + "HYPERTEXT_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_CAPTION_CHANGED), + "TABLE_CAPTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_COLUMN_DESCRIPTION_CHANGED), + "TABLE_COLUMN_DESCRIPTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_COLUMN_HEADER_CHANGED), + "TABLE_COLUMN_HEADER_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_MODEL_CHANGED), + "TABLE_MODEL_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_ROW_DESCRIPTION_CHANGED), + "TABLE_ROW_DESCRIPTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_ROW_HEADER_CHANGED), + "TABLE_ROW_HEADER_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_SUMMARY_CHANGED), + "TABLE_SUMMARY_CHANGED"); + + maRoleMap.put (Integer.valueOf(AccessibleRole.UNKNOWN), "UNKNOWN"); + maRoleMap.put (Integer.valueOf(AccessibleRole.UNKNOWN), "UNKNOWN"); + maRoleMap.put (Integer.valueOf(AccessibleRole.ALERT), "ALERT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.COLUMN_HEADER), "COLUMN_HEADER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.CANVAS), "CANVAS"); + maRoleMap.put (Integer.valueOf(AccessibleRole.CHECK_BOX), "CHECK_BOX"); + maRoleMap.put (Integer.valueOf(AccessibleRole.CHECK_MENU_ITEM), "CHECK_MENU_ITEM"); + maRoleMap.put (Integer.valueOf(AccessibleRole.COLOR_CHOOSER), "COLOR_CHOOSER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.COMBO_BOX), "COMBO_BOX"); + maRoleMap.put (Integer.valueOf(AccessibleRole.DESKTOP_ICON), "DESKTOP_ICON"); + maRoleMap.put (Integer.valueOf(AccessibleRole.DESKTOP_PANE), "DESKTOP_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.DIRECTORY_PANE), "DIRECTORY_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.DIALOG), "DIALOG"); + maRoleMap.put (Integer.valueOf(AccessibleRole.DOCUMENT), "DOCUMENT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.EMBEDDED_OBJECT), "EMBEDDED_OBJECT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.END_NOTE), "END_NOTE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FILE_CHOOSER), "FILE_CHOOSER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FILLER), "FILLER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FONT_CHOOSER), "FONT_CHOOSER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FOOTER), "FOOTER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FOOTNOTE), "FOOTNOTE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FRAME), "FRAME"); + maRoleMap.put (Integer.valueOf(AccessibleRole.GLASS_PANE), "GLASS_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.GRAPHIC), "GRAPHIC"); + maRoleMap.put (Integer.valueOf(AccessibleRole.GROUP_BOX), "GROUP_BOX"); + maRoleMap.put (Integer.valueOf(AccessibleRole.HEADER), "HEADER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.HEADING), "HEADING"); + maRoleMap.put (Integer.valueOf(AccessibleRole.HYPER_LINK), "HYPER_LINK"); + maRoleMap.put (Integer.valueOf(AccessibleRole.ICON), "ICON"); + maRoleMap.put (Integer.valueOf(AccessibleRole.INTERNAL_FRAME), "INTERNAL_FRAME"); + maRoleMap.put (Integer.valueOf(AccessibleRole.LABEL), "LABEL"); + maRoleMap.put (Integer.valueOf(AccessibleRole.LAYERED_PANE), "LAYERED_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.LIST), "LIST"); + maRoleMap.put (Integer.valueOf(AccessibleRole.LIST_ITEM), "LIST_ITEM"); + maRoleMap.put (Integer.valueOf(AccessibleRole.MENU), "MENU"); + maRoleMap.put (Integer.valueOf(AccessibleRole.MENU_BAR), "MENU_BAR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.MENU_ITEM), "MENU_ITEM"); + maRoleMap.put (Integer.valueOf(AccessibleRole.OPTION_PANE), "OPTION_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PAGE_TAB), "PAGE_TAB"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PAGE_TAB_LIST), "PAGE_TAB_LIST"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PANEL), "PANEL"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PARAGRAPH), "PARAGRAPH"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PASSWORD_TEXT), "PASSWORD_TEXT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.POPUP_MENU), "POPUP_MENU"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PUSH_BUTTON), "PUSH_BUTTON"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PROGRESS_BAR), "PROGRESS_BAR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.RADIO_BUTTON), "RADIO_BUTTON"); + maRoleMap.put (Integer.valueOf(AccessibleRole.RADIO_MENU_ITEM), "RADIO_MENU_ITEM"); + maRoleMap.put (Integer.valueOf(AccessibleRole.ROW_HEADER), "ROW_HEADER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.ROOT_PANE), "ROOT_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SCROLL_BAR), "SCROLL_BAR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SCROLL_PANE), "SCROLL_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SHAPE), "SHAPE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SEPARATOR), "SEPARATOR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SLIDER), "SLIDER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SPIN_BOX), "SPIN_BOX"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SPLIT_PANE), "SPLIT_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.STATIC), "STATIC"); + maRoleMap.put (Integer.valueOf(AccessibleRole.STATUS_BAR), "STATUS_BAR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TABLE), "TABLE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TABLE_CELL), "TABLE_CELL"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TEXT), "TEXT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TEXT_FRAME), "TEXT_FRAME"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TOGGLE_BUTTON), "TOGGLE_BUTTON"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TOOL_BAR), "TOOL_BAR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TOOL_TIP), "TOOL_TIP"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TREE), "TREE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.VIEW_PORT), "VIEW_PORT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.WINDOW), "WINDOW"); + + maRelationMap.put (Integer.valueOf(AccessibleRelationType.INVALID), "INVALID"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.CONTENT_FLOWS_FROM), "CONTENT_FLOWS_FROM"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.CONTENT_FLOWS_TO), "CONTENT_FLOWS_TO"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.CONTROLLED_BY), "CONTROLLED_BY"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.CONTROLLER_FOR), "CONTROLLER_FOR"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.LABEL_FOR), "LABEL_FOR"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.LABELED_BY), "LABELED_BY"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.MEMBER_OF), "MEMBER_OF"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.SUB_WINDOW_OF), "SUB_WINDOW_OF"); + } +} diff --git a/toolkit/util/tk.component b/toolkit/util/tk.component new file mode 100644 index 0000000000..f93d1550a0 --- /dev/null +++ b/toolkit/util/tk.component @@ -0,0 +1,394 @@ +<?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.awt.comp.AsyncCallback" + constructor="com_sun_star_awt_comp_AsyncCallback_get_implementation"> + <service name="com.sun.star.awt.AsyncCallback"/> + </implementation> + <implementation name="com.sun.star.comp.embed.HatchWindowFactory" + constructor="com_sun_star_comp_embed_HatchWindowFactory_get_implementation"> + <service name="com.sun.star.comp.embed.HatchWindowFactory"/> + <service name="com.sun.star.embed.HatchWindowFactory"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlTabPage" + constructor="stardiv_Toolkit_UnoControlTabPage_get_implementation"> + <service name="com.sun.star.awt.tab.UnoControlTabPage"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlTabPageModel" + constructor="stardiv_Toolkit_UnoControlTabPageModel_get_implementation"> + <service name="com.sun.star.awt.tab.UnoControlTabPageModel"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlTabPageContainerModel" + constructor="stardiv_Toolkit_UnoControlTabPageContainerModel_get_implementation"> + <service name="com.sun.star.awt.tab.UnoControlTabPageContainerModel"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlTabPageContainer" + constructor="stardiv_Toolkit_UnoControlTabPageContainer_get_implementation"> + <service name="com.sun.star.awt.tab.UnoControlTabPageContainer"/> + </implementation> + <implementation name="stardiv.Toolkit.DefaultGridColumnModel" + constructor="stardiv_Toolkit_DefaultGridColumnModel_get_implementation"> + <service name="com.sun.star.awt.grid.DefaultGridColumnModel"/> + </implementation> + <implementation name="stardiv.Toolkit.DefaultGridDataModel" + constructor="stardiv_Toolkit_DefaultGridDataModel_get_implementation"> + <service name="com.sun.star.awt.grid.DefaultGridDataModel"/> + </implementation> + <implementation name="org.openoffice.comp.toolkit.GridColumn" + constructor="org_openoffice_comp_toolkit_GridColumn_get_implementation"> + <service name="com.sun.star.awt.grid.GridColumn"/> + </implementation> + <implementation name="org.openoffice.comp.toolkit.SortableGridDataModel" + constructor="org_openoffice_comp_toolkit_SortableGridDataModel_get_implementation"> + <service name="com.sun.star.awt.grid.SortableGridDataModel"/> + </implementation> + <implementation name="stardiv.Toolkit.GridControl" + constructor="stardiv_Toolkit_GridControl_get_implementation"> + <service name="com.sun.star.awt.grid.UnoControlGrid"/> + </implementation> + <implementation name="stardiv.Toolkit.GridControlModel" + constructor="stardiv_Toolkit_GridControlModel_get_implementation"> + <service name="com.sun.star.awt.grid.UnoControlGridModel"/> + </implementation> + <implementation name="stardiv.Toolkit.MutableTreeDataModel" + constructor="stardiv_Toolkit_MutableTreeDataModel_get_implementation"> + <service name="com.sun.star.awt.tree.MutableTreeDataModel"/> + </implementation> + <implementation name="stardiv.Toolkit.StdTabController" + constructor="stardiv_Toolkit_StdTabController_get_implementation"> + <service name="com.sun.star.awt.TabController"/> + <service name="stardiv.vcl.control.TabController"/> + </implementation> + <implementation name="stardiv.Toolkit.StdTabControllerModel" + constructor="stardiv_Toolkit_StdTabControllerModel_get_implementation"> + <service name="com.sun.star.awt.TabControllerModel"/> + <service name="stardiv.vcl.controlmodel.TabController"/> + </implementation> + <implementation name="stardiv.Toolkit.TreeControl" + constructor="stardiv_Toolkit_TreeControl_get_implementation"> + <service name="com.sun.star.awt.tree.TreeControl"/> + </implementation> + <implementation name="stardiv.Toolkit.TreeControlModel" + constructor="stardiv_Toolkit_TreeControlModel_get_implementation"> + <service name="com.sun.star.awt.tree.TreeControlModel"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoButtonControl" + constructor="stardiv_Toolkit_UnoButtonControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlButton"/> + <service name="stardiv.vcl.control.Button"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoCheckBoxControl" + constructor="stardiv_Toolkit_UnoCheckBoxControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlCheckBox"/> + <service name="stardiv.vcl.control.CheckBox"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoComboBoxControl" + constructor="stardiv_Toolkit_UnoComboBoxControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlComboBox"/> + <service name="stardiv.vcl.control.ComboBox"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlButtonModel" + constructor="stardiv_Toolkit_UnoControlButtonModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlButtonModel"/> + <service name="stardiv.vcl.controlmodel.Button"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlCheckBoxModel" + constructor="stardiv_Toolkit_UnoControlCheckBoxModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlCheckBoxModel"/> + <service name="stardiv.vcl.controlmodel.CheckBox"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlComboBoxModel" + constructor="stardiv_Toolkit_UnoControlComboBoxModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlComboBoxModel"/> + <service name="stardiv.vcl.controlmodel.ComboBox"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlContainer" + constructor="stardiv_Toolkit_UnoControlContainer_get_implementation"> + <service name="com.sun.star.awt.UnoControlContainer"/> + <service name="stardiv.vcl.control.ControlContainer"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlContainerModel" + constructor="stardiv_Toolkit_UnoControlContainerModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlContainerModel"/> + <service name="stardiv.vcl.controlmodel.ControlContainer"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlCurrencyFieldModel" + constructor="stardiv_Toolkit_UnoControlCurrencyFieldModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlCurrencyFieldModel"/> + <service name="stardiv.vcl.controlmodel.CurrencyField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlDateFieldModel" + constructor="stardiv_Toolkit_UnoControlDateFieldModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlDateFieldModel"/> + <service name="stardiv.vcl.controlmodel.DateField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlDialogModel" + constructor="stardiv_Toolkit_UnoControlDialogModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlDialogModel"/> + <service name="stardiv.vcl.controlmodel.Dialog"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlEditModel" + constructor="stardiv_Toolkit_UnoControlEditModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlEditModel"/> + <service name="stardiv.vcl.controlmodel.Edit"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlFileControlModel" + constructor="stardiv_Toolkit_UnoControlFileControlModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlFileControlModel"/> + <service name="stardiv.vcl.controlmodel.FileControl"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlFixedHyperlinkModel" + constructor="stardiv_Toolkit_UnoControlFixedHyperlinkModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlFixedHyperlinkModel"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlFixedLineModel" + constructor="stardiv_Toolkit_UnoControlFixedLineModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlFixedLineModel"/> + <service name="stardiv.vcl.controlmodel.FixedLine"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlFixedTextModel" + constructor="stardiv_Toolkit_UnoControlFixedTextModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlFixedTextModel"/> + <service name="stardiv.vcl.controlmodel.FixedText"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlFormattedFieldModel" + constructor="stardiv_Toolkit_UnoControlFormattedFieldModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlFormattedFieldModel"/> + <service name="stardiv.vcl.controlmodel.FormattedField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlGroupBoxModel" + constructor="stardiv_Toolkit_UnoControlGroupBoxModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlGroupBoxModel"/> + <service name="stardiv.vcl.controlmodel.GroupBox"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlImageControlModel" + constructor="stardiv_Toolkit_UnoControlImageControlModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlImageButtonModel"/> + <service name="com.sun.star.awt.UnoControlImageControlModel"/> + <service name="stardiv.vcl.controlmodel.ImageButton"/> + <service name="stardiv.vcl.controlmodel.ImageControl"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlListBoxModel" + constructor="stardiv_Toolkit_UnoControlListBoxModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlListBoxModel"/> + <service name="stardiv.vcl.controlmodel.ListBox"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlNumericFieldModel" + constructor="stardiv_Toolkit_UnoControlNumericFieldModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlNumericFieldModel"/> + <service name="stardiv.vcl.controlmodel.NumericField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlPatternFieldModel" + constructor="stardiv_Toolkit_UnoControlPatternFieldModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlPatternFieldModel"/> + <service name="stardiv.vcl.controlmodel.PatternField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlProgressBarModel" + constructor="stardiv_Toolkit_UnoControlProgressBarModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlProgressBarModel"/> + <service name="stardiv.vcl.controlmodel.ProgressBar"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlRadioButtonModel" + constructor="stardiv_Toolkit_UnoControlRadioButtonModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlRadioButtonModel"/> + <service name="stardiv.vcl.controlmodel.RadioButton"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlRoadmapModel" + constructor="stardiv_Toolkit_UnoControlRoadmapModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlRoadmapModel"/> + <service name="stardiv.vcl.controlmodel.Roadmap"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlScrollBarModel" + constructor="stardiv_Toolkit_UnoControlScrollBarModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlScrollBarModel"/> + <service name="stardiv.vcl.controlmodel.ScrollBar"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoControlTimeFieldModel" + constructor="stardiv_Toolkit_UnoControlTimeFieldModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlTimeFieldModel"/> + <service name="stardiv.vcl.controlmodel.TimeField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoCurrencyFieldControl" + constructor="stardiv_Toolkit_UnoCurrencyFieldControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlCurrencyField"/> + <service name="stardiv.vcl.control.CurrencyField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoDateFieldControl" + constructor="stardiv_Toolkit_UnoDateFieldControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlDateField"/> + <service name="stardiv.vcl.control.DateField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoDialogControl" + constructor="stardiv_Toolkit_UnoDialogControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlDialog"/> + <service name="stardiv.vcl.control.Dialog"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoEditControl" + constructor="stardiv_Toolkit_UnoEditControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlEdit"/> + <service name="stardiv.vcl.control.Edit"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoFileControl" + constructor="stardiv_Toolkit_UnoFileControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlFileControl"/> + <service name="stardiv.vcl.control.FileControl"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoFixedHyperlinkControl" + constructor="stardiv_Toolkit_UnoFixedHyperlinkControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlFixedHyperlink"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoFixedLineControl" + constructor="stardiv_Toolkit_UnoFixedLineControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlFixedLine"/> + <service name="stardiv.vcl.control.FixedLine"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoFixedTextControl" + constructor="stardiv_Toolkit_UnoFixedTextControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlFixedText"/> + <service name="stardiv.vcl.control.FixedText"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoFormattedFieldControl" + constructor="stardiv_Toolkit_UnoFormattedFieldControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlFormattedField"/> + <service name="stardiv.vcl.control.FormattedField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoGroupBoxControl" + constructor="stardiv_Toolkit_UnoGroupBoxControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlGroupBox"/> + <service name="stardiv.vcl.control.GroupBox"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoImageControlControl" + constructor="stardiv_Toolkit_UnoImageControlControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlImageButton"/> + <service name="com.sun.star.awt.UnoControlImageControl"/> + <service name="stardiv.vcl.control.ImageButton"/> + <service name="stardiv.vcl.control.ImageControl"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoListBoxControl" + constructor="stardiv_Toolkit_UnoListBoxControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlListBox"/> + <service name="stardiv.vcl.control.ListBox"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoNumericFieldControl" + constructor="stardiv_Toolkit_UnoNumericFieldControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlNumericField"/> + <service name="stardiv.vcl.control.NumericField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoPatternFieldControl" + constructor="stardiv_Toolkit_UnoPatternFieldControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlPatternField"/> + <service name="stardiv.vcl.control.PatternField"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoProgressBarControl" + constructor="stardiv_Toolkit_UnoProgressBarControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlProgressBar"/> + <service name="stardiv.vcl.control.ProgressBar"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoRadioButtonControl" + constructor="stardiv_Toolkit_UnoRadioButtonControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlRadioButton"/> + <service name="stardiv.vcl.control.RadioButton"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoRoadmapControl" + constructor="stardiv_Toolkit_UnoRoadmapControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlRoadmap"/> + <service name="stardiv.vcl.control.Roadmap"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoScrollBarControl" + constructor="stardiv_Toolkit_UnoScrollBarControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlScrollBar"/> + <service name="stardiv.vcl.control.ScrollBar"/> + </implementation> + <implementation name="org.openoffice.comp.toolkit.SpinningProgressControlModel" + constructor="org_openoffice_comp_toolkit_SpinningProgressControlModel_get_implementation"> + <service name="com.sun.star.awt.SpinningProgressControlModel"/> + </implementation> + <implementation name="org.openoffice.comp.toolkit.AnimatedImagesControlModel" + constructor="org_openoffice_comp_toolkit_AnimatedImagesControlModel_get_implementation"> + <service name="com.sun.star.awt.AnimatedImagesControlModel"/> + </implementation> + <implementation name="org.openoffice.comp.toolkit.AnimatedImagesControl" + constructor="org_openoffice_comp_toolkit_AnimatedImagesControl_get_implementation"> + <service name="com.sun.star.awt.AnimatedImagesControl"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoSpinButtonControl" + constructor="stardiv_Toolkit_UnoSpinButtonControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlSpinButton"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoSpinButtonModel" + constructor="stardiv_Toolkit_UnoSpinButtonModel_get_implementation"> + <service name="com.sun.star.awt.UnoControlSpinButtonModel"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoTimeFieldControl" + constructor="stardiv_Toolkit_UnoTimeFieldControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlTimeField"/> + <service name="stardiv.vcl.control.TimeField"/> + </implementation> + <implementation name="stardiv.Toolkit.VCLXMenuBar" + constructor="stardiv_Toolkit_VCLXMenuBar_get_implementation"> + <service name="com.sun.star.awt.MenuBar"/> + <service name="stardiv.vcl.MenuBar"/> + </implementation> + <implementation name="stardiv.Toolkit.VCLXPointer" + constructor="stardiv_Toolkit_VCLXPointer_get_implementation"> + <service name="com.sun.star.awt.Pointer"/> + <service name="stardiv.vcl.Pointer"/> + </implementation> + <implementation name="stardiv.Toolkit.VCLXPopupMenu" + constructor="stardiv_Toolkit_VCLXPopupMenu_get_implementation"> + <service name="com.sun.star.awt.PopupMenu"/> + <service name="stardiv.vcl.PopupMenu"/> + </implementation> + <implementation name="stardiv.Toolkit.VCLXPrinterServer" + constructor="stardiv_Toolkit_VCLXPrinterServer_get_implementation"> + <service name="com.sun.star.awt.PrinterServer"/> + <service name="stardiv.vcl.PrinterServer"/> + </implementation> + <implementation name="stardiv.Toolkit.VCLXToolkit" + constructor="stardiv_Toolkit_VCLXToolkit_get_implementation"> + <service name="com.sun.star.awt.Toolkit"/> + <service name="stardiv.vcl.VclToolkit"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoMultiPageModel" + constructor="stardiv_Toolkit_UnoMultiPageModel_get_implementation"> + <service name="com.sun.star.awt.UnoMultiPageModel"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoMultiPageControl" + constructor="stardiv_Toolkit_UnoMultiPageControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlMultiPage"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoPageModel" + constructor="stardiv_Toolkit_UnoPageModel_get_implementation"> + <service name="com.sun.star.awt.UnoPageModel"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoPageControl" + constructor="stardiv_Toolkit_UnoPageControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlPage"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoFrameModel" + constructor="stardiv_Toolkit_UnoFrameModel_get_implementation"> + <service name="com.sun.star.awt.UnoFrameModel"/> + </implementation> + <implementation name="stardiv.Toolkit.UnoFrameControl" + constructor="stardiv_Toolkit_UnoFrameControl_get_implementation"> + <service name="com.sun.star.awt.UnoControlFrame"/> + </implementation> +</component> |