diff options
author | Caolán McNamara <caolanm@redhat.com> | 2019-08-13 10:37:50 +0100 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2019-09-30 16:53:32 +0200 |
commit | 1ae450504cf57457f9702684b1517fda1dd3c481 (patch) | |
tree | 2b1fc3870594dcc01217e3e406322ab084e258ef | |
parent | 25ca657f8d0f1febaf0d5984bc862f3072ad197b (diff) |
drop gtk2 support
Change-Id: Ie838cabfecfef7e3225c1555536d5c9cf3b43f15
Reviewed-on: https://gerrit.libreoffice.org/77405
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
125 files changed, 11741 insertions, 22179 deletions
diff --git a/Repository.mk b/Repository.mk index 8068e52572bc..965889197a9a 100644 --- a/Repository.mk +++ b/Repository.mk @@ -224,14 +224,6 @@ $(eval $(call gb_Helper_register_executables_for_install,OOO,python, \ ) \ )) -ifeq ($(USING_X11), TRUE) -$(eval $(call gb_Helper_register_executables_for_install,OOO,gnome, \ - $(if $(ENABLE_GTK),\ - xid-fullscreen-on-all-monitors \ - ) \ -)) -endif - ifneq ($(ENABLE_POPPLER),) $(eval $(call gb_Helper_register_executables_for_install,OOO,pdfimport, \ xpdfimport \ @@ -290,7 +282,6 @@ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,onlineupdate, \ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,gnome, \ $(if $(ENABLE_EVOAB2),evoab) \ - $(if $(ENABLE_GTK),vclplug_gtk) \ $(if $(ENABLE_GTK3),vclplug_gtk3) \ $(if $(ENABLE_GIO),losessioninstall) \ $(if $(ENABLE_GIO),ucpgio1) \ diff --git a/RepositoryExternal.mk b/RepositoryExternal.mk index 57f66b8f344a..4d99f79e86ec 100644 --- a/RepositoryExternal.mk +++ b/RepositoryExternal.mk @@ -2605,34 +2605,6 @@ gb_LinkTarget__use_avahi := endif # ENABLE_AVAHI - -define gb_LinkTarget__use_gtk -$(call gb_LinkTarget_set_include,$(1),\ - $$(INCLUDE) \ - $(GTK_CFLAGS) \ -) - -$(call gb_LinkTarget_add_libs,$(1),$(GTK_LIBS)) - -ifeq ($(ENABLE_GTK_PRINT),TRUE) - -$(call gb_LinkTarget_add_defs,$(1),-DENABLE_GTK_PRINT) - -$(call gb_LinkTarget_set_include,$(1),\ - $$(INCLUDE) \ - $(GTK_PRINT_CFLAGS) \ -) - -$(call gb_LinkTarget_add_libs,$(1),$(GTK_PRINT_LIBS)) - -endif - -endef - -define gb_LinkTarget__use_gthread -$(call gb_LinkTarget_add_libs,$(1),$(GTHREAD_LIBS)) -endef - ifeq ($(ENABLE_CUPS),TRUE) define gb_LinkTarget__use_cups diff --git a/bin/check-elf-dynamic-objects b/bin/check-elf-dynamic-objects index 5492aafaca34..f64044dd1e49 100755 --- a/bin/check-elf-dynamic-objects +++ b/bin/check-elf-dynamic-objects @@ -93,7 +93,6 @@ x11whitelist="libX11.so.6 libXext.so.6 libSM.so.6 libICE.so.6 libXinerama.so.1 l openglwhitelist="libGL.so.1" giowhitelist="libgio-2.0.so.0 libgobject-2.0.so.0 libgmodule-2.0.so.0 libgthread-2.0.so.0 libglib-2.0.so.0 libdbus-glib-1.so.2 libdbus-1.so.3" gstreamerwhitelist="libgstaudio-1.0.so.0 libgstpbutils-1.0.so.0 libgstvideo-1.0.so.0 libgstbase-1.0.so.0 libgstreamer-1.0.so.0" -gtk2whitelist="libgtk-x11-2.0.so.0 libgdk-x11-2.0.so.0 libpangocairo-1.0.so.0 libfribidi.so.0 libatk-1.0.so.0 libcairo.so.2 libgio-2.0.so.0 libpangoft2-1.0.so.0 libpango-1.0.so.0 libfontconfig.so.1 libfreetype.so.6 libgdk_pixbuf-2.0.so.0 libgobject-2.0.so.0 libglib-2.0.so.0 libgmodule-2.0.so.0 libgthread-2.0.so.0 libdbus-glib-1.so.2 libdbus-1.so.3" gtk3whitelist="libgtk-3.so.0 libgdk-3.so.0 libcairo-gobject.so.2 libpangocairo-1.0.so.0 libfribidi.so.0 libatk-1.0.so.0 libcairo.so.2 libgio-2.0.so.0 libpangoft2-1.0.so.0 libpango-1.0.so.0 libfontconfig.so.1 libfreetype.so.6 libgdk_pixbuf-2.0.so.0 libgobject-2.0.so.0 libglib-2.0.so.0 libgmodule-2.0.so.0 libgthread-2.0.so.0 libdbus-glib-1.so.2 libdbus-1.so.3" qt5whitelist="libQt5Core.so.5 libQt5Gui.so.5 libQt5Network.so.5 libQt5Widgets.so.5 libQt5X11Extras.so.5 libcairo.so.2 libglib-2.0.so.0 libgobject-2.0.so.0 libxcb.so.1" kf5whitelist="libKF5ConfigCore.so.5 libKF5CoreAddons.so.5 libKF5I18n.so.5 libKF5KIOCore.so.5 libKF5KIOFileWidgets.so.5 libKF5KIOWidgets.so.5 libKF5WindowSystem.so.5" @@ -129,10 +128,7 @@ local file="$1" whitelist="${whitelist} ${qt5whitelist} ${kf5whitelist}" fi ;; - */libvclplug_gtklo.so|*/libqstart_gtklo.so|*/updater) - whitelist="${whitelist} ${x11whitelist} ${gtk2whitelist}" - ;; - */libvclplug_gtk3lo.so) + */libvclplug_gtk3lo.so|*/updater) whitelist="${whitelist} ${x11whitelist} ${gtk3whitelist}" ;; */libvclplug_qt5lo.so) diff --git a/bin/distro-install-file-lists b/bin/distro-install-file-lists index 4cbe0be407ac..ef016e3df6cc 100755 --- a/bin/distro-install-file-lists +++ b/bin/distro-install-file-lists @@ -324,9 +324,6 @@ if test "z$OOO_VENDOR" != "zDebian" ; then mv_file_between_flists core_list.txt common_list.txt "$INSTALLDIR/$f" done - # Ship ooqstart for gnome in gnome package - mv_file_between_flists gnome_list.txt core_list.txt "$INSTALLDIR/program/libqstart_gtk680.*" - # themes are included in other packages # don't use remove_file as we don't want them removed from the buildroot. mv_file_between_flists /dev/null common_list.txt $INSTALLDIR/share/config/images_crystal.zip diff --git a/bin/moveglobalheaders.sh b/bin/moveglobalheaders.sh index 6c8cff729505..ca202832bd6a 100755 --- a/bin/moveglobalheaders.sh +++ b/bin/moveglobalheaders.sh @@ -36,8 +36,7 @@ done # we like to be special ... sed -ie 's/\/svtools\/inc\/svtools/\/include\/svtools\//' svtools/Library_svt.mk sed -ie 's/\/sfx2\/inc\/sfx2/\/include\/sfx2\//' sfx2/Library_sfx.mk -sed -ie 's/\/sfx2\/inc\/sfx2/\/include\/sfx2\//' sfx2/Library_qstart_gtk.mk -git add svtools/Library_svt.mk sfx2/Library_sfx.mk sfx2/Library_qstart_gtk.mk +git add svtools/Library_svt.mk sfx2/Library_sfx.mk # urgh sed -ie 's/\.\.\/svx\//svx\//' svx/source/svdraw/svdoashp.cxx diff --git a/config_host.mk.in b/config_host.mk.in index 122c9de745dd..29b85688b83f 100644 --- a/config_host.mk.in +++ b/config_host.mk.in @@ -142,8 +142,6 @@ export ENABLE_GIO=@ENABLE_GIO@ export ENABLE_GPGMEPP=@ENABLE_GPGMEPP@ export ENABLE_GSTREAMER_1_0=@ENABLE_GSTREAMER_1_0@ export ENABLE_GTK3=@ENABLE_GTK3@ -export ENABLE_GTK=@ENABLE_GTK@ -export ENABLE_GTK_PRINT=@ENABLE_GTK_PRINT@ export DISABLE_GUI=@DISABLE_GUI@ export ENABLE_HTMLHELP=@ENABLE_HTMLHELP@ export ENABLE_IOS_LIBREOFFICELIGHT_APP=@ENABLE_IOS_LIBREOFFICELIGHT_APP@ @@ -236,13 +234,8 @@ export GSSAPI_LIBS=@GSSAPI_LIBS@ export GSTREAMER_1_0_CFLAGS=$(gb_SPACE)@GSTREAMER_1_0_CFLAGS@ export GSTREAMER_1_0_LIBS=$(gb_SPACE)@GSTREAMER_1_0_LIBS@ export GTHREAD_CFLAGS=$(gb_SPACE)@GTHREAD_CFLAGS@ -export GTHREAD_LIBS=$(gb_SPACE)@GTHREAD_LIBS@ export GTK3_CFLAGS=$(gb_SPACE)@GTK3_CFLAGS@ export GTK3_LIBS=$(gb_SPACE)@GTK3_LIBS@ -export GTK_CFLAGS=$(gb_SPACE)@GTK_CFLAGS@ -export GTK_LIBS=$(gb_SPACE)@GTK_LIBS@ -export GTK_PRINT_CFLAGS=$(gb_SPACE)@GTK_PRINT_CFLAGS@ -export GTK_PRINT_LIBS=$(gb_SPACE)@GTK_PRINT_LIBS@ export USING_X11=@USING_X11@ export HAMCREST_JAR=@HAMCREST_JAR@ export HAVE_BROKEN_GCC_WMAYBE_UNINITIALIZED=@HAVE_BROKEN_GCC_WMAYBE_UNINITIALIZED@ diff --git a/config_host/config_vclplug.h.in b/config_host/config_vclplug.h.in index 694334dd4bf3..8ff5a9ce527f 100644 --- a/config_host/config_vclplug.h.in +++ b/config_host/config_vclplug.h.in @@ -7,7 +7,6 @@ Settings about which desktops have support enabled. #ifndef CONFIG_VCLPLUG_H #define CONFIG_VCLPLUG_H -#define ENABLE_GTK 0 #define ENABLE_GTK3 0 #define ENABLE_GTK3_KDE5 0 #define ENABLE_KF5 0 diff --git a/configure.ac b/configure.ac index cf0f6e501611..ce79ad5395af 100644 --- a/configure.ac +++ b/configure.ac @@ -574,7 +574,6 @@ SDKDIRNAME=sdk case "$host_os" in solaris*) - test_gtk=yes build_gstreamer_1_0=yes test_freetype=yes _os=SunOS @@ -600,7 +599,6 @@ solaris*) ;; linux-gnu*|k*bsd*-gnu*) - test_gtk=yes build_gstreamer_1_0=yes test_kf5=yes test_gtk3_kde5=yes @@ -649,7 +647,6 @@ cygwin*|interix*) ;; darwin*) # macOS or iOS - test_gtk=yes test_randr=no test_xrender=no test_freetype=no @@ -662,7 +659,6 @@ darwin*) # macOS or iOS if test "$host_cpu" = "arm64" -o "$enable_ios_simulator" = "yes"; then build_for_ios=YES _os=iOS - test_gtk=no test_cups=no enable_mpl_subset=yes enable_lotuswordpro=no @@ -694,7 +690,6 @@ darwin*) # macOS or iOS ;; freebsd*) - test_gtk=yes build_gstreamer_1_0=yes test_kf5=yes test_gtk3_kde5=yes @@ -722,7 +717,6 @@ freebsd*) ;; *netbsd*) - test_gtk=yes build_gstreamer_1_0=yes test_kf5=yes test_gtk3_kde5=yes @@ -739,7 +733,6 @@ aix*) ;; openbsd*) - test_gtk=yes test_freetype=yes PTHREAD_CFLAGS="-D_THREAD_SAFE" PTHREAD_LIBS="-pthread" @@ -747,7 +740,6 @@ openbsd*) ;; dragonfly*) - test_gtk=yes build_gstreamer_1_0=yes test_kf5=yes test_gtk3_kde5=yes @@ -770,7 +762,6 @@ linux-android*) test_dbus=no test_fontconfig=no test_freetype=no - test_gtk=no test_kf5=no test_qt5=no test_gtk3_kde5=no @@ -1204,11 +1195,6 @@ AC_ARG_ENABLE(python, 'fully-internal' even forces the internal version for uses of Python during the build.])) -libo_FUZZ_ARG_ENABLE(gtk, - AS_HELP_STRING([--disable-gtk], - [Determines whether to use Gtk+ vclplug on platforms where Gtk+ is available.]), -,test "${enable_gtk+set}" = set || enable_gtk=yes) - libo_FUZZ_ARG_ENABLE(gtk3, AS_HELP_STRING([--disable-gtk3], [Determines whether to use Gtk+ 3.0 vclplug on platforms where Gtk+ 3.0 is available.]), @@ -1652,12 +1638,12 @@ AC_ARG_WITH(system-jars, AC_ARG_WITH(system-cairo, AS_HELP_STRING([--with-system-cairo], [Use cairo libraries already on system. Happens automatically for - (implicit) --enable-gtk and for --enable-gtk3.])) + (implicit) --enable-gtk3.])) AC_ARG_WITH(system-epoxy, AS_HELP_STRING([--with-system-epoxy], [Use epoxy libraries already on system. Happens automatically for - --enable-gtk3.]),, + (implicit) --enable-gtk3.]),, [with_system_epoxy="$with_system_libs"]) AC_ARG_WITH(myspell-dicts, @@ -4716,7 +4702,6 @@ if test "$cross_compiling" = "yes"; then ./configure \ --disable-cups \ --disable-gstreamer-1-0 \ - --disable-gtk \ --disable-gtk3 \ --disable-pdfimport \ --disable-postgresql-sdbc \ @@ -4849,7 +4834,6 @@ if test "$USING_X11" != TRUE; then test_xrender=no test_cups=no test_dbus=no - test_gtk=no build_gstreamer_1_0=no test_kf5=no test_qt5=no @@ -10328,7 +10312,6 @@ dnl Test which vclplugs have to be built. dnl =================================================================== R="" if test "$USING_X11" != TRUE; then - enable_gtk=no enable_gtk3=no fi GTK3_CFLAGS="" @@ -10369,18 +10352,6 @@ if test "$enable_introspection" = yes; then fi fi -ENABLE_GTK="" -if test "x$enable_gtk" = "xyes"; then - if test "$with_system_cairo" = no; then - AC_MSG_ERROR([System cairo required for gtk support, do not use --without-system-cairo or use --disable-gtk]) - fi - : ${with_system_cairo:=yes} - ENABLE_GTK="TRUE" - AC_DEFINE(ENABLE_GTK) - R="gtk $R" -fi -AC_SUBST(ENABLE_GTK) - ENABLE_QT5="" if test "x$enable_qt5" = "xyes"; then ENABLE_QT5="TRUE" @@ -10531,61 +10502,27 @@ AC_SUBST(ENABLE_SDREMOTE_BLUETOOTH) AC_SUBST(SYSTEM_BLUEZ) dnl =================================================================== -dnl Check whether the gtk 2.0 libraries are available. -dnl =================================================================== - -GTK_CFLAGS="" -GTK_LIBS="" -if test "$test_gtk" = "yes"; then - - if test "$ENABLE_GTK" = "TRUE"; then - PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 2.18.0 ,,AC_MSG_ERROR([requirements to build the gtk-plugin not met (gtk+-2.0 >= 2.18.0). Use --disable-gtk or install the missing packages])) - PKG_CHECK_MODULES(GTK, gdk-pixbuf-2.0 >= 2.2 ,,AC_MSG_ERROR([requirements to build the gtk-plugin not met (gdk-pixbuf-2.0 >= 2.2). Use --disable-gtk or install the missing packages])) - GTK_CFLAGS=$(printf '%s' "$GTK_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g") - FilterLibs "${GTK_LIBS}" - GTK_LIBS="${filteredlibs}" - PKG_CHECK_MODULES(GTHREAD, gthread-2.0,,AC_MSG_ERROR([requirements to build the gtk-plugin not met (gthread-2.0). Use --disable-gtk or install the missing packages])) - BUILD_TYPE="$BUILD_TYPE GTK" - GTHREAD_CFLAGS=$(printf '%s' "$GTK_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g") - FilterLibs "${GTHREAD_LIBS}" - GTHREAD_LIBS="${filteredlibs}" - - AC_MSG_CHECKING([whether to enable Gtk print dialog support]) - PKG_CHECK_MODULES([GTK_PRINT], [gtk+-unix-print-2.0 >= 2.10.0], - [ENABLE_GTK_PRINT="TRUE"], - [ENABLE_GTK_PRINT=""]) - GTK_PRINT_CFLAGS=$(printf '%s' "$GTK_PRINT_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g") - FilterLibs "${GTK_PRINT_LIBS}" - GTK_PRINT_LIBS="${filteredlibs}" - fi - - if test "$ENABLE_GTK" = "TRUE" || test "$ENABLE_GTK3" = "TRUE"; then - AC_MSG_CHECKING([whether to enable GIO support]) - if test "$_os" != "WINNT" -a "$_os" != "Darwin" -a "$enable_gio" = "yes"; then - dnl Need at least 2.26 for the dbus support. - PKG_CHECK_MODULES([GIO], [gio-2.0 >= 2.26], - [ENABLE_GIO="TRUE"], [ENABLE_GIO=""]) - if test "$ENABLE_GIO" = "TRUE"; then - AC_DEFINE(ENABLE_GIO) - GIO_CFLAGS=$(printf '%s' "$GIO_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g") - FilterLibs "${GIO_LIBS}" - GIO_LIBS="${filteredlibs}" - fi - else - AC_MSG_RESULT([no]) +dnl Check whether to enable GIO support +dnl =================================================================== +if test "$ENABLE_GTK3" = "TRUE"; then + AC_MSG_CHECKING([whether to enable GIO support]) + if test "$_os" != "WINNT" -a "$_os" != "Darwin" -a "$enable_gio" = "yes"; then + dnl Need at least 2.26 for the dbus support. + PKG_CHECK_MODULES([GIO], [gio-2.0 >= 2.26], + [ENABLE_GIO="TRUE"], [ENABLE_GIO=""]) + if test "$ENABLE_GIO" = "TRUE"; then + AC_DEFINE(ENABLE_GIO) + GIO_CFLAGS=$(printf '%s' "$GIO_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g") + FilterLibs "${GIO_LIBS}" + GIO_LIBS="${filteredlibs}" fi + else + AC_MSG_RESULT([no]) fi fi AC_SUBST(ENABLE_GIO) AC_SUBST(GIO_CFLAGS) AC_SUBST(GIO_LIBS) -AC_SUBST(GTK_CFLAGS) -AC_SUBST(GTK_LIBS) -AC_SUBST(GTHREAD_CFLAGS) -AC_SUBST(GTHREAD_LIBS) -AC_SUBST([ENABLE_GTK_PRINT]) -AC_SUBST([GTK_PRINT_CFLAGS]) -AC_SUBST([GTK_PRINT_LIBS]) dnl =================================================================== diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk index 8a2bf42d79c1..54e3733daef1 100644 --- a/cui/Library_cui.mk +++ b/cui/Library_cui.mk @@ -20,7 +20,6 @@ $(eval $(call gb_Library_set_include,cui,\ $(eval $(call gb_Library_set_precompiled_header,cui,cui/inc/pch/precompiled_cui)) $(eval $(call gb_Library_add_defs,cui,\ - $(if $(filter TRUE,$(ENABLE_GTK)),-DENABLE_GTK) \ -DCUI_DLLIMPLEMENTATION \ )) diff --git a/cui/source/options/optgdlg.cxx b/cui/source/options/optgdlg.cxx index bcf1d7a73fab..c0b840302864 100644 --- a/cui/source/options/optgdlg.cxx +++ b/cui/source/options/optgdlg.cxx @@ -18,6 +18,7 @@ */ #include <config_features.h> +#include <config_vclplug.h> #include <svl/zforlist.hxx> #include <svl/currencytable.hxx> #include <svtools/langhelp.hxx> @@ -257,7 +258,7 @@ OfaMiscTabPage::OfaMiscTabPage(weld::Container* pPage, weld::DialogController* p m_xFileDlgCB->set_sensitive(false); } -#if ! ENABLE_GTK +#if !ENABLE_GTK3 m_xPrintDlgFrame->hide(); #else if (!SvtMiscOptions().IsExperimentalMode()) @@ -269,7 +270,7 @@ OfaMiscTabPage::OfaMiscTabPage(weld::Container* pPage, weld::DialogController* p m_xQuickLaunchCB->show(); //Only available in Win or if building the gtk systray -#if !defined(_WIN32) && ! ENABLE_GTK +#if !defined(_WIN32) m_xQuickStarterFrame->hide(); #endif diff --git a/desktop/Pagein_common.mk b/desktop/Pagein_common.mk index ca5873db7903..c1661a39b641 100644 --- a/desktop/Pagein_common.mk +++ b/desktop/Pagein_common.mk @@ -28,7 +28,7 @@ $(eval $(call gb_Pagein_add_objects,common,\ ucb1 \ configmgr \ vclplug_gen \ - $(if $(findstring TRUE,$(ENABLE_GTK)),vclplug_gtk) \ + $(if $(findstring TRUE,$(ENABLE_GTK3)),vclplug_gtk3) \ basegfx \ sot \ xmlscript \ diff --git a/distro-configs/LibreOfficeFlatpak.conf b/distro-configs/LibreOfficeFlatpak.conf index 5bdba7501869..0d0f9ce5c881 100644 --- a/distro-configs/LibreOfficeFlatpak.conf +++ b/distro-configs/LibreOfficeFlatpak.conf @@ -1,4 +1,3 @@ ---disable-gtk --disable-odk --enable-release-build --with-ant-home=/run/build/libreoffice/ant diff --git a/distro-configs/LibreOfficeMacOSX.conf b/distro-configs/LibreOfficeMacOSX.conf index a4f913c7ec7e..fc7291ca7f9d 100644 --- a/distro-configs/LibreOfficeMacOSX.conf +++ b/distro-configs/LibreOfficeMacOSX.conf @@ -5,7 +5,6 @@ --enable-extension-integration --enable-online-update --without-system-postgresql ---disable-gtk --with-help=html --with-myspell-dicts --with-package-format=dmg diff --git a/distro-configs/LibreOfficeOnline.conf b/distro-configs/LibreOfficeOnline.conf index 75b885a4afb1..a007b85103c7 100644 --- a/distro-configs/LibreOfficeOnline.conf +++ b/distro-configs/LibreOfficeOnline.conf @@ -8,7 +8,6 @@ --disable-firebird-sdbc --disable-gio --disable-gstreamer-1-0 ---disable-gtk --disable-gtk3 --disable-gui --disable-qt5 diff --git a/distro-configs/LibreOfficeOssFuzz.conf b/distro-configs/LibreOfficeOssFuzz.conf index 1cca285ff058..ab9ec3b3b01e 100644 --- a/distro-configs/LibreOfficeOssFuzz.conf +++ b/distro-configs/LibreOfficeOssFuzz.conf @@ -4,7 +4,6 @@ --disable-runtime-optimizations --disable-database-connectivity --disable-gui ---disable-gtk --disable-gtk3 --disable-pdfium --disable-postgresql-sdbc diff --git a/onlineupdate/Executable_test_updater_dialog.mk b/onlineupdate/Executable_test_updater_dialog.mk index bd144c8bca18..a2988d0079d6 100644 --- a/onlineupdate/Executable_test_updater_dialog.mk +++ b/onlineupdate/Executable_test_updater_dialog.mk @@ -15,6 +15,7 @@ $(eval $(call gb_Executable_set_include,test_updater_dialog,\ -I$(SRCDIR)/onlineupdate/source/update/updater/xpcom/glue \ -I$(SRCDIR)/onlineupdate/source/update/updater \ $$(INCLUDE) \ + $(if $(filter-out WNT,$(OS)),$$(GTK3_CFLAGS) ) \ )) $(eval $(call gb_Executable_use_static_libraries,test_updater_dialog,\ @@ -48,15 +49,10 @@ $(eval $(call gb_Executable_add_defs,test_updater_dialog,\ $(eval $(call gb_Executable_use_externals,test_updater_dialog,\ nss3 \ - gtk \ )) $(eval $(call gb_Executable_add_libs,test_updater_dialog,\ - -lX11 \ - -lXext \ - -lXrender \ - -lSM \ - -lICE \ + $(GTK3_LIBS) \ )) endif diff --git a/onlineupdate/Executable_updater.mk b/onlineupdate/Executable_updater.mk index 7c24b7beb3bb..ab15a94e4c60 100644 --- a/onlineupdate/Executable_updater.mk +++ b/onlineupdate/Executable_updater.mk @@ -14,6 +14,7 @@ $(eval $(call gb_Executable_set_include,updater,\ -I$(SRCDIR)/onlineupdate/source/update/common \ -I$(SRCDIR)/onlineupdate/source/update/updater/xpcom/glue \ $$(INCLUDE) \ + $(if $(filter-out WNT,$(OS)),$$(GTK3_CFLAGS) ) \ )) $(eval $(call gb_Executable_use_custom_headers,updater,onlineupdate/generated)) @@ -61,16 +62,12 @@ $(eval $(call gb_Executable_add_defs,updater,\ $(eval $(call gb_Executable_use_externals,updater,\ nss3 \ - gtk \ )) $(eval $(call gb_Executable_add_libs,updater,\ - -lX11 \ - -lXext \ - -lXrender \ - -lSM \ - -lICE \ + $(GTK3_LIBS) \ )) + endif $(eval $(call gb_Executable_add_exception_objects,updater,\ diff --git a/onlineupdate/source/update/updater/progressui_gtk.cxx b/onlineupdate/source/update/updater/progressui_gtk.cxx index 47d27ea02c17..7c3bcfcd8b2d 100644 --- a/onlineupdate/source/update/updater/progressui_gtk.cxx +++ b/onlineupdate/source/update/updater/progressui_gtk.cxx @@ -14,6 +14,11 @@ #include <string.h> #include "progressui_gtk_icon.h" +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + #define TIMER_INTERVAL 100 static float sProgressVal; // between 0 and 100 @@ -133,4 +138,9 @@ UpdateProgressUI(float progress) { sProgressVal = progress; // 32-bit writes are atomic } + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + #endif // defined(UNIX) || defined(MACOSX) diff --git a/scp2/InstallScript_setup_osl.mk b/scp2/InstallScript_setup_osl.mk index 0c3b97bef6d7..6f7b58204225 100644 --- a/scp2/InstallScript_setup_osl.mk +++ b/scp2/InstallScript_setup_osl.mk @@ -33,7 +33,7 @@ $(eval $(call gb_InstallScript_use_modules,setup_osl,\ $(if $(WITH_EXTENSION_INTEGRATION),\ scp2/extensions \ ) \ - $(if $(filter TRUE,$(ENABLE_EVOAB2) $(ENABLE_GIO) $(ENABLE_GTK) $(ENABLE_GTK3)),\ + $(if $(filter TRUE,$(ENABLE_EVOAB2) $(ENABLE_GIO) $(ENABLE_GTK3)),\ scp2/gnome \ ) \ $(if $(filter TRUE,$(ENABLE_QT5) $(ENABLE_KF5) $(ENABLE_GTK3_KDE5)),\ diff --git a/scp2/Module_scp2.mk b/scp2/Module_scp2.mk index aa06d6301f85..25ae8167df56 100644 --- a/scp2/Module_scp2.mk +++ b/scp2/Module_scp2.mk @@ -37,7 +37,7 @@ $(eval $(call gb_Module_add_targets,scp2,\ InstallModule_windows \ InstallModule_winexplorerext \ ) \ - $(if $(filter TRUE,$(ENABLE_EVOAB2) $(ENABLE_GIO) $(ENABLE_GTK) $(ENABLE_GTK3)),\ + $(if $(filter TRUE,$(ENABLE_EVOAB2) $(ENABLE_GIO) $(ENABLE_GTK3)),\ InstallModule_gnome \ ) \ $(if $(filter TRUE,$(ENABLE_QT5) $(ENABLE_KF5) $(ENABLE_GTK3_KDE5)),\ diff --git a/sd/CppunitTest_sd_uimpress.mk b/sd/CppunitTest_sd_uimpress.mk index 4a36cc687062..94229cc2c8e3 100644 --- a/sd/CppunitTest_sd_uimpress.mk +++ b/sd/CppunitTest_sd_uimpress.mk @@ -71,7 +71,6 @@ endif $(eval $(call gb_CppunitTest_use_externals,sd_uimpress,\ boost_headers \ - gtk \ dbus \ $(if $(ENABLE_AVAHI), \ avahi \ diff --git a/solenv/clang-format/blacklist b/solenv/clang-format/blacklist index c89b6861b7d9..d694f0b3d02b 100644 --- a/solenv/clang-format/blacklist +++ b/solenv/clang-format/blacklist @@ -18113,51 +18113,13 @@ vcl/unx/generic/window/salframe.cxx vcl/unx/generic/window/salobj.cxx vcl/unx/generic/window/screensaverinhibitor.cxx vcl/unx/glxtest.cxx -vcl/unx/gtk/a11y/atkaction.cxx -vcl/unx/gtk/a11y/atkbridge.cxx -vcl/unx/gtk/a11y/atkcomponent.cxx -vcl/unx/gtk/a11y/atkeditabletext.cxx -vcl/unx/gtk/a11y/atkfactory.cxx -vcl/unx/gtk/a11y/atkfactory.hxx -vcl/unx/gtk/a11y/atkhypertext.cxx -vcl/unx/gtk/a11y/atkimage.cxx -vcl/unx/gtk/a11y/atklistener.cxx -vcl/unx/gtk/a11y/atklistener.hxx -vcl/unx/gtk/a11y/atkregistry.cxx -vcl/unx/gtk/a11y/atkregistry.hxx -vcl/unx/gtk/a11y/atkselection.cxx -vcl/unx/gtk/a11y/atktable.cxx -vcl/unx/gtk/a11y/atktext.cxx -vcl/unx/gtk/a11y/atktextattributes.cxx -vcl/unx/gtk/a11y/atktextattributes.hxx -vcl/unx/gtk/a11y/atkutil.cxx -vcl/unx/gtk/a11y/atkutil.hxx -vcl/unx/gtk/a11y/atkvalue.cxx -vcl/unx/gtk/a11y/atkwindow.cxx -vcl/unx/gtk/a11y/atkwindow.hxx -vcl/unx/gtk/a11y/atkwrapper.cxx -vcl/unx/gtk/a11y/atkwrapper.hxx -vcl/unx/gtk/fpicker/SalGtkFilePicker.cxx -vcl/unx/gtk/fpicker/SalGtkFilePicker.hxx -vcl/unx/gtk/fpicker/SalGtkFolderPicker.cxx -vcl/unx/gtk/fpicker/SalGtkFolderPicker.hxx -vcl/unx/gtk/fpicker/SalGtkPicker.cxx -vcl/unx/gtk/fpicker/SalGtkPicker.hxx -vcl/unx/gtk/fpicker/eventnotification.hxx -vcl/unx/gtk/fpicker/resourceprovider.cxx -vcl/unx/gtk/gloactiongroup.cxx -vcl/unx/gtk/glomenu.cxx -vcl/unx/gtk/gtkdata.cxx -vcl/unx/gtk/gtkinst.cxx -vcl/unx/gtk/gtkobject.cxx -vcl/unx/gtk/gtkprintwrapper.cxx -vcl/unx/gtk/gtksalframe.cxx -vcl/unx/gtk/gtksalmenu.cxx -vcl/unx/gtk/gtksys.cxx -vcl/unx/gtk/hudawareness.cxx -vcl/unx/gtk/salnativewidgets-gtk.cxx -vcl/unx/gtk/salprn-gtk.cxx -vcl/unx/gtk/xid_fullscreen_on_all_monitors.c +vcl/unx/gtk3/a11y/atkfactory.hxx +vcl/unx/gtk3/a11y/atklistener.hxx +vcl/unx/gtk3/a11y/atkregistry.hxx +vcl/unx/gtk3/a11y/atktextattributes.hxx +vcl/unx/gtk3/a11y/atkutil.hxx +vcl/unx/gtk3/a11y/atkwindow.hxx +vcl/unx/gtk3/a11y/atkwrapper.hxx vcl/unx/gtk3/a11y/gtk3atkaction.cxx vcl/unx/gtk3/a11y/gtk3atkbridge.cxx vcl/unx/gtk3/a11y/gtk3atkcomponent.cxx @@ -18175,9 +18137,16 @@ vcl/unx/gtk3/a11y/gtk3atkutil.cxx vcl/unx/gtk3/a11y/gtk3atkvalue.cxx vcl/unx/gtk3/a11y/gtk3atkwindow.cxx vcl/unx/gtk3/a11y/gtk3atkwrapper.cxx +vcl/unx/gtk3/fpicker/SalGtkFilePicker.cxx +vcl/unx/gtk3/fpicker/SalGtkFilePicker.hxx +vcl/unx/gtk3/fpicker/SalGtkFolderPicker.cxx +vcl/unx/gtk3/fpicker/SalGtkFolderPicker.hxx +vcl/unx/gtk3/fpicker/SalGtkPicker.cxx +vcl/unx/gtk3/fpicker/SalGtkPicker.hxx +vcl/unx/gtk3/fpicker/eventnotification.hxx +vcl/unx/gtk3/fpicker/resourceprovider.cxx vcl/unx/gtk3/cairo_gtk3_cairo.cxx vcl/unx/gtk3/cairo_gtk3_cairo.hxx -vcl/unx/gtk3/gtk3fpicker.cxx vcl/unx/gtk3/gtk3gloactiongroup.cxx vcl/unx/gtk3/gtk3glomenu.cxx vcl/unx/gtk3/gtk3gtkdata.cxx diff --git a/solenv/gbuild/CppunitTest.mk b/solenv/gbuild/CppunitTest.mk index 7c5c251eff46..0d40bd6cdb5d 100644 --- a/solenv/gbuild/CppunitTest.mk +++ b/solenv/gbuild/CppunitTest.mk @@ -224,7 +224,6 @@ ifeq ($(USING_X11),TRUE) $(call gb_CppunitTest_get_target,$(1)) : $(call gb_Library_get_target,desktop_detector) $(call gb_CppunitTest_get_target,$(1)) : $(if $(filter $(2),$(true)),, \ $(call gb_Library_get_target,vclplug_gen) \ - $(if $(ENABLE_GTK),$(call gb_Library_get_target,vclplug_gtk)) \ $(if $(ENABLE_GTK3),$(call gb_Library_get_target,vclplug_gtk3)) \ $(if $(ENABLE_QT5),$(call gb_Library_get_target,vclplug_qt5)) \ ) diff --git a/vcl/Executable_xid_fullscreen_on_all_monitors.mk b/vcl/Executable_xid_fullscreen_on_all_monitors.mk deleted file mode 100644 index 296436abd106..000000000000 --- a/vcl/Executable_xid_fullscreen_on_all_monitors.mk +++ /dev/null @@ -1,22 +0,0 @@ -# -*- 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_Executable_Executable,xid-fullscreen-on-all-monitors)) - -ifeq ($(filter ANDROID WNT DRAGONFLY FREEBSD NETBSD OPENBSD MACOSX,$(OS)),) -$(eval $(call gb_Executable_add_libs,xid-fullscreen-on-all-monitors,\ - -ldl \ -)) -endif - -$(eval $(call gb_Executable_add_cobjects,xid-fullscreen-on-all-monitors,\ - vcl/unx/gtk/xid_fullscreen_on_all_monitors \ -)) - -# vim: set noet sw=4 ts=4: diff --git a/vcl/Library_vclplug_gtk.mk b/vcl/Library_vclplug_gtk.mk deleted file mode 100644 index 360eabf46020..000000000000 --- a/vcl/Library_vclplug_gtk.mk +++ /dev/null @@ -1,132 +0,0 @@ -# -*- 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,vclplug_gtk)) - -$(eval $(call gb_Library_set_include,vclplug_gtk,\ - $$(INCLUDE) \ - -I$(SRCDIR)/vcl/inc \ - -I$(SRCDIR)/vcl/unx \ - -I$(SRCDIR)/vcl/unx/gtk \ -)) - -$(eval $(call gb_Library_add_defs,vclplug_gtk,\ - -DVCLPLUG_GTK_IMPLEMENTATION \ -)) - -$(eval $(call gb_Library_use_custom_headers,vclplug_gtk,\ - officecfg/registry \ -)) - -$(eval $(call gb_Library_use_sdk_api,vclplug_gtk)) - -$(eval $(call gb_Library_use_libraries,vclplug_gtk,\ - vclplug_gen \ - vcl \ - tl \ - utl \ - sot \ - ucbhelper \ - basegfx \ - comphelper \ - cppuhelper \ - i18nlangtag \ - i18nutil \ - $(if $(ENABLE_JAVA), \ - jvmaccess) \ - cppu \ - sal \ -)) - -$(eval $(call gb_Library_use_externals,vclplug_gtk,\ - boost_headers \ - cairo \ - dbus \ - epoxy \ - gio \ - graphite \ - gthread \ - gtk \ - harfbuzz \ - icuuc \ -)) - -$(eval $(call gb_Library_add_libs,vclplug_gtk,\ - -lX11 \ - -lXext \ - -lSM \ - -lICE \ -)) - -$(eval $(call gb_Library_add_exception_objects,vclplug_gtk,\ - vcl/unx/gtk/a11y/atkaction \ - vcl/unx/gtk/a11y/atkbridge \ - vcl/unx/gtk/a11y/atkcomponent \ - vcl/unx/gtk/a11y/atkeditabletext \ - vcl/unx/gtk/a11y/atkfactory \ - vcl/unx/gtk/a11y/atkhypertext \ - vcl/unx/gtk/a11y/atkimage \ - vcl/unx/gtk/a11y/atklistener \ - vcl/unx/gtk/a11y/atkregistry \ - vcl/unx/gtk/a11y/atkselection \ - vcl/unx/gtk/a11y/atktable \ - vcl/unx/gtk/a11y/atktextattributes \ - vcl/unx/gtk/a11y/atktext \ - vcl/unx/gtk/a11y/atkutil \ - vcl/unx/gtk/a11y/atkvalue \ - vcl/unx/gtk/a11y/atkwindow \ - vcl/unx/gtk/a11y/atkwrapper \ - vcl/unx/gtk/gtkdata \ - vcl/unx/gtk/gtkinst \ - vcl/unx/gtk/gtksys \ - vcl/unx/gtk/salnativewidgets-gtk \ - vcl/unx/gtk/gtksalframe \ - vcl/unx/gtk/gtkobject \ - vcl/unx/gtk/fpicker/resourceprovider \ - vcl/unx/gtk/fpicker/SalGtkPicker \ - vcl/unx/gtk/fpicker/SalGtkFilePicker \ - vcl/unx/gtk/fpicker/SalGtkFolderPicker \ -)) - -ifneq ($(ENABLE_DBUS),) -ifneq ($(ENABLE_GIO),) -$(eval $(call gb_Library_add_exception_objects,vclplug_gtk,\ - vcl/unx/gtk/gloactiongroup \ - vcl/unx/gtk/gtksalmenu \ - vcl/unx/gtk/glomenu \ - vcl/unx/gtk/hudawareness \ -)) -endif -endif - -ifeq ($(ENABLE_GTK_PRINT),TRUE) -$(eval $(call gb_Library_add_exception_objects,vclplug_gtk,\ - vcl/unx/gtk/gtkprintwrapper \ - vcl/unx/gtk/salprn-gtk \ -)) -endif - -ifeq ($(OS),LINUX) -$(eval $(call gb_Library_add_libs,vclplug_gtk,\ - -lm \ - -ldl \ -)) -endif - -# vim: set noet sw=4 ts=4: diff --git a/vcl/Library_vclplug_gtk3.mk b/vcl/Library_vclplug_gtk3.mk index c0ad6cd21e52..9f6504c38f56 100644 --- a/vcl/Library_vclplug_gtk3.mk +++ b/vcl/Library_vclplug_gtk3.mk @@ -46,7 +46,6 @@ $(eval $(call gb_Library_use_sdk_api,vclplug_gtk3)) $(eval $(call gb_Library_add_libs,vclplug_gtk3,\ $(GTK3_LIBS) \ - $(GTHREAD_LIBS) \ -lX11 \ -lXext \ -lSM \ @@ -97,10 +96,13 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_gtk3,\ vcl/unx/gtk3/a11y/gtk3atkvalue \ vcl/unx/gtk3/a11y/gtk3atkwindow \ vcl/unx/gtk3/a11y/gtk3atkwrapper \ + vcl/unx/gtk3/fpicker/resourceprovider \ + vcl/unx/gtk3/fpicker/SalGtkFilePicker \ + vcl/unx/gtk3/fpicker/SalGtkFolderPicker \ + vcl/unx/gtk3/fpicker/SalGtkPicker \ vcl/unx/gtk3/gtk3gtkdata \ vcl/unx/gtk3/gtk3gtkinst \ vcl/unx/gtk3/gtk3gtksys \ - vcl/unx/gtk3/gtk3fpicker \ vcl/unx/gtk3/cairo_gtk3_cairo \ vcl/unx/gtk3/gtk3gtkprintwrapper \ vcl/unx/gtk3/gtk3salnativewidgets-gtk \ diff --git a/vcl/Library_vclplug_gtk3_kde5.mk b/vcl/Library_vclplug_gtk3_kde5.mk index d67c53e2a6f5..231726449c4e 100644 --- a/vcl/Library_vclplug_gtk3_kde5.mk +++ b/vcl/Library_vclplug_gtk3_kde5.mk @@ -51,7 +51,6 @@ $(eval $(call gb_Library_use_sdk_api,vclplug_gtk3_kde5)) $(eval $(call gb_Library_add_libs,vclplug_gtk3_kde5,\ $(GTK3_LIBS) \ - $(GTHREAD_LIBS) \ -lX11 \ -lXext \ -lSM \ diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk index 54e3fbd6b07f..859702ca4032 100644 --- a/vcl/Module_vcl.mk +++ b/vcl/Module_vcl.mk @@ -66,12 +66,6 @@ $(eval $(call gb_Module_add_targets,vcl,\ Package_fontunxpsprint \ )) -ifneq ($(ENABLE_GTK),) -$(eval $(call gb_Module_add_targets,vcl,\ - Executable_xid_fullscreen_on_all_monitors \ - Library_vclplug_gtk \ -)) -endif ifneq ($(ENABLE_GTK3),) $(eval $(call gb_Module_add_targets,vcl,\ Library_vclplug_gtk3 \ diff --git a/vcl/inc/unx/gtk/gtkdata.hxx b/vcl/inc/unx/gtk/gtkdata.hxx index f1750b7dc3a6..84e2d19f6c94 100644 --- a/vcl/inc/unx/gtk/gtkdata.hxx +++ b/vcl/inc/unx/gtk/gtkdata.hxx @@ -42,44 +42,22 @@ class DocumentFocusListener; inline GdkWindow * widget_get_window(GtkWidget *widget) { -#if GTK_CHECK_VERSION(3,0,0) return gtk_widget_get_window(widget); -#else - return widget->window; -#endif } inline ::Window widget_get_xid(GtkWidget *widget) { -#if GTK_CHECK_VERSION(3,0,0) return GDK_WINDOW_XID(gtk_widget_get_window(widget)); -#else - return GDK_WINDOW_XWINDOW(widget->window); -#endif } inline void widget_set_can_focus(GtkWidget *widget, gboolean can_focus) { -#if GTK_CHECK_VERSION(3,0,0) return gtk_widget_set_can_focus(widget, can_focus); -#else - if (can_focus) - GTK_WIDGET_SET_FLAGS( widget, GTK_CAN_FOCUS ); - else - GTK_WIDGET_UNSET_FLAGS( widget, GTK_CAN_FOCUS ); -#endif } inline void widget_set_can_default(GtkWidget *widget, gboolean can_default) { -#if GTK_CHECK_VERSION(3,0,0) return gtk_widget_set_can_default(widget, can_default); -#else - if (can_default) - GTK_WIDGET_SET_FLAGS( widget, GTK_CAN_DEFAULT ); - else - GTK_WIDGET_UNSET_FLAGS( widget, GTK_CAN_DEFAULT ); -#endif } class GtkSalTimer final : public SalTimer @@ -132,11 +110,7 @@ public: class GtkSalFrame; -#if GTK_CHECK_VERSION(3,0,0) class GtkSalDisplay : public SalGenericDisplay -#else -class GtkSalDisplay : public SalDisplay -#endif { GtkSalSystem* m_pSys; GdkDisplay* const m_pGdkDisplay; @@ -161,20 +135,11 @@ public: virtual void deregisterFrame( SalFrame* pFrame ) override; GdkCursor *getCursor( PointerStyle ePointerStyle ); -#if GTK_CHECK_VERSION(3,0,0) virtual int CaptureMouse( SalFrame* pFrame ); -#else - virtual int CaptureMouse( SalFrame* pFrame ) override; -#endif SalX11Screen GetDefaultXScreen() { return m_pSys->GetDisplayDefaultXScreen(); } Size GetScreenSize( int nDisplayScreen ); int GetXScreenCount() { return m_pSys->GetDisplayXScreenCount(); } -#if GTK_CHECK_VERSION(3,0,0) -// int GetScreenCount() { return m_pSys->GetDisplayScreenCount(); } -#else - virtual ScreenData *initScreen( SalX11Screen nXScreen ) const override; -#endif GdkFilterReturn filterGdkEvent( GdkXEvent* sys_event ); void startupNotificationCompleted() { m_bStartupCompleted = true; } @@ -185,12 +150,7 @@ public: virtual void TriggerUserEventProcessing() override; virtual void TriggerAllUserEventsProcessed() override; -#if !GTK_CHECK_VERSION(3,0,0) - virtual bool Dispatch( XEvent *pEvent ) override; -#endif -#if GTK_CHECK_VERSION(3,0,0) void RefreshMenusUnity(); -#endif }; inline GtkSalData* GetGtkSalData() diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx index 084339f96d70..f9a41c6c3cf6 100644 --- a/vcl/inc/unx/gtk/gtkframe.hxx +++ b/vcl/inc/unx/gtk/gtkframe.hxx @@ -24,9 +24,7 @@ #include <gdk/gdk.h> #include <gdk/gdkx.h> #include <gtk/gtk.h> -#if GTK_CHECK_VERSION(3,0,0) -# include <gtk/gtkx.h> -#endif +#include <gtk/gtkx.h> #include <gdk/gdkkeysyms.h> #include <salframe.hxx> @@ -54,7 +52,6 @@ class GtkSalGraphics; class GtkSalDisplay; -#if GTK_CHECK_VERSION(3,0,0) typedef sal_uIntPtr GdkNativeWindow; #define GDK_WINDOW_XWINDOW(o) GDK_WINDOW_XID(o) #define gdk_set_sm_client_id(i) gdk_x11_set_sm_client_id(i) @@ -62,11 +59,6 @@ typedef sal_uIntPtr GdkNativeWindow; class GtkDropTarget; class GtkDragSource; class GtkDnDTransferable; -#endif - -#if !(GLIB_MAJOR_VERSION > 2 || GLIB_MINOR_VERSION >= 26) - typedef void GDBusConnection; -#endif class GtkSalMenu; @@ -172,10 +164,8 @@ class GtkSalFrame : public SalFrame SalX11Screen m_nXScreen; GtkWidget* m_pWindow; -#if GTK_CHECK_VERSION(3,0,0) GtkHeaderBar* m_pHeaderBar; GtkGrid* m_pTopLevelGrid; -#endif GtkEventBox* m_pEventBox; GtkFixed* m_pFixedContainer; GdkWindow* m_pForeignParent; @@ -208,7 +198,6 @@ class GtkSalFrame : public SalFrame Size m_aMinSize; tools::Rectangle m_aRestorePosSize; -#if GTK_CHECK_VERSION(3,0,0) OUString m_aTooltip; tools::Rectangle m_aHelpArea; tools::Rectangle m_aFloatRect; @@ -220,10 +209,6 @@ class GtkSalFrame : public SalFrame GtkDropTarget* m_pDropTarget; GtkDragSource* m_pDragSource; bool m_bGeometryIsProvisional; -#else - GdkRegion* m_pRegion; - bool m_bSetFocusOnMap; -#endif GtkSalMenu* m_pSalMenu; @@ -242,12 +227,7 @@ class GtkSalFrame : public SalFrame // signals static gboolean signalButton( GtkWidget*, GdkEventButton*, gpointer ); -#if GTK_CHECK_VERSION(3,0,0) static void signalStyleUpdated(GtkWidget*, gpointer); -#else - static void signalStyleSet(GtkWidget*, GtkStyle* pPrevious, gpointer); -#endif -#if GTK_CHECK_VERSION(3,0,0) static gboolean signalDraw( GtkWidget*, cairo_t *cr, gpointer ); static void signalRealize(GtkWidget*, gpointer frame); static void sizeAllocated(GtkWidget*, GdkRectangle *pAllocation, gpointer frame); @@ -270,11 +250,6 @@ class GtkSalFrame : public SalFrame static void gestureSwipe(GtkGestureSwipe* gesture, gdouble velocity_x, gdouble velocity_y, gpointer frame); static void gestureLongPress(GtkGestureLongPress* gesture, gdouble x, gdouble y, gpointer frame); -#else - static gboolean signalExpose( GtkWidget*, GdkEventExpose*, gpointer ); - void askForXEmbedFocus( sal_Int32 nTimecode ); - void grabKeyboard(bool bGrab); -#endif static gboolean signalFocus( GtkWidget*, GdkEventFocus*, gpointer ); static gboolean signalMap( GtkWidget*, GdkEvent*, gpointer ); static gboolean signalUnmap( GtkWidget*, GdkEvent*, gpointer ); @@ -345,7 +320,6 @@ class GtkSalFrame : public SalFrame void SetScreen( unsigned int nNewScreen, SetType eType, tools::Rectangle const *pSize = nullptr ); public: -#if GTK_CHECK_VERSION(3,0,0) cairo_surface_t* m_pSurface; basegfx::B2IVector m_aFrameSize; DamageHandler m_aDamageHandler; @@ -353,7 +327,6 @@ public: Idle m_aSmoothScrollIdle; int m_nGrabLevel; bool m_bSalObjectSetPosSize; -#endif GtkSalFrame( SalFrame* pParent, SalFrameStyleFlags nStyle ); GtkSalFrame( SystemParentData* pSysData ); @@ -374,9 +347,7 @@ public: GtkFixed* getFixedContainer() const { return m_pFixedContainer; } GtkEventBox* getEventBox() const { return m_pEventBox; } GtkWidget* getMouseEventWidget() const; -#if GTK_CHECK_VERSION(3,0,0) GtkGrid* getTopLevelGridWidget() const { return m_pTopLevelGrid; } -#endif GdkWindow* getForeignParent() const { return m_pForeignParent; } GdkNativeWindow getForeignParentWindow() const { return m_aForeignParentWindow; } GdkWindow* getForeignTopLevel() const { return m_pForeignTopLevel; } @@ -385,8 +356,6 @@ public: int GetDisplayScreen() const { return maGeometry.nDisplayScreenNumber; } void updateScreenNumber(); -#if GTK_CHECK_VERSION(3,0,0) - // only for gtk3 ... cairo_t* getCairoContext() const; void damaged(sal_Int32 nExtentsLeft, sal_Int32 nExtentsTop, sal_Int32 nExtentsRight, sal_Int32 nExtentsBottom) const; @@ -428,7 +397,6 @@ public: void LaunchAsyncScroll(GdkEvent const * pEvent); DECL_LINK(AsyncScroll, Timer *, void); -#endif virtual ~GtkSalFrame() override; // SalGraphics or NULL, but two Graphics for all SalFrames @@ -525,7 +493,6 @@ public: // done setting up the clipregion virtual void EndSetClipRegion() override; -#if GTK_CHECK_VERSION(3,0,0) virtual void PositionByToolkit(const tools::Rectangle& rRect, FloatWinPopupFlags nFlags) override; virtual void SetModal(bool bModal) override; virtual bool GetModal() const override; @@ -535,7 +502,6 @@ public: virtual bool UpdatePopover(void* nId, const OUString& rHelpText, vcl::Window* pParent, const tools::Rectangle& rHelpArea) override; virtual bool HidePopover(void* nId) override; virtual weld::Window* GetFrameWeld() const override; -#endif static GtkSalFrame *getFromWindow( GtkWindow *pWindow ); diff --git a/vcl/inc/unx/gtk/gtkgdi.hxx b/vcl/inc/unx/gtk/gtkgdi.hxx index c9b7be49f13e..e19d01205e49 100644 --- a/vcl/inc/unx/gtk/gtkgdi.hxx +++ b/vcl/inc/unx/gtk/gtkgdi.hxx @@ -29,8 +29,6 @@ #include <unx/gtk/gtkframe.hxx> #include <ControlCacheKey.hxx> -#if GTK_CHECK_VERSION(3,0,0) - #include <headless/svpgdi.hxx> #include <textrender.hxx> @@ -394,8 +392,6 @@ protected: ControlState nState, const ImplControlValue& aValue ); }; -#endif // !gtk3 - #endif // INCLUDED_VCL_INC_UNX_GTK_GTKGDI_HXX /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/unx/gtk/gtkinst.hxx b/vcl/inc/unx/gtk/gtkinst.hxx index 65caa384c0ae..78dd9bc6e8b9 100644 --- a/vcl/inc/unx/gtk/gtkinst.hxx +++ b/vcl/inc/unx/gtk/gtkinst.hxx @@ -57,7 +57,6 @@ public: void ThreadsLeave(); }; -#if GTK_CHECK_VERSION(3,0,0) class GtkSalFrame; struct VclToGtkHelper @@ -182,20 +181,10 @@ public: css::uno::Reference<css::datatransfer::XTransferable> const & GetTransferrable() const { return m_xTrans; } }; -#endif - class GtkSalTimer; -#if GTK_CHECK_VERSION(3,0,0) class GtkInstance : public SvpSalInstance -#else -class GtkInstance : public X11SalInstance -#endif { -#if GTK_CHECK_VERSION(3,0,0) typedef SvpSalInstance Superclass_t; -#else - typedef X11SalInstance Superclass_t; -#endif public: GtkInstance( std::unique_ptr<SalYieldMutex> pMutex ); virtual ~GtkInstance() override; @@ -233,7 +222,6 @@ public: virtual css::uno::Reference< css::ui::dialogs::XFolderPicker2 > createFolderPicker( const css::uno::Reference< css::uno::XComponentContext >& ) override; -#if GTK_CHECK_VERSION(3,0,0) virtual css::uno::Reference< css::uno::XInterface > CreateClipboard( const css::uno::Sequence< css::uno::Any >& i_rArguments ) override; virtual css::uno::Reference< css::uno::XInterface > CreateDragSource() override; virtual css::uno::Reference< css::uno::XInterface > CreateDropTarget() override; @@ -241,7 +229,6 @@ public: virtual weld::Builder* CreateBuilder(weld::Widget* pParent, const OUString& rUIRoot, const OUString& rUIFile) override; virtual weld::MessageDialog* CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString &rPrimaryMessage) override; virtual weld::Window* GetFrameWeld(const css::uno::Reference<css::awt::XWindow>& rWindow) override; -#endif virtual const cairo_font_options_t* GetCairoFontOptions() override; const cairo_font_options_t* GetLastSeenCairoFontOptions() const; @@ -251,15 +238,11 @@ public: std::shared_ptr<vcl::unx::GtkPrintWrapper> const & getPrintWrapper() const; -#if GTK_CHECK_VERSION(3,0,0) void* CreateGStreamerSink(const SystemChildWindow*) override; -#endif private: GtkSalTimer *m_pTimer; -#if GTK_CHECK_VERSION(3,0,0) std::unordered_map< GdkAtom, css::uno::Reference<css::uno::XInterface> > m_aClipboards; -#endif bool IsTimerExpired(); bool bNeedsInit; cairo_font_options_t* m_pLastCairoFontOptions; diff --git a/vcl/inc/unx/gtk/gtkobject.hxx b/vcl/inc/unx/gtk/gtkobject.hxx index 4523e9998abd..f54d7af3f04c 100644 --- a/vcl/inc/unx/gtk/gtkobject.hxx +++ b/vcl/inc/unx/gtk/gtkobject.hxx @@ -29,12 +29,8 @@ class GtkSalObject : public SalObject { SystemEnvData m_aSystemData; GtkWidget* m_pSocket; -#if GTK_CHECK_VERSION(3,0,0) GtkSalFrame* m_pParent; cairo_region_t* m_pRegion; -#else - GdkRegion* m_pRegion; -#endif // signals static gboolean signalButton( GtkWidget*, GdkEventButton*, gpointer ); diff --git a/vcl/inc/unx/gtk/gtkprintwrapper.hxx b/vcl/inc/unx/gtk/gtkprintwrapper.hxx index 4862c8cc2fbc..589c800d77d1 100644 --- a/vcl/inc/unx/gtk/gtkprintwrapper.hxx +++ b/vcl/inc/unx/gtk/gtkprintwrapper.hxx @@ -12,14 +12,11 @@ #include <gtk/gtk.h> -#if defined ENABLE_GTK_PRINT || GTK_CHECK_VERSION(3,0,0) #include <gtk/gtkunixprint.h> #include <osl/module.hxx> #include <sal/types.h> -#endif - namespace vcl { namespace unx @@ -30,7 +27,6 @@ class GtkPrintWrapper private: GtkPrintWrapper(const GtkPrintWrapper&) = delete; GtkPrintWrapper& operator=(const GtkPrintWrapper&) = delete; -#if defined ENABLE_GTK_PRINT || GTK_CHECK_VERSION(3,0,0) public: GtkPrintWrapper(); ~GtkPrintWrapper(); @@ -63,59 +59,6 @@ public: // print selection support, since 2.17.4 void print_unix_dialog_set_support_selection(GtkPrintUnixDialog* dialog, gboolean support_selection) const; void print_unix_dialog_set_has_selection(GtkPrintUnixDialog* dialog, gboolean has_selection) const; - -#if !GTK_CHECK_VERSION(3,0,0) -private: - void impl_load(); - -private: - typedef GtkPageSetup* (* page_setup_new_t)(); - typedef GtkPrintJob* (* print_job_new_t)(const gchar*, GtkPrinter*, GtkPrintSettings*, GtkPageSetup*); - typedef void (* print_job_send_t)(GtkPrintJob*, GtkPrintJobCompleteFunc, gpointer, GDestroyNotify); - typedef gboolean (* print_job_set_source_file_t)(GtkPrintJob*, const gchar*, GError**); - typedef const gchar* (* print_settings_get_t)(GtkPrintSettings*, const gchar*); - typedef gboolean (* print_settings_get_collate_t)(GtkPrintSettings*); - typedef void (* print_settings_set_collate_t)(GtkPrintSettings*, gboolean); - typedef gint (* print_settings_get_n_copies_t)(GtkPrintSettings*); - typedef void (* print_settings_set_n_copies_t)(GtkPrintSettings*, gint); - typedef GtkPageRange* (* print_settings_get_page_ranges_t)(GtkPrintSettings*, gint*); - typedef void (* print_settings_set_print_pages_t)(GtkPrintSettings*, GtkPrintPages); - typedef GtkWidget* (* print_unix_dialog_new_t)(const gchar*, GtkWindow*); - typedef void (* print_unix_dialog_add_custom_tab_t)(GtkPrintUnixDialog*, GtkWidget*, GtkWidget*); - typedef GtkPrinter* (* print_unix_dialog_get_selected_printer_t)(GtkPrintUnixDialog*); - typedef void (* print_unix_dialog_set_manual_capabilities_t)(GtkPrintUnixDialog*, GtkPrintCapabilities); - typedef GtkPrintSettings* (* print_unix_dialog_get_settings_t)(GtkPrintUnixDialog*); - typedef void (* print_unix_dialog_set_settings_t)(GtkPrintUnixDialog*, GtkPrintSettings*); - typedef void (* print_unix_dialog_set_support_selection_t)(GtkPrintUnixDialog*, gboolean); - typedef void (* print_unix_dialog_set_has_selection_t)(GtkPrintUnixDialog*, gboolean); - -private: - osl::Module m_aModule; - - // general printing support, since 2.10.0 - page_setup_new_t m_page_setup_new; - print_job_new_t m_print_job_new; - print_job_send_t m_print_job_send; - print_job_set_source_file_t m_print_job_set_source_file; - print_settings_get_t m_print_settings_get; - print_settings_get_collate_t m_print_settings_get_collate; - print_settings_set_collate_t m_print_settings_set_collate; - print_settings_get_n_copies_t m_print_settings_get_n_copies; - print_settings_set_n_copies_t m_print_settings_set_n_copies; - print_settings_get_page_ranges_t m_print_settings_get_page_ranges; - print_settings_set_print_pages_t m_print_settings_set_print_pages; - print_unix_dialog_new_t m_print_unix_dialog_new; - print_unix_dialog_add_custom_tab_t m_print_unix_dialog_add_custom_tab; - print_unix_dialog_get_selected_printer_t m_print_unix_dialog_get_selected_printer; - print_unix_dialog_set_manual_capabilities_t m_print_unix_dialog_set_manual_capabilities; - print_unix_dialog_get_settings_t m_print_unix_dialog_get_settings; - print_unix_dialog_set_settings_t m_print_unix_dialog_set_settings; - - // print selection support, since 2.17.4 - print_unix_dialog_set_support_selection_t m_print_unix_dialog_set_support_selection; - print_unix_dialog_set_has_selection_t m_print_unix_dialog_set_has_selection; -#endif -#endif }; } diff --git a/vcl/inc/unx/gtk/gtksalmenu.hxx b/vcl/inc/unx/gtk/gtksalmenu.hxx index 2d812cad3962..ddef9ef42310 100644 --- a/vcl/inc/unx/gtk/gtksalmenu.hxx +++ b/vcl/inc/unx/gtk/gtksalmenu.hxx @@ -23,27 +23,8 @@ #include <unotools/tempfile.hxx> #include <vcl/idle.hxx> -#if GTK_CHECK_VERSION(3,0,0) -# define ENABLE_GMENU_INTEGRATION -# include <unx/gtk/glomenu.h> -# include <unx/gtk/gloactiongroup.h> -#elif ENABLE_DBUS && ENABLE_GIO && \ - (GLIB_MAJOR_VERSION > 2 || GLIB_MINOR_VERSION >= 36) -# define ENABLE_GMENU_INTEGRATION -# include <unx/gtk/glomenu.h> -# include <unx/gtk/gloactiongroup.h> -#else -# if !(GLIB_MAJOR_VERSION > 2 || GLIB_MINOR_VERSION >= 32) - typedef void GMenuModel; -# endif -# if !(GLIB_MAJOR_VERSION > 2 || GLIB_MINOR_VERSION >= 28) - typedef void GActionGroup; -# endif -#endif - -#if !GTK_CHECK_VERSION(3,0,0) -typedef void GtkCssProvider; -#endif +#include <unx/gtk/glomenu.h> +#include <unx/gtk/gloactiongroup.h> class MenuItemList; class GtkSalMenuItem; diff --git a/vcl/source/app/salplug.cxx b/vcl/source/app/salplug.cxx index 20604edb7f46..ce3fb551d6af 100644 --- a/vcl/source/app/salplug.cxx +++ b/vcl/source/app/salplug.cxx @@ -95,7 +95,7 @@ SalInstance* tryInstance( const OUString& rModuleBase, bool bForce = false ) * So make sure libgtk+ & co are still mapped into memory when * atk-bridge's atexit handler gets called. */ - if( aUsedModuleBase == "gtk" || aUsedModuleBase == "gtk3" || aUsedModuleBase == "gtk3_kde5" || aUsedModuleBase == "win" ) + if( aUsedModuleBase == "gtk3" || aUsedModuleBase == "gtk3_kde5" || aUsedModuleBase == "win" ) { pCloseModule = nullptr; } @@ -154,12 +154,12 @@ SalInstance* autodetect_plugin() #if ENABLE_GTK3_KDE5 "gtk3_kde5", #endif - "gtk3", "gtk", "gen", nullptr + "gtk3", "gen", nullptr }; static const char* const pStandardFallbackList[] = { - "gtk3", "gtk", "gen", nullptr + "gtk3", "gen", nullptr }; #ifdef HEADLESS_VCLPLUG @@ -259,7 +259,7 @@ SalInstance *CreateSalInstance() #ifdef MACOSX "osx" #else - "gtk3", "gtk", "kf5", "gen" + "gtk3", "kf5", "gen" #endif #endif }; diff --git a/vcl/unx/gtk/a11y/atkaction.cxx b/vcl/unx/gtk/a11y/atkaction.cxx deleted file mode 100644 index 14a172fe6ae3..000000000000 --- a/vcl/unx/gtk/a11y/atkaction.cxx +++ /dev/null @@ -1,275 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atkwrapper.hxx" - -#include <com/sun/star/accessibility/XAccessibleAction.hpp> -#include <com/sun/star/accessibility/XAccessibleKeyBinding.hpp> - -#include <com/sun/star/awt/Key.hpp> -#include <com/sun/star/awt/KeyModifier.hpp> - -#include <rtl/strbuf.hxx> -#include <algorithm> -#include <map> - -using namespace ::com::sun::star; - -// FIXME -static const gchar * -getAsConst( const OString& rString ) -{ - static const int nMax = 10; - static OString aUgly[nMax]; - static int nIdx = 0; - nIdx = (nIdx + 1) % nMax; - aUgly[nIdx] = rString; - return aUgly[ nIdx ].getStr(); -} - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleAction> - getAction( AtkAction *action ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( action ); - - if( pWrap ) - { - if( !pWrap->mpAction.is() ) - { - pWrap->mpAction.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpAction; - } - - return css::uno::Reference<css::accessibility::XAccessibleAction>(); -} - -extern "C" { - -static gboolean -action_wrapper_do_action (AtkAction *action, - gint i) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleAction> pAction - = getAction( action ); - if( pAction.is() ) - return pAction->doAccessibleAction( i ); - } - catch(const uno::Exception&) { - g_warning( "Exception in doAccessibleAction()" ); - } - - return FALSE; -} - -static gint -action_wrapper_get_n_actions (AtkAction *action) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleAction> pAction - = getAction( action ); - if( pAction.is() ) - return pAction->getAccessibleActionCount(); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleActionCount()" ); - } - - return 0; -} - -static const gchar * -action_wrapper_get_description (AtkAction *, gint) -{ - // GAIL implement this only for cells - g_warning( "Not implemented: get_description()" ); - return ""; -} - -static const gchar * -action_wrapper_get_localized_name (AtkAction *, gint) -{ - // GAIL doesn't implement this as well - g_warning( "Not implemented: get_localized_name()" ); - return ""; -} - -#define ACTION_NAME_PAIR( OOoName, AtkName ) \ - std::pair< const OUString, const gchar * > ( OUString( OOoName ), AtkName ) - -static const gchar * -action_wrapper_get_name (AtkAction *action, - gint i) -{ - static std::map< OUString, const gchar * > aNameMap; - - if( aNameMap.empty() ) - { - aNameMap.insert( ACTION_NAME_PAIR( "click", "click" ) ); - aNameMap.insert( ACTION_NAME_PAIR( "select", "click" ) ); - aNameMap.insert( ACTION_NAME_PAIR( "togglePopup", "push" ) ); - } - - try { - css::uno::Reference<css::accessibility::XAccessibleAction> pAction - = getAction( action ); - if( pAction.is() ) - { - std::map< OUString, const gchar * >::iterator iter; - - OUString aDesc( pAction->getAccessibleActionDescription( i ) ); - - iter = aNameMap.find( aDesc ); - if( iter != aNameMap.end() ) - return iter->second; - - std::pair< const OUString, const gchar * > aNewVal( aDesc, - g_strdup( OUStringToConstGChar(aDesc) ) ); - - if( aNameMap.insert( aNewVal ).second ) - return aNewVal.second; - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleActionDescription()" ); - } - - return ""; -} - -/* -* GNOME Expects a string in the format: -* -* <mnemonic>;<full-path>;<accelerator> -* -* The keybindings in <full-path> should be separated by ":" -*/ - -static void -appendKeyStrokes(OStringBuffer& rBuffer, const uno::Sequence< awt::KeyStroke >& rKeyStrokes) -{ - for( const auto& rKeyStroke : rKeyStrokes ) - { - if( rKeyStroke.Modifiers & awt::KeyModifier::SHIFT ) - rBuffer.append("<Shift>"); - if( rKeyStroke.Modifiers & awt::KeyModifier::MOD1 ) - rBuffer.append("<Control>"); - if( rKeyStroke.Modifiers & awt::KeyModifier::MOD2 ) - rBuffer.append("<Alt>"); - - if( ( rKeyStroke.KeyCode >= awt::Key::A ) && ( rKeyStroke.KeyCode <= awt::Key::Z ) ) - rBuffer.append( static_cast<sal_Char>( 'a' + ( rKeyStroke.KeyCode - awt::Key::A ) ) ); - else - { - sal_Char c = '\0'; - - switch( rKeyStroke.KeyCode ) - { - case awt::Key::TAB: c = '\t'; break; - case awt::Key::SPACE: c = ' '; break; - case awt::Key::ADD: c = '+'; break; - case awt::Key::SUBTRACT: c = '-'; break; - case awt::Key::MULTIPLY: c = '*'; break; - case awt::Key::DIVIDE: c = '/'; break; - case awt::Key::POINT: c = '.'; break; - case awt::Key::COMMA: c = ','; break; - case awt::Key::LESS: c = '<'; break; - case awt::Key::GREATER: c = '>'; break; - case awt::Key::EQUAL: c = '='; break; - case 0: - break; - default: - g_warning( "Unmapped KeyCode: %d", rKeyStroke.KeyCode ); - break; - } - - if( c != '\0' ) - rBuffer.append( c ); - else - { - // The KeyCode approach did not work, probably a non ascii character - // let's hope that there is a character given in KeyChar. - rBuffer.append( OUStringToGChar( OUString( rKeyStroke.KeyChar ) ) ); - } - } - } -} - -static const gchar * -action_wrapper_get_keybinding (AtkAction *action, - gint i) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleAction> pAction - = getAction( action ); - if( pAction.is() ) - { - uno::Reference< accessibility::XAccessibleKeyBinding > xBinding( pAction->getAccessibleActionKeyBinding( i )); - - if( xBinding.is() ) - { - OStringBuffer aRet; - - sal_Int32 nmax = std::min( xBinding->getAccessibleKeyBindingCount(), sal_Int32(3) ); - for( sal_Int32 n = 0; n < nmax; n++ ) - { - appendKeyStrokes( aRet, xBinding->getAccessibleKeyBinding( n ) ); - - if( n < 2 ) - aRet.append( ';' ); - } - - // !! FIXME !! remember keystroke in wrapper object ? - return getAsConst( aRet.makeStringAndClear() ); - } - } - } - catch(const uno::Exception&) { - g_warning( "Exception in get_keybinding()" ); - } - - return ""; -} - -static gboolean -action_wrapper_set_description (AtkAction *, gint, const gchar *) -{ - return FALSE; -} - -} // extern "C" - -void -actionIfaceInit (AtkActionIface *iface) -{ - g_return_if_fail (iface != nullptr); - - iface->do_action = action_wrapper_do_action; - iface->get_n_actions = action_wrapper_get_n_actions; - iface->get_description = action_wrapper_get_description; - iface->get_keybinding = action_wrapper_get_keybinding; - iface->get_name = action_wrapper_get_name; - iface->get_localized_name = action_wrapper_get_localized_name; - iface->set_description = action_wrapper_set_description; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkbridge.cxx b/vcl/unx/gtk/a11y/atkbridge.cxx deleted file mode 100644 index a74f1a91885f..000000000000 --- a/vcl/unx/gtk/a11y/atkbridge.cxx +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 <unx/gtk/atkbridge.hxx> -#include <unx/gtk/gtkframe.hxx> - -#include "atkfactory.hxx" -#include "atkutil.hxx" -#include "atkwindow.hxx" - -bool InitAtkBridge() -{ -#if GTK_CHECK_VERSION(3,0,0) - ooo_atk_util_ensure_event_listener(); -#else - const char* pVersion = atk_get_toolkit_version(); - if( ! pVersion ) - return false; - - unsigned int major, minor, micro; - - /* check gail minimum version requirements */ - if( sscanf( pVersion, "%u.%u.%u", &major, &minor, µ) < 3 ) - { - // g_warning( "unable to parse gail version number" ); - return false; - } - - if( ( (major << 16) | (minor << 8) | micro ) < ( (1 << 16) | 8 << 8 | 6 ) ) - { - g_warning( "libgail >= 1.8.6 required for accessibility support" ); - return false; - } - - /* Initialize the AtkUtilityWrapper class */ - g_type_class_unref( g_type_class_ref( ooo_atk_util_get_type() ) ); - - /* Initialize the GailWindow wrapper class */ - g_type_class_unref( g_type_class_ref( ooo_window_wrapper_get_type() ) ); - - /* Register AtkObject wrapper factory */ - AtkRegistry * registry = atk_get_default_registry(); - if( registry ) - atk_registry_set_factory_type( registry, OOO_TYPE_FIXED, wrapper_factory_get_type() ); -#endif - return true; -} - -void DeInitAtkBridge() -{ -#if !GTK_CHECK_VERSION(3,0,0) - restore_gail_window_vtable(); -#endif -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkcomponent.cxx b/vcl/unx/gtk/a11y/atkcomponent.cxx deleted file mode 100644 index da5a48eca1f2..000000000000 --- a/vcl/unx/gtk/a11y/atkcomponent.cxx +++ /dev/null @@ -1,387 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atkwrapper.hxx" -#include <com/sun/star/accessibility/XAccessibleComponent.hpp> -#include <gtk/gtk.h> - -using namespace ::com::sun::star; - -static AtkObjectWrapper* getObjectWrapper(AtkComponent *pComponent) -{ - AtkObjectWrapper *pWrap = nullptr; - if (ATK_IS_OBJECT_WRAPPER(pComponent)) - pWrap = ATK_OBJECT_WRAPPER(pComponent); - else if (GTK_IS_DRAWING_AREA(pComponent)) //when using a GtkDrawingArea as a custom widget in welded gtk3 - { - GtkWidget* pDrawingArea = GTK_WIDGET(pComponent); - AtkObject* pAtkObject = gtk_widget_get_accessible(pDrawingArea); - pWrap = ATK_IS_OBJECT_WRAPPER(pAtkObject) ? ATK_OBJECT_WRAPPER(pAtkObject) : nullptr; - } - return pWrap; -} - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleComponent> - getComponent(AtkObjectWrapper *pWrap) -{ - if (pWrap) - { - if (!pWrap->mpComponent.is()) - pWrap->mpComponent.set(pWrap->mpContext, css::uno::UNO_QUERY); - return pWrap->mpComponent; - } - - return css::uno::Reference<css::accessibility::XAccessibleComponent>(); -} - -/*****************************************************************************/ - -static awt::Point -translatePoint( css::uno::Reference<accessibility::XAccessibleComponent> const & pComponent, - gint x, gint y, AtkCoordType t) -{ - awt::Point aOrigin( 0, 0 ); - if( t == ATK_XY_SCREEN ) - aOrigin = pComponent->getLocationOnScreen(); - return awt::Point( x - aOrigin.X, y - aOrigin.Y ); -} - -/*****************************************************************************/ - -extern "C" { - -static gboolean -component_wrapper_grab_focus (AtkComponent *component) -{ - AtkObjectWrapper* obj = getObjectWrapper(component); - //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y - if (obj && obj->mpOrig) - return atk_component_grab_focus(ATK_COMPONENT(obj->mpOrig)); - - try - { - css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent - = getComponent(obj); - if( pComponent.is() ) - { - pComponent->grabFocus(); - return TRUE; - } - } - catch( const uno::Exception & ) - { - g_warning( "Exception in grabFocus()" ); - } - - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -component_wrapper_contains (AtkComponent *component, - gint x, - gint y, - AtkCoordType coord_type) -{ - AtkObjectWrapper* obj = getObjectWrapper(component); - //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y - if (obj && obj->mpOrig) - return atk_component_contains(ATK_COMPONENT(obj->mpOrig), x, y, coord_type); - - try - { - css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent - = getComponent(obj); - if( pComponent.is() ) - return pComponent->containsPoint( translatePoint( pComponent, x, y, coord_type ) ); - } - catch( const uno::Exception & ) - { - g_warning( "Exception in containsPoint()" ); - } - - return FALSE; -} - -/*****************************************************************************/ - -static AtkObject * -component_wrapper_ref_accessible_at_point (AtkComponent *component, - gint x, - gint y, - AtkCoordType coord_type) -{ - AtkObjectWrapper* obj = getObjectWrapper(component); - //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y - if (obj && obj->mpOrig) - return atk_component_ref_accessible_at_point(ATK_COMPONENT(obj->mpOrig), x, y, coord_type); - - try - { - css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent - = getComponent(obj); - - if( pComponent.is() ) - { - uno::Reference< accessibility::XAccessible > xAccessible = pComponent->getAccessibleAtPoint( - translatePoint( pComponent, x, y, coord_type ) ); - return atk_object_wrapper_ref( xAccessible ); - } - } - catch( const uno::Exception & ) - { - g_warning( "Exception in getAccessibleAtPoint()" ); - } - - return nullptr; -} - -/*****************************************************************************/ - -static void -component_wrapper_get_position (AtkComponent *component, - gint *x, - gint *y, - AtkCoordType coord_type) -{ - AtkObjectWrapper* obj = getObjectWrapper(component); - //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y - if (obj && obj->mpOrig) - { - atk_component_get_extents(ATK_COMPONENT(obj->mpOrig), x, y, nullptr, nullptr, coord_type); - return; - } - - *x = *y = -1; - - try - { - css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent - = getComponent(obj); - if( pComponent.is() ) - { - awt::Point aPos; - - if( coord_type == ATK_XY_SCREEN ) - aPos = pComponent->getLocationOnScreen(); - else - aPos = pComponent->getLocation(); - - *x = aPos.X; - *y = aPos.Y; - } - } - catch( const uno::Exception & ) - { - g_warning( "Exception in getLocation[OnScreen]()" ); - } -} - -/*****************************************************************************/ - -static void -component_wrapper_get_size (AtkComponent *component, - gint *width, - gint *height) -{ - AtkObjectWrapper* obj = getObjectWrapper(component); - //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y - if (obj && obj->mpOrig) - { - atk_component_get_extents(ATK_COMPONENT(obj->mpOrig), nullptr, nullptr, width, height, ATK_XY_WINDOW); - return; - } - - *width = *height = -1; - - try - { - css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent - = getComponent(obj); - if( pComponent.is() ) - { - awt::Size aSize = pComponent->getSize(); - *width = aSize.Width; - *height = aSize.Height; - } - } - catch( const uno::Exception & ) - { - g_warning( "Exception in getSize()" ); - } -} - -/*****************************************************************************/ - -static void -component_wrapper_get_extents (AtkComponent *component, - gint *x, - gint *y, - gint *width, - gint *height, - AtkCoordType coord_type) -{ - component_wrapper_get_position( component, x, y, coord_type ); - component_wrapper_get_size( component, width, height ); -} - -/*****************************************************************************/ - -static gboolean -component_wrapper_set_extents (AtkComponent *, gint, gint, gint, gint, AtkCoordType) -{ - g_warning( "AtkComponent::set_extents unimplementable" ); - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -component_wrapper_set_position (AtkComponent *, gint, gint, AtkCoordType) -{ - g_warning( "AtkComponent::set_position unimplementable" ); - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -component_wrapper_set_size (AtkComponent *, gint, gint) -{ - g_warning( "AtkComponent::set_size unimplementable" ); - return FALSE; -} - -/*****************************************************************************/ - -static AtkLayer -component_wrapper_get_layer (AtkComponent *component) -{ - AtkRole role = atk_object_get_role( ATK_OBJECT( component ) ); - AtkLayer layer = ATK_LAYER_WIDGET; - - switch (role) - { - case ATK_ROLE_POPUP_MENU: - case ATK_ROLE_MENU_ITEM: - case ATK_ROLE_CHECK_MENU_ITEM: - case ATK_ROLE_SEPARATOR: - case ATK_ROLE_LIST_ITEM: - layer = ATK_LAYER_POPUP; - break; - case ATK_ROLE_MENU: - { - AtkObject * parent = atk_object_get_parent( ATK_OBJECT( component ) ); - if( atk_object_get_role( parent ) != ATK_ROLE_MENU_BAR ) - layer = ATK_LAYER_POPUP; - } - break; - - case ATK_ROLE_LIST: - { - AtkObject * parent = atk_object_get_parent( ATK_OBJECT( component ) ); - if( atk_object_get_role( parent ) == ATK_ROLE_COMBO_BOX ) - layer = ATK_LAYER_POPUP; - } - break; - - default: - ; - } - - return layer; -} - -/*****************************************************************************/ - -static gint -component_wrapper_get_mdi_zorder (AtkComponent *) -{ - // only needed for ATK_LAYER_MDI (not used) or ATK_LAYER_WINDOW (inherited from GAIL) - return G_MININT; -} - -/*****************************************************************************/ - -// This code is mostly stolen from libgail .. - -static guint -component_wrapper_add_focus_handler (AtkComponent *component, - AtkFocusHandler handler) -{ - GSignalMatchType match_type; - gulong ret; - guint signal_id; - - match_type = GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC); - signal_id = g_signal_lookup( "focus-event", ATK_TYPE_OBJECT ); - - ret = g_signal_handler_find( component, match_type, signal_id, 0, nullptr, - static_cast<gpointer>(&handler), nullptr); - if (!ret) - { - return g_signal_connect_closure_by_id (component, - signal_id, 0, - g_cclosure_new ( - G_CALLBACK (handler), nullptr, - nullptr), - FALSE); - } - else - { - return 0; - } -} - -/*****************************************************************************/ - -static void -component_wrapper_remove_focus_handler (AtkComponent *component, - guint handler_id) -{ - g_signal_handler_disconnect (component, handler_id); -} - -/*****************************************************************************/ - -} // extern "C" - -void -componentIfaceInit (AtkComponentIface *iface) -{ - g_return_if_fail (iface != nullptr); - - iface->add_focus_handler = component_wrapper_add_focus_handler; - iface->contains = component_wrapper_contains; - iface->get_extents = component_wrapper_get_extents; - iface->get_layer = component_wrapper_get_layer; - iface->get_mdi_zorder = component_wrapper_get_mdi_zorder; - iface->get_position = component_wrapper_get_position; - iface->get_size = component_wrapper_get_size; - iface->grab_focus = component_wrapper_grab_focus; - iface->ref_accessible_at_point = component_wrapper_ref_accessible_at_point; - iface->remove_focus_handler = component_wrapper_remove_focus_handler; - iface->set_extents = component_wrapper_set_extents; - iface->set_position = component_wrapper_set_position; - iface->set_size = component_wrapper_set_size; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkeditabletext.cxx b/vcl/unx/gtk/a11y/atkeditabletext.cxx deleted file mode 100644 index 49d3eb9ccfc9..000000000000 --- a/vcl/unx/gtk/a11y/atkeditabletext.cxx +++ /dev/null @@ -1,194 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atkwrapper.hxx" -#include "atktextattributes.hxx" - -#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> -#include <com/sun/star/accessibility/TextSegment.hpp> - -#include <string.h> - -using namespace ::com::sun::star; - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleEditableText> - getEditableText( AtkEditableText *pEditableText ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pEditableText ); - if( pWrap ) - { - if( !pWrap->mpEditableText.is() ) - { - pWrap->mpEditableText.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpEditableText; - } - - return css::uno::Reference<css::accessibility::XAccessibleEditableText>(); -} - -/*****************************************************************************/ - -extern "C" { - -static gboolean -editable_text_wrapper_set_run_attributes( AtkEditableText *text, - AtkAttributeSet *attribute_set, - gint nStartOffset, - gint nEndOffset) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleEditableText> - pEditableText = getEditableText( text ); - if( pEditableText.is() ) - { - uno::Sequence< beans::PropertyValue > aAttributeList; - - if( attribute_set_map_to_property_values( attribute_set, aAttributeList ) ) - return pEditableText->setAttributes(nStartOffset, nEndOffset, aAttributeList); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in setAttributes()" ); - } - - return FALSE; -} - -static void -editable_text_wrapper_set_text_contents( AtkEditableText *text, - const gchar *string ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleEditableText> - pEditableText = getEditableText( text ); - if( pEditableText.is() ) - { - OUString aString ( string, strlen(string), RTL_TEXTENCODING_UTF8 ); - pEditableText->setText( aString ); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in setText()" ); - } -} - -static void -editable_text_wrapper_insert_text( AtkEditableText *text, - const gchar *string, - gint length, - gint *pos ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleEditableText> - pEditableText = getEditableText( text ); - if( pEditableText.is() ) - { - OUString aString ( string, length, RTL_TEXTENCODING_UTF8 ); - if( pEditableText->insertText( aString, *pos ) ) - *pos += length; - } - } - catch(const uno::Exception&) { - g_warning( "Exception in insertText()" ); - } -} - -static void -editable_text_wrapper_cut_text( AtkEditableText *text, - gint start, - gint end ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleEditableText> - pEditableText = getEditableText( text ); - if( pEditableText.is() ) - pEditableText->cutText( start, end ); - } - catch(const uno::Exception&) { - g_warning( "Exception in cutText()" ); - } -} - -static void -editable_text_wrapper_delete_text( AtkEditableText *text, - gint start, - gint end ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleEditableText> - pEditableText = getEditableText( text ); - if( pEditableText.is() ) - pEditableText->deleteText( start, end ); - } - catch(const uno::Exception&) { - g_warning( "Exception in deleteText()" ); - } -} - -static void -editable_text_wrapper_paste_text( AtkEditableText *text, - gint pos ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleEditableText> - pEditableText = getEditableText( text ); - if( pEditableText.is() ) - pEditableText->pasteText( pos ); - } - catch(const uno::Exception&) { - g_warning( "Exception in pasteText()" ); - } -} - -static void -editable_text_wrapper_copy_text( AtkEditableText *text, - gint start, - gint end ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleEditableText> - pEditableText = getEditableText( text ); - if( pEditableText.is() ) - pEditableText->copyText( start, end ); - } - catch(const uno::Exception&) { - g_warning( "Exception in copyText()" ); - } -} - -} // extern "C" - -void -editableTextIfaceInit (AtkEditableTextIface *iface) -{ - g_return_if_fail (iface != nullptr); - - iface->set_text_contents = editable_text_wrapper_set_text_contents; - iface->insert_text = editable_text_wrapper_insert_text; - iface->copy_text = editable_text_wrapper_copy_text; - iface->cut_text = editable_text_wrapper_cut_text; - iface->delete_text = editable_text_wrapper_delete_text; - iface->paste_text = editable_text_wrapper_paste_text; - iface->set_run_attributes = editable_text_wrapper_set_run_attributes; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkfactory.cxx b/vcl/unx/gtk/a11y/atkfactory.cxx deleted file mode 100644 index 3a776b20dcc7..000000000000 --- a/vcl/unx/gtk/a11y/atkfactory.cxx +++ /dev/null @@ -1,195 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 <unx/gtk/gtkframe.hxx> -#include <vcl/window.hxx> -#include "atkwrapper.hxx" -#include "atkfactory.hxx" -#include "atkregistry.hxx" - -using namespace ::com::sun::star; - -extern "C" { - -/* - * Instances of this dummy object class are returned whenever we have to - * create an AtkObject, but can't touch the OOo object anymore since it - * is already disposed. - */ - -static AtkStateSet * -noop_wrapper_ref_state_set( AtkObject * ) -{ - AtkStateSet *state_set = atk_state_set_new(); - atk_state_set_add_state( state_set, ATK_STATE_DEFUNCT ); - return state_set; -} - -static void -atk_noop_object_wrapper_class_init(AtkNoOpObjectClass *klass) -{ - AtkObjectClass *atk_class = ATK_OBJECT_CLASS( klass ); - atk_class->ref_state_set = noop_wrapper_ref_state_set; -} - -static GType -atk_noop_object_wrapper_get_type() -{ - static GType type = 0; - - if (!type) - { - static const GTypeInfo typeInfo = - { - sizeof (AtkNoOpObjectClass), - nullptr, - nullptr, - reinterpret_cast<GClassInitFunc>(atk_noop_object_wrapper_class_init), - nullptr, - nullptr, - sizeof (AtkObjectWrapper), - 0, - nullptr, - nullptr - } ; - - type = g_type_register_static (ATK_TYPE_OBJECT, "OOoAtkNoOpObj", &typeInfo, GTypeFlags(0)) ; - } - return type; -} - -static AtkObject* -atk_noop_object_wrapper_new() -{ - AtkObject *accessible; - - accessible = static_cast<AtkObject *>(g_object_new (atk_noop_object_wrapper_get_type(), nullptr)); - g_return_val_if_fail (accessible != nullptr, nullptr); - - accessible->role = ATK_ROLE_INVALID; - accessible->layer = ATK_LAYER_INVALID; - - return accessible; -} - -/* - * The wrapper factory - */ - -static GType -wrapper_factory_get_accessible_type() -{ - return atk_object_wrapper_get_type(); -} - -static AtkObject* -wrapper_factory_create_accessible( GObject *obj ) -{ -#if GTK_CHECK_VERSION(3,0,0) - GtkWidget* pEventBox = gtk_widget_get_parent(GTK_WIDGET(obj)); - - // gail_container_real_remove_gtk tries to re-instantiate an accessible - // for a widget that is about to vanish .. - if (!pEventBox) - return atk_noop_object_wrapper_new(); - - GtkWidget* pTopLevelGrid = gtk_widget_get_parent(pEventBox); - if (!pTopLevelGrid) - return atk_noop_object_wrapper_new(); - - GtkWidget* pTopLevel = gtk_widget_get_parent(pTopLevelGrid); - if (!pTopLevel) - return atk_noop_object_wrapper_new(); -#else - GtkWidget* pTopLevel = gtk_widget_get_parent(GTK_WIDGET(obj)); - - // gail_container_real_remove_gtk tries to re-instantiate an accessible - // for a widget that is about to vanish .. - if (!pTopLevel) - return atk_noop_object_wrapper_new(); -#endif - - GtkSalFrame* pFrame = GtkSalFrame::getFromWindow(GTK_WINDOW(pTopLevel)); - g_return_val_if_fail( pFrame != nullptr, nullptr ); - - vcl::Window* pFrameWindow = pFrame->GetWindow(); - if( pFrameWindow ) - { - vcl::Window* pWindow = pFrameWindow; - - // skip accessible objects already exposed by the frame objects - if( WindowType::BORDERWINDOW == pWindow->GetType() ) - pWindow = pFrameWindow->GetAccessibleChildWindow(0); - - if( pWindow ) - { - uno::Reference< accessibility::XAccessible > xAccessible = pWindow->GetAccessible(); - if( xAccessible.is() ) - { - AtkObject *accessible = ooo_wrapper_registry_get( xAccessible ); - - if( accessible ) - g_object_ref( G_OBJECT(accessible) ); - else - accessible = atk_object_wrapper_new( xAccessible, gtk_widget_get_accessible(pTopLevel) ); - - return accessible; - } - } - } - - return nullptr; -} - -AtkObject* ooo_fixed_get_accessible(GtkWidget *obj) -{ - return wrapper_factory_create_accessible(G_OBJECT(obj)); -} - -static void -wrapper_factory_class_init( AtkObjectFactoryClass *klass ) -{ - klass->create_accessible = wrapper_factory_create_accessible; - klass->get_accessible_type = wrapper_factory_get_accessible_type; -} - -GType -wrapper_factory_get_type() -{ - static GType t = 0; - - if (!t) { - static const GTypeInfo tinfo = - { - sizeof (AtkObjectFactoryClass), - nullptr, nullptr, reinterpret_cast<GClassInitFunc>(wrapper_factory_class_init), - nullptr, nullptr, sizeof (AtkObjectFactory), 0, nullptr, nullptr - }; - - t = g_type_register_static ( - ATK_TYPE_OBJECT_FACTORY, "OOoAtkObjectWrapperFactory", - &tinfo, GTypeFlags(0)); - } - - return t; -} - -} // extern C - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkhypertext.cxx b/vcl/unx/gtk/a11y/atkhypertext.cxx deleted file mode 100644 index ab94126dfc51..000000000000 --- a/vcl/unx/gtk/a11y/atkhypertext.cxx +++ /dev/null @@ -1,273 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atkwrapper.hxx" - -#include <com/sun/star/accessibility/XAccessibleHypertext.hpp> - -using namespace ::com::sun::star; - -// ---------------------- AtkHyperlink ---------------------- - -struct HyperLink { - AtkHyperlink const atk_hyper_link; - - uno::Reference< accessibility::XAccessibleHyperlink > xLink; -}; - -static uno::Reference< accessibility::XAccessibleHyperlink > const & - getHyperlink( AtkHyperlink *pHyperlink ) -{ - HyperLink *pLink = reinterpret_cast<HyperLink *>(pHyperlink); - return pLink->xLink; -} - -static GObjectClass *hyper_parent_class = nullptr; - -extern "C" { - -static void -hyper_link_finalize (GObject *obj) -{ - HyperLink *hl = reinterpret_cast<HyperLink *>(obj); - hl->xLink.clear(); - hyper_parent_class->finalize (obj); -} - -static gchar * -hyper_link_get_uri( AtkHyperlink *pLink, - gint i ) -{ - try { - uno::Any aAny = getHyperlink( pLink )->getAccessibleActionObject( i ); - OUString aUri = aAny.get< OUString > (); - return OUStringToGChar(aUri); - } - catch(const uno::Exception&) { - g_warning( "Exception in hyper_link_get_uri" ); - } - return nullptr; -} - -static AtkObject * -hyper_link_get_object( AtkHyperlink *, - gint ) -{ - g_warning( "FIXME: hyper_link_get_object unimplemented" ); - return nullptr; -} -static gint -hyper_link_get_end_index( AtkHyperlink *pLink ) -{ - try { - return getHyperlink( pLink )->getEndIndex(); - } - catch(const uno::Exception&) { - } - return -1; -} -static gint -hyper_link_get_start_index( AtkHyperlink *pLink ) -{ - try { - return getHyperlink( pLink )->getStartIndex(); - } - catch(const uno::Exception&) { - } - return -1; -} -static gboolean -hyper_link_is_valid( AtkHyperlink *pLink ) -{ - try { - return getHyperlink( pLink )->isValid(); - } - catch(const uno::Exception&) { - } - return FALSE; -} -static gint -hyper_link_get_n_anchors( AtkHyperlink *pLink ) -{ - try { - return getHyperlink( pLink )->getAccessibleActionCount(); - } - catch(const uno::Exception&) { - } - return 0; -} - -static guint -hyper_link_link_state( AtkHyperlink * ) -{ - g_warning( "FIXME: hyper_link_link_state unimplemented" ); - return 0; -} -static gboolean -hyper_link_is_selected_link( AtkHyperlink * ) -{ - g_warning( "FIXME: hyper_link_is_selected_link unimplemented" ); - return FALSE; -} - -static void -hyper_link_class_init (AtkHyperlinkClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->finalize = hyper_link_finalize; - - hyper_parent_class = static_cast<GObjectClass *>(g_type_class_peek_parent (klass)); - - klass->get_uri = hyper_link_get_uri; - klass->get_object = hyper_link_get_object; - klass->get_end_index = hyper_link_get_end_index; - klass->get_start_index = hyper_link_get_start_index; - klass->is_valid = hyper_link_is_valid; - klass->get_n_anchors = hyper_link_get_n_anchors; - klass->link_state = hyper_link_link_state; - klass->is_selected_link = hyper_link_is_selected_link; -} - -static GType -hyper_link_get_type() -{ - static GType type = 0; - - if (!type) { - static const GTypeInfo tinfo = { - sizeof (AtkHyperlinkClass), - nullptr, /* base init */ - nullptr, /* base finalize */ - reinterpret_cast<GClassInitFunc>(hyper_link_class_init), - nullptr, /* class finalize */ - nullptr, /* class data */ - sizeof (HyperLink), /* instance size */ - 0, /* nb preallocs */ - nullptr, /* instance init */ - nullptr /* value table */ - }; - - static const GInterfaceInfo atk_action_info = { - reinterpret_cast<GInterfaceInitFunc>(actionIfaceInit), - nullptr, - nullptr - }; - - type = g_type_register_static (ATK_TYPE_HYPERLINK, - "OOoAtkObjHyperLink", &tinfo, - GTypeFlags(0)); - g_type_add_interface_static (type, ATK_TYPE_ACTION, - &atk_action_info); - } - - return type; -} - -// ---------------------- AtkHyperText ---------------------- - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleHypertext> - getHypertext( AtkHypertext *pHypertext ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pHypertext ); - if( pWrap ) - { - if( !pWrap->mpHypertext.is() ) - { - pWrap->mpHypertext.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpHypertext; - } - - return css::uno::Reference<css::accessibility::XAccessibleHypertext>(); -} - -static AtkHyperlink * -hypertext_get_link( AtkHypertext *hypertext, - gint link_index) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleHypertext> pHypertext - = getHypertext( hypertext ); - if( pHypertext.is() ) - { - HyperLink *pLink = static_cast<HyperLink *>(g_object_new( hyper_link_get_type(), nullptr )); - pLink->xLink = pHypertext->getHyperLink( link_index ); - if( !pLink->xLink.is() ) { - g_object_unref( G_OBJECT( pLink ) ); - pLink = nullptr; - } - return ATK_HYPERLINK( pLink ); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getHyperLink()" ); - } - - return nullptr; -} - -static gint -hypertext_get_n_links( AtkHypertext *hypertext ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleHypertext> pHypertext - = getHypertext( hypertext ); - if( pHypertext.is() ) - return pHypertext->getHyperLinkCount(); - } - catch(const uno::Exception&) { - g_warning( "Exception in getHyperLinkCount()" ); - } - - return 0; -} - -static gint -hypertext_get_link_index( AtkHypertext *hypertext, - gint index) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleHypertext> pHypertext - = getHypertext( hypertext ); - if( pHypertext.is() ) - return pHypertext->getHyperLinkIndex( index ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getHyperLinkIndex()" ); - } - - return 0; -} - -} // extern "C" - -void -hypertextIfaceInit (AtkHypertextIface *iface) -{ - g_return_if_fail (iface != nullptr); - - iface->get_link = hypertext_get_link; - iface->get_n_links = hypertext_get_n_links; - iface->get_link_index = hypertext_get_link_index; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkimage.cxx b/vcl/unx/gtk/a11y/atkimage.cxx deleted file mode 100644 index acd43f467690..000000000000 --- a/vcl/unx/gtk/a11y/atkimage.cxx +++ /dev/null @@ -1,131 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atkwrapper.hxx" - -#include <com/sun/star/accessibility/XAccessibleImage.hpp> - -using namespace ::com::sun::star; - -// FIXME -static const gchar * -getAsConst( const OUString& rString ) -{ - static const int nMax = 10; - static OString aUgly[nMax]; - static int nIdx = 0; - nIdx = (nIdx + 1) % nMax; - aUgly[nIdx] = OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ); - return aUgly[ nIdx ].getStr(); -} - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleImage> - getImage( AtkImage *pImage ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pImage ); - if( pWrap ) - { - if( !pWrap->mpImage.is() ) - { - pWrap->mpImage.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpImage; - } - - return css::uno::Reference<css::accessibility::XAccessibleImage>(); -} - -extern "C" { - -static const gchar * -image_get_image_description( AtkImage *image ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleImage> pImage - = getImage( image ); - if( pImage.is() ) - return getAsConst( pImage->getAccessibleImageDescription() ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleImageDescription()" ); - } - - return nullptr; -} - -static void -image_get_image_position( AtkImage *image, - gint *x, - gint *y, - AtkCoordType coord_type ) -{ - *x = *y = -1; - if( ATK_IS_COMPONENT( image ) ) - { - SAL_WNODEPRECATED_DECLARATIONS_PUSH - atk_component_get_position( ATK_COMPONENT( image ), x, y, coord_type ); - SAL_WNODEPRECATED_DECLARATIONS_POP - } - else - g_warning( "FIXME: no image position information" ); -} - -static void -image_get_image_size( AtkImage *image, - gint *width, - gint *height ) -{ - *width = *height = -1; - try { - css::uno::Reference<css::accessibility::XAccessibleImage> pImage - = getImage( image ); - if( pImage.is() ) - { - *width = pImage->getAccessibleImageWidth(); - *height = pImage->getAccessibleImageHeight(); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleImageHeight() or Width" ); - } -} - -static gboolean -image_set_image_description( AtkImage *, const gchar * ) -{ - g_warning ("FIXME: no set image description"); - return FALSE; -} - -} // extern "C" - -void -imageIfaceInit (AtkImageIface *iface) -{ - g_return_if_fail (iface != nullptr); - - iface->set_image_description = image_set_image_description; - iface->get_image_description = image_get_image_description; - iface->get_image_position = image_get_image_position; - iface->get_image_size = image_get_image_size; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atklistener.cxx b/vcl/unx/gtk/a11y/atklistener.cxx deleted file mode 100644 index c30bc638bced..000000000000 --- a/vcl/unx/gtk/a11y/atklistener.cxx +++ /dev/null @@ -1,739 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.apache.org/licenses/LICENSE-2.0 . - */ - -#ifdef AIX -#define _LINUX_SOURCE_COMPAT -#include <sys/timer.h> -#undef _LINUX_SOURCE_COMPAT -#endif - -#include <com/sun/star/accessibility/TextSegment.hpp> -#include <com/sun/star/accessibility/AccessibleEventId.hpp> -#include <com/sun/star/accessibility/AccessibleStateType.hpp> -#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp> -#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> -#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> -#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> - -#include "atklistener.hxx" -#include "atkwrapper.hxx" -#include <vcl/svapp.hxx> - -#include <rtl/ref.hxx> -#include <sal/log.hxx> - -#define DEBUG_ATK_LISTENER 0 - -#if DEBUG_ATK_LISTENER -#include <iostream> -#include <sstream> -#endif - -using namespace com::sun::star; - -AtkListener::AtkListener( AtkObjectWrapper* pWrapper ) : mpWrapper( pWrapper ) -{ - if( mpWrapper ) - { - g_object_ref( mpWrapper ); - updateChildList( mpWrapper->mpContext ); - } -} - -AtkListener::~AtkListener() -{ - if( mpWrapper ) - g_object_unref( mpWrapper ); -} - -/*****************************************************************************/ - -static AtkStateType mapState( const uno::Any &rAny ) -{ - sal_Int16 nState = accessibility::AccessibleStateType::INVALID; - rAny >>= nState; - return mapAtkState( nState ); -} - -/*****************************************************************************/ - -extern "C" { - // rhbz#1001768 - down to horrific problems releasing the solar mutex - // while destroying a Window - which occurs inside these notifications. - static gboolean - idle_defunc_state_change( AtkObject *atk_obj ) - { - SolarMutexGuard aGuard; - - // This is an equivalent to a state change to DEFUNC(T). - atk_object_notify_state_change( atk_obj, ATK_STATE_DEFUNCT, TRUE ); - if( atk_get_focus_object() == atk_obj ) - { - SAL_WNODEPRECATED_DECLARATIONS_PUSH - atk_focus_tracker_notify( nullptr ); - SAL_WNODEPRECATED_DECLARATIONS_POP - } - g_object_unref( G_OBJECT( atk_obj ) ); - return false; - } -} - -// XEventListener implementation -void AtkListener::disposing( const lang::EventObject& ) -{ - if( mpWrapper ) - { - AtkObject *atk_obj = ATK_OBJECT( mpWrapper ); - - // Release all interface references to avoid shutdown problems with - // global mutex - atk_object_wrapper_dispose( mpWrapper ); - - g_idle_add( reinterpret_cast<GSourceFunc>(idle_defunc_state_change), - g_object_ref( G_OBJECT( atk_obj ) ) ); - - // Release the wrapper object so that it can vanish .. - g_object_unref( mpWrapper ); - mpWrapper = nullptr; - } -} - -/*****************************************************************************/ - -static AtkObject *getObjFromAny( const uno::Any &rAny ) -{ - uno::Reference< accessibility::XAccessible > xAccessible; - rAny >>= xAccessible; - return xAccessible.is() ? atk_object_wrapper_ref( xAccessible ) : nullptr; -} - -/*****************************************************************************/ - -// Updates the child list held to provide the old IndexInParent on children_changed::remove -void AtkListener::updateChildList( - css::uno::Reference<css::accessibility::XAccessibleContext> const & - pContext) -{ - m_aChildList.clear(); - - uno::Reference< accessibility::XAccessibleStateSet > xStateSet = pContext->getAccessibleStateSet(); - if( xStateSet.is() - && !xStateSet->contains(accessibility::AccessibleStateType::DEFUNC) - && !xStateSet->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS) ) - { - sal_Int32 nChildren = pContext->getAccessibleChildCount(); - m_aChildList.resize(nChildren); - for(sal_Int32 n = 0; n < nChildren; n++) - { - try - { - m_aChildList[n] = pContext->getAccessibleChild(n); - } - catch (lang::IndexOutOfBoundsException const&) - { - sal_Int32 nChildren2 = pContext->getAccessibleChildCount(); - assert(nChildren2 <= n && "consistency?"); - m_aChildList.resize(std::min(nChildren2, n)); - break; - } - } - } -} - -/*****************************************************************************/ - -void AtkListener::handleChildAdded( - const uno::Reference< accessibility::XAccessibleContext >& rxParent, - const uno::Reference< accessibility::XAccessible>& rxAccessible) -{ - AtkObject * pChild = rxAccessible.is() ? atk_object_wrapper_ref( rxAccessible ) : nullptr; - - if( pChild ) - { - updateChildList(rxParent); - - atk_object_wrapper_add_child( mpWrapper, pChild, - atk_object_get_index_in_parent( pChild )); - - g_object_unref( pChild ); - } -} - -/*****************************************************************************/ - -void AtkListener::handleChildRemoved( - const uno::Reference< accessibility::XAccessibleContext >& rxParent, - const uno::Reference< accessibility::XAccessible>& rxChild) -{ - sal_Int32 nIndex = -1; - - // Locate the child in the children list - size_t n, nmax = m_aChildList.size(); - for( n = 0; n < nmax; ++n ) - { - if( rxChild == m_aChildList[n] ) - { - nIndex = n; - break; - } - } - - // FIXME: two problems here: - // a) we get child-removed events for objects that are no real children - // in the accessibility hierarchy or have been removed before due to - // some child removing batch. - // b) spi_atk_bridge_signal_listener ignores the given parameters - // for children_changed events and always asks the parent for the - // 0. child, which breaks somehow on vanishing list boxes. - // Ignoring "remove" events for objects not in the m_aChildList - // for now. - if( nIndex >= 0 ) - { - uno::Reference<accessibility::XAccessibleEventBroadcaster> xBroadcaster( - rxChild->getAccessibleContext(), uno::UNO_QUERY); - - if (xBroadcaster.is()) - { - uno::Reference<accessibility::XAccessibleEventListener> xListener(this); - xBroadcaster->removeAccessibleEventListener(xListener); - } - - updateChildList(rxParent); - - AtkObject * pChild = atk_object_wrapper_ref( rxChild, false ); - if( pChild ) - { - atk_object_wrapper_remove_child( mpWrapper, pChild, nIndex ); - g_object_unref( pChild ); - } - } -} - -/*****************************************************************************/ - -void AtkListener::handleInvalidateChildren( - const uno::Reference< accessibility::XAccessibleContext >& rxParent) -{ - // Send notifications for all previous children - size_t n = m_aChildList.size(); - while( n-- > 0 ) - { - if( m_aChildList[n].is() ) - { - AtkObject * pChild = atk_object_wrapper_ref( m_aChildList[n], false ); - if( pChild ) - { - atk_object_wrapper_remove_child( mpWrapper, pChild, n ); - g_object_unref( pChild ); - } - } - } - - updateChildList(rxParent); - - // Send notifications for all new children - size_t nmax = m_aChildList.size(); - for( n = 0; n < nmax; ++n ) - { - if( m_aChildList[n].is() ) - { - AtkObject * pChild = atk_object_wrapper_ref( m_aChildList[n] ); - - if( pChild ) - { - atk_object_wrapper_add_child( mpWrapper, pChild, n ); - g_object_unref( pChild ); - } - } - } -} - -/*****************************************************************************/ - -static uno::Reference< accessibility::XAccessibleContext > -getAccessibleContextFromSource( const uno::Reference< uno::XInterface >& rxSource ) -{ - uno::Reference< accessibility::XAccessibleContext > xContext(rxSource, uno::UNO_QUERY); - if( ! xContext.is() ) - { - g_warning( "ERROR: Event source does not implement XAccessibleContext" ); - - // Second try - query for XAccessible, which should give us access to - // XAccessibleContext. - uno::Reference< accessibility::XAccessible > xAccessible(rxSource, uno::UNO_QUERY); - if( xAccessible.is() ) - xContext = xAccessible->getAccessibleContext(); - } - - return xContext; -} - -#if DEBUG_ATK_LISTENER - -namespace { - -void printNotifyEvent( const accessibility::AccessibleEventObject& rEvent ) -{ - static std::vector<const char*> aLabels = { - 0, - "NAME_CHANGED", // 01 - "DESCRIPTION_CHANGED", // 02 - "ACTION_CHANGED", // 03 - "STATE_CHANGED", // 04 - "ACTIVE_DESCENDANT_CHANGED", // 05 - "BOUNDRECT_CHANGED", // 06 - "CHILD", // 07 - "INVALIDATE_ALL_CHILDREN", // 08 - "SELECTION_CHANGED", // 09 - "VISIBLE_DATA_CHANGED", // 10 - "VALUE_CHANGED", // 11 - "CONTENT_FLOWS_FROM_RELATION_CHANGED", // 12 - "CONTENT_FLOWS_TO_RELATION_CHANGED", // 13 - "CONTROLLED_BY_RELATION_CHANGED", // 14 - "CONTROLLER_FOR_RELATION_CHANGED", // 15 - "LABEL_FOR_RELATION_CHANGED", // 16 - "LABELED_BY_RELATION_CHANGED", // 17 - "MEMBER_OF_RELATION_CHANGED", // 18 - "SUB_WINDOW_OF_RELATION_CHANGED", // 19 - "CARET_CHANGED", // 20 - "TEXT_SELECTION_CHANGED", // 21 - "TEXT_CHANGED", // 22 - "TEXT_ATTRIBUTE_CHANGED", // 23 - "HYPERTEXT_CHANGED", // 24 - "TABLE_CAPTION_CHANGED", // 25 - "TABLE_COLUMN_DESCRIPTION_CHANGED", // 26 - "TABLE_COLUMN_HEADER_CHANGED", // 27 - "TABLE_MODEL_CHANGED", // 28 - "TABLE_ROW_DESCRIPTION_CHANGED", // 29 - "TABLE_ROW_HEADER_CHANGED", // 30 - "TABLE_SUMMARY_CHANGED", // 31 - "LISTBOX_ENTRY_EXPANDED", // 32 - "LISTBOX_ENTRY_COLLAPSED", // 33 - "ACTIVE_DESCENDANT_CHANGED_NOFOCUS", // 34 - "SELECTION_CHANGED_ADD", // 35 - "SELECTION_CHANGED_REMOVE", // 36 - "SELECTION_CHANGED_WITHIN", // 37 - "PAGE_CHANGED", // 38 - "SECTION_CHANGED", // 39 - "COLUMN_CHANGED", // 40 - "ROLE_CHANGED", // 41 - }; - - static std::vector<const char*> aStates = { - "INVALID", // 00 - "ACTIVE", // 01 - "ARMED", // 02 - "BUSY", // 03 - "CHECKED", // 04 - "DEFUNC", // 05 - "EDITABLE", // 06 - "ENABLED", // 07 - "EXPANDABLE", // 08 - "EXPANDED", // 09 - "FOCUSABLE", // 10 - "FOCUSED", // 11 - "HORIZONTAL", // 12 - "ICONIFIED", // 13 - "INDETERMINATE", // 14 - "MANAGES_DESCENDANTS", // 15 - "MODAL", // 16 - "MULTI_LINE", // 17 - "MULTI_SELECTABLE", // 18 - "OPAQUE", // 19 - "PRESSED", // 20 - "RESIZABLE", // 21 - "SELECTABLE", // 22 - "SELECTED", // 23 - "SENSITIVE", // 24 - "SHOWING", // 25 - "SINGLE_LINE", // 26 - "STALE", // 27 - "TRANSIENT", // 28 - "VERTICAL", // 29 - "VISIBLE", // 30 - "MOVEABLE", // 31 - "DEFAULT", // 32 - "OFFSCREEN", // 33 - "COLLAPSE", // 34 - }; - - auto getOrUnknown = [](const std::vector<const char*>& rCont, size_t nIndex) -> std::string - { - return (nIndex < rCont.size()) ? rCont[nIndex] : "<unknown>"; - }; - - std::ostringstream os; - os << "--" << std::endl; - os << "* event = " << getOrUnknown(aLabels, rEvent.EventId) << std::endl; - - switch (rEvent.EventId) - { - case accessibility::AccessibleEventId::STATE_CHANGED: - { - sal_Int16 nState; - if (rEvent.OldValue >>= nState) - os << " * old state = " << getOrUnknown(aStates, nState); - if (rEvent.NewValue >>= nState) - os << " * new state = " << getOrUnknown(aStates, nState); - - os << std::endl; - break; - } - default: - ; - } - - std::cout << os.str(); -} - -} - -#endif - -void AtkListener::notifyEvent( const accessibility::AccessibleEventObject& aEvent ) -{ - if( !mpWrapper ) - return; - - AtkObject *atk_obj = ATK_OBJECT( mpWrapper ); - - switch( aEvent.EventId ) - { - // AtkObject signals: - // Hierarchy signals - case accessibility::AccessibleEventId::CHILD: - { - uno::Reference< accessibility::XAccessibleContext > xParent; - uno::Reference< accessibility::XAccessible > xChild; - - xParent = getAccessibleContextFromSource(aEvent.Source); - g_return_if_fail( xParent.is() ); - - if( aEvent.OldValue >>= xChild ) - handleChildRemoved(xParent, xChild); - - if( aEvent.NewValue >>= xChild ) - handleChildAdded(xParent, xChild); - break; - } - - case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN: - { - uno::Reference< accessibility::XAccessibleContext > xParent = getAccessibleContextFromSource(aEvent.Source); - g_return_if_fail( xParent.is() ); - - handleInvalidateChildren(xParent); - break; - } - - case accessibility::AccessibleEventId::NAME_CHANGED: - { - OUString aName; - if( aEvent.NewValue >>= aName ) - { - atk_object_set_name(atk_obj, - OUStringToOString(aName, RTL_TEXTENCODING_UTF8).getStr()); - } - break; - } - - case accessibility::AccessibleEventId::DESCRIPTION_CHANGED: - { - OUString aDescription; - if( aEvent.NewValue >>= aDescription ) - { - atk_object_set_description(atk_obj, - OUStringToOString(aDescription, RTL_TEXTENCODING_UTF8).getStr()); - } - break; - } - - case accessibility::AccessibleEventId::STATE_CHANGED: - { - AtkStateType eOldState = mapState( aEvent.OldValue ); - AtkStateType eNewState = mapState( aEvent.NewValue ); - - gboolean bState = eNewState != ATK_STATE_INVALID; - AtkStateType eRealState = bState ? eNewState : eOldState; - - atk_object_notify_state_change( atk_obj, eRealState, bState ); - break; - } - - case accessibility::AccessibleEventId::BOUNDRECT_CHANGED: - -#ifdef HAS_ATKRECTANGLE - if( ATK_IS_COMPONENT( atk_obj ) ) - { - AtkRectangle rect; - - atk_component_get_extents( ATK_COMPONENT( atk_obj ), - &rect.x, - &rect.y, - &rect.width, - &rect.height, - ATK_XY_SCREEN ); - - g_signal_emit_by_name( atk_obj, "bounds_changed", &rect ); - } - else - g_warning( "bounds_changed event for object not implementing AtkComponent\n"); -#endif - - break; - - case accessibility::AccessibleEventId::VISIBLE_DATA_CHANGED: - g_signal_emit_by_name( atk_obj, "visible-data-changed" ); - break; - - case accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED: - { - AtkObject *pChild = getObjFromAny( aEvent.NewValue ); - if( pChild ) - { - g_signal_emit_by_name( atk_obj, "active-descendant-changed", pChild ); - g_object_unref( pChild ); - } - break; - } - - //ACTIVE_DESCENDANT_CHANGED_NOFOCUS (sic) appears to have been added - //as a workaround or an aid for the ia2 winaccessibility implementation - //so ignore it silently without warning here - case accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS: - break; - - // #i92103# - case accessibility::AccessibleEventId::LISTBOX_ENTRY_EXPANDED: - { - AtkObject *pChild = getObjFromAny( aEvent.NewValue ); - if( pChild ) - { - atk_object_notify_state_change( pChild, ATK_STATE_EXPANDED, true ); - g_object_unref( pChild ); - } - break; - } - - case accessibility::AccessibleEventId::LISTBOX_ENTRY_COLLAPSED: - { - AtkObject *pChild = getObjFromAny( aEvent.NewValue ); - if( pChild ) - { - atk_object_notify_state_change( pChild, ATK_STATE_EXPANDED, false ); - g_object_unref( pChild ); - } - break; - } - - // AtkAction signals ... - case accessibility::AccessibleEventId::ACTION_CHANGED: - g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-actions"); - break; - - // AtkText - case accessibility::AccessibleEventId::CARET_CHANGED: - { - sal_Int32 nPos=0; - aEvent.NewValue >>= nPos; - g_signal_emit_by_name( atk_obj, "text_caret_moved", nPos ); - break; - } - case accessibility::AccessibleEventId::TEXT_CHANGED: - { - // TESTME: and remove this comment: - // cf. comphelper/source/misc/accessibletexthelper.cxx (implInitTextChangedEvent) - accessibility::TextSegment aDeletedText; - accessibility::TextSegment aInsertedText; - - // TODO: when GNOME starts to send "update" kind of events, change - // we need to re-think this implementation as well - if( aEvent.OldValue >>= aDeletedText ) - { - /* Remember the text segment here to be able to return removed text in get_text(). - * This is clearly a hack to be used until appropriate API exists in atk to pass - * the string value directly or we find a compelling reason to start caching the - * UTF-8 converted strings in the atk wrapper object. - */ - - g_object_set_data( G_OBJECT(atk_obj), "ooo::text_changed::delete", &aDeletedText); - - g_signal_emit_by_name( atk_obj, "text_changed::delete", - static_cast<gint>(aDeletedText.SegmentStart), - static_cast<gint>( aDeletedText.SegmentEnd - aDeletedText.SegmentStart ) ); - - g_object_steal_data( G_OBJECT(atk_obj), "ooo::text_changed::delete" ); - } - - if( aEvent.NewValue >>= aInsertedText ) - g_signal_emit_by_name( atk_obj, "text_changed::insert", - static_cast<gint>(aInsertedText.SegmentStart), - static_cast<gint>( aInsertedText.SegmentEnd - aInsertedText.SegmentStart ) ); - break; - } - - case accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED: - { - g_signal_emit_by_name( atk_obj, "text-selection-changed" ); - break; - } - - case accessibility::AccessibleEventId::TEXT_ATTRIBUTE_CHANGED: - g_signal_emit_by_name( atk_obj, "text-attributes-changed" ); - break; - - // AtkValue - case accessibility::AccessibleEventId::VALUE_CHANGED: - g_object_notify( G_OBJECT( atk_obj ), "accessible-value" ); - break; - - case accessibility::AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED: - case accessibility::AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED: - case accessibility::AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED: - case accessibility::AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED: - case accessibility::AccessibleEventId::LABEL_FOR_RELATION_CHANGED: - case accessibility::AccessibleEventId::LABELED_BY_RELATION_CHANGED: - case accessibility::AccessibleEventId::MEMBER_OF_RELATION_CHANGED: - case accessibility::AccessibleEventId::SUB_WINDOW_OF_RELATION_CHANGED: - // FIXME: ask Bill how Atk copes with this little lot ... - break; - - // AtkTable - case accessibility::AccessibleEventId::TABLE_MODEL_CHANGED: - { - accessibility::AccessibleTableModelChange aChange; - aEvent.NewValue >>= aChange; - - sal_Int32 nRowsChanged = aChange.LastRow - aChange.FirstRow + 1; - sal_Int32 nColumnsChanged = aChange.LastColumn - aChange.FirstColumn + 1; - - static const struct { - const char *row; - const char *col; - } aSignalNames[] = - { - { nullptr, nullptr }, // dummy - { "row_inserted", "column_inserted" }, // INSERT = 1 - { "row_deleted", "column_deleted" } // DELETE = 2 - }; - switch( aChange.Type ) - { - case accessibility::AccessibleTableModelChangeType::INSERT: - case accessibility::AccessibleTableModelChangeType::DELETE: - if( nRowsChanged > 0 ) - g_signal_emit_by_name( G_OBJECT( atk_obj ), - aSignalNames[aChange.Type].row, - aChange.FirstRow, nRowsChanged ); - if( nColumnsChanged > 0 ) - g_signal_emit_by_name( G_OBJECT( atk_obj ), - aSignalNames[aChange.Type].col, - aChange.FirstColumn, nColumnsChanged ); - break; - - case accessibility::AccessibleTableModelChangeType::UPDATE: - // This is not really a model change, is it ? - break; - default: - g_warning( "TESTME: unusual table model change %d\n", aChange.Type ); - break; - } - g_signal_emit_by_name( G_OBJECT( atk_obj ), "model-changed" ); - break; - } - - case accessibility::AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED: - { - accessibility::AccessibleTableModelChange aChange; - aEvent.NewValue >>= aChange; - - AtkPropertyValues values; - memset(&values, 0, sizeof(AtkPropertyValues)); - g_value_init (&values.new_value, G_TYPE_INT); - values.property_name = "accessible-table-column-header"; - - for (sal_Int32 nChangedColumn = aChange.FirstColumn; nChangedColumn <= aChange.LastColumn; ++nChangedColumn) - { - g_value_set_int (&values.new_value, nChangedColumn); - g_signal_emit_by_name(G_OBJECT(atk_obj), "property_change::accessible-table-column-header", &values, nullptr); - } - break; - } - - case accessibility::AccessibleEventId::TABLE_CAPTION_CHANGED: - g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-caption"); - break; - - case accessibility::AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED: - g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-column-description"); - break; - - case accessibility::AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED: - g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-row-description"); - break; - - case accessibility::AccessibleEventId::TABLE_ROW_HEADER_CHANGED: - g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-row-header"); - break; - - case accessibility::AccessibleEventId::TABLE_SUMMARY_CHANGED: - g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-summary"); - break; - - case accessibility::AccessibleEventId::SELECTION_CHANGED: - case accessibility::AccessibleEventId::SELECTION_CHANGED_ADD: - case accessibility::AccessibleEventId::SELECTION_CHANGED_REMOVE: - case accessibility::AccessibleEventId::SELECTION_CHANGED_WITHIN: - if (ATK_IS_SELECTION(atk_obj)) - g_signal_emit_by_name(G_OBJECT(atk_obj), "selection_changed"); - else - { - // e.g. tdf#122353, when such dialogs become native the problem will go away anyway - SAL_INFO("vcl.gtk", "selection change from obj which doesn't support XAccessibleSelection"); - } - break; - - case accessibility::AccessibleEventId::HYPERTEXT_CHANGED: - g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-hypertext-offset"); - break; - - case accessibility::AccessibleEventId::ROLE_CHANGED: - { - uno::Reference< accessibility::XAccessibleContext > xContext = getAccessibleContextFromSource( aEvent.Source ); - atk_object_wrapper_set_role( mpWrapper, xContext->getAccessibleRole() ); - break; - } - - case accessibility::AccessibleEventId::PAGE_CHANGED: - { - /* // If we implemented AtkDocument then I imagine this is what this - // handler should look like - sal_Int32 nPos=0; - aEvent.NewValue >>= nPos; - g_signal_emit_by_name( G_OBJECT( atk_obj ), "page_changed", nPos ); - */ - break; - } - - default: - SAL_WARN("vcl.gtk", "Unknown event notification: " << aEvent.EventId); - break; - } -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkregistry.cxx b/vcl/unx/gtk/a11y/atkregistry.cxx deleted file mode 100644 index ff96378c41dc..000000000000 --- a/vcl/unx/gtk/a11y/atkregistry.cxx +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atkregistry.hxx" - -using namespace ::com::sun::star::accessibility; -using namespace ::com::sun::star::uno; - -static GHashTable *uno_to_gobject = nullptr; - -/*****************************************************************************/ - -AtkObject * -ooo_wrapper_registry_get(const Reference< XAccessible >& rxAccessible) -{ - if( uno_to_gobject ) - { - gpointer cached = - g_hash_table_lookup(uno_to_gobject, static_cast<gpointer>(rxAccessible.get())); - - if( cached ) - return ATK_OBJECT( cached ); - } - - return nullptr; -} - -/*****************************************************************************/ - -void -ooo_wrapper_registry_add(const Reference< XAccessible >& rxAccessible, AtkObject *obj) -{ - if( !uno_to_gobject ) - uno_to_gobject = g_hash_table_new (nullptr, nullptr); - - g_hash_table_insert( uno_to_gobject, static_cast<gpointer>(rxAccessible.get()), obj ); -} - -/*****************************************************************************/ - -void -ooo_wrapper_registry_remove( - css::uno::Reference<css::accessibility::XAccessible> const & pAccessible) -{ - if( uno_to_gobject ) - g_hash_table_remove( - uno_to_gobject, static_cast<gpointer>(pAccessible.get()) ); -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkselection.cxx b/vcl/unx/gtk/a11y/atkselection.cxx deleted file mode 100644 index 1d9772bc1587..000000000000 --- a/vcl/unx/gtk/a11y/atkselection.cxx +++ /dev/null @@ -1,190 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atkwrapper.hxx" - -#include <com/sun/star/accessibility/XAccessibleSelection.hpp> - -using namespace ::com::sun::star; - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleSelection> - getSelection( AtkSelection *pSelection ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pSelection ); - if( pWrap ) - { - if( !pWrap->mpSelection.is() ) - { - pWrap->mpSelection.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpSelection; - } - - return css::uno::Reference<css::accessibility::XAccessibleSelection>(); -} - -extern "C" { - -static gboolean -selection_add_selection( AtkSelection *selection, - gint i ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection - = getSelection( selection ); - if( pSelection.is() ) - { - pSelection->selectAccessibleChild( i ); - return TRUE; - } - } - catch(const uno::Exception&) { - g_warning( "Exception in selectAccessibleChild()" ); - } - - return FALSE; -} - -static gboolean -selection_clear_selection( AtkSelection *selection ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection - = getSelection( selection ); - if( pSelection.is() ) - { - pSelection->clearAccessibleSelection(); - return TRUE; - } - } - catch(const uno::Exception&) { - g_warning( "Exception in selectAccessibleChild()" ); - } - - return FALSE; -} - -static AtkObject* -selection_ref_selection( AtkSelection *selection, - gint i ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection - = getSelection( selection ); - if( pSelection.is() ) - return atk_object_wrapper_ref( pSelection->getSelectedAccessibleChild( i ) ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getSelectedAccessibleChild()" ); - } - - return nullptr; -} - -static gint -selection_get_selection_count( AtkSelection *selection) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection - = getSelection( selection ); - if( pSelection.is() ) - return pSelection->getSelectedAccessibleChildCount(); - } - catch(const uno::Exception&) { - g_warning( "Exception in getSelectedAccessibleChildCount()" ); - } - - return -1; -} - -static gboolean -selection_is_child_selected( AtkSelection *selection, - gint i) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection - = getSelection( selection ); - if( pSelection.is() ) - return pSelection->isAccessibleChildSelected( i ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getSelectedAccessibleChildCount()" ); - } - - return FALSE; -} - -static gboolean -selection_remove_selection( AtkSelection *selection, - gint i ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection - = getSelection( selection ); - if( pSelection.is() ) - { - pSelection->deselectAccessibleChild( i ); - return TRUE; - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getSelectedAccessibleChildCount()" ); - } - - return FALSE; -} - -static gboolean -selection_select_all_selection( AtkSelection *selection) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection - = getSelection( selection ); - if( pSelection.is() ) - { - pSelection->selectAllAccessibleChildren(); - return TRUE; - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getSelectedAccessibleChildCount()" ); - } - - return FALSE; -} - -} // extern "C" - -void -selectionIfaceInit( AtkSelectionIface *iface) -{ - g_return_if_fail (iface != nullptr); - - iface->add_selection = selection_add_selection; - iface->clear_selection = selection_clear_selection; - iface->ref_selection = selection_ref_selection; - iface->get_selection_count = selection_get_selection_count; - iface->is_child_selected = selection_is_child_selected; - iface->remove_selection = selection_remove_selection; - iface->select_all_selection = selection_select_all_selection; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atktable.cxx b/vcl/unx/gtk/a11y/atktable.cxx deleted file mode 100644 index 221e55d2b287..000000000000 --- a/vcl/unx/gtk/a11y/atktable.cxx +++ /dev/null @@ -1,580 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atkwrapper.hxx" - -#include <com/sun/star/accessibility/XAccessibleTable.hpp> -#include <comphelper/sequence.hxx> - -using namespace ::com::sun::star; - -static AtkObject * -atk_object_wrapper_conditional_ref( const uno::Reference< accessibility::XAccessible >& rxAccessible ) -{ - if( rxAccessible.is() ) - return atk_object_wrapper_ref( rxAccessible ); - - return nullptr; -} - -/*****************************************************************************/ - -// FIXME -static const gchar * -getAsConst( const OUString& rString ) -{ - static const int nMax = 10; - static OString aUgly[nMax]; - static int nIdx = 0; - nIdx = (nIdx + 1) % nMax; - aUgly[nIdx] = OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ); - return aUgly[ nIdx ].getStr(); -} - -/*****************************************************************************/ - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleTable> - getTable( AtkTable *pTable ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pTable ); - if( pWrap ) - { - if( !pWrap->mpTable.is() ) - { - pWrap->mpTable.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpTable; - } - - return css::uno::Reference<css::accessibility::XAccessibleTable>(); -} - -/*****************************************************************************/ - -extern "C" { - -static AtkObject* -table_wrapper_ref_at (AtkTable *table, - gint row, - gint column) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable = getTable( table ); - if( pTable.is() ) - return atk_object_wrapper_conditional_ref( pTable->getAccessibleCellAt( row, column ) ); - } - - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleCellAt()" ); - } - - return nullptr; -} - -/*****************************************************************************/ - -static gint -table_wrapper_get_index_at (AtkTable *table, - gint row, - gint column) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return pTable->getAccessibleIndex( row, column ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleIndex()" ); - } - - return -1; -} - -/*****************************************************************************/ - -static gint -table_wrapper_get_column_at_index (AtkTable *table, - gint nIndex) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return pTable->getAccessibleColumn( nIndex ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleColumn()" ); - } - - return -1; -} - -/*****************************************************************************/ - -static gint -table_wrapper_get_row_at_index( AtkTable *table, - gint nIndex ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return pTable->getAccessibleRow( nIndex ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleRow()" ); - } - - return -1; -} - -/*****************************************************************************/ - -static gint -table_wrapper_get_n_columns( AtkTable *table ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return pTable->getAccessibleColumnCount(); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleColumnCount()" ); - } - - return -1; -} - -/*****************************************************************************/ - -static gint -table_wrapper_get_n_rows( AtkTable *table ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return pTable->getAccessibleRowCount(); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleRowCount()" ); - } - - return -1; -} - -/*****************************************************************************/ - -static gint -table_wrapper_get_column_extent_at( AtkTable *table, - gint row, - gint column ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return pTable->getAccessibleColumnExtentAt( row, column ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleColumnExtentAt()" ); - } - - return -1; -} - -/*****************************************************************************/ - -static gint -table_wrapper_get_row_extent_at( AtkTable *table, - gint row, - gint column ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return pTable->getAccessibleRowExtentAt( row, column ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleRowExtentAt()" ); - } - - return -1; -} - -/*****************************************************************************/ - -static AtkObject * -table_wrapper_get_caption( AtkTable *table ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return atk_object_wrapper_conditional_ref( pTable->getAccessibleCaption() ); - } - - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleCaption()" ); - } - - return nullptr; -} - -/*****************************************************************************/ - -static const gchar * -table_wrapper_get_row_description( AtkTable *table, - gint row ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return getAsConst( pTable->getAccessibleRowDescription( row ) ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleRowDescription()" ); - } - - return nullptr; -} - -/*****************************************************************************/ - -static const gchar * -table_wrapper_get_column_description( AtkTable *table, - gint column ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return getAsConst( pTable->getAccessibleColumnDescription( column ) ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleColumnDescription()" ); - } - - return nullptr; -} - -/*****************************************************************************/ - -static AtkObject * -table_wrapper_get_row_header( AtkTable *table, - gint row ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - { - uno::Reference< accessibility::XAccessibleTable > xRowHeaders( pTable->getAccessibleRowHeaders() ); - if( xRowHeaders.is() ) - return atk_object_wrapper_conditional_ref( xRowHeaders->getAccessibleCellAt( row, 0 ) ); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleRowHeaders()" ); - } - - return nullptr; -} - -/*****************************************************************************/ - -static AtkObject * -table_wrapper_get_column_header( AtkTable *table, - gint column ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - { - uno::Reference< accessibility::XAccessibleTable > xColumnHeaders( pTable->getAccessibleColumnHeaders() ); - if( xColumnHeaders.is() ) - return atk_object_wrapper_conditional_ref( xColumnHeaders->getAccessibleCellAt( 0, column ) ); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleColumnHeaders()" ); - } - - return nullptr; -} - -/*****************************************************************************/ - -static AtkObject * -table_wrapper_get_summary( AtkTable *table ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - { - return atk_object_wrapper_conditional_ref( pTable->getAccessibleSummary() ); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleSummary()" ); - } - - return nullptr; -} - -/*****************************************************************************/ - -static gint -convertToGIntArray( const uno::Sequence< ::sal_Int32 >& aSequence, gint **pSelected ) -{ - if( aSequence.hasElements() ) - { - *pSelected = g_new( gint, aSequence.getLength() ); - - *pSelected = comphelper::sequenceToArray(*pSelected, aSequence); - } - - return aSequence.getLength(); -} - -/*****************************************************************************/ - -static gint -table_wrapper_get_selected_columns( AtkTable *table, - gint **pSelected ) -{ - *pSelected = nullptr; - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return convertToGIntArray( pTable->getSelectedAccessibleColumns(), pSelected ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getSelectedAccessibleColumns()" ); - } - - return 0; -} - -/*****************************************************************************/ - -static gint -table_wrapper_get_selected_rows( AtkTable *table, - gint **pSelected ) -{ - *pSelected = nullptr; - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return convertToGIntArray( pTable->getSelectedAccessibleRows(), pSelected ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getSelectedAccessibleRows()" ); - } - - return 0; -} - -/*****************************************************************************/ - -static gboolean -table_wrapper_is_column_selected( AtkTable *table, - gint column ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return pTable->isAccessibleColumnSelected( column ); - } - catch(const uno::Exception&) { - g_warning( "Exception in isAccessibleColumnSelected()" ); - } - - return 0; -} - -/*****************************************************************************/ - -static gboolean -table_wrapper_is_row_selected( AtkTable *table, - gint row ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return pTable->isAccessibleRowSelected( row ); - } - catch(const uno::Exception&) { - g_warning( "Exception in isAccessibleRowSelected()" ); - } - - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -table_wrapper_is_selected( AtkTable *table, - gint row, - gint column ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleTable> pTable - = getTable( table ); - if( pTable.is() ) - return pTable->isAccessibleSelected( row, column ); - } - catch(const uno::Exception&) { - g_warning( "Exception in isAccessibleSelected()" ); - } - - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -table_wrapper_add_row_selection( AtkTable *, gint ) -{ - g_warning( "FIXME: no simple analogue for add_row_selection" ); - return 0; -} - -/*****************************************************************************/ - -static gboolean -table_wrapper_remove_row_selection( AtkTable *, gint ) -{ - g_warning( "FIXME: no simple analogue for remove_row_selection" ); - return 0; -} - -/*****************************************************************************/ - -static gboolean -table_wrapper_add_column_selection( AtkTable *, gint ) -{ - g_warning( "FIXME: no simple analogue for add_column_selection" ); - return 0; -} - -/*****************************************************************************/ - -static gboolean -table_wrapper_remove_column_selection( AtkTable *, gint ) -{ - g_warning( "FIXME: no simple analogue for remove_column_selection" ); - return 0; -} - -/*****************************************************************************/ - -static void -table_wrapper_set_caption( AtkTable *, AtkObject * ) -{ // meaningless helper -} - -/*****************************************************************************/ - -static void -table_wrapper_set_column_description( AtkTable *, gint, const gchar * ) -{ // meaningless helper -} - -/*****************************************************************************/ - -static void -table_wrapper_set_column_header( AtkTable *, gint, AtkObject * ) -{ // meaningless helper -} - -/*****************************************************************************/ - -static void -table_wrapper_set_row_description( AtkTable *, gint, const gchar * ) -{ // meaningless helper -} - -/*****************************************************************************/ - -static void -table_wrapper_set_row_header( AtkTable *, gint, AtkObject * ) -{ // meaningless helper -} - -/*****************************************************************************/ - -static void -table_wrapper_set_summary( AtkTable *, AtkObject * ) -{ // meaningless helper -} - -/*****************************************************************************/ - -} // extern "C" - -void -tableIfaceInit (AtkTableIface *iface) -{ - g_return_if_fail (iface != nullptr); - - iface->ref_at = table_wrapper_ref_at; - iface->get_n_rows = table_wrapper_get_n_rows; - iface->get_n_columns = table_wrapper_get_n_columns; - iface->get_index_at = table_wrapper_get_index_at; - iface->get_column_at_index = table_wrapper_get_column_at_index; - iface->get_row_at_index = table_wrapper_get_row_at_index; - iface->is_row_selected = table_wrapper_is_row_selected; - iface->is_selected = table_wrapper_is_selected; - iface->get_selected_rows = table_wrapper_get_selected_rows; - iface->add_row_selection = table_wrapper_add_row_selection; - iface->remove_row_selection = table_wrapper_remove_row_selection; - iface->add_column_selection = table_wrapper_add_column_selection; - iface->remove_column_selection = table_wrapper_remove_column_selection; - iface->get_selected_columns = table_wrapper_get_selected_columns; - iface->is_column_selected = table_wrapper_is_column_selected; - iface->get_column_extent_at = table_wrapper_get_column_extent_at; - iface->get_row_extent_at = table_wrapper_get_row_extent_at; - iface->get_row_header = table_wrapper_get_row_header; - iface->set_row_header = table_wrapper_set_row_header; - iface->get_column_header = table_wrapper_get_column_header; - iface->set_column_header = table_wrapper_set_column_header; - iface->get_caption = table_wrapper_get_caption; - iface->set_caption = table_wrapper_set_caption; - iface->get_summary = table_wrapper_get_summary; - iface->set_summary = table_wrapper_set_summary; - iface->get_row_description = table_wrapper_get_row_description; - iface->set_row_description = table_wrapper_set_row_description; - iface->get_column_description = table_wrapper_get_column_description; - iface->set_column_description = table_wrapper_set_column_description; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atktext.cxx b/vcl/unx/gtk/a11y/atktext.cxx deleted file mode 100644 index 1406ceea5544..000000000000 --- a/vcl/unx/gtk/a11y/atktext.cxx +++ /dev/null @@ -1,838 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atkwrapper.hxx" -#include "atktextattributes.hxx" -#include <algorithm> - -#include <osl/diagnose.h> - -#include <com/sun/star/accessibility/AccessibleTextType.hpp> -#include <com/sun/star/accessibility/TextSegment.hpp> -#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp> -#include <com/sun/star/accessibility/XAccessibleText.hpp> -#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp> -#include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp> -#include <com/sun/star/text/TextMarkupType.hpp> - -using namespace ::com::sun::star; - -static sal_Int16 -text_type_from_boundary(AtkTextBoundary boundary_type) -{ - switch(boundary_type) - { - case ATK_TEXT_BOUNDARY_CHAR: - return accessibility::AccessibleTextType::CHARACTER; - case ATK_TEXT_BOUNDARY_WORD_START: - case ATK_TEXT_BOUNDARY_WORD_END: - return accessibility::AccessibleTextType::WORD; - case ATK_TEXT_BOUNDARY_SENTENCE_START: - case ATK_TEXT_BOUNDARY_SENTENCE_END: - return accessibility::AccessibleTextType::SENTENCE; - case ATK_TEXT_BOUNDARY_LINE_START: - case ATK_TEXT_BOUNDARY_LINE_END: - return accessibility::AccessibleTextType::LINE; - default: - return -1; - } -} - -/*****************************************************************************/ - -static gchar * -adjust_boundaries( css::uno::Reference<css::accessibility::XAccessibleText> const & pText, - accessibility::TextSegment const & rTextSegment, - AtkTextBoundary boundary_type, - gint * start_offset, gint * end_offset ) -{ - accessibility::TextSegment aTextSegment; - OUString aString; - gint start = 0, end = 0; - - if( !rTextSegment.SegmentText.isEmpty() ) - { - switch(boundary_type) - { - case ATK_TEXT_BOUNDARY_CHAR: - case ATK_TEXT_BOUNDARY_LINE_START: - case ATK_TEXT_BOUNDARY_LINE_END: - case ATK_TEXT_BOUNDARY_SENTENCE_START: - start = rTextSegment.SegmentStart; - end = rTextSegment.SegmentEnd; - aString = rTextSegment.SegmentText; - break; - - // the OOo break iterator behaves as SENTENCE_START - case ATK_TEXT_BOUNDARY_SENTENCE_END: - start = rTextSegment.SegmentStart; - end = rTextSegment.SegmentEnd; - - if( start > 0 ) - --start; - if( end > 0 && end < pText->getCharacterCount() - 1 ) - --end; - - aString = pText->getTextRange(start, end); - break; - - case ATK_TEXT_BOUNDARY_WORD_START: - start = rTextSegment.SegmentStart; - - // Determine the start index of the next segment - aTextSegment = pText->getTextBehindIndex(rTextSegment.SegmentEnd, - text_type_from_boundary(boundary_type)); - if( !aTextSegment.SegmentText.isEmpty() ) - end = aTextSegment.SegmentStart; - else - end = pText->getCharacterCount(); - - aString = pText->getTextRange(start, end); - break; - - case ATK_TEXT_BOUNDARY_WORD_END: - end = rTextSegment.SegmentEnd; - - // Determine the end index of the previous segment - aTextSegment = pText->getTextBeforeIndex(rTextSegment.SegmentStart, - text_type_from_boundary(boundary_type)); - if( !aTextSegment.SegmentText.isEmpty() ) - start = aTextSegment.SegmentEnd; - else - start = 0; - - aString = pText->getTextRange(start, end); - break; - - default: - return nullptr; - } - } - - *start_offset = start; - *end_offset = end; - - return OUStringToGChar(aString); -} - -/*****************************************************************************/ - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleText> - getText( AtkText *pText ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); - if( pWrap ) - { - if( !pWrap->mpText.is() ) - { - pWrap->mpText.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpText; - } - - return css::uno::Reference<css::accessibility::XAccessibleText>(); -} - -/*****************************************************************************/ - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleTextMarkup> - getTextMarkup( AtkText *pText ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); - if( pWrap ) - { - if( !pWrap->mpTextMarkup.is() ) - { - pWrap->mpTextMarkup.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpTextMarkup; - } - - return css::uno::Reference<css::accessibility::XAccessibleTextMarkup>(); -} - -/*****************************************************************************/ - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleTextAttributes> - getTextAttributes( AtkText *pText ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); - if( pWrap ) - { - if( !pWrap->mpTextAttributes.is() ) - { - pWrap->mpTextAttributes.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpTextAttributes; - } - - return css::uno::Reference<css::accessibility::XAccessibleTextAttributes>(); -} - -/*****************************************************************************/ - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleMultiLineText> - getMultiLineText( AtkText *pText ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); - if( pWrap ) - { - if( !pWrap->mpMultiLineText.is() ) - { - pWrap->mpMultiLineText.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpMultiLineText; - } - - return css::uno::Reference<css::accessibility::XAccessibleMultiLineText>(); -} - -/*****************************************************************************/ - -extern "C" { - -static gchar * -text_wrapper_get_text (AtkText *text, - gint start_offset, - gint end_offset) -{ - gchar * ret = nullptr; - - g_return_val_if_fail( (end_offset == -1) || (end_offset >= start_offset), nullptr ); - - /* at-spi expects the delete event to be send before the deletion happened - * so we save the deleted string object in the UNO event notification and - * fool libatk-bridge.so here .. - */ - void * pData = g_object_get_data( G_OBJECT(text), "ooo::text_changed::delete" ); - if( pData != nullptr ) - { - accessibility::TextSegment * pTextSegment = - static_cast <accessibility::TextSegment *> (pData); - - if( pTextSegment->SegmentStart == start_offset && - pTextSegment->SegmentEnd == end_offset ) - { - OString aUtf8 = OUStringToOString( pTextSegment->SegmentText, RTL_TEXTENCODING_UTF8 ); - return g_strdup( aUtf8.getStr() ); - } - } - - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - { - OUString aText; - sal_Int32 n = pText->getCharacterCount(); - - if( -1 == end_offset ) - aText = pText->getText(); - else if( start_offset < n ) - aText = pText->getTextRange(start_offset, end_offset); - - ret = g_strdup( OUStringToOString(aText, RTL_TEXTENCODING_UTF8 ).getStr() ); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getText()" ); - } - - return ret; -} - -static gchar * -text_wrapper_get_text_after_offset (AtkText *text, - gint offset, - AtkTextBoundary boundary_type, - gint *start_offset, - gint *end_offset) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - { - accessibility::TextSegment aTextSegment = pText->getTextBehindIndex(offset, text_type_from_boundary(boundary_type)); - return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in get_text_after_offset()" ); - } - - return nullptr; -} - -static gchar * -text_wrapper_get_text_at_offset (AtkText *text, - gint offset, - AtkTextBoundary boundary_type, - gint *start_offset, - gint *end_offset) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - { - /* If the user presses the 'End' key, the caret will be placed behind the last character, - * which is the same index as the first character of the next line. In atk the magic offset - * '-2' is used to cover this special case. - */ - if ( - -2 == offset && - (ATK_TEXT_BOUNDARY_LINE_START == boundary_type || - ATK_TEXT_BOUNDARY_LINE_END == boundary_type) - ) - { - css::uno::Reference< - css::accessibility::XAccessibleMultiLineText> pMultiLineText - = getMultiLineText( text ); - if( pMultiLineText.is() ) - { - accessibility::TextSegment aTextSegment = pMultiLineText->getTextAtLineWithCaret(); - return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); - } - } - - accessibility::TextSegment aTextSegment = pText->getTextAtIndex(offset, text_type_from_boundary(boundary_type)); - return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in get_text_at_offset()" ); - } - - return nullptr; -} - -static gunichar -text_wrapper_get_character_at_offset (AtkText *text, - gint offset) -{ - gint start, end; - gunichar uc = 0; - - gchar * char_as_string = - text_wrapper_get_text_at_offset(text, offset, ATK_TEXT_BOUNDARY_CHAR, - &start, &end); - if( char_as_string ) - { - uc = g_utf8_get_char( char_as_string ); - g_free( char_as_string ); - } - - return uc; -} - -static gchar * -text_wrapper_get_text_before_offset (AtkText *text, - gint offset, - AtkTextBoundary boundary_type, - gint *start_offset, - gint *end_offset) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - { - accessibility::TextSegment aTextSegment = pText->getTextBeforeIndex(offset, text_type_from_boundary(boundary_type)); - return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in text_before_offset()" ); - } - - return nullptr; -} - -static gint -text_wrapper_get_caret_offset (AtkText *text) -{ - gint offset = -1; - - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - offset = pText->getCaretPosition(); - } - catch(const uno::Exception&) { - g_warning( "Exception in getCaretPosition()" ); - } - - return offset; -} - -static gboolean -text_wrapper_set_caret_offset (AtkText *text, - gint offset) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - return pText->setCaretPosition( offset ); - } - catch(const uno::Exception&) { - g_warning( "Exception in setCaretPosition()" ); - } - - return FALSE; -} - -// #i92232# -static AtkAttributeSet* -handle_text_markup_as_run_attribute( css::uno::Reference<css::accessibility::XAccessibleTextMarkup> const & pTextMarkup, - const gint nTextMarkupType, - const gint offset, - AtkAttributeSet* pSet, - gint *start_offset, - gint *end_offset ) -{ - const gint nTextMarkupCount( pTextMarkup->getTextMarkupCount( nTextMarkupType ) ); - if ( nTextMarkupCount > 0 ) - { - for ( gint nTextMarkupIndex = 0; - nTextMarkupIndex < nTextMarkupCount; - ++nTextMarkupIndex ) - { - accessibility::TextSegment aTextSegment = - pTextMarkup->getTextMarkup( nTextMarkupIndex, nTextMarkupType ); - const gint nStartOffsetTextMarkup = aTextSegment.SegmentStart; - const gint nEndOffsetTextMarkup = aTextSegment.SegmentEnd; - if ( nStartOffsetTextMarkup <= offset ) - { - if ( offset < nEndOffsetTextMarkup ) - { - // text markup at <offset> - *start_offset = ::std::max( *start_offset, - nStartOffsetTextMarkup ); - *end_offset = ::std::min( *end_offset, - nEndOffsetTextMarkup ); - switch ( nTextMarkupType ) - { - case css::text::TextMarkupType::SPELLCHECK: - { - pSet = attribute_set_prepend_misspelled( pSet ); - } - break; - case css::text::TextMarkupType::TRACK_CHANGE_INSERTION: - { - pSet = attribute_set_prepend_tracked_change_insertion( pSet ); - } - break; - case css::text::TextMarkupType::TRACK_CHANGE_DELETION: - { - pSet = attribute_set_prepend_tracked_change_deletion( pSet ); - } - break; - case css::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE: - { - pSet = attribute_set_prepend_tracked_change_formatchange( pSet ); - } - break; - default: - { - OSL_ASSERT( false ); - } - } - break; // no further iteration needed. - } - else - { - *start_offset = ::std::max( *start_offset, - nEndOffsetTextMarkup ); - // continue iteration. - } - } - else - { - *end_offset = ::std::min( *end_offset, - nStartOffsetTextMarkup ); - break; // no further iteration. - } - } // eof iteration over text markups - } - - return pSet; -} - -static AtkAttributeSet * -text_wrapper_get_run_attributes( AtkText *text, - gint offset, - gint *start_offset, - gint *end_offset) -{ - AtkAttributeSet *pSet = nullptr; - - try { - bool bOffsetsAreValid = false; - - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is()) - { - uno::Sequence< beans::PropertyValue > aAttributeList; - - css::uno::Reference<css::accessibility::XAccessibleTextAttributes> - pTextAttributes = getTextAttributes( text ); - if(pTextAttributes.is()) // Text attributes are available for paragraphs only - { - aAttributeList = pTextAttributes->getRunAttributes( offset, uno::Sequence< OUString > () ); - } - else // For other text objects use character attributes - { - aAttributeList = pText->getCharacterAttributes( offset, uno::Sequence< OUString > () ); - } - - pSet = attribute_set_new_from_property_values( aAttributeList, true, text ); - // #i100938# - // - always provide start_offset and end_offset - { - accessibility::TextSegment aTextSegment = - pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN); - - *start_offset = aTextSegment.SegmentStart; - // #i100938# - // Do _not_ increment the end_offset provide by <accessibility::TextSegment> instance - *end_offset = aTextSegment.SegmentEnd; - bOffsetsAreValid = true; - } - } - - // Special handling for misspelled text - // #i92232# - // - add special handling for tracked changes and refactor the - // corresponding code for handling misspelled text. - css::uno::Reference<css::accessibility::XAccessibleTextMarkup> - pTextMarkup = getTextMarkup( text ); - if( pTextMarkup.is() ) - { - // Get attribute run here if it hasn't been done before - if (!bOffsetsAreValid && pText.is()) - { - accessibility::TextSegment aAttributeTextSegment = - pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN); - *start_offset = aAttributeTextSegment.SegmentStart; - *end_offset = aAttributeTextSegment.SegmentEnd; - } - // handle misspelled text - pSet = handle_text_markup_as_run_attribute( - pTextMarkup, - css::text::TextMarkupType::SPELLCHECK, - offset, pSet, start_offset, end_offset ); - // handle tracked changes - pSet = handle_text_markup_as_run_attribute( - pTextMarkup, - css::text::TextMarkupType::TRACK_CHANGE_INSERTION, - offset, pSet, start_offset, end_offset ); - pSet = handle_text_markup_as_run_attribute( - pTextMarkup, - css::text::TextMarkupType::TRACK_CHANGE_DELETION, - offset, pSet, start_offset, end_offset ); - pSet = handle_text_markup_as_run_attribute( - pTextMarkup, - css::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE, - offset, pSet, start_offset, end_offset ); - } - } - catch(const uno::Exception&){ - - g_warning( "Exception in get_run_attributes()" ); - - if( pSet ) - { - atk_attribute_set_free( pSet ); - pSet = nullptr; - } - } - - return pSet; -} - -/*****************************************************************************/ - -static AtkAttributeSet * -text_wrapper_get_default_attributes( AtkText *text ) -{ - AtkAttributeSet *pSet = nullptr; - - try { - css::uno::Reference<css::accessibility::XAccessibleTextAttributes> - pTextAttributes = getTextAttributes( text ); - if( pTextAttributes.is() ) - { - uno::Sequence< beans::PropertyValue > aAttributeList = - pTextAttributes->getDefaultAttributes( uno::Sequence< OUString > () ); - - pSet = attribute_set_new_from_property_values( aAttributeList, false, text ); - } - } - catch(const uno::Exception&) { - - g_warning( "Exception in get_default_attributes()" ); - - if( pSet ) - { - atk_attribute_set_free( pSet ); - pSet = nullptr; - } - } - - return pSet; -} - -/*****************************************************************************/ - -static void -text_wrapper_get_character_extents( AtkText *text, - gint offset, - gint *x, - gint *y, - gint *width, - gint *height, - AtkCoordType coords ) -{ - *x = *y = *width = *height = -1; - - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - { - awt::Rectangle aRect = pText->getCharacterBounds( offset ); - - gint origin_x = 0; - gint origin_y = 0; - - if( coords == ATK_XY_SCREEN ) - { - g_return_if_fail( ATK_IS_COMPONENT( text ) ); - SAL_WNODEPRECATED_DECLARATIONS_PUSH - atk_component_get_position( ATK_COMPONENT( text ), &origin_x, &origin_y, coords); - SAL_WNODEPRECATED_DECLARATIONS_POP - } - - *x = aRect.X + origin_x; - *y = aRect.Y + origin_y; - *width = aRect.Width; - *height = aRect.Height; - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getCharacterBounds" ); - } -} - -static gint -text_wrapper_get_character_count (AtkText *text) -{ - gint rv = 0; - - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - rv = pText->getCharacterCount(); - } - catch(const uno::Exception&) { - g_warning( "Exception in getCharacterCount" ); - } - - return rv; -} - -static gint -text_wrapper_get_offset_at_point (AtkText *text, - gint x, - gint y, - AtkCoordType coords) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - { - gint origin_x = 0; - gint origin_y = 0; - - if( coords == ATK_XY_SCREEN ) - { - g_return_val_if_fail( ATK_IS_COMPONENT( text ), -1 ); - SAL_WNODEPRECATED_DECLARATIONS_PUSH - atk_component_get_position( ATK_COMPONENT( text ), &origin_x, &origin_y, coords); - SAL_WNODEPRECATED_DECLARATIONS_POP - } - - return pText->getIndexAtPoint( awt::Point(x - origin_x, y - origin_y) ); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getIndexAtPoint" ); - } - - return -1; -} - -// FIXME: the whole series of selections API is problematic ... - -static gint -text_wrapper_get_n_selections (AtkText *text) -{ - gint rv = 0; - - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - rv = ( pText->getSelectionEnd() > pText->getSelectionStart() ) ? 1 : 0; - } - catch(const uno::Exception&) { - g_warning( "Exception in getSelectionEnd() or getSelectionStart()" ); - } - - return rv; -} - -static gchar * -text_wrapper_get_selection (AtkText *text, - gint selection_num, - gint *start_offset, - gint *end_offset) -{ - g_return_val_if_fail( selection_num == 0, FALSE ); - - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - { - *start_offset = pText->getSelectionStart(); - *end_offset = pText->getSelectionEnd(); - - return OUStringToGChar( pText->getSelectedText() ); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getSelectionEnd(), getSelectionStart() or getSelectedText()" ); - } - - return nullptr; -} - -static gboolean -text_wrapper_add_selection (AtkText *text, - gint start_offset, - gint end_offset) -{ - // FIXME: can we try to be more compatible by expanding an - // existing adjacent selection ? - - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - return pText->setSelection( start_offset, end_offset ); // ? - } - catch(const uno::Exception&) { - g_warning( "Exception in setSelection()" ); - } - - return FALSE; -} - -static gboolean -text_wrapper_remove_selection (AtkText *text, - gint selection_num) -{ - g_return_val_if_fail( selection_num == 0, FALSE ); - - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - return pText->setSelection( 0, 0 ); // ? - } - catch(const uno::Exception&) { - g_warning( "Exception in setSelection()" ); - } - - return FALSE; -} - -static gboolean -text_wrapper_set_selection (AtkText *text, - gint selection_num, - gint start_offset, - gint end_offset) -{ - g_return_val_if_fail( selection_num == 0, FALSE ); - - try { - css::uno::Reference<css::accessibility::XAccessibleText> pText - = getText( text ); - if( pText.is() ) - return pText->setSelection( start_offset, end_offset ); - } - catch(const uno::Exception&) { - g_warning( "Exception in setSelection()" ); - } - - return FALSE; -} - -} // extern "C" - -void -textIfaceInit (AtkTextIface *iface) -{ - g_return_if_fail (iface != nullptr); - - iface->get_text = text_wrapper_get_text; - iface->get_character_at_offset = text_wrapper_get_character_at_offset; - iface->get_text_before_offset = text_wrapper_get_text_before_offset; - iface->get_text_at_offset = text_wrapper_get_text_at_offset; - iface->get_text_after_offset = text_wrapper_get_text_after_offset; - iface->get_caret_offset = text_wrapper_get_caret_offset; - iface->set_caret_offset = text_wrapper_set_caret_offset; - iface->get_character_count = text_wrapper_get_character_count; - iface->get_n_selections = text_wrapper_get_n_selections; - iface->get_selection = text_wrapper_get_selection; - iface->add_selection = text_wrapper_add_selection; - iface->remove_selection = text_wrapper_remove_selection; - iface->set_selection = text_wrapper_set_selection; - iface->get_run_attributes = text_wrapper_get_run_attributes; - iface->get_default_attributes = text_wrapper_get_default_attributes; - iface->get_character_extents = text_wrapper_get_character_extents; - iface->get_offset_at_point = text_wrapper_get_offset_at_point; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atktextattributes.cxx b/vcl/unx/gtk/a11y/atktextattributes.cxx deleted file mode 100644 index 0ba7fd561862..000000000000 --- a/vcl/unx/gtk/a11y/atktextattributes.cxx +++ /dev/null @@ -1,1383 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atktextattributes.hxx" - -#include <com/sun/star/awt/FontSlant.hpp> -#include <com/sun/star/awt/FontStrikeout.hpp> -#include <com/sun/star/awt/FontUnderline.hpp> - -#include <com/sun/star/style/CaseMap.hpp> -#include <com/sun/star/style/LineSpacing.hpp> -#include <com/sun/star/style/LineSpacingMode.hpp> -#include <com/sun/star/style/ParagraphAdjust.hpp> -#include <com/sun/star/style/TabAlign.hpp> -#include <com/sun/star/style/TabStop.hpp> - -#include <com/sun/star/text/WritingMode2.hpp> - -#include "atkwrapper.hxx" - -#include <com/sun/star/accessibility/XAccessibleComponent.hpp> - -#include <i18nlangtag/languagetag.hxx> -#include <vcl/svapp.hxx> -#include <vcl/outdev.hxx> - -#include <stdio.h> -#include <string.h> - -using namespace ::com::sun::star; - -typedef gchar* (* AtkTextAttrFunc) ( const uno::Any& rAny ); -typedef bool (* TextPropertyValueFunc) ( uno::Any& rAny, const gchar * value ); - -#define STRNCMP_PARAM( s ) s,sizeof( s )-1 - -/*****************************************************************************/ - -static AtkTextAttribute atk_text_attribute_paragraph_style = ATK_TEXT_ATTR_INVALID; -static AtkTextAttribute atk_text_attribute_font_effect = ATK_TEXT_ATTR_INVALID; -static AtkTextAttribute atk_text_attribute_decoration = ATK_TEXT_ATTR_INVALID; -static AtkTextAttribute atk_text_attribute_line_height = ATK_TEXT_ATTR_INVALID; -static AtkTextAttribute atk_text_attribute_rotation = ATK_TEXT_ATTR_INVALID; -static AtkTextAttribute atk_text_attribute_shadow = ATK_TEXT_ATTR_INVALID; -static AtkTextAttribute atk_text_attribute_tab_interval = ATK_TEXT_ATTR_INVALID; -static AtkTextAttribute atk_text_attribute_tab_stops = ATK_TEXT_ATTR_INVALID; -static AtkTextAttribute atk_text_attribute_writing_mode = ATK_TEXT_ATTR_INVALID; -static AtkTextAttribute atk_text_attribute_vertical_align = ATK_TEXT_ATTR_INVALID; -static AtkTextAttribute atk_text_attribute_misspelled = ATK_TEXT_ATTR_INVALID; -// #i92232# -static AtkTextAttribute atk_text_attribute_tracked_change = ATK_TEXT_ATTR_INVALID; -// #i92233# -static AtkTextAttribute atk_text_attribute_mm_to_pixel_ratio = ATK_TEXT_ATTR_INVALID; - -/*****************************************************************************/ - -/** - * !! IMPORTANT NOTE !! : when adding items to this list, KEEP THE LIST SORTED - * and re-arrange the enum values accordingly. - */ - -enum ExportedAttribute -{ - TEXT_ATTRIBUTE_BACKGROUND_COLOR = 0, - TEXT_ATTRIBUTE_CASEMAP, - TEXT_ATTRIBUTE_FOREGROUND_COLOR, - TEXT_ATTRIBUTE_CONTOURED, - TEXT_ATTRIBUTE_CHAR_ESCAPEMENT, - TEXT_ATTRIBUTE_BLINKING, - TEXT_ATTRIBUTE_FONT_NAME, - TEXT_ATTRIBUTE_HEIGHT, - TEXT_ATTRIBUTE_HIDDEN, - TEXT_ATTRIBUTE_KERNING, - TEXT_ATTRIBUTE_LOCALE, - TEXT_ATTRIBUTE_POSTURE, - TEXT_ATTRIBUTE_RELIEF, - TEXT_ATTRIBUTE_ROTATION, - TEXT_ATTRIBUTE_SCALE, - TEXT_ATTRIBUTE_SHADOWED, - TEXT_ATTRIBUTE_STRIKETHROUGH, - TEXT_ATTRIBUTE_UNDERLINE, - TEXT_ATTRIBUTE_WEIGHT, - // #i92233# - TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO, - TEXT_ATTRIBUTE_JUSTIFICATION, - TEXT_ATTRIBUTE_BOTTOM_MARGIN, - TEXT_ATTRIBUTE_FIRST_LINE_INDENT, - TEXT_ATTRIBUTE_LEFT_MARGIN, - TEXT_ATTRIBUTE_LINE_SPACING, - TEXT_ATTRIBUTE_RIGHT_MARGIN, - TEXT_ATTRIBUTE_STYLE_NAME, - TEXT_ATTRIBUTE_TAB_STOPS, - TEXT_ATTRIBUTE_TOP_MARGIN, - TEXT_ATTRIBUTE_WRITING_MODE, - TEXT_ATTRIBUTE_LAST -}; - -static const char * ExportedTextAttributes[TEXT_ATTRIBUTE_LAST] = -{ - "CharBackColor", // TEXT_ATTRIBUTE_BACKGROUND_COLOR - "CharCaseMap", // TEXT_ATTRIBUTE_CASEMAP - "CharColor", // TEXT_ATTRIBUTE_FOREGROUND_COLOR - "CharContoured", // TEXT_ATTRIBUTE_CONTOURED - "CharEscapement", // TEXT_ATTRIBUTE_CHAR_ESCAPEMENT - "CharFlash", // TEXT_ATTRIBUTE_BLINKING - "CharFontName", // TEXT_ATTRIBUTE_FONT_NAME - "CharHeight", // TEXT_ATTRIBUTE_HEIGHT - "CharHidden", // TEXT_ATTRIBUTE_HIDDEN - "CharKerning", // TEXT_ATTRIBUTE_KERNING - "CharLocale", // TEXT_ATTRIBUTE_LOCALE - "CharPosture", // TEXT_ATTRIBUTE_POSTURE - "CharRelief", // TEXT_ATTRIBUTE_RELIEF - "CharRotation", // TEXT_ATTRIBUTE_ROTATION - "CharScaleWidth", // TEXT_ATTRIBUTE_SCALE - "CharShadowed", // TEXT_ATTRIBUTE_SHADOWED - "CharStrikeout", // TEXT_ATTRIBUTE_STRIKETHROUGH - "CharUnderline", // TEXT_ATTRIBUTE_UNDERLINE - "CharWeight", // TEXT_ATTRIBUTE_WEIGHT - // #i92233# - "MMToPixelRatio", // TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO - "ParaAdjust", // TEXT_ATTRIBUTE_JUSTIFICATION - "ParaBottomMargin", // TEXT_ATTRIBUTE_BOTTOM_MARGIN - "ParaFirstLineIndent", // TEXT_ATTRIBUTE_FIRST_LINE_INDENT - "ParaLeftMargin", // TEXT_ATTRIBUTE_LEFT_MARGIN - "ParaLineSpacing", // TEXT_ATTRIBUTE_LINE_SPACING - "ParaRightMargin", // TEXT_ATTRIBUTE_RIGHT_MARGIN - "ParaStyleName", // TEXT_ATTRIBUTE_STYLE_NAME - "ParaTabStops", // TEXT_ATTRIBUTE_TAB_STOPS - "ParaTopMargin", // TEXT_ATTRIBUTE_TOP_MARGIN - "WritingMode" // TEXT_ATTRIBUTE_WRITING_MODE -}; - -/*****************************************************************************/ - -static gchar* -get_value( const uno::Sequence< beans::PropertyValue >& rAttributeList, - sal_Int32 nIndex, AtkTextAttrFunc func ) -{ - if( nIndex != -1 ) - return func(rAttributeList[nIndex].Value); - - return nullptr; -} - -#define get_bool_value( list, index ) get_value( list, index, Bool2String ) -#define get_height_value( list, index ) get_value( list, index, Float2String ) -#define get_justification_value( list, index ) get_value( list, index, Adjust2Justification ) -#define get_cmm_value( list, index ) get_value( list, index, CMM2UnitString ) -#define get_scale_width( list, index ) get_value( list, index, Scale2String ) -#define get_strikethrough_value( list, index ) get_value( list, index, Strikeout2String ) -#define get_string_value( list, index ) get_value( list, index, GetString ) -#define get_style_value( list, index ) get_value( list, index, FontSlant2Style ) -#define get_underline_value( list, index ) get_value( list, index, Underline2String ) -#define get_variant_value( list, index ) get_value( list, index, CaseMap2String ) -#define get_weight_value( list, index ) get_value( list, index, Weight2String ) -#define get_language_string( list, index ) get_value( list, index, Locale2String ) - -static double toPoint(sal_Int16 n) -{ - // 100th mm -> pt - return static_cast<double>(n * 72) / 2540; -} - -/*****************************************************************************/ - -static bool -InvalidValue( uno::Any&, const gchar * ) -{ - return false; -} - -/*****************************************************************************/ - -static gchar* -Float2String(const uno::Any& rAny) -{ - return g_strdup_printf( "%g", rAny.get<float>() ); -} - -static bool -String2Float( uno::Any& rAny, const gchar * value ) -{ - float fval; - - if( 1 != sscanf( value, "%g", &fval ) ) - return false; - - rAny <<= fval; - return true; -} - -/*****************************************************************************/ - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleComponent> - getComponent( AtkText *pText ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); - if( pWrap ) - { - if( !pWrap->mpComponent.is() ) - { - pWrap->mpComponent.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpComponent; - } - - return css::uno::Reference<css::accessibility::XAccessibleComponent>(); -} - -static gchar* -get_color_value(const uno::Sequence< beans::PropertyValue >& rAttributeList, - const sal_Int32 * pIndexArray, - ExportedAttribute attr, - AtkText * text) -{ - sal_Int32 nColor = -1; // AUTOMATIC - sal_Int32 nIndex = pIndexArray[attr]; - - if( nIndex != -1 ) - nColor = rAttributeList[nIndex].Value.get<sal_Int32>(); - - /* - * Check for color value for 100% alpha white, which means - * "automatic". Grab the RGB value from XAccessibleComponent - * in this case. - */ - - if( (nColor == -1) && text ) - { - try - { - css::uno::Reference<css::accessibility::XAccessibleComponent> - pComponent = getComponent( text ); - if( pComponent.is() ) - { - switch( attr ) - { - case TEXT_ATTRIBUTE_BACKGROUND_COLOR: - nColor = pComponent->getBackground(); - break; - case TEXT_ATTRIBUTE_FOREGROUND_COLOR: - nColor = pComponent->getForeground(); - break; - default: - break; - } - } - } - - catch(const uno::Exception&) { - g_warning( "Exception in get[Fore|Back]groundColor()" ); - } - } - - if( nColor != -1 ) - { - sal_uInt8 blue = nColor & 0xFF; - sal_uInt8 green = (nColor >> 8) & 0xFF; - sal_uInt8 red = (nColor >> 16) & 0xFF; - - return g_strdup_printf( "%u,%u,%u", red, green, blue ); - } - - return nullptr; -} - -static bool -String2Color( uno::Any& rAny, const gchar * value ) -{ - int red, green, blue; - - if( 3 != sscanf( value, "%d,%d,%d", &red, &green, &blue ) ) - return false; - - sal_Int32 nColor = static_cast<sal_Int32>(blue) | ( static_cast<sal_Int32>(green) << 8 ) | ( static_cast<sal_Int32>(red) << 16 ); - rAny <<= nColor; - return true; -} - -/*****************************************************************************/ - -static gchar* -FontSlant2Style(const uno::Any& rAny) -{ - const gchar * value = nullptr; - - awt::FontSlant aFontSlant; - if(!(rAny >>= aFontSlant)) - return nullptr; - - switch( aFontSlant ) - { - case awt::FontSlant_NONE: - value = "normal"; - break; - - case awt::FontSlant_OBLIQUE: - value = "oblique"; - break; - - case awt::FontSlant_ITALIC: - value = "italic"; - break; - - case awt::FontSlant_REVERSE_OBLIQUE: - value = "reverse oblique"; - break; - - case awt::FontSlant_REVERSE_ITALIC: - value = "reverse italic"; - break; - - default: - break; - } - - if( value ) - return g_strdup( value ); - - return nullptr; -} - -static bool -Style2FontSlant( uno::Any& rAny, const gchar * value ) -{ - awt::FontSlant aFontSlant; - - if( strncmp( value, STRNCMP_PARAM( "normal" ) ) == 0 ) - aFontSlant = awt::FontSlant_NONE; - else if( strncmp( value, STRNCMP_PARAM( "oblique" ) ) == 0 ) - aFontSlant = awt::FontSlant_OBLIQUE; - else if( strncmp( value, STRNCMP_PARAM( "italic" ) ) == 0 ) - aFontSlant = awt::FontSlant_ITALIC; - else if( strncmp( value, STRNCMP_PARAM( "reverse oblique" ) ) == 0 ) - aFontSlant = awt::FontSlant_REVERSE_OBLIQUE; - else if( strncmp( value, STRNCMP_PARAM( "reverse italic" ) ) == 0 ) - aFontSlant = awt::FontSlant_REVERSE_ITALIC; - else - return false; - - rAny <<= aFontSlant; - return true; -} - -/*****************************************************************************/ - -static gchar* -Weight2String(const uno::Any& rAny) -{ - return g_strdup_printf( "%g", rAny.get<float>() * 4 ); -} - -static bool -String2Weight( uno::Any& rAny, const gchar * value ) -{ - float weight; - - if( 1 != sscanf( value, "%g", &weight ) ) - return false; - - rAny <<= weight / 4; - return true; -} - -/*****************************************************************************/ - -static gchar* -Adjust2Justification(const uno::Any& rAny) -{ - const gchar * value = nullptr; - - switch( static_cast<style::ParagraphAdjust>(rAny.get<short>()) ) - { - case style::ParagraphAdjust_LEFT: - value = "left"; - break; - - case style::ParagraphAdjust_RIGHT: - value = "right"; - break; - - case style::ParagraphAdjust_BLOCK: - case style::ParagraphAdjust_STRETCH: - value = "fill"; - break; - - case style::ParagraphAdjust_CENTER: - value = "center"; - break; - - default: - break; - } - - if( value ) - return g_strdup( value ); - - return nullptr; -} - -static bool -Justification2Adjust( uno::Any& rAny, const gchar * value ) -{ - style::ParagraphAdjust nParagraphAdjust; - - if( strncmp( value, STRNCMP_PARAM( "left" ) ) == 0 ) - nParagraphAdjust = style::ParagraphAdjust_LEFT; - else if( strncmp( value, STRNCMP_PARAM( "right" ) ) == 0 ) - nParagraphAdjust = style::ParagraphAdjust_RIGHT; - else if( strncmp( value, STRNCMP_PARAM( "fill" ) ) == 0 ) - nParagraphAdjust = style::ParagraphAdjust_BLOCK; - else if( strncmp( value, STRNCMP_PARAM( "center" ) ) == 0 ) - nParagraphAdjust = style::ParagraphAdjust_CENTER; - else - return false; - - rAny <<= static_cast<short>(nParagraphAdjust); - return true; -} - -/*****************************************************************************/ - -const gchar * const font_strikethrough[] = { - "none", // FontStrikeout::NONE - "single", // FontStrikeout::SINGLE - "double", // FontStrikeout::DOUBLE - nullptr, // FontStrikeout::DONTKNOW - "bold", // FontStrikeout::BOLD - "with /", // FontStrikeout::SLASH - "with X" // FontStrikeout::X -}; - -static gchar* -Strikeout2String(const uno::Any& rAny) -{ - sal_Int16 n = rAny.get<sal_Int16>(); - - if( n >= 0 && n < sal_Int16(SAL_N_ELEMENTS(font_strikethrough)) ) - return g_strdup( font_strikethrough[n] ); - - return nullptr; -} - -static bool -String2Strikeout( uno::Any& rAny, const gchar * value ) -{ - for( sal_Int16 n=0; n < sal_Int16(SAL_N_ELEMENTS(font_strikethrough)); ++n ) - { - if( ( nullptr != font_strikethrough[n] ) && - 0 == strncmp( value, font_strikethrough[n], strlen( font_strikethrough[n] ) ) ) - { - rAny <<= n; - return true; - } - } - - return false; -} - -/*****************************************************************************/ - -static gchar* -Underline2String(const uno::Any& rAny) -{ - const gchar * value = nullptr; - - switch( rAny.get<sal_Int16>() ) - { - case awt::FontUnderline::NONE: - value = "none"; - break; - - case awt::FontUnderline::SINGLE: - value = "single"; - break; - - case awt::FontUnderline::DOUBLE: - value = "double"; - break; - - default: - break; - } - - if( value ) - return g_strdup( value ); - - return nullptr; -} - -static bool -String2Underline( uno::Any& rAny, const gchar * value ) -{ - short nUnderline; - - if( strncmp( value, STRNCMP_PARAM( "none" ) ) == 0 ) - nUnderline = awt::FontUnderline::NONE; - else if( strncmp( value, STRNCMP_PARAM( "single" ) ) == 0 ) - nUnderline = awt::FontUnderline::SINGLE; - else if( strncmp( value, STRNCMP_PARAM( "double" ) ) == 0 ) - nUnderline = awt::FontUnderline::DOUBLE; - else - return false; - - rAny <<= nUnderline; - return true; -} - -/*****************************************************************************/ - -static gchar* -GetString(const uno::Any& rAny) -{ - OString aFontName = OUStringToOString( rAny.get< OUString > (), RTL_TEXTENCODING_UTF8 ); - - if( !aFontName.isEmpty() ) - return g_strdup( aFontName.getStr() ); - - return nullptr; -} - -static bool -SetString( uno::Any& rAny, const gchar * value ) -{ - OString aFontName( value ); - - if( !aFontName.isEmpty() ) - { - rAny <<= OStringToOUString( aFontName, RTL_TEXTENCODING_UTF8 ); - return true; - } - - return false; -} - -/*****************************************************************************/ - -// @see http://developer.gnome.org/doc/API/2.0/atk/AtkText.html#AtkTextAttribute - -// CMM = 100th of mm -static gchar* -CMM2UnitString(const uno::Any& rAny) -{ - double fValue = rAny.get<sal_Int32>(); - fValue = fValue * 0.01; - - return g_strdup_printf( "%gmm", fValue ); -} - -static bool -UnitString2CMM( uno::Any& rAny, const gchar * value ) -{ - float fValue = 0.0; // pb: don't use double here because of warning on linux - - if( 1 != sscanf( value, "%gmm", &fValue ) ) - return false; - - fValue = fValue * 100; - - rAny <<= static_cast<sal_Int32>(fValue); - return true; -} - -/*****************************************************************************/ - -static const gchar * bool_values[] = { "true", "false" }; - -static gchar * -Bool2String( const uno::Any& rAny ) -{ - int n = 1; - - if( rAny.get<bool>() ) - n = 0; - - return g_strdup( bool_values[n] ); -} - -static bool -String2Bool( uno::Any& rAny, const gchar * value ) -{ - bool bValue; - - if( strncmp( value, STRNCMP_PARAM( "true" ) ) == 0 ) - bValue = true; - else if( strncmp( value, STRNCMP_PARAM( "false" ) ) == 0 ) - bValue = false; - else - return false; - - rAny <<= bValue; - return true; -} - -/*****************************************************************************/ - -static gchar* -Scale2String( const uno::Any& rAny ) -{ - return g_strdup_printf( "%g", static_cast<double>(rAny.get< sal_Int16 > ()) / 100 ); -} - -static bool -String2Scale( uno::Any& rAny, const gchar * value ) -{ - double dval; - - if( 1 != sscanf( value, "%lg", &dval ) ) - return false; - - rAny <<= static_cast<sal_Int16>(dval * 100); - return true; -} - -/*****************************************************************************/ - -static gchar * -CaseMap2String( const uno::Any& rAny ) -{ - const gchar * value; - - switch( rAny.get<short>() ) - { - case style::CaseMap::SMALLCAPS: - value = "small_caps"; - break; - - default: - value = "normal"; - break; - } - - return g_strdup(value); -} - -static bool -String2CaseMap( uno::Any& rAny, const gchar * value ) -{ - short nCaseMap; - - if( strncmp( value, STRNCMP_PARAM( "normal" ) ) == 0 ) - nCaseMap = style::CaseMap::NONE; - else if( strncmp( value, STRNCMP_PARAM( "small_caps" ) ) == 0 ) - nCaseMap = style::CaseMap::SMALLCAPS; - else - return false; - - rAny <<= nCaseMap; - return true; -} - -/*****************************************************************************/ - -const gchar * const font_stretch[] = { - "ultra_condensed", - "extra_condensed", - "condensed", - "semi_condensed", - "normal", - "semi_expanded", - "expanded", - "extra_expanded", - "ultra_expanded" -}; - -static gchar* -Kerning2Stretch(const uno::Any& rAny) -{ - sal_Int16 n = rAny.get<sal_Int16>(); - int i = 4; - - // No good idea for a mapping - just return the basic info - if( n < 0 ) - i=2; - else if( n > 0 ) - i=6; - - return g_strdup(font_stretch[i]); -} - -/*****************************************************************************/ - -static gchar* -Locale2String(const uno::Any& rAny) -{ - /* FIXME-BCP47: support language tags? And why is country lowercase? */ - lang::Locale aLocale = rAny.get<lang::Locale> (); - LanguageTag aLanguageTag( aLocale); - return g_strdup_printf( "%s-%s", - OUStringToOString( aLanguageTag.getLanguage(), RTL_TEXTENCODING_ASCII_US).getStr(), - OUStringToOString( aLanguageTag.getCountry(), RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase().getStr() ); -} - -static bool -String2Locale( uno::Any& rAny, const gchar * value ) -{ - /* FIXME-BCP47: support language tags? */ - bool ret = false; - - gchar ** str_array = g_strsplit_set( value, "-.@", -1 ); - if( str_array[0] != nullptr ) - { - ret = true; - - lang::Locale aLocale; - - aLocale.Language = OUString::createFromAscii(str_array[0]); - if( str_array[1] != nullptr ) - { - gchar * country = g_ascii_strup(str_array[1], -1); - aLocale.Country = OUString::createFromAscii(country); - g_free(country); - } - - rAny <<= aLocale; - } - - g_strfreev(str_array); - return ret; -} - -/*****************************************************************************/ - -// @see http://www.w3.org/TR/2002/WD-css3-fonts-20020802/#font-effect-prop -static const gchar * relief[] = { "none", "emboss", "engrave" }; -static const gchar * const outline = "outline"; - -static gchar * -get_font_effect(const uno::Sequence< beans::PropertyValue >& rAttributeList, - sal_Int32 nContourIndex, sal_Int32 nReliefIndex) -{ - if( nContourIndex != -1 ) - { - if( rAttributeList[nContourIndex].Value.get<bool>() ) - return g_strdup(outline); - } - - if( nReliefIndex != -1 ) - { - sal_Int16 n = rAttributeList[nReliefIndex].Value.get<sal_Int16>(); - if( n < 3) - return g_strdup(relief[n]); - } - - return nullptr; -} - -/*****************************************************************************/ - -// @see http://www.w3.org/TR/REC-CSS2/text.html#lining-striking-props - -enum -{ - DECORATION_NONE = 0, - DECORATION_BLINK, - DECORATION_UNDERLINE, - DECORATION_LINE_THROUGH -}; - -static const gchar * decorations[] = { "none", "blink", "underline", "line-through" }; - -static gchar * -get_text_decoration(const uno::Sequence< beans::PropertyValue >& rAttributeList, - sal_Int32 nBlinkIndex, sal_Int32 nUnderlineIndex, - sal_Int16 nStrikeoutIndex) -{ - gchar * value_list[4] = { nullptr, nullptr, nullptr, nullptr }; - gint count = 0; - - // no property value found - if( ( nBlinkIndex == -1 ) && (nUnderlineIndex == -1 ) && (nStrikeoutIndex == -1)) - return nullptr; - - if( nBlinkIndex != -1 ) - { - if( rAttributeList[nBlinkIndex].Value.get<bool>() ) - value_list[count++] = const_cast <gchar *> (decorations[DECORATION_BLINK]); - } - if( nUnderlineIndex != -1 ) - { - sal_Int16 n = rAttributeList[nUnderlineIndex].Value.get<sal_Int16> (); - if( n != awt::FontUnderline::NONE ) - value_list[count++] = const_cast <gchar *> (decorations[DECORATION_UNDERLINE]); - } - if( nStrikeoutIndex != -1 ) - { - sal_Int16 n = rAttributeList[nStrikeoutIndex].Value.get<sal_Int16> (); - if( n != awt::FontStrikeout::NONE && n != awt::FontStrikeout::DONTKNOW ) - value_list[count++] = const_cast <gchar *> (decorations[DECORATION_LINE_THROUGH]); - } - - if( count == 0 ) - value_list[count++] = const_cast <gchar *> (decorations[DECORATION_NONE]); - - return g_strjoinv(" ", value_list); -} - -/*****************************************************************************/ - -// @see http://www.w3.org/TR/REC-CSS2/text.html#propdef-text-shadow - -static const gchar * shadow_values[] = { "none", "black" }; - -static gchar * -Bool2Shadow( const uno::Any& rAny ) -{ - int n = 0; - - if( rAny.get<bool>() ) - n = 1; - - return g_strdup( shadow_values[n] ); -} - -/*****************************************************************************/ - -static gchar * -Short2Degree( const uno::Any& rAny ) -{ - float f = rAny.get<sal_Int16>() / 10.0; - return g_strdup_printf( "%g", f ); -} - -/*****************************************************************************/ - -const gchar * const directions[] = { "ltr", "rtl", "rtl", "ltr", "none" }; - -static gchar * -WritingMode2Direction( const uno::Any& rAny ) -{ - sal_Int16 n = rAny.get<sal_Int16>(); - - if( 0 <= n && n <= text::WritingMode2::PAGE ) - return g_strdup(directions[n]); - - return nullptr; -} - -// @see http://www.w3.org/TR/2001/WD-css3-text-20010517/#PrimaryTextAdvanceDirection - -const gchar * const writing_modes[] = { "lr-tb", "rl-tb", "tb-rl", "tb-lr", "none" }; -static gchar * -WritingMode2String( const uno::Any& rAny ) -{ - sal_Int16 n = rAny.get<sal_Int16>(); - - if( 0 <= n && n <= text::WritingMode2::PAGE ) - return g_strdup(writing_modes[n]); - - return nullptr; -} - -/*****************************************************************************/ - -const char * const baseline_values[] = { "baseline", "sub", "super" }; - -// @see http://www.w3.org/TR/REC-CSS2/visudet.html#propdef-vertical-align -static gchar * -Escapement2VerticalAlign( const uno::Any& rAny ) -{ - sal_Int16 n = rAny.get<sal_Int16>(); - gchar * ret = nullptr; - - // Values are in %, 101% means "automatic" - if( n == 0 ) - ret = g_strdup(baseline_values[0]); - else if( n == 101 ) - ret = g_strdup(baseline_values[2]); - else if( n == -101 ) - ret = g_strdup(baseline_values[1]); - else - ret = g_strdup_printf( "%d%%", n ); - - return ret; -} - -/*****************************************************************************/ - -// @see http://www.w3.org/TR/REC-CSS2/visudet.html#propdef-line-height -static gchar * -LineSpacing2LineHeight( const uno::Any& rAny ) -{ - style::LineSpacing ls; - gchar * ret = nullptr; - - if( rAny >>= ls ) - { - if( ls.Mode == style::LineSpacingMode::PROP ) - ret = g_strdup_printf( "%d%%", ls.Height ); - else if( ls.Mode == style::LineSpacingMode::FIX ) - ret = g_strdup_printf( "%.3gpt", toPoint(ls.Height) ); - } - - return ret; -} - -/*****************************************************************************/ - -// @see http://www.w3.org/People/howcome/t/970224HTMLERB-CSS/WD-tabs-970117.html -static gchar * -TabStopList2String( const uno::Any& rAny, bool default_tabs ) -{ - uno::Sequence< style::TabStop > theTabStops; - gchar * ret = nullptr; - - if( rAny >>= theTabStops) - { - sal_Unicode lastFillChar = ' '; - - for( const auto& rTabStop : std::as_const(theTabStops) ) - { - bool is_default_tab = (style::TabAlign_DEFAULT == rTabStop.Alignment); - - if( is_default_tab != default_tabs ) - continue; - - double fValue = rTabStop.Position; - fValue = fValue * 0.01; - - const gchar * tab_align = ""; - switch( rTabStop.Alignment ) - { - case style::TabAlign_LEFT : - tab_align = "left "; - break; - case style::TabAlign_CENTER : - tab_align = "center "; - break; - case style::TabAlign_RIGHT : - tab_align = "right "; - break; - case style::TabAlign_DECIMAL : - tab_align = "decimal "; - break; - default: - break; - } - - const gchar * lead_char = ""; - - if( rTabStop.FillChar != lastFillChar ) - { - lastFillChar = rTabStop.FillChar; - switch (lastFillChar) - { - case ' ': - lead_char = "blank "; - break; - - case '.': - lead_char = "dotted "; - break; - - case '-': - lead_char = "dashed "; - break; - - case '_': - lead_char = "lined "; - break; - - default: - lead_char = "custom "; - break; - } - } - - gchar * tab_str = g_strdup_printf( "%s%s%gmm", lead_char, tab_align, fValue ); - - if( ret ) - { - gchar * old_tab_str = ret; - ret = g_strconcat(old_tab_str, " ", tab_str, nullptr); - g_free( old_tab_str ); - } - else - ret = tab_str; - } - } - - return ret; -} - -static gchar * -TabStops2String( const uno::Any& rAny ) -{ - return TabStopList2String(rAny, false); -} - -static gchar * -DefaultTabStops2String( const uno::Any& rAny ) -{ - return TabStopList2String(rAny, true); -} - -/*****************************************************************************/ - -extern "C" { - -static int -attr_compare(const void *p1,const void *p2) -{ - const rtl_uString * pustr = static_cast<const rtl_uString *>(p1); - const char * pc = *static_cast<const char * const *>(p2); - - return rtl_ustr_ascii_compare_WithLength(pustr->buffer, pustr->length, pc); -} - -} - -static void -find_exported_attributes( sal_Int32 *pArray, - const css::uno::Sequence< css::beans::PropertyValue >& rAttributeList ) -{ - for( sal_Int32 i = 0; i < rAttributeList.getLength(); i++ ) - { - const char ** pAttr = static_cast<const char **>(bsearch(rAttributeList[i].Name.pData, - ExportedTextAttributes, TEXT_ATTRIBUTE_LAST, sizeof(const char *), - attr_compare)); - - if( pAttr ) - { - sal_Int32 nIndex = pAttr - ExportedTextAttributes; - pArray[nIndex] = i; - } - } -} - -/*****************************************************************************/ - -static AtkAttributeSet* -attribute_set_prepend( AtkAttributeSet* attribute_set, - AtkTextAttribute attribute, - gchar * value ) -{ - if( value ) - { - AtkAttribute *at = static_cast<AtkAttribute *>(g_malloc( sizeof (AtkAttribute) )); - at->name = g_strdup( atk_text_attribute_get_name( attribute ) ); - at->value = value; - - return g_slist_prepend(attribute_set, at); - } - - return attribute_set; -} - -/*****************************************************************************/ - -AtkAttributeSet* -attribute_set_new_from_property_values( - const uno::Sequence< beans::PropertyValue >& rAttributeList, - bool run_attributes_only, - AtkText *text) -{ - AtkAttributeSet* attribute_set = nullptr; - - sal_Int32 aIndexList[TEXT_ATTRIBUTE_LAST] = { -1 }; - - // Initialize index array with -1 - for(sal_Int32 & rn : aIndexList) - rn = -1; - - find_exported_attributes(aIndexList, rAttributeList); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_BG_COLOR, - get_color_value(rAttributeList, aIndexList, TEXT_ATTRIBUTE_BACKGROUND_COLOR, run_attributes_only ? nullptr : text ) ); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_FG_COLOR, - get_color_value(rAttributeList, aIndexList, TEXT_ATTRIBUTE_FOREGROUND_COLOR, run_attributes_only ? nullptr : text) ); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_INVISIBLE, - get_bool_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_HIDDEN])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_UNDERLINE, - get_underline_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_UNDERLINE])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STRIKETHROUGH, - get_strikethrough_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_STRIKETHROUGH])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_SIZE, - get_height_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_HEIGHT])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_WEIGHT, - get_weight_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WEIGHT])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_FAMILY_NAME, - get_string_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_FONT_NAME])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_VARIANT, - get_variant_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CASEMAP])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STYLE, - get_style_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_POSTURE])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_SCALE, - get_scale_width(rAttributeList, aIndexList[TEXT_ATTRIBUTE_SCALE])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_LANGUAGE, - get_language_string(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LOCALE])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_DIRECTION, - get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WRITING_MODE], WritingMode2Direction)); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STRETCH, - get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_KERNING], Kerning2Stretch)); - - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_font_effect ) - atk_text_attribute_font_effect = atk_text_attribute_register("font-effect"); - - attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_font_effect, - get_font_effect(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CONTOURED], aIndexList[TEXT_ATTRIBUTE_RELIEF])); - - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_decoration ) - atk_text_attribute_decoration = atk_text_attribute_register("text-decoration"); - - attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_decoration, - get_text_decoration(rAttributeList, aIndexList[TEXT_ATTRIBUTE_BLINKING], - aIndexList[TEXT_ATTRIBUTE_UNDERLINE], aIndexList[TEXT_ATTRIBUTE_STRIKETHROUGH])); - - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_rotation ) - atk_text_attribute_rotation = atk_text_attribute_register("text-rotation"); - - attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_rotation, - get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_ROTATION], Short2Degree)); - - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_shadow ) - atk_text_attribute_shadow = atk_text_attribute_register("text-shadow"); - - attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_shadow, - get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_SHADOWED], Bool2Shadow)); - - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_writing_mode ) - atk_text_attribute_writing_mode = atk_text_attribute_register("writing-mode"); - - attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_writing_mode, - get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WRITING_MODE], WritingMode2String)); - - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_vertical_align ) - atk_text_attribute_vertical_align = atk_text_attribute_register("vertical-align"); - - attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_vertical_align, - get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CHAR_ESCAPEMENT], Escapement2VerticalAlign)); - - if( run_attributes_only ) - return attribute_set; - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_LEFT_MARGIN, - get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LEFT_MARGIN])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_RIGHT_MARGIN, - get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_RIGHT_MARGIN])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_INDENT, - get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_FIRST_LINE_INDENT])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, - get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TOP_MARGIN])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, - get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_BOTTOM_MARGIN])); - - attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_JUSTIFICATION, - get_justification_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_JUSTIFICATION])); - - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_paragraph_style ) - atk_text_attribute_paragraph_style = atk_text_attribute_register("paragraph-style"); - - attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_paragraph_style, - get_string_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_STYLE_NAME])); - - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_line_height ) - atk_text_attribute_line_height = atk_text_attribute_register("line-height"); - - attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_line_height, - get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LINE_SPACING], LineSpacing2LineHeight)); - - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tab_interval ) - atk_text_attribute_tab_interval = atk_text_attribute_register("tab-interval"); - - attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_tab_interval, - get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TAB_STOPS], DefaultTabStops2String)); - - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tab_stops ) - atk_text_attribute_tab_stops = atk_text_attribute_register("tab-stops"); - - attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_tab_stops, - get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TAB_STOPS], TabStops2String)); - - // #i92233# - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_mm_to_pixel_ratio ) - atk_text_attribute_mm_to_pixel_ratio = atk_text_attribute_register("mm-to-pixel-ratio"); - - attribute_set = attribute_set_prepend( attribute_set, atk_text_attribute_mm_to_pixel_ratio, - get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO], Float2String)); - - return attribute_set; -} - -AtkAttributeSet* -attribute_set_new_from_extended_attributes( - const css::uno::Reference< css::accessibility::XAccessibleExtendedAttributes >& rExtendedAttributes ) -{ - AtkAttributeSet *pSet = nullptr; - - // extended attributes is a string of colon-separated pairs of property and value, - // with pairs separated by semicolons. Example: "heading-level:2;weight:bold;" - uno::Any anyVal = rExtendedAttributes->getExtendedAttributes(); - OUString sExtendedAttrs; - anyVal >>= sExtendedAttrs; - sal_Int32 nIndex = 0; - do - { - OUString sProperty = sExtendedAttrs.getToken( 0, ';', nIndex ); - - sal_Int32 nColonPos = 0; - OString sPropertyName = OUStringToOString( sProperty.getToken( 0, ':', nColonPos ), - RTL_TEXTENCODING_UTF8 ); - OString sPropertyValue = OUStringToOString( sProperty.getToken( 0, ':', nColonPos ), - RTL_TEXTENCODING_UTF8 ); - - pSet = attribute_set_prepend( pSet, - atk_text_attribute_register( sPropertyName.getStr() ), - g_strdup_printf( "%s", sPropertyValue.getStr() ) ); - } - while ( nIndex >= 0 && nIndex < sExtendedAttrs.getLength() ); - - return pSet; -} - -AtkAttributeSet* attribute_set_prepend_misspelled( AtkAttributeSet* attribute_set ) -{ - if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_misspelled ) - atk_text_attribute_misspelled = atk_text_attribute_register( "text-spelling" ); - - attribute_set = attribute_set_prepend( attribute_set, atk_text_attribute_misspelled, - g_strdup_printf( "misspelled" ) ); - - return attribute_set; -} - -// #i92232# -AtkAttributeSet* attribute_set_prepend_tracked_change_insertion( AtkAttributeSet* attribute_set ) -{ - if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change ) - { - atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" ); - } - - attribute_set = attribute_set_prepend( attribute_set, - atk_text_attribute_tracked_change, - g_strdup_printf( "insertion" ) ); - - return attribute_set; -} - -AtkAttributeSet* attribute_set_prepend_tracked_change_deletion( AtkAttributeSet* attribute_set ) -{ - if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change ) - { - atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" ); - } - - attribute_set = attribute_set_prepend( attribute_set, - atk_text_attribute_tracked_change, - g_strdup_printf( "deletion" ) ); - - return attribute_set; -} - -AtkAttributeSet* attribute_set_prepend_tracked_change_formatchange( AtkAttributeSet* attribute_set ) -{ - if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change ) - { - atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" ); - } - - attribute_set = attribute_set_prepend( attribute_set, - atk_text_attribute_tracked_change, - g_strdup_printf( "attribute-change" ) ); - - return attribute_set; -} - -/*****************************************************************************/ - -struct AtkTextAttrMapping -{ - const char * name; - TextPropertyValueFunc const toPropertyValue; -}; - -const AtkTextAttrMapping g_TextAttrMap[] = -{ - { "", InvalidValue }, // ATK_TEXT_ATTR_INVALID = 0 - { "ParaLeftMargin", UnitString2CMM }, // ATK_TEXT_ATTR_LEFT_MARGIN - { "ParaRightMargin", UnitString2CMM }, // ATK_TEXT_ATTR_RIGHT_MARGIN - { "ParaFirstLineIndent", UnitString2CMM }, // ATK_TEXT_ATTR_INDENT - { "CharHidden", String2Bool }, // ATK_TEXT_ATTR_INVISIBLE - { "", InvalidValue }, // ATK_TEXT_ATTR_EDITABLE - { "ParaTopMargin", UnitString2CMM }, // ATK_TEXT_ATTR_PIXELS_ABOVE_LINES - { "ParaBottomMargin", UnitString2CMM }, // ATK_TEXT_ATTR_PIXELS_BELOW_LINES - { "", InvalidValue }, // ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP - { "", InvalidValue }, // ATK_TEXT_ATTR_BG_FULL_HEIGHT - { "", InvalidValue }, // ATK_TEXT_ATTR_RISE - { "CharUnderline", String2Underline }, // ATK_TEXT_ATTR_UNDERLINE - { "CharStrikeout", String2Strikeout }, // ATK_TEXT_ATTR_STRIKETHROUGH - { "CharHeight", String2Float }, // ATK_TEXT_ATTR_SIZE - { "CharScaleWidth", String2Scale }, // ATK_TEXT_ATTR_SCALE - { "CharWeight", String2Weight }, // ATK_TEXT_ATTR_WEIGHT - { "CharLocale", String2Locale }, // ATK_TEXT_ATTR_LANGUAGE - { "CharFontName", SetString }, // ATK_TEXT_ATTR_FAMILY_NAME - { "CharBackColor", String2Color }, // ATK_TEXT_ATTR_BG_COLOR - { "CharColor", String2Color }, // ATK_TEXT_ATTR_FG_COLOR - { "", InvalidValue }, // ATK_TEXT_ATTR_BG_STIPPLE - { "", InvalidValue }, // ATK_TEXT_ATTR_FG_STIPPLE - { "", InvalidValue }, // ATK_TEXT_ATTR_WRAP_MODE - { "", InvalidValue }, // ATK_TEXT_ATTR_DIRECTION - { "ParaAdjust", Justification2Adjust }, // ATK_TEXT_ATTR_JUSTIFICATION - { "", InvalidValue }, // ATK_TEXT_ATTR_STRETCH - { "CharCaseMap", String2CaseMap }, // ATK_TEXT_ATTR_VARIANT - { "CharPosture", Style2FontSlant } // ATK_TEXT_ATTR_STYLE -}; - -/*****************************************************************************/ - -bool -attribute_set_map_to_property_values( - AtkAttributeSet* attribute_set, - uno::Sequence< beans::PropertyValue >& rValueList ) -{ - // Ensure enough space .. - uno::Sequence< beans::PropertyValue > aAttributeList (SAL_N_ELEMENTS(g_TextAttrMap)); - - sal_Int32 nIndex = 0; - for( GSList * item = attribute_set; item != nullptr; item = g_slist_next( item ) ) - { - AtkAttribute* attribute = reinterpret_cast<AtkAttribute *>(item); - - AtkTextAttribute text_attr = atk_text_attribute_for_name( attribute->name ); - if( text_attr < SAL_N_ELEMENTS(g_TextAttrMap) ) - { - if( g_TextAttrMap[text_attr].name[0] != '\0' ) - { - if( ! g_TextAttrMap[text_attr].toPropertyValue( aAttributeList[nIndex].Value, attribute->value) ) - return false; - - aAttributeList[nIndex].Name = OUString::createFromAscii( g_TextAttrMap[text_attr].name ); - aAttributeList[nIndex].State = beans::PropertyState_DIRECT_VALUE; - ++nIndex; - } - } - else - { - // Unsupported text attribute - return false; - } - } - - aAttributeList.realloc( nIndex ); - rValueList = aAttributeList; - return true; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkutil.cxx b/vcl/unx/gtk/a11y/atkutil.cxx deleted file mode 100644 index 50ed2793bbf0..000000000000 --- a/vcl/unx/gtk/a11y/atkutil.cxx +++ /dev/null @@ -1,789 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.apache.org/licenses/LICENSE-2.0 . - */ - -#ifdef AIX -#define _LINUX_SOURCE_COMPAT -#include <sys/timer.h> -#undef _LINUX_SOURCE_COMPAT -#endif - -#include <com/sun/star/accessibility/XAccessibleContext.hpp> -#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> -#include <com/sun/star/accessibility/XAccessibleSelection.hpp> -#include <com/sun/star/accessibility/AccessibleEventId.hpp> -#include <com/sun/star/accessibility/AccessibleStateType.hpp> -#include <com/sun/star/accessibility/XAccessibleText.hpp> -#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> -#include <cppuhelper/implbase.hxx> -#include <cppuhelper/weakref.hxx> -#include <rtl/ref.hxx> -#include <sal/log.hxx> - -#include <vcl/svapp.hxx> -#include <vcl/window.hxx> -#include <vcl/menu.hxx> -#include <vcl/toolbox.hxx> - -#include <unx/gtk/gtkdata.hxx> -#include "atkwrapper.hxx" -#include "atkutil.hxx" - -#include <gtk/gtk.h> -#include <config_version.h> - -#include <cassert> -#include <set> - -using namespace ::com::sun::star; - -namespace -{ - struct theNextFocusObject : - public rtl::Static< uno::WeakReference< accessibility::XAccessible >, theNextFocusObject> - { - }; -} - -static guint focus_notify_handler = 0; - -/*****************************************************************************/ - -extern "C" { - -static gboolean -atk_wrapper_focus_idle_handler (gpointer data) -{ - SolarMutexGuard aGuard; - - focus_notify_handler = 0; - - uno::Reference< accessibility::XAccessible > xAccessible = theNextFocusObject::get(); - if( xAccessible.get() == static_cast < accessibility::XAccessible * > (data) ) - { - AtkObject *atk_obj = xAccessible.is() ? atk_object_wrapper_ref( xAccessible ) : nullptr; - // Gail does not notify focus changes to NULL, so do we .. - if( atk_obj ) - { - SAL_WNODEPRECATED_DECLARATIONS_PUSH - atk_focus_tracker_notify(atk_obj); - SAL_WNODEPRECATED_DECLARATIONS_POP - // #i93269# - // emit text_caret_moved event for <XAccessibleText> object, - // if cursor is inside the <XAccessibleText> object. - // also emit state-changed:focused event under the same condition. - { - AtkObjectWrapper* wrapper_obj = ATK_OBJECT_WRAPPER (atk_obj); - if( wrapper_obj && !wrapper_obj->mpText.is() ) - { - wrapper_obj->mpText.set(wrapper_obj->mpContext, css::uno::UNO_QUERY); - if ( wrapper_obj->mpText.is() ) - { - gint caretPos = -1; - - try { - caretPos = wrapper_obj->mpText->getCaretPosition(); - } - catch(const uno::Exception&) { - g_warning( "Exception in getCaretPosition()" ); - } - - if ( caretPos != -1 ) - { - atk_object_notify_state_change( atk_obj, ATK_STATE_FOCUSED, TRUE ); - g_signal_emit_by_name( atk_obj, "text_caret_moved", caretPos ); - } - } - } - } - g_object_unref(atk_obj); - } - } - - return false; -} - -} // extern "C" - -/*****************************************************************************/ - -static void -atk_wrapper_focus_tracker_notify_when_idle( const uno::Reference< accessibility::XAccessible > &xAccessible ) -{ - if( focus_notify_handler ) - g_source_remove(focus_notify_handler); - - theNextFocusObject::get() = xAccessible; - - focus_notify_handler = g_idle_add (atk_wrapper_focus_idle_handler, xAccessible.get()); -} - -/*****************************************************************************/ - -class DocumentFocusListener : - public ::cppu::WeakImplHelper< accessibility::XAccessibleEventListener > -{ - - std::set< uno::Reference< uno::XInterface > > m_aRefList; - -public: - /// @throws lang::IndexOutOfBoundsException - /// @throws uno::RuntimeException - void attachRecursive( - const uno::Reference< accessibility::XAccessible >& xAccessible - ); - - /// @throws lang::IndexOutOfBoundsException - /// @throws uno::RuntimeException - void attachRecursive( - const uno::Reference< accessibility::XAccessible >& xAccessible, - const uno::Reference< accessibility::XAccessibleContext >& xContext - ); - - /// @throws lang::IndexOutOfBoundsException - /// @throws uno::RuntimeException - void attachRecursive( - const uno::Reference< accessibility::XAccessible >& xAccessible, - const uno::Reference< accessibility::XAccessibleContext >& xContext, - const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet - ); - - /// @throws lang::IndexOutOfBoundsException - /// @throws uno::RuntimeException - void detachRecursive( - const uno::Reference< accessibility::XAccessible >& xAccessible - ); - - /// @throws lang::IndexOutOfBoundsException - /// @throws uno::RuntimeException - void detachRecursive( - const uno::Reference< accessibility::XAccessibleContext >& xContext - ); - - /// @throws lang::IndexOutOfBoundsException - /// @throws uno::RuntimeException - void detachRecursive( - const uno::Reference< accessibility::XAccessibleContext >& xContext, - const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet - ); - - /// @throws lang::IndexOutOfBoundsException - /// @throws uno::RuntimeException - static uno::Reference< accessibility::XAccessible > getAccessible(const lang::EventObject& aEvent ); - - // XEventListener - virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; - - // XAccessibleEventListener - virtual void SAL_CALL notifyEvent( const accessibility::AccessibleEventObject& aEvent ) override; -}; - -/*****************************************************************************/ - -void DocumentFocusListener::disposing( const lang::EventObject& aEvent ) -{ - - // Unref the object here, but do not remove as listener since the object - // might no longer be in a state that safely allows this. - if( aEvent.Source.is() ) - m_aRefList.erase(aEvent.Source); - -} - -/*****************************************************************************/ - -void DocumentFocusListener::notifyEvent( const accessibility::AccessibleEventObject& aEvent ) -{ - try { - switch( aEvent.EventId ) - { - case accessibility::AccessibleEventId::STATE_CHANGED: - { - sal_Int16 nState = accessibility::AccessibleStateType::INVALID; - aEvent.NewValue >>= nState; - - if( accessibility::AccessibleStateType::FOCUSED == nState ) - atk_wrapper_focus_tracker_notify_when_idle( getAccessible(aEvent) ); - - break; - } - - case accessibility::AccessibleEventId::CHILD: - { - uno::Reference< accessibility::XAccessible > xChild; - if( (aEvent.OldValue >>= xChild) && xChild.is() ) - detachRecursive(xChild); - - if( (aEvent.NewValue >>= xChild) && xChild.is() ) - attachRecursive(xChild); - - break; - } - - case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN: - SAL_INFO("vcl.a11y", "Invalidate all children called"); - break; - - default: - break; - } - } - catch( const lang::IndexOutOfBoundsException& e ) - { - g_warning("Focused object has invalid index in parent"); - } -} - -/*****************************************************************************/ - -uno::Reference< accessibility::XAccessible > DocumentFocusListener::getAccessible(const lang::EventObject& aEvent ) -{ - uno::Reference< accessibility::XAccessible > xAccessible(aEvent.Source, uno::UNO_QUERY); - - if( xAccessible.is() ) - return xAccessible; - - uno::Reference< accessibility::XAccessibleContext > xContext(aEvent.Source, uno::UNO_QUERY); - - if( xContext.is() ) - { - uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() ); - if( xParent.is() ) - { - uno::Reference< accessibility::XAccessibleContext > xParentContext( xParent->getAccessibleContext() ); - if( xParentContext.is() ) - { - return xParentContext->getAccessibleChild( xContext->getAccessibleIndexInParent() ); - } - } - } - - return uno::Reference< accessibility::XAccessible >(); -} - -/*****************************************************************************/ - -void DocumentFocusListener::attachRecursive( - const uno::Reference< accessibility::XAccessible >& xAccessible -) -{ - uno::Reference< accessibility::XAccessibleContext > xContext = - xAccessible->getAccessibleContext(); - - if( xContext.is() ) - attachRecursive(xAccessible, xContext); -} - -/*****************************************************************************/ - -void DocumentFocusListener::attachRecursive( - const uno::Reference< accessibility::XAccessible >& xAccessible, - const uno::Reference< accessibility::XAccessibleContext >& xContext -) -{ - uno::Reference< accessibility::XAccessibleStateSet > xStateSet = - xContext->getAccessibleStateSet(); - - if( xStateSet.is() ) - attachRecursive(xAccessible, xContext, xStateSet); -} - -/*****************************************************************************/ - -void DocumentFocusListener::attachRecursive( - const uno::Reference< accessibility::XAccessible >& xAccessible, - const uno::Reference< accessibility::XAccessibleContext >& xContext, - const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet -) -{ - if( xStateSet->contains(accessibility::AccessibleStateType::FOCUSED ) ) - atk_wrapper_focus_tracker_notify_when_idle( xAccessible ); - - uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY); - - if (!xBroadcaster.is()) - return; - - // If not already done, add the broadcaster to the list and attach as listener. - const uno::Reference< uno::XInterface >& xInterface = xBroadcaster; - if( m_aRefList.insert(xInterface).second ) - { - xBroadcaster->addAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener *>(this)); - - if( ! xStateSet->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS ) ) - { - sal_Int32 n, nmax = xContext->getAccessibleChildCount(); - for( n = 0; n < nmax; n++ ) - { - uno::Reference< accessibility::XAccessible > xChild( xContext->getAccessibleChild( n ) ); - - if( xChild.is() ) - attachRecursive(xChild); - } - } - } -} - -/*****************************************************************************/ - -void DocumentFocusListener::detachRecursive( - const uno::Reference< accessibility::XAccessible >& xAccessible -) -{ - uno::Reference< accessibility::XAccessibleContext > xContext = - xAccessible->getAccessibleContext(); - - if( xContext.is() ) - detachRecursive(xContext); -} - -/*****************************************************************************/ - -void DocumentFocusListener::detachRecursive( - const uno::Reference< accessibility::XAccessibleContext >& xContext -) -{ - uno::Reference< accessibility::XAccessibleStateSet > xStateSet = - xContext->getAccessibleStateSet(); - - if( xStateSet.is() ) - detachRecursive(xContext, xStateSet); -} - -/*****************************************************************************/ - -void DocumentFocusListener::detachRecursive( - const uno::Reference< accessibility::XAccessibleContext >& xContext, - const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet -) -{ - uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY); - - if( xBroadcaster.is() && 0 < m_aRefList.erase(xBroadcaster) ) - { - xBroadcaster->removeAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener *>(this)); - - if( ! xStateSet->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS ) ) - { - sal_Int32 n, nmax = xContext->getAccessibleChildCount(); - for( n = 0; n < nmax; n++ ) - { - uno::Reference< accessibility::XAccessible > xChild( xContext->getAccessibleChild( n ) ); - - if( xChild.is() ) - detachRecursive(xChild); - } - } - } -} - -/*****************************************************************************/ - -/* - * page tabs in gtk are widgets, so we need to simulate focus events for those - */ - -static void handle_tabpage_activated(vcl::Window *pWindow) -{ - uno::Reference< accessibility::XAccessible > xAccessible = - pWindow->GetAccessible(); - - if( ! xAccessible.is() ) - return; - - uno::Reference< accessibility::XAccessibleSelection > xSelection( - xAccessible->getAccessibleContext(), uno::UNO_QUERY); - - if( xSelection.is() ) - atk_wrapper_focus_tracker_notify_when_idle( xSelection->getSelectedAccessibleChild(0) ); -} - -/*****************************************************************************/ - -/* - * toolbar items in gtk are widgets, so we need to simulate focus events for those - */ - -static void notify_toolbox_item_focus(ToolBox *pToolBox) -{ - uno::Reference< accessibility::XAccessible > xAccessible = - pToolBox->GetAccessible(); - - if( ! xAccessible.is() ) - return; - - uno::Reference< accessibility::XAccessibleContext > xContext = - xAccessible->getAccessibleContext(); - - if( ! xContext.is() ) - return; - - ToolBox::ImplToolItems::size_type nPos = pToolBox->GetItemPos( pToolBox->GetHighlightItemId() ); - if( nPos != ToolBox::ITEM_NOTFOUND ) - atk_wrapper_focus_tracker_notify_when_idle( xContext->getAccessibleChild( nPos ) ); - //TODO: ToolBox::ImplToolItems::size_type -> sal_Int32 -} - -static void handle_toolbox_highlight(vcl::Window *pWindow) -{ - ToolBox *pToolBox = static_cast <ToolBox *> (pWindow); - - // Make sure either the toolbox or its parent toolbox has the focus - if ( ! pToolBox->HasFocus() ) - { - ToolBox* pToolBoxParent = dynamic_cast< ToolBox* >( pToolBox->GetParent() ); - if ( ! pToolBoxParent || ! pToolBoxParent->HasFocus() ) - return; - } - - notify_toolbox_item_focus(pToolBox); -} - -static void handle_toolbox_highlightoff(vcl::Window const *pWindow) -{ - ToolBox* pToolBoxParent = dynamic_cast< ToolBox* >( pWindow->GetParent() ); - - // Notify when leaving sub toolboxes - if( pToolBoxParent && pToolBoxParent->HasFocus() ) - notify_toolbox_item_focus( pToolBoxParent ); -} - -/*****************************************************************************/ - -static void create_wrapper_for_child( - const uno::Reference< accessibility::XAccessibleContext >& xContext, - sal_Int32 index) -{ - if( xContext.is() ) - { - uno::Reference< accessibility::XAccessible > xChild(xContext->getAccessibleChild(index)); - if( xChild.is() ) - { - // create the wrapper object - it will survive the unref unless it is a transient object - g_object_unref( atk_object_wrapper_ref( xChild ) ); - } - } -} - -/*****************************************************************************/ - -static void handle_toolbox_buttonchange(VclWindowEvent const *pEvent) -{ - vcl::Window* pWindow = pEvent->GetWindow(); - sal_Int32 index = static_cast<sal_Int32>(reinterpret_cast<sal_IntPtr>(pEvent->GetData())); - - if( pWindow && pWindow->IsReallyVisible() ) - { - uno::Reference< accessibility::XAccessible > xAccessible(pWindow->GetAccessible()); - if( xAccessible.is() ) - { - create_wrapper_for_child(xAccessible->getAccessibleContext(), index); - } - } -} - -/*****************************************************************************/ - -namespace { - -struct WindowList { - ~WindowList() { assert(list.empty()); }; - // needs to be empty already on DeInitVCL, but at least check it's empty - // on exit - - std::set< VclPtr<vcl::Window> > list; -}; - -WindowList g_aWindowList; - -} - -DocumentFocusListener & GtkSalData::GetDocumentFocusListener() -{ - if (!m_pDocumentFocusListener) - { - m_pDocumentFocusListener = new DocumentFocusListener; - m_xDocumentFocusListener.set(m_pDocumentFocusListener); - } - return *m_pDocumentFocusListener; -} - -static void handle_get_focus(::VclWindowEvent const * pEvent) -{ - GtkSalData *const pSalData(GetGtkSalData()); - assert(pSalData); - - DocumentFocusListener & rDocumentFocusListener(pSalData->GetDocumentFocusListener()); - - vcl::Window *pWindow = pEvent->GetWindow(); - - // The menu bar is handled through VclEventId::MenuHighlightED - if( ! pWindow || !pWindow->IsReallyVisible() || pWindow->GetType() == WindowType::MENUBARWINDOW ) - return; - - // ToolBoxes are handled through VclEventId::ToolboxHighlight - if( pWindow->GetType() == WindowType::TOOLBOX ) - return; - - if( pWindow->GetType() == WindowType::TABCONTROL ) - { - handle_tabpage_activated( pWindow ); - return; - } - - uno::Reference< accessibility::XAccessible > xAccessible = - pWindow->GetAccessible(); - - if( ! xAccessible.is() ) - return; - - uno::Reference< accessibility::XAccessibleContext > xContext = - xAccessible->getAccessibleContext(); - - if( ! xContext.is() ) - return; - - uno::Reference< accessibility::XAccessibleStateSet > xStateSet = - xContext->getAccessibleStateSet(); - - if( ! xStateSet.is() ) - return; - -/* the UNO ToolBox wrapper does not (yet?) support XAccessibleSelection, so we - * need to add listeners to the children instead of re-using the tabpage stuff - */ - if( xStateSet->contains(accessibility::AccessibleStateType::FOCUSED) && - ( pWindow->GetType() != WindowType::TREELISTBOX ) ) - { - atk_wrapper_focus_tracker_notify_when_idle( xAccessible ); - } - else - { - if( g_aWindowList.list.insert(pWindow).second ) - { - try - { - rDocumentFocusListener.attachRecursive(xAccessible, xContext, xStateSet); - } - catch (const uno::Exception&) - { - g_warning( "Exception caught processing focus events" ); - } - } - } -} - -/*****************************************************************************/ - -static void handle_menu_highlighted(::VclMenuEvent const * pEvent) -{ - try - { - Menu* pMenu = pEvent->GetMenu(); - sal_uInt16 nPos = pEvent->GetItemPos(); - - if( pMenu && nPos != 0xFFFF) - { - uno::Reference< accessibility::XAccessible > xAccessible ( pMenu->GetAccessible() ); - - if( xAccessible.is() ) - { - uno::Reference< accessibility::XAccessibleContext > xContext ( xAccessible->getAccessibleContext() ); - - if( xContext.is() ) - atk_wrapper_focus_tracker_notify_when_idle( xContext->getAccessibleChild( nPos ) ); - } - } - } - catch (const uno::Exception&) - { - g_warning( "Exception caught processing menu highlight events" ); - } -} - -/*****************************************************************************/ - -static void WindowEventHandler(void *, VclSimpleEvent& rEvent) -{ - try - { - switch (rEvent.GetId()) - { - case VclEventId::WindowShow: - break; - case VclEventId::WindowHide: - break; - case VclEventId::WindowClose: - break; - case VclEventId::WindowGetFocus: - handle_get_focus(static_cast< ::VclWindowEvent const * >(&rEvent)); - break; - case VclEventId::WindowLoseFocus: - break; - case VclEventId::WindowMinimize: - break; - case VclEventId::WindowNormalize: - break; - case VclEventId::WindowKeyInput: - case VclEventId::WindowKeyUp: - case VclEventId::WindowCommand: - case VclEventId::WindowMouseMove: - break; - - case VclEventId::MenuHighlight: - if (const VclMenuEvent* pMenuEvent = dynamic_cast<const VclMenuEvent*>(&rEvent)) - { - handle_menu_highlighted(pMenuEvent); - } - else if (const VclAccessibleEvent* pAccEvent = dynamic_cast<const VclAccessibleEvent*>(&rEvent)) - { - const uno::Reference< accessibility::XAccessible >& xAccessible = pAccEvent->GetAccessible(); - if (xAccessible.is()) - atk_wrapper_focus_tracker_notify_when_idle(xAccessible); - } - break; - - case VclEventId::ToolboxHighlight: - handle_toolbox_highlight(static_cast< ::VclWindowEvent const * >(&rEvent)->GetWindow()); - break; - - case VclEventId::ToolboxButtonStateChanged: - handle_toolbox_buttonchange(static_cast< ::VclWindowEvent const * >(&rEvent)); - break; - - case VclEventId::ObjectDying: - g_aWindowList.list.erase( static_cast< ::VclWindowEvent const * >(&rEvent)->GetWindow() ); - [[fallthrough]]; - case VclEventId::ToolboxHighlightOff: - handle_toolbox_highlightoff(static_cast< ::VclWindowEvent const * >(&rEvent)->GetWindow()); - break; - - case VclEventId::TabpageActivate: - handle_tabpage_activated(static_cast< ::VclWindowEvent const * >(&rEvent)->GetWindow()); - break; - - case VclEventId::ComboboxSetText: - // This looks quite strange to me. Stumbled over this when fixing #i104290#. - // This kicked in when leaving the combobox in the toolbar, after that the events worked. - // I guess this was a try to work around missing combobox events, which didn't do the full job, and shouldn't be necessary anymore. - // Fix for #i104290# was done in toolkit/source/awt/vclxaccessiblecomponent, FOCUSED state for compound controls in general. - // create_wrapper_for_children(static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); - break; - - default: - break; - } - } - catch (const lang::IndexOutOfBoundsException&) - { - g_warning("Focused object has invalid index in parent"); - } -} - -static Link<VclSimpleEvent&,void> g_aEventListenerLink( nullptr, WindowEventHandler ); - -/*****************************************************************************/ - -extern "C" { - -static const gchar * -ooo_atk_util_get_toolkit_name() -{ - return "VCL"; -} - -/*****************************************************************************/ - -static const gchar * -ooo_atk_util_get_toolkit_version() -{ - return LIBO_VERSION_DOTTED; -} - -/*****************************************************************************/ - -/* - * GObject inheritance - */ - -static void -ooo_atk_util_class_init (AtkUtilClass *) -{ - AtkUtilClass *atk_class; - gpointer data; - - data = g_type_class_peek (ATK_TYPE_UTIL); - atk_class = ATK_UTIL_CLASS (data); - - atk_class->get_toolkit_name = ooo_atk_util_get_toolkit_name; - atk_class->get_toolkit_version = ooo_atk_util_get_toolkit_version; - - ooo_atk_util_ensure_event_listener(); -} - -} // extern "C" - -void ooo_atk_util_ensure_event_listener() -{ - static bool bInited; - if (!bInited) - { - Application::AddEventListener( g_aEventListenerLink ); - bInited = true; - } -} - -GType -ooo_atk_util_get_type() -{ - static GType type = 0; - - if (!type) - { - GType parent_type = g_type_from_name( "GailUtil" ); - - if( ! parent_type ) - { - g_warning( "Unknown type: GailUtil" ); - parent_type = ATK_TYPE_UTIL; - } - - GTypeQuery type_query; - g_type_query( parent_type, &type_query ); - - static const GTypeInfo typeInfo = - { - static_cast<guint16>(type_query.class_size), - nullptr, - nullptr, - reinterpret_cast<GClassInitFunc>(ooo_atk_util_class_init), - nullptr, - nullptr, - static_cast<guint16>(type_query.instance_size), - 0, - nullptr, - nullptr - } ; - - type = g_type_register_static (parent_type, "OOoUtil", &typeInfo, GTypeFlags(0)) ; - } - - return type; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkvalue.cxx b/vcl/unx/gtk/a11y/atkvalue.cxx deleted file mode 100644 index f5e45d3b2556..000000000000 --- a/vcl/unx/gtk/a11y/atkvalue.cxx +++ /dev/null @@ -1,138 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 "atkwrapper.hxx" - -#include <com/sun/star/accessibility/XAccessibleValue.hpp> - -#include <string.h> - -using namespace ::com::sun::star; - -/// @throws uno::RuntimeException -static css::uno::Reference<css::accessibility::XAccessibleValue> - getValue( AtkValue *pValue ) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pValue ); - if( pWrap ) - { - if( !pWrap->mpValue.is() ) - { - pWrap->mpValue.set(pWrap->mpContext, css::uno::UNO_QUERY); - } - - return pWrap->mpValue; - } - - return css::uno::Reference<css::accessibility::XAccessibleValue>(); -} - -static void anyToGValue( const uno::Any& aAny, GValue *pValue ) -{ - // FIXME: expand to lots of types etc. - double aDouble=0; - aAny >>= aDouble; - - memset( pValue, 0, sizeof( GValue ) ); - g_value_init( pValue, G_TYPE_DOUBLE ); - g_value_set_double( pValue, aDouble ); -} - -extern "C" { - -static void -value_wrapper_get_current_value( AtkValue *value, - GValue *gval ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleValue> pValue - = getValue( value ); - if( pValue.is() ) - anyToGValue( pValue->getCurrentValue(), gval ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getCurrentValue()" ); - } -} - -static void -value_wrapper_get_maximum_value( AtkValue *value, - GValue *gval ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleValue> pValue - = getValue( value ); - if( pValue.is() ) - anyToGValue( pValue->getMaximumValue(), gval ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getCurrentValue()" ); - } -} - -static void -value_wrapper_get_minimum_value( AtkValue *value, - GValue *gval ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleValue> pValue - = getValue( value ); - if( pValue.is() ) - anyToGValue( pValue->getMinimumValue(), gval ); - } - catch(const uno::Exception&) { - g_warning( "Exception in getCurrentValue()" ); - } -} - -static gboolean -value_wrapper_set_current_value( AtkValue *value, - const GValue *gval ) -{ - try { - css::uno::Reference<css::accessibility::XAccessibleValue> pValue - = getValue( value ); - if( pValue.is() ) - { - // FIXME - this needs expanding - double aDouble = g_value_get_double( gval ); - return pValue->setCurrentValue( uno::Any(aDouble) ); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getCurrentValue()" ); - } - - return FALSE; -} - -} // extern "C" - -void -valueIfaceInit (AtkValueIface *iface) -{ - g_return_if_fail (iface != nullptr); - - iface->get_current_value = value_wrapper_get_current_value; - iface->get_maximum_value = value_wrapper_get_maximum_value; - iface->get_minimum_value = value_wrapper_get_minimum_value; - iface->set_current_value = value_wrapper_set_current_value; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkwindow.cxx b/vcl/unx/gtk/a11y/atkwindow.cxx deleted file mode 100644 index eb72edf4908c..000000000000 --- a/vcl/unx/gtk/a11y/atkwindow.cxx +++ /dev/null @@ -1,330 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 <unx/gtk/gtkframe.hxx> -#include <vcl/svapp.hxx> -#include <vcl/window.hxx> -#include <vcl/popupmenuwindow.hxx> -#include <sal/log.hxx> - -#include "atkwindow.hxx" -#include "atkwrapper.hxx" -#include "atkregistry.hxx" - -#include <com/sun/star/accessibility/AccessibleRole.hpp> - -using namespace ::com::sun::star::accessibility; -using namespace ::com::sun::star::uno; - -extern "C" { - -static void (* window_real_initialize) (AtkObject *obj, gpointer data) = nullptr; -static void (* window_real_finalize) (GObject *obj) = nullptr; - -static void -init_from_window( AtkObject *accessible, vcl::Window const *pWindow ) -{ - static AtkRole aDefaultRole = ATK_ROLE_INVALID; - - // Special role for sub-menu and combo-box popups that are exposed directly - // by their parents already. - if( aDefaultRole == ATK_ROLE_INVALID ) - { - SAL_WNODEPRECATED_DECLARATIONS_PUSH - aDefaultRole = atk_role_register( "redundant object" ); - SAL_WNODEPRECATED_DECLARATIONS_POP - } - - AtkRole role = aDefaultRole; - - // Determine the appropriate role for the GtkWindow - switch( pWindow->GetAccessibleRole() ) - { - case AccessibleRole::ALERT: - role = ATK_ROLE_ALERT; - break; - - case AccessibleRole::DIALOG: - role = ATK_ROLE_DIALOG; - break; - - case AccessibleRole::FRAME: - role = ATK_ROLE_FRAME; - break; - - /* Ignore window objects for sub-menus, combo- and list boxes, - * which are exposed as children of their parents. - */ - case AccessibleRole::WINDOW: - { - WindowType type = WindowType::WINDOW; - bool parentIsMenuFloatingWindow = false; - - vcl::Window *pParent = pWindow->GetParent(); - if( pParent ) { - type = pParent->GetType(); - parentIsMenuFloatingWindow = pParent->IsMenuFloatingWindow(); - } - - if( (WindowType::LISTBOX != type) && (WindowType::COMBOBOX != type) && - (WindowType::MENUBARWINDOW != type) && ! parentIsMenuFloatingWindow ) - { - role = ATK_ROLE_WINDOW; - } - } - break; - - default: - { - vcl::Window *pChild = pWindow->GetWindow(GetWindowType::FirstChild); - if( pChild ) - { - if( WindowType::HELPTEXTWINDOW == pChild->GetType() ) - { - role = ATK_ROLE_TOOL_TIP; - pChild->SetAccessibleRole( AccessibleRole::LABEL ); - accessible->name = g_strdup( OUStringToOString( pChild->GetText(), RTL_TEXTENCODING_UTF8 ).getStr() ); - } - else if ( pWindow->GetType() == WindowType::BORDERWINDOW && pChild->GetType() == WindowType::FLOATINGWINDOW ) - { - PopupMenuFloatingWindow* p = dynamic_cast<PopupMenuFloatingWindow*>(pChild); - if (p && p->IsPopupMenu() && p->GetMenuStackLevel() == 0) - { - // This is a top-level menu popup. Register it. - role = ATK_ROLE_POPUP_MENU; - pChild->SetAccessibleRole( AccessibleRole::POPUP_MENU ); - accessible->name = g_strdup( OUStringToOString( pChild->GetText(), RTL_TEXTENCODING_UTF8 ).getStr() ); - } - } - } - break; - } - } - - accessible->role = role; -} - -/*****************************************************************************/ - -static gboolean -ooo_window_wrapper_clear_focus(gpointer) -{ - SolarMutexGuard aGuard; - SAL_WNODEPRECATED_DECLARATIONS_PUSH - atk_focus_tracker_notify( nullptr ); - SAL_WNODEPRECATED_DECLARATIONS_POP - return false; -} - -/*****************************************************************************/ - -static gboolean -ooo_window_wrapper_real_focus_gtk (GtkWidget *, GdkEventFocus *) -{ - g_idle_add( ooo_window_wrapper_clear_focus, nullptr ); - return false; -} - -static gboolean ooo_tooltip_map( GtkWidget* pToolTip, gpointer ) -{ - AtkObject* pAccessible = gtk_widget_get_accessible( pToolTip ); - if( pAccessible ) - atk_object_notify_state_change( pAccessible, ATK_STATE_SHOWING, TRUE ); - return FALSE; -} - -static gboolean ooo_tooltip_unmap( GtkWidget* pToolTip, gpointer ) -{ - AtkObject* pAccessible = gtk_widget_get_accessible( pToolTip ); - if( pAccessible ) - atk_object_notify_state_change( pAccessible, ATK_STATE_SHOWING, FALSE ); - return FALSE; -} - -/*****************************************************************************/ - -static bool -isChildPopupMenu(vcl::Window* pWindow) -{ - vcl::Window* pChild = pWindow->GetAccessibleChildWindow(0); - if (!pChild) - return false; - - if (WindowType::FLOATINGWINDOW != pChild->GetType()) - return false; - - PopupMenuFloatingWindow* p = dynamic_cast<PopupMenuFloatingWindow*>(pChild); - if (!p) - return false; - - return p->IsPopupMenu(); -} - -static void -ooo_window_wrapper_real_initialize(AtkObject *obj, gpointer data) -{ - window_real_initialize(obj, data); - - GtkSalFrame *pFrame = GtkSalFrame::getFromWindow( GTK_WINDOW( data ) ); - if( pFrame ) - { - vcl::Window *pWindow = pFrame->GetWindow(); - if( pWindow ) - { - init_from_window( obj, pWindow ); - - Reference< XAccessible > xAccessible( pWindow->GetAccessible() ); - - /* We need the wrapper object for the top-level XAccessible to be - * in the wrapper registry when atk traverses the hierarchy up on - * focus events - */ - if( WindowType::BORDERWINDOW == pWindow->GetType() ) - { - if ( isChildPopupMenu(pWindow) ) - { - AtkObject *child = atk_object_wrapper_new( xAccessible, obj ); - ooo_wrapper_registry_add( xAccessible, child ); - } - else - { - ooo_wrapper_registry_add( xAccessible, obj ); - g_object_set_data( G_OBJECT(obj), "ooo:atk-wrapper-key", xAccessible.get() ); - } - } - else - { - AtkObject *child = atk_object_wrapper_new( xAccessible, obj ); - child->role = ATK_ROLE_FILLER; - if( (ATK_ROLE_DIALOG == obj->role) || (ATK_ROLE_ALERT == obj->role) ) - child->role = ATK_ROLE_OPTION_PANE; - ooo_wrapper_registry_add( xAccessible, child ); - } - } - } - - g_signal_connect_after( GTK_WIDGET( data ), "focus-out-event", - G_CALLBACK (ooo_window_wrapper_real_focus_gtk), - nullptr); - - if( obj->role == ATK_ROLE_TOOL_TIP ) - { - g_signal_connect_after( GTK_WIDGET( data ), "map-event", - G_CALLBACK (ooo_tooltip_map), - nullptr); - g_signal_connect_after( GTK_WIDGET( data ), "unmap-event", - G_CALLBACK (ooo_tooltip_unmap), - nullptr); - } -} - -/*****************************************************************************/ - -static void -ooo_window_wrapper_real_finalize (GObject *obj) -{ - ooo_wrapper_registry_remove( static_cast<XAccessible *>(g_object_get_data( obj, "ooo:atk-wrapper-key" ))); - window_real_finalize( obj ); -} - -/*****************************************************************************/ - -static void -ooo_window_wrapper_class_init (AtkObjectClass *klass, gpointer) -{ - AtkObjectClass *atk_class; - GObjectClass *gobject_class; - gpointer data; - - /* - * Patch the gobject vtable of GailWindow to refer to our instance of - * "initialize". - */ - - data = g_type_class_peek_parent( klass ); - atk_class = ATK_OBJECT_CLASS (data); - - window_real_initialize = atk_class->initialize; - atk_class->initialize = ooo_window_wrapper_real_initialize; - - gobject_class = G_OBJECT_CLASS (data); - - window_real_finalize = gobject_class->finalize; - gobject_class->finalize = ooo_window_wrapper_real_finalize; -} - -} // extern "C" - -/*****************************************************************************/ - -GType -ooo_window_wrapper_get_type() -{ - static GType type = 0; - - if (!type) - { - GType parent_type = g_type_from_name( "GailWindow" ); - - if( ! parent_type ) - { - SAL_INFO("vcl.a11y", "Unknown type: GailWindow"); - parent_type = ATK_TYPE_OBJECT; - } - - GTypeQuery type_query; - g_type_query( parent_type, &type_query ); - - static const GTypeInfo typeInfo = - { - static_cast<guint16>(type_query.class_size), - nullptr, - nullptr, - reinterpret_cast<GClassInitFunc>(ooo_window_wrapper_class_init), - nullptr, - nullptr, - static_cast<guint16>(type_query.instance_size), - 0, - nullptr, - nullptr - } ; - - type = g_type_register_static (parent_type, "OOoWindowAtkObject", &typeInfo, GTypeFlags(0)) ; - } - - return type; -} - -void restore_gail_window_vtable() -{ - AtkObjectClass *atk_class; - gpointer data; - - GType type = g_type_from_name( "GailWindow" ); - - if( type == G_TYPE_INVALID ) - return; - - data = g_type_class_peek( type ); - atk_class = ATK_OBJECT_CLASS (data); - - atk_class->initialize = window_real_initialize; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/a11y/atkwrapper.cxx b/vcl/unx/gtk/a11y/atkwrapper.cxx deleted file mode 100644 index cd439022cf2a..000000000000 --- a/vcl/unx/gtk/a11y/atkwrapper.cxx +++ /dev/null @@ -1,959 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.apache.org/licenses/LICENSE-2.0 . - */ - -#include <com/sun/star/uno/Any.hxx> -#include <com/sun/star/uno/Type.hxx> -#include <com/sun/star/uno/Sequence.hxx> -#include <com/sun/star/accessibility/AccessibleRole.hpp> -#include <com/sun/star/accessibility/AccessibleRelation.hpp> -#include <com/sun/star/accessibility/AccessibleRelationType.hpp> -#include <com/sun/star/accessibility/AccessibleStateType.hpp> -#include <com/sun/star/accessibility/XAccessible.hpp> -#include <com/sun/star/accessibility/XAccessibleText.hpp> -#include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp> -#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp> -#include <com/sun/star/accessibility/XAccessibleValue.hpp> -#include <com/sun/star/accessibility/XAccessibleAction.hpp> -#include <com/sun/star/accessibility/XAccessibleContext.hpp> -#include <com/sun/star/accessibility/XAccessibleContext2.hpp> -#include <com/sun/star/accessibility/XAccessibleComponent.hpp> -#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> -#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp> -#include <com/sun/star/accessibility/XAccessibleStateSet.hpp> -#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp> -#include <com/sun/star/accessibility/XAccessibleTable.hpp> -#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> -#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp> -#include <com/sun/star/accessibility/XAccessibleImage.hpp> -#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp> -#include <com/sun/star/accessibility/XAccessibleHypertext.hpp> -#include <com/sun/star/accessibility/XAccessibleSelection.hpp> -#include <com/sun/star/awt/XExtendedToolkit.hpp> -#include <com/sun/star/awt/XTopWindow.hpp> -#include <com/sun/star/awt/XTopWindowListener.hpp> -#include <com/sun/star/awt/XWindow.hpp> -#include <com/sun/star/lang/XComponent.hpp> -#include <com/sun/star/lang/XServiceInfo.hpp> -#include <com/sun/star/lang/XInitialization.hpp> -#include <com/sun/star/lang/XMultiServiceFactory.hpp> -#include <com/sun/star/lang/XSingleServiceFactory.hpp> -#include <com/sun/star/beans/Property.hpp> - -#include <rtl/ref.hxx> -#include <rtl/strbuf.hxx> -#include <osl/diagnose.h> -#include <cppuhelper/factory.hxx> -#include <cppuhelper/queryinterface.hxx> - -#include "atkwrapper.hxx" -#include "atkregistry.hxx" -#include "atklistener.hxx" -#include "atktextattributes.hxx" - -#include <string.h> -#include <vector> - -using namespace ::com::sun::star; - -static GObjectClass *parent_class = nullptr; - -static AtkRelationType mapRelationType( sal_Int16 nRelation ) -{ - AtkRelationType type = ATK_RELATION_NULL; - - switch( nRelation ) - { - case accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM: - type = ATK_RELATION_FLOWS_FROM; - break; - - case accessibility::AccessibleRelationType::CONTENT_FLOWS_TO: - type = ATK_RELATION_FLOWS_TO; - break; - - case accessibility::AccessibleRelationType::CONTROLLED_BY: - type = ATK_RELATION_CONTROLLED_BY; - break; - - case accessibility::AccessibleRelationType::CONTROLLER_FOR: - type = ATK_RELATION_CONTROLLER_FOR; - break; - - case accessibility::AccessibleRelationType::LABEL_FOR: - type = ATK_RELATION_LABEL_FOR; - break; - - case accessibility::AccessibleRelationType::LABELED_BY: - type = ATK_RELATION_LABELLED_BY; - break; - - case accessibility::AccessibleRelationType::MEMBER_OF: - type = ATK_RELATION_MEMBER_OF; - break; - - case accessibility::AccessibleRelationType::SUB_WINDOW_OF: - type = ATK_RELATION_SUBWINDOW_OF; - break; - - case accessibility::AccessibleRelationType::NODE_CHILD_OF: - type = ATK_RELATION_NODE_CHILD_OF; - break; - - default: - break; - } - - return type; -} - -AtkStateType mapAtkState( sal_Int16 nState ) -{ - AtkStateType type = ATK_STATE_INVALID; - - // A perfect / complete mapping ... - switch( nState ) - { -#define MAP_DIRECT( a ) \ - case accessibility::AccessibleStateType::a: \ - type = ATK_STATE_##a; break - - MAP_DIRECT( INVALID ); - MAP_DIRECT( ACTIVE ); - MAP_DIRECT( ARMED ); - MAP_DIRECT( BUSY ); - MAP_DIRECT( CHECKED ); - MAP_DIRECT( EDITABLE ); - MAP_DIRECT( ENABLED ); - MAP_DIRECT( EXPANDABLE ); - MAP_DIRECT( EXPANDED ); - MAP_DIRECT( FOCUSABLE ); - MAP_DIRECT( FOCUSED ); - MAP_DIRECT( HORIZONTAL ); - MAP_DIRECT( ICONIFIED ); - MAP_DIRECT( INDETERMINATE ); - MAP_DIRECT( MANAGES_DESCENDANTS ); - MAP_DIRECT( MODAL ); - MAP_DIRECT( MULTI_LINE ); - MAP_DIRECT( OPAQUE ); - MAP_DIRECT( PRESSED ); - MAP_DIRECT( RESIZABLE ); - MAP_DIRECT( SELECTABLE ); - MAP_DIRECT( SELECTED ); - MAP_DIRECT( SENSITIVE ); - MAP_DIRECT( SHOWING ); - MAP_DIRECT( SINGLE_LINE ); - MAP_DIRECT( STALE ); - MAP_DIRECT( TRANSIENT ); - MAP_DIRECT( VERTICAL ); - MAP_DIRECT( VISIBLE ); - MAP_DIRECT( DEFAULT ); - // a spelling error ... - case accessibility::AccessibleStateType::DEFUNC: - type = ATK_STATE_DEFUNCT; break; - case accessibility::AccessibleStateType::MULTI_SELECTABLE: - type = ATK_STATE_MULTISELECTABLE; break; - default: - //Mis-use ATK_STATE_LAST_DEFINED to check if a state is unmapped - //NOTE! Do not report it - type = ATK_STATE_LAST_DEFINED; - break; - } - - return type; -} - -static AtkRole getRoleForName( const gchar * name ) -{ - AtkRole ret = atk_role_for_name( name ); - if( ATK_ROLE_INVALID == ret ) - { - // this should only happen in old ATK versions - SAL_WNODEPRECATED_DECLARATIONS_PUSH - ret = atk_role_register( name ); - SAL_WNODEPRECATED_DECLARATIONS_POP - } - - return ret; -} - -static AtkRole mapToAtkRole( sal_Int16 nRole ) -{ - AtkRole role = ATK_ROLE_UNKNOWN; - - static AtkRole roleMap[] = { - ATK_ROLE_UNKNOWN, - ATK_ROLE_ALERT, - ATK_ROLE_COLUMN_HEADER, - ATK_ROLE_CANVAS, - ATK_ROLE_CHECK_BOX, - ATK_ROLE_CHECK_MENU_ITEM, - ATK_ROLE_COLOR_CHOOSER, - ATK_ROLE_COMBO_BOX, - ATK_ROLE_DATE_EDITOR, - ATK_ROLE_DESKTOP_ICON, - ATK_ROLE_DESKTOP_FRAME, // ? pane - ATK_ROLE_DIRECTORY_PANE, - ATK_ROLE_DIALOG, - ATK_ROLE_UNKNOWN, // DOCUMENT - registered below - ATK_ROLE_UNKNOWN, // EMBEDDED_OBJECT - registered below - ATK_ROLE_UNKNOWN, // END_NOTE - registered below - ATK_ROLE_FILE_CHOOSER, - ATK_ROLE_FILLER, - ATK_ROLE_FONT_CHOOSER, - ATK_ROLE_FOOTER, - ATK_ROLE_UNKNOWN, // FOOTNOTE - registered below - ATK_ROLE_FRAME, - ATK_ROLE_GLASS_PANE, - ATK_ROLE_IMAGE, // GRAPHIC - ATK_ROLE_UNKNOWN, // GROUP_BOX - registered below - ATK_ROLE_HEADER, - ATK_ROLE_HEADING, - ATK_ROLE_TEXT, // HYPER_LINK - registered below - ATK_ROLE_ICON, - ATK_ROLE_INTERNAL_FRAME, - ATK_ROLE_LABEL, - ATK_ROLE_LAYERED_PANE, - ATK_ROLE_LIST, - ATK_ROLE_LIST_ITEM, - ATK_ROLE_MENU, - ATK_ROLE_MENU_BAR, - ATK_ROLE_MENU_ITEM, - ATK_ROLE_OPTION_PANE, - ATK_ROLE_PAGE_TAB, - ATK_ROLE_PAGE_TAB_LIST, - ATK_ROLE_PANEL, - ATK_ROLE_PARAGRAPH, - ATK_ROLE_PASSWORD_TEXT, - ATK_ROLE_POPUP_MENU, - ATK_ROLE_PUSH_BUTTON, - ATK_ROLE_PROGRESS_BAR, - ATK_ROLE_RADIO_BUTTON, - ATK_ROLE_RADIO_MENU_ITEM, - ATK_ROLE_ROW_HEADER, - ATK_ROLE_ROOT_PANE, - ATK_ROLE_SCROLL_BAR, - ATK_ROLE_SCROLL_PANE, - ATK_ROLE_PANEL, // SHAPE - ATK_ROLE_SEPARATOR, - ATK_ROLE_SLIDER, - ATK_ROLE_SPIN_BUTTON, // SPIN_BOX ? - ATK_ROLE_SPLIT_PANE, - ATK_ROLE_STATUSBAR, - ATK_ROLE_TABLE, - ATK_ROLE_TABLE_CELL, - ATK_ROLE_TEXT, - ATK_ROLE_PANEL, // TEXT_FRAME - ATK_ROLE_TOGGLE_BUTTON, - ATK_ROLE_TOOL_BAR, - ATK_ROLE_TOOL_TIP, - ATK_ROLE_TREE, - ATK_ROLE_VIEWPORT, - ATK_ROLE_WINDOW, - ATK_ROLE_PUSH_BUTTON, // BUTTON_DROPDOWN - ATK_ROLE_PUSH_BUTTON, // BUTTON_MENU - ATK_ROLE_UNKNOWN, // CAPTION - registered below - ATK_ROLE_UNKNOWN, // CHART - registered below - ATK_ROLE_UNKNOWN, // EDIT_BAR - registered below - ATK_ROLE_UNKNOWN, // FORM - registered below - ATK_ROLE_UNKNOWN, // IMAGE_MAP - registered below - ATK_ROLE_UNKNOWN, // NOTE - registered below - ATK_ROLE_UNKNOWN, // PAGE - registered below - ATK_ROLE_RULER, - ATK_ROLE_UNKNOWN, // SECTION - registered below - ATK_ROLE_UNKNOWN, // TREE_ITEM - registered below - ATK_ROLE_TREE_TABLE, - ATK_ROLE_SCROLL_PANE, // COMMENT - mapped to atk_role_scroll_pane - ATK_ROLE_UNKNOWN // COMMENT_END - mapped to atk_role_unknown -#if defined(ATK_CHECK_VERSION) - //older ver that doesn't define ATK_CHECK_VERSION doesn't have the following - , ATK_ROLE_DOCUMENT_PRESENTATION - , ATK_ROLE_DOCUMENT_SPREADSHEET - , ATK_ROLE_DOCUMENT_TEXT -#if ATK_CHECK_VERSION(2,15,2) - , ATK_ROLE_STATIC -#else - , ATK_ROLE_LABEL -#endif -#else - //older version should fallback to DOCUMENT_FRAME role - , ATK_ROLE_DOCUMENT_FRAME - , ATK_ROLE_DOCUMENT_FRAME - , ATK_ROLE_DOCUMENT_FRAME - , ATK_ROLE_LABEL -#endif - }; - - static bool initialized = false; - - if( ! initialized ) - { - // the accessible roles below were added to ATK in later versions, - // with role_for_name we will know if they exist in runtime. - roleMap[accessibility::AccessibleRole::EDIT_BAR] = getRoleForName("edit bar"); - roleMap[accessibility::AccessibleRole::EMBEDDED_OBJECT] = getRoleForName("embedded"); - roleMap[accessibility::AccessibleRole::CHART] = getRoleForName("chart"); - roleMap[accessibility::AccessibleRole::CAPTION] = getRoleForName("caption"); - roleMap[accessibility::AccessibleRole::DOCUMENT] = getRoleForName("document frame"); - roleMap[accessibility::AccessibleRole::PAGE] = getRoleForName("page"); - roleMap[accessibility::AccessibleRole::SECTION] = getRoleForName("section"); - roleMap[accessibility::AccessibleRole::FORM] = getRoleForName("form"); - roleMap[accessibility::AccessibleRole::GROUP_BOX] = getRoleForName("grouping"); - roleMap[accessibility::AccessibleRole::COMMENT] = getRoleForName("comment"); - roleMap[accessibility::AccessibleRole::IMAGE_MAP] = getRoleForName("image map"); - roleMap[accessibility::AccessibleRole::TREE_ITEM] = getRoleForName("tree item"); - roleMap[accessibility::AccessibleRole::HYPER_LINK] = getRoleForName("link"); - roleMap[accessibility::AccessibleRole::END_NOTE] = getRoleForName("footnote"); - roleMap[accessibility::AccessibleRole::FOOTNOTE] = getRoleForName("footnote"); - roleMap[accessibility::AccessibleRole::NOTE] = getRoleForName("comment"); - - initialized = true; - } - - static const sal_Int32 nMapSize = SAL_N_ELEMENTS(roleMap); - if( 0 <= nRole && nMapSize > nRole ) - role = roleMap[nRole]; - - return role; -} - -/*****************************************************************************/ - -extern "C" { - -/*****************************************************************************/ - -static const gchar* -wrapper_get_name( AtkObject *atk_obj ) -{ - AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); - - if( obj->mpContext.is() ) - { - try { - OString aName = - OUStringToOString( - obj->mpContext->getAccessibleName(), - RTL_TEXTENCODING_UTF8); - - int nCmp = atk_obj->name ? rtl_str_compare( atk_obj->name, aName.getStr() ) : -1; - if( nCmp != 0 ) - { - if( atk_obj->name ) - g_free(atk_obj->name); - atk_obj->name = g_strdup(aName.getStr()); - } - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleName()" ); - } - } - - return ATK_OBJECT_CLASS (parent_class)->get_name(atk_obj); -} - -/*****************************************************************************/ - -static const gchar* -wrapper_get_description( AtkObject *atk_obj ) -{ - AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); - - if( obj->mpContext.is() ) - { - try { - OString aDescription = - OUStringToOString( - obj->mpContext->getAccessibleDescription(), - RTL_TEXTENCODING_UTF8); - - g_free(atk_obj->description); - atk_obj->description = g_strdup(aDescription.getStr()); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleDescription()" ); - } - } - - return ATK_OBJECT_CLASS (parent_class)->get_description(atk_obj); - -} - -/*****************************************************************************/ - -static AtkAttributeSet * -wrapper_get_attributes( AtkObject *atk_obj ) -{ - AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER( atk_obj ); - AtkAttributeSet *pSet = nullptr; - - try - { - uno::Reference< accessibility::XAccessibleExtendedAttributes > - xExtendedAttrs( obj->mpContext, uno::UNO_QUERY ); - if( xExtendedAttrs.is() ) - pSet = attribute_set_new_from_extended_attributes( xExtendedAttrs ); - } - catch(const uno::Exception&) - { - g_warning( "Exception in getAccessibleAttributes()" ); - } - - return pSet; -} - -/*****************************************************************************/ - -static gint -wrapper_get_n_children( AtkObject *atk_obj ) -{ - AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); - gint n = 0; - - if( obj->mpContext.is() ) - { - try { - n = obj->mpContext->getAccessibleChildCount(); - } - catch(const uno::Exception&) { - OSL_FAIL("Exception in getAccessibleChildCount()" ); - } - } - - return n; -} - -/*****************************************************************************/ - -static AtkObject * -wrapper_ref_child( AtkObject *atk_obj, - gint i ) -{ - AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); - AtkObject* child = nullptr; - - // see comments above atk_object_wrapper_remove_child - if( -1 < i && obj->index_of_child_about_to_be_removed == i ) - { - g_object_ref( obj->child_about_to_be_removed ); - return obj->child_about_to_be_removed; - } - - if( obj->mpContext.is() ) - { - try { - uno::Reference< accessibility::XAccessible > xAccessible = - obj->mpContext->getAccessibleChild( i ); - - child = atk_object_wrapper_ref( xAccessible ); - } - catch(const uno::Exception&) { - OSL_FAIL("Exception in getAccessibleChild"); - } - } - - return child; -} - -/*****************************************************************************/ - -static gint -wrapper_get_index_in_parent( AtkObject *atk_obj ) -{ - AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); - - //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y - if (obj->mpOrig) - return atk_object_get_index_in_parent(obj->mpOrig); - - gint i = -1; - - if( obj->mpContext.is() ) - { - try { - i = obj->mpContext->getAccessibleIndexInParent(); - } - catch(const uno::Exception&) { - g_warning( "Exception in getAccessibleIndexInParent()" ); - } - } - return i; -} - -/*****************************************************************************/ - -AtkRelation* -atk_object_wrapper_relation_new(const accessibility::AccessibleRelation& rRelation) -{ - sal_uInt32 nTargetCount = rRelation.TargetSet.getLength(); - - std::vector<AtkObject*> aTargets; - - for (const auto& rTarget : rRelation.TargetSet) - { - uno::Reference< accessibility::XAccessible > xAccessible( rTarget, uno::UNO_QUERY ); - aTargets.push_back(atk_object_wrapper_ref(xAccessible)); - } - - AtkRelation *pRel = - atk_relation_new( - aTargets.data(), nTargetCount, - mapRelationType( rRelation.RelationType ) - ); - - return pRel; -} - -static AtkRelationSet * -wrapper_ref_relation_set( AtkObject *atk_obj ) -{ - AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); - - //if we're a native GtkDrawingArea with custom a11y, use the default toolkit relation set impl - if (obj->mpOrig) - return atk_object_ref_relation_set(obj->mpOrig); - - AtkRelationSet *pSet = atk_relation_set_new(); - - if( obj->mpContext.is() ) - { - try { - uno::Reference< accessibility::XAccessibleRelationSet > xRelationSet( - obj->mpContext->getAccessibleRelationSet() - ); - - sal_Int32 nRelations = xRelationSet.is() ? xRelationSet->getRelationCount() : 0; - for( sal_Int32 n = 0; n < nRelations; n++ ) - { - AtkRelation *pRel = atk_object_wrapper_relation_new(xRelationSet->getRelation(n)); - atk_relation_set_add(pSet, pRel); - g_object_unref(pRel); - } - } - catch(const uno::Exception &) { - g_object_unref( G_OBJECT( pSet ) ); - pSet = nullptr; - } - } - - return pSet; -} - -static AtkStateSet * -wrapper_ref_state_set( AtkObject *atk_obj ) -{ - AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); - AtkStateSet *pSet = atk_state_set_new(); - - if( obj->mpContext.is() ) - { - try { - uno::Reference< accessibility::XAccessibleStateSet > xStateSet( - obj->mpContext->getAccessibleStateSet()); - - if( xStateSet.is() ) - { - uno::Sequence< sal_Int16 > aStates = xStateSet->getStates(); - - for( const auto nState : aStates ) - { - // ATK_STATE_LAST_DEFINED is used to check if the state - // is unmapped, do not report it to Atk - if ( mapAtkState( nState ) != ATK_STATE_LAST_DEFINED ) - atk_state_set_add_state( pSet, mapAtkState( nState ) ); - } - - // We need to emulate FOCUS state for menus, menu-items etc. - if( atk_obj == atk_get_focus_object() ) - atk_state_set_add_state( pSet, ATK_STATE_FOCUSED ); -/* FIXME - should we do this ? - else - atk_state_set_remove_state( pSet, ATK_STATE_FOCUSED ); -*/ - } - } - - catch(const uno::Exception &) { - g_warning( "Exception in wrapper_ref_state_set" ); - atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT ); - } - } - else - atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT ); - - return pSet; -} - -/*****************************************************************************/ - -static void -atk_object_wrapper_finalize (GObject *obj) -{ - AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER (obj); - - if( pWrap->mpAccessible.is() ) - { - ooo_wrapper_registry_remove( pWrap->mpAccessible ); - pWrap->mpAccessible.clear(); - } - - atk_object_wrapper_dispose( pWrap ); - - parent_class->finalize( obj ); -} - -static void -atk_object_wrapper_class_init (AtkObjectWrapperClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS( klass ); - AtkObjectClass *atk_class = ATK_OBJECT_CLASS( klass ); - - parent_class = static_cast<GObjectClass *>(g_type_class_peek_parent (klass)); - - // GObject methods - gobject_class->finalize = atk_object_wrapper_finalize; - - // AtkObject methods - atk_class->get_name = wrapper_get_name; - atk_class->get_description = wrapper_get_description; - atk_class->get_attributes = wrapper_get_attributes; - atk_class->get_n_children = wrapper_get_n_children; - atk_class->ref_child = wrapper_ref_child; - atk_class->get_index_in_parent = wrapper_get_index_in_parent; - atk_class->ref_relation_set = wrapper_ref_relation_set; - atk_class->ref_state_set = wrapper_ref_state_set; -} - -static void -atk_object_wrapper_init (AtkObjectWrapper *wrapper, - AtkObjectWrapperClass*) -{ - wrapper->mpAction = nullptr; - wrapper->mpComponent = nullptr; - wrapper->mpEditableText = nullptr; - wrapper->mpHypertext = nullptr; - wrapper->mpImage = nullptr; - wrapper->mpSelection = nullptr; - wrapper->mpTable = nullptr; - wrapper->mpText = nullptr; - wrapper->mpValue = nullptr; -} - -} // extern "C" - -GType -atk_object_wrapper_get_type() -{ - static GType type = 0; - - if (!type) - { - static const GTypeInfo typeInfo = - { - sizeof (AtkObjectWrapperClass), - nullptr, - nullptr, - reinterpret_cast<GClassInitFunc>(atk_object_wrapper_class_init), - nullptr, - nullptr, - sizeof (AtkObjectWrapper), - 0, - reinterpret_cast<GInstanceInitFunc>(atk_object_wrapper_init), - nullptr - } ; - type = g_type_register_static (ATK_TYPE_OBJECT, - "OOoAtkObj", - &typeInfo, GTypeFlags(0)) ; - } - return type; -} - -static bool -isOfType( uno::XInterface *pInterface, const uno::Type & rType ) -{ - g_return_val_if_fail( pInterface != nullptr, false ); - - bool bIs = false; - try { - uno::Any aRet = pInterface->queryInterface( rType ); - - bIs = ( ( typelib_TypeClass_INTERFACE == aRet.pType->eTypeClass ) && - ( aRet.pReserved != nullptr ) ); - } catch( const uno::Exception &) { } - - return bIs; -} - -extern "C" { -typedef GType (* GetGIfaceType ) (); -} -const struct { - const char *name; - GInterfaceInitFunc const aInit; - GetGIfaceType const aGetGIfaceType; - const uno::Type & (*aGetUnoType) (); -} aTypeTable[] = { -// re-location heaven: - { - "Comp", reinterpret_cast<GInterfaceInitFunc>(componentIfaceInit), - atk_component_get_type, - cppu::UnoType<accessibility::XAccessibleComponent>::get - }, - { - "Act", reinterpret_cast<GInterfaceInitFunc>(actionIfaceInit), - atk_action_get_type, - cppu::UnoType<accessibility::XAccessibleAction>::get - }, - { - "Txt", reinterpret_cast<GInterfaceInitFunc>(textIfaceInit), - atk_text_get_type, - cppu::UnoType<accessibility::XAccessibleText>::get - }, - { - "Val", reinterpret_cast<GInterfaceInitFunc>(valueIfaceInit), - atk_value_get_type, - cppu::UnoType<accessibility::XAccessibleValue>::get - }, - { - "Tab", reinterpret_cast<GInterfaceInitFunc>(tableIfaceInit), - atk_table_get_type, - cppu::UnoType<accessibility::XAccessibleTable>::get - }, - { - "Edt", reinterpret_cast<GInterfaceInitFunc>(editableTextIfaceInit), - atk_editable_text_get_type, - cppu::UnoType<accessibility::XAccessibleEditableText>::get - }, - { - "Img", reinterpret_cast<GInterfaceInitFunc>(imageIfaceInit), - atk_image_get_type, - cppu::UnoType<accessibility::XAccessibleImage>::get - }, - { - "Hyp", reinterpret_cast<GInterfaceInitFunc>(hypertextIfaceInit), - atk_hypertext_get_type, - cppu::UnoType<accessibility::XAccessibleHypertext>::get - }, - { - "Sel", reinterpret_cast<GInterfaceInitFunc>(selectionIfaceInit), - atk_selection_get_type, - cppu::UnoType<accessibility::XAccessibleSelection>::get - } - // AtkDocument is a nastily broken interface (so far) - // we could impl. get_document_type perhaps though. -}; - -const int aTypeTableSize = G_N_ELEMENTS( aTypeTable ); - -static GType -ensureTypeFor( uno::XInterface *pAccessible ) -{ - int i; - bool bTypes[ aTypeTableSize ] = { false, }; - OStringBuffer aTypeNameBuf( "OOoAtkObj" ); - - for( i = 0; i < aTypeTableSize; i++ ) - { - if( isOfType( pAccessible, aTypeTable[i].aGetUnoType() ) ) - { - aTypeNameBuf.append(aTypeTable[i].name); - bTypes[i] = true; - } - } - - OString aTypeName = aTypeNameBuf.makeStringAndClear(); - GType nType = g_type_from_name( aTypeName.getStr() ); - if( nType == G_TYPE_INVALID ) - { - GTypeInfo aTypeInfo = { - sizeof( AtkObjectWrapperClass ), - nullptr, nullptr, nullptr, nullptr, nullptr, - sizeof( AtkObjectWrapper ), - 0, nullptr, nullptr - } ; - nType = g_type_register_static( ATK_TYPE_OBJECT_WRAPPER, - aTypeName.getStr(), &aTypeInfo, - GTypeFlags(0) ) ; - - for( int j = 0; j < aTypeTableSize; j++ ) - if( bTypes[j] ) - { - GInterfaceInfo aIfaceInfo = { nullptr, nullptr, nullptr }; - aIfaceInfo.interface_init = aTypeTable[j].aInit; - g_type_add_interface_static (nType, aTypeTable[j].aGetGIfaceType(), - &aIfaceInfo); - } - } - return nType; -} - -AtkObject * -atk_object_wrapper_ref( const uno::Reference< accessibility::XAccessible > &rxAccessible, bool create ) -{ - g_return_val_if_fail( rxAccessible.get() != nullptr, nullptr ); - - AtkObject *obj = ooo_wrapper_registry_get(rxAccessible); - if( obj ) - { - g_object_ref( obj ); - return obj; - } - - if( create ) - return atk_object_wrapper_new( rxAccessible ); - - return nullptr; -} - -AtkObject * -atk_object_wrapper_new( const css::uno::Reference< css::accessibility::XAccessible >& rxAccessible, - AtkObject* parent, AtkObject* orig ) -{ - g_return_val_if_fail( rxAccessible.get() != nullptr, nullptr ); - - AtkObjectWrapper *pWrap = nullptr; - - try { - uno::Reference< accessibility::XAccessibleContext > xContext(rxAccessible->getAccessibleContext()); - - g_return_val_if_fail( xContext.get() != nullptr, nullptr ); - - GType nType = ensureTypeFor( xContext.get() ); - gpointer obj = g_object_new( nType, nullptr); - - pWrap = ATK_OBJECT_WRAPPER( obj ); - pWrap->mpAccessible = rxAccessible; - - pWrap->index_of_child_about_to_be_removed = -1; - pWrap->child_about_to_be_removed = nullptr; - - pWrap->mpContext = xContext; - pWrap->mpOrig = orig; - - AtkObject* atk_obj = ATK_OBJECT(pWrap); - atk_obj->role = mapToAtkRole( xContext->getAccessibleRole() ); - atk_obj->accessible_parent = parent; - - ooo_wrapper_registry_add( rxAccessible, atk_obj ); - - if( parent ) - g_object_ref( atk_obj->accessible_parent ); - else - { - /* gail_focus_tracker remembers the focused object at the first - * parent in the hierarchy that is a Gtk+ widget, but at the time the - * event gets processed (at idle), it may be too late to create the - * hierarchy, so doing it now .. - */ - uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() ); - - if( xParent.is() ) - atk_obj->accessible_parent = atk_object_wrapper_ref( xParent ); - } - - // Attach a listener to the UNO object if it's not TRANSIENT - uno::Reference< accessibility::XAccessibleStateSet > xStateSet( xContext->getAccessibleStateSet() ); - if( xStateSet.is() && ! xStateSet->contains( accessibility::AccessibleStateType::TRANSIENT ) ) - { - uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY); - if( xBroadcaster.is() ) - { - uno::Reference<accessibility::XAccessibleEventListener> xListener(new AtkListener(pWrap)); - xBroadcaster->addAccessibleEventListener(xListener); - } - else - OSL_ASSERT( false ); - } - -#if ATK_CHECK_VERSION(2,33,1) - { - css::uno::Reference<css::accessibility::XAccessibleContext2> xContext2(xContext, css::uno::UNO_QUERY); - if( xContext2.is() ) - { - OString aId = OUStringToOString( xContext2->getAccessibleId(), RTL_TEXTENCODING_UTF8); - atk_object_set_accessible_id(atk_obj, aId.getStr()); - } - } -#endif - - return ATK_OBJECT( pWrap ); - } - catch (const uno::Exception &) - { - if( pWrap ) - g_object_unref( pWrap ); - - return nullptr; - } -} - -/*****************************************************************************/ - -void atk_object_wrapper_add_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index) -{ - AtkObject *atk_obj = ATK_OBJECT( wrapper ); - - atk_object_set_parent( child, atk_obj ); - g_signal_emit_by_name( atk_obj, "children_changed::add", index, child, nullptr ); -} - -/*****************************************************************************/ - -void atk_object_wrapper_remove_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index) -{ - /* - * the atk-bridge GTK+ module gets back to the event source to ref the child just - * vanishing, so we keep this reference because the semantic on OOo side is different. - */ - wrapper->child_about_to_be_removed = child; - wrapper->index_of_child_about_to_be_removed = index; - - g_signal_emit_by_name( ATK_OBJECT( wrapper ), "children_changed::remove", index, child, nullptr ); - - wrapper->index_of_child_about_to_be_removed = -1; - wrapper->child_about_to_be_removed = nullptr; -} - -/*****************************************************************************/ - -void atk_object_wrapper_set_role(AtkObjectWrapper* wrapper, sal_Int16 role) -{ - AtkObject *atk_obj = ATK_OBJECT( wrapper ); - atk_object_set_role( atk_obj, mapToAtkRole( role ) ); -} - -/*****************************************************************************/ - -void atk_object_wrapper_dispose(AtkObjectWrapper* wrapper) -{ - wrapper->mpContext.clear(); - wrapper->mpAction.clear(); - wrapper->mpComponent.clear(); - wrapper->mpEditableText.clear(); - wrapper->mpHypertext.clear(); - wrapper->mpImage.clear(); - wrapper->mpSelection.clear(); - wrapper->mpMultiLineText.clear(); - wrapper->mpTable.clear(); - wrapper->mpText.clear(); - wrapper->mpTextMarkup.clear(); - wrapper->mpTextAttributes.clear(); - wrapper->mpValue.clear(); -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/gloactiongroup.cxx b/vcl/unx/gtk/gloactiongroup.cxx deleted file mode 100644 index 56782e2cd46f..000000000000 --- a/vcl/unx/gtk/gloactiongroup.cxx +++ /dev/null @@ -1,398 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.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 <unx/gtk/gtksalmenu.hxx> - -#ifdef ENABLE_GMENU_INTEGRATION - -#include <unx/gtk/gloactiongroup.h> -#include <unx/gtk/gtkinst.hxx> -#include <unx/gtk/gtkframe.hxx> - -#include <sal/log.hxx> - -/* - * GLOAction - */ - -#define G_TYPE_LO_ACTION (g_lo_action_get_type ()) -#define G_LO_ACTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ - G_TYPE_LO_ACTION, GLOAction)) - -struct GLOAction -{ - GObject parent_instance; - - gint item_id; // Menu item ID. - gboolean submenu; // TRUE if action is a submenu action. - gboolean enabled; // TRUE if action is enabled. - GVariantType* parameter_type; // A GVariantType with the action parameter type. - GVariantType* state_type; // A GVariantType with item state type - GVariant* state_hint; // A GVariant with state hints. - GVariant* state; // A GVariant with current item state -}; - -typedef GObjectClass GLOActionClass; - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif -G_DEFINE_TYPE (GLOAction, g_lo_action, G_TYPE_OBJECT); -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -static GLOAction* -g_lo_action_new() -{ - return G_LO_ACTION (g_object_new (G_TYPE_LO_ACTION, nullptr)); -} - -static void -g_lo_action_init (GLOAction *action) -{ - action->item_id = -1; - action->submenu = FALSE; - action->enabled = TRUE; - action->parameter_type = nullptr; - action->state_type = nullptr; - action->state_hint = nullptr; - action->state = nullptr; -} - -static void -g_lo_action_finalize (GObject *object) -{ - GLOAction* action = G_LO_ACTION(object); - - if (action->parameter_type) - g_variant_type_free (action->parameter_type); - - if (action->state_type) - g_variant_type_free (action->state_type); - - if (action->state_hint) - g_variant_unref (action->state_hint); - - if (action->state) - g_variant_unref (action->state); - - G_OBJECT_CLASS (g_lo_action_parent_class)->finalize (object); -} - -static void -g_lo_action_class_init (GLOActionClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->finalize = g_lo_action_finalize; -} - -/* - * GLOActionGroup - */ - -struct GLOActionGroupPrivate -{ - GHashTable *table; /* string -> GLOAction */ -}; - -static void g_lo_action_group_iface_init (GActionGroupInterface *); - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif -G_DEFINE_TYPE_WITH_CODE (GLOActionGroup, - g_lo_action_group, G_TYPE_OBJECT, - G_ADD_PRIVATE(GLOActionGroup) - G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, - g_lo_action_group_iface_init)); -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -static gchar ** -g_lo_action_group_list_actions (GActionGroup *group) -{ - GLOActionGroup *loGroup = G_LO_ACTION_GROUP (group); - GHashTableIter iter; - gint n, i = 0; - gchar **keys; - gpointer key; - - n = g_hash_table_size (loGroup->priv->table); - keys = g_new (gchar *, n + 1); - - g_hash_table_iter_init (&iter, loGroup->priv->table); - while (g_hash_table_iter_next (&iter, &key, nullptr)) - keys[i++] = g_strdup (static_cast<gchar*>(key)); - g_assert_cmpint (i, ==, n); - keys[n] = nullptr; - - return keys; -} - -static gboolean -g_lo_action_group_query_action (GActionGroup *group, - const gchar *action_name, - gboolean *enabled, - const GVariantType **parameter_type, - const GVariantType **state_type, - GVariant **state_hint, - GVariant **state) -{ - //SAL_INFO("vcl.unity", "g_lo_action_group_query_action on " << group); - GLOActionGroup *lo_group = G_LO_ACTION_GROUP (group); - GLOAction* action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name)); - - if (action == nullptr) - return FALSE; - - if (enabled) - { - *enabled = action->enabled; - } - - if (parameter_type) - *parameter_type = action->parameter_type; - - if (state_type) - *state_type = action->state_type; - - if (state_hint) - *state_hint = (action->state_hint) ? g_variant_ref (action->state_hint) : nullptr; - - if (state) - *state = (action->state) ? g_variant_ref (action->state) : nullptr; - - return TRUE; -} - -static void -g_lo_action_group_perform_submenu_action (GLOActionGroup *group, - const gchar *action_name, - GVariant *state) -{ - gboolean bState = g_variant_get_boolean (state); - SAL_INFO("vcl.unity", "g_lo_action_group_perform_submenu_action on " << group << " to " << bState); - - if (bState) - GtkSalMenu::Activate(action_name); - else - GtkSalMenu::Deactivate(action_name); -} - -static void -g_lo_action_group_change_state (GActionGroup *group, - const gchar *action_name, - GVariant *value) -{ - SAL_INFO("vcl.unity", "g_lo_action_group_change_state on " << group ); - g_return_if_fail (value != nullptr); - - g_variant_ref_sink (value); - - if (action_name != nullptr) - { - GLOActionGroup* lo_group = G_LO_ACTION_GROUP (group); - GLOAction* action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name)); - - if (action != nullptr) - { - if (action->submenu) - g_lo_action_group_perform_submenu_action (lo_group, action_name, value); - else - { - gboolean is_new = FALSE; - - /* If action already exists but has no state, it should be removed and added again. */ - if (action->state_type == nullptr) - { - g_action_group_action_removed (G_ACTION_GROUP (group), action_name); - action->state_type = g_variant_type_copy (g_variant_get_type(value)); - is_new = TRUE; - } - - if (g_variant_is_of_type (value, action->state_type)) - { - if (action->state) - g_variant_unref(action->state); - - action->state = g_variant_ref (value); - - if (is_new) - g_action_group_action_added (G_ACTION_GROUP (group), action_name); - else - g_action_group_action_state_changed (group, action_name, value); - } - } - } - } - - g_variant_unref (value); -} - -static void -g_lo_action_group_activate (GActionGroup *group, - const gchar *action_name, - GVariant *parameter) -{ - if (parameter != nullptr) - g_action_group_change_action_state(group, action_name, parameter); - GtkSalMenu::DispatchCommand(action_name); -} - -void -g_lo_action_group_insert (GLOActionGroup *group, - const gchar *action_name, - gint item_id, - gboolean submenu) -{ - g_lo_action_group_insert_stateful (group, action_name, item_id, submenu, nullptr, nullptr, nullptr, nullptr); -} - -void -g_lo_action_group_insert_stateful (GLOActionGroup *group, - const gchar *action_name, - gint item_id, - gboolean submenu, - const GVariantType *parameter_type, - const GVariantType *state_type, - GVariant *state_hint, - GVariant *state) -{ - g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); - - GLOAction* old_action = G_LO_ACTION (g_hash_table_lookup (group->priv->table, action_name)); - - if (old_action == nullptr || old_action->item_id != item_id) - { - if (old_action != nullptr) - g_lo_action_group_remove (group, action_name); - - GLOAction* action = g_lo_action_new(); - - g_hash_table_insert (group->priv->table, g_strdup (action_name), action); - - action->item_id = item_id; - action->submenu = submenu; - - if (parameter_type) - action->parameter_type = const_cast<GVariantType*>(parameter_type); - - if (state_type) - action->state_type = const_cast<GVariantType*>(state_type); - - if (state_hint) - action->state_hint = g_variant_ref_sink (state_hint); - - if (state) - action->state = g_variant_ref_sink (state); - - g_action_group_action_added (G_ACTION_GROUP (group), action_name); - } -} - -static void -g_lo_action_group_finalize (GObject *object) -{ - GLOActionGroup *lo_group = G_LO_ACTION_GROUP (object); - - g_hash_table_unref (lo_group->priv->table); - - G_OBJECT_CLASS (g_lo_action_group_parent_class)->finalize (object); -} - -static void -g_lo_action_group_init (GLOActionGroup *group) -{ - SAL_INFO("vcl.unity", "g_lo_action_group_init on " << group); - group->priv = static_cast<GLOActionGroupPrivate *>(g_lo_action_group_get_instance_private (group)); - group->priv->table = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_object_unref); -} - -static void -g_lo_action_group_class_init (GLOActionGroupClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = g_lo_action_group_finalize; -} - -static void -g_lo_action_group_iface_init (GActionGroupInterface *iface) -{ - iface->list_actions = g_lo_action_group_list_actions; - iface->query_action = g_lo_action_group_query_action; - iface->change_action_state = g_lo_action_group_change_state; - iface->activate_action = g_lo_action_group_activate; -} - -GLOActionGroup * -g_lo_action_group_new() -{ - GLOActionGroup* group = G_LO_ACTION_GROUP (g_object_new (G_TYPE_LO_ACTION_GROUP, nullptr)); - return group; -} - -void -g_lo_action_group_set_action_enabled (GLOActionGroup *group, - const gchar *action_name, - gboolean enabled) -{ - SAL_INFO("vcl.unity", "g_lo_action_group_set_action_enabled on " << group); - g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); - g_return_if_fail (action_name != nullptr); - - GLOAction* action = G_LO_ACTION (g_hash_table_lookup (group->priv->table, action_name)); - - if (action == nullptr) - return; - - action->enabled = enabled; - - g_action_group_action_enabled_changed (G_ACTION_GROUP (group), action_name, enabled); -} - -void -g_lo_action_group_remove (GLOActionGroup *group, - const gchar *action_name) -{ - SAL_INFO("vcl.unity", "g_lo_action_group_remove on " << group); - g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); - - if (action_name != nullptr) - { - g_action_group_action_removed (G_ACTION_GROUP (group), action_name); - g_hash_table_remove (group->priv->table, action_name); - } -} - -void -g_lo_action_group_clear (GLOActionGroup *group) -{ - SAL_INFO("vcl.unity", "g_lo_action_group_clear on " << group); - g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); - - GList* keys = g_hash_table_get_keys (group->priv->table); - - for (GList* element = g_list_first (keys); element != nullptr; element = g_list_next (element)) - { - g_lo_action_group_remove (group, static_cast<gchar*>(element->data)); - } - - g_list_free (keys); -} - -#endif - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/glomenu.cxx b/vcl/unx/gtk/glomenu.cxx deleted file mode 100644 index 5457ac6eaa05..000000000000 --- a/vcl/unx/gtk/glomenu.cxx +++ /dev/null @@ -1,691 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.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 <string.h> - -#include <unx/gtk/gtksalmenu.hxx> - -#ifdef ENABLE_GMENU_INTEGRATION - -#include <unx/gtk/glomenu.h> - -struct GLOMenu -{ - GMenuModel const parent_instance; - - GArray *items; -}; - -typedef GMenuModelClass GLOMenuClass; - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif -G_DEFINE_TYPE (GLOMenu, g_lo_menu, G_TYPE_MENU_MODEL); -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -struct item -{ - GHashTable* attributes; // Item attributes. - GHashTable* links; // Item links. -}; - -static void -g_lo_menu_struct_item_init (struct item *menu_item) -{ - menu_item->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, reinterpret_cast<GDestroyNotify>(g_variant_unref)); - menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); -} - -/* We treat attribute names the same as GSettings keys: - * - only lowercase ascii, digits and '-' - * - must start with lowercase - * - must not end with '-' - * - no consecutive '-' - * - not longer than 1024 chars - */ -static gboolean -valid_attribute_name (const gchar *name) -{ - gint i; - - if (!g_ascii_islower (name[0])) - return FALSE; - - for (i = 1; name[i]; i++) - { - if (name[i] != '-' && - !g_ascii_islower (name[i]) && - !g_ascii_isdigit (name[i])) - return FALSE; - - if (name[i] == '-' && name[i + 1] == '-') - return FALSE; - } - - if (name[i - 1] == '-') - return FALSE; - - if (i > 1024) - return FALSE; - - return TRUE; -} - -/* - * GLOMenu - */ - -static gboolean -g_lo_menu_is_mutable (GMenuModel*) -{ - // Menu is always mutable. - return TRUE; -} - -static gint -g_lo_menu_get_n_items (GMenuModel *model) -{ - g_return_val_if_fail (model != nullptr, 0); - GLOMenu *menu = G_LO_MENU (model); - g_return_val_if_fail (menu->items != nullptr, 0); - - return menu->items->len; -} - -gint -g_lo_menu_get_n_items_from_section (GLOMenu *menu, - gint section) -{ - g_return_val_if_fail (0 <= section && section < static_cast<gint>(menu->items->len), 0); - - GLOMenu *model = g_lo_menu_get_section (menu, section); - - g_return_val_if_fail (model != nullptr, 0); - - gint length = model->items->len; - - g_object_unref (model); - - return length; -} - -static void -g_lo_menu_get_item_attributes (GMenuModel *model, - gint position, - GHashTable **table) -{ - GLOMenu *menu = G_LO_MENU (model); - *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).attributes); -} - -static void -g_lo_menu_get_item_links (GMenuModel *model, - gint position, - GHashTable **table) -{ - GLOMenu *menu = G_LO_MENU (model); - *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).links); -} - -void -g_lo_menu_insert (GLOMenu *menu, - gint position, - const gchar *label) -{ - g_lo_menu_insert_section (menu, position, label, nullptr); -} - -void -g_lo_menu_insert_in_section (GLOMenu *menu, - gint section, - gint position, - const gchar *label) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - g_return_if_fail (0 <= section && section < static_cast<gint>(menu->items->len)); - - GLOMenu *model = g_lo_menu_get_section (menu, section); - - g_return_if_fail (model != nullptr); - - g_lo_menu_insert (model, position, label); - - g_object_unref (model); -} - -GLOMenu * -g_lo_menu_new() -{ - return G_LO_MENU( g_object_new (G_TYPE_LO_MENU, nullptr) ); -} - -static void -g_lo_menu_set_attribute_value (GLOMenu *menu, - gint position, - const gchar *attribute, - GVariant *value) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - g_return_if_fail (attribute != nullptr); - g_return_if_fail (valid_attribute_name (attribute)); - - if (position >= static_cast<gint>(menu->items->len)) - return; - - struct item menu_item = g_array_index (menu->items, struct item, position); - - if (value != nullptr) - g_hash_table_insert (menu_item.attributes, g_strdup (attribute), g_variant_ref_sink (value)); - else - g_hash_table_remove (menu_item.attributes, attribute); -} - -static GVariant* -g_lo_menu_get_attribute_value_from_item_in_section (GLOMenu *menu, - gint section, - gint position, - const gchar *attribute, - const GVariantType *type) -{ - GMenuModel *model = G_MENU_MODEL (g_lo_menu_get_section (menu, section)); - - g_return_val_if_fail (model != nullptr, nullptr); - - GVariant *value = g_menu_model_get_item_attribute_value (model, - position, - attribute, - type); - - g_object_unref (model); - - return value; -} - -void -g_lo_menu_set_label (GLOMenu *menu, - gint position, - const gchar *label) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - - GVariant *value; - - if (label != nullptr) - value = g_variant_new_string (label); - else - value = nullptr; - - g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_LABEL, value); -} - -void -g_lo_menu_set_icon (GLOMenu *menu, - gint position, - const GIcon *icon) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - - GVariant *value; - - if (icon != nullptr) - { -#if GLIB_CHECK_VERSION(2,38,0) - value = g_icon_serialize (const_cast<GIcon*>(icon)); -#else - value = nullptr; -#endif - } - else - value = nullptr; - -#ifndef G_MENU_ATTRIBUTE_ICON -# define G_MENU_ATTRIBUTE_ICON "icon" -#endif - - g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_ICON, value); - if (value) - g_variant_unref (value); -} - -void -g_lo_menu_set_label_to_item_in_section (GLOMenu *menu, - gint section, - gint position, - const gchar *label) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - - GLOMenu *model = g_lo_menu_get_section (menu, section); - - g_return_if_fail (model != nullptr); - - g_lo_menu_set_label (model, position, label); - - // Notify the update. - g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1); - - g_object_unref (model); -} - -void -g_lo_menu_set_icon_to_item_in_section (GLOMenu *menu, - gint section, - gint position, - const GIcon *icon) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - - GLOMenu *model = g_lo_menu_get_section (menu, section); - - g_return_if_fail (model != nullptr); - - g_lo_menu_set_icon (model, position, icon); - - // Notify the update. - g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1); - - g_object_unref (model); -} - -gchar * -g_lo_menu_get_label_from_item_in_section (GLOMenu *menu, - gint section, - gint position) -{ - g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr); - - GVariant *label_value = g_lo_menu_get_attribute_value_from_item_in_section (menu, - section, - position, - G_MENU_ATTRIBUTE_LABEL, - G_VARIANT_TYPE_STRING); - - gchar *label = nullptr; - - if (label_value) - { - label = g_variant_dup_string (label_value, nullptr); - g_variant_unref (label_value); - } - - return label; -} - -void -g_lo_menu_set_action_and_target_value (GLOMenu *menu, - gint position, - const gchar *action, - GVariant *target_value) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - - GVariant *action_value; - - if (action != nullptr) - { - action_value = g_variant_new_string (action); - } - else - { - action_value = nullptr; - target_value = nullptr; - } - - g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_ACTION, action_value); - g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_TARGET, target_value); - g_lo_menu_set_attribute_value (menu, position, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION, nullptr); - - g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 1); -} - -void -g_lo_menu_set_action_and_target_value_to_item_in_section (GLOMenu *menu, - gint section, - gint position, - const gchar *command, - GVariant *target_value) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - - GLOMenu *model = g_lo_menu_get_section (menu, section); - - g_return_if_fail (model != nullptr); - - g_lo_menu_set_action_and_target_value (model, position, command, target_value); - - g_object_unref (model); -} - -void -g_lo_menu_set_accelerator_to_item_in_section (GLOMenu *menu, - gint section, - gint position, - const gchar *accelerator) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - - GLOMenu *model = g_lo_menu_get_section (menu, section); - - g_return_if_fail (model != nullptr); - - GVariant *value; - - if (accelerator != nullptr) - value = g_variant_new_string (accelerator); - else - value = nullptr; - - g_lo_menu_set_attribute_value (model, position, G_LO_MENU_ATTRIBUTE_ACCELERATOR, value); - - // Notify the update. - g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1); - - g_object_unref (model); -} - -gchar * -g_lo_menu_get_accelerator_from_item_in_section (GLOMenu *menu, - gint section, - gint position) -{ - g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr); - - GVariant *accel_value = g_lo_menu_get_attribute_value_from_item_in_section (menu, - section, - position, - G_LO_MENU_ATTRIBUTE_ACCELERATOR, - G_VARIANT_TYPE_STRING); - - gchar *accel = nullptr; - - if (accel_value != nullptr) - { - accel = g_variant_dup_string (accel_value, nullptr); - g_variant_unref (accel_value); - } - - return accel; -} - -void -g_lo_menu_set_command_to_item_in_section (GLOMenu *menu, - gint section, - gint position, - const gchar *command) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - - GLOMenu *model = g_lo_menu_get_section (menu, section); - - g_return_if_fail (model != nullptr); - - GVariant *value; - - if (command != nullptr) - value = g_variant_new_string (command); - else - value = nullptr; - - g_lo_menu_set_attribute_value (model, position, G_LO_MENU_ATTRIBUTE_COMMAND, value); - - // Notify the update. - g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1); - - g_object_unref (model); -} - -gchar * -g_lo_menu_get_command_from_item_in_section (GLOMenu *menu, - gint section, - gint position) -{ - g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr); - - GVariant *command_value = g_lo_menu_get_attribute_value_from_item_in_section (menu, - section, - position, - G_LO_MENU_ATTRIBUTE_COMMAND, - G_VARIANT_TYPE_STRING); - - gchar *command = nullptr; - - if (command_value != nullptr) - { - command = g_variant_dup_string (command_value, nullptr); - g_variant_unref (command_value); - } - - return command; -} - -static void -g_lo_menu_set_link (GLOMenu *menu, - gint position, - const gchar *link, - GMenuModel *model) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - g_return_if_fail (link != nullptr); - g_return_if_fail (valid_attribute_name (link)); - - if (position < 0 || position >= static_cast<gint>(menu->items->len)) - position = menu->items->len - 1; - - struct item menu_item = g_array_index (menu->items, struct item, position); - - if (model != nullptr) - g_hash_table_insert (menu_item.links, g_strdup (link), g_object_ref (model)); - else - g_hash_table_remove (menu_item.links, link); -} - -void -g_lo_menu_insert_section (GLOMenu *menu, - gint position, - const gchar *label, - GMenuModel *section) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - - if (position < 0 || position > static_cast<gint>(menu->items->len)) - position = menu->items->len; - - struct item menu_item; - - g_lo_menu_struct_item_init(&menu_item); - - g_array_insert_val (menu->items, position, menu_item); - - g_lo_menu_set_label (menu, position, label); - g_lo_menu_set_link (menu, position, G_MENU_LINK_SECTION, section); - - g_menu_model_items_changed (G_MENU_MODEL (menu), position, 0, 1); -} - -void -g_lo_menu_new_section (GLOMenu *menu, - gint position, - const gchar *label) -{ - GMenuModel *section = G_MENU_MODEL (g_lo_menu_new()); - - g_lo_menu_insert_section (menu, position, label, section); - - g_object_unref (section); -} - -GLOMenu * -g_lo_menu_get_section (GLOMenu *menu, - gint section) -{ - g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr); - - return G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class) - ->get_item_link (G_MENU_MODEL (menu), section, G_MENU_LINK_SECTION)); -} - -void -g_lo_menu_new_submenu_in_item_in_section (GLOMenu *menu, - gint section, - gint position) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - g_return_if_fail (0 <= section && section < static_cast<gint>(menu->items->len)); - - GLOMenu* model = g_lo_menu_get_section (menu, section); - - g_return_if_fail (model != nullptr); - - if (0 <= position && position < static_cast<gint>(model->items->len)) { - GMenuModel* submenu = G_MENU_MODEL (g_lo_menu_new()); - - g_lo_menu_set_link (model, position, G_MENU_LINK_SUBMENU, submenu); - - g_object_unref (submenu); - - g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1); - - g_object_unref (model); - } -} - -GLOMenu * -g_lo_menu_get_submenu_from_item_in_section (GLOMenu *menu, - gint section, - gint position) -{ - g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr); - g_return_val_if_fail (0 <= section && section < static_cast<gint>(menu->items->len), nullptr); - - GLOMenu *model = g_lo_menu_get_section (menu, section); - - g_return_val_if_fail (model != nullptr, nullptr); - - GLOMenu *submenu = nullptr; - - if (0 <= position && position < static_cast<gint>(model->items->len)) - submenu = G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class) - ->get_item_link (G_MENU_MODEL (model), position, G_MENU_LINK_SUBMENU)); - //submenu = g_menu_model_get_item_link (G_MENU_MODEL (model), position, G_MENU_LINK_SUBMENU); - - g_object_unref (model); - - return submenu; -} - -void -g_lo_menu_set_submenu_action_to_item_in_section (GLOMenu *menu, - gint section, - gint position, - const gchar *action) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - - GMenuModel *model = G_MENU_MODEL (g_lo_menu_get_section (menu, section)); - - g_return_if_fail (model != nullptr); - - GVariant *value; - - if (action != nullptr) - value = g_variant_new_string (action); - else - value = nullptr; - - g_lo_menu_set_attribute_value (G_LO_MENU (model), position, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION, value); - - // Notify the update. - g_menu_model_items_changed (model, position, 1, 1); - - g_object_unref (model); -} - -static void -g_lo_menu_clear_item (struct item *menu_item) -{ - if (menu_item->attributes != nullptr) - g_hash_table_unref (menu_item->attributes); - if (menu_item->links != nullptr) - g_hash_table_unref (menu_item->links); -} - -void -g_lo_menu_remove (GLOMenu *menu, - gint position) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - g_return_if_fail (0 <= position && position < static_cast<gint>(menu->items->len)); - - g_lo_menu_clear_item (&g_array_index (menu->items, struct item, position)); - g_array_remove_index (menu->items, position); - g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 0); -} - -void -g_lo_menu_remove_from_section (GLOMenu *menu, - gint section, - gint position) -{ - g_return_if_fail (G_IS_LO_MENU (menu)); - g_return_if_fail (0 <= section && section < static_cast<gint>(menu->items->len)); - - GLOMenu *model = g_lo_menu_get_section (menu, section); - - g_return_if_fail (model != nullptr); - - g_lo_menu_remove (model, position); - - g_object_unref (model); -} - -static void -g_lo_menu_finalize (GObject *object) -{ - GLOMenu *menu = G_LO_MENU (object); - struct item *items; - gint n_items; - gint i; - - n_items = menu->items->len; - items = reinterpret_cast<struct item *>(g_array_free (menu->items, FALSE)); - for (i = 0; i < n_items; i++) - g_lo_menu_clear_item (&items[i]); - g_free (items); - - G_OBJECT_CLASS (g_lo_menu_parent_class) - ->finalize (object); -} - -static void -g_lo_menu_init (GLOMenu *menu) -{ - menu->items = g_array_new (FALSE, FALSE, sizeof (struct item)); -} - -static void -g_lo_menu_class_init (GLOMenuClass *klass) -{ - GMenuModelClass *model_class = G_MENU_MODEL_CLASS (klass); - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = g_lo_menu_finalize; - - model_class->is_mutable = g_lo_menu_is_mutable; - model_class->get_n_items = g_lo_menu_get_n_items; - model_class->get_item_attributes = g_lo_menu_get_item_attributes; - model_class->get_item_links = g_lo_menu_get_item_links; -} - -#endif - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/gtkdata.cxx b/vcl/unx/gtk/gtkdata.cxx deleted file mode 100644 index ca7a567f7402..000000000000 --- a/vcl/unx/gtk/gtkdata.cxx +++ /dev/null @@ -1,890 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 <unistd.h> -#include <fcntl.h> - -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <limits.h> -#include <errno.h> -#include <poll.h> -#if defined(FREEBSD) || defined(NETBSD) -#include <sys/types.h> -#include <sys/time.h> -#endif -#include <unx/gtk/gtkdata.hxx> -#include <unx/gtk/gtkinst.hxx> -#include <unx/gtk/gtkframe.hxx> -#include <unx/gtk/gtksalmenu.hxx> -#include <unx/salobj.h> -#include <unx/geninst.h> -#include <osl/thread.h> -#include <osl/process.h> -#include <sal/log.hxx> - -#include <unx/i18n_im.hxx> -#include <unx/i18n_xkb.hxx> -#include <unx/wmadaptor.hxx> - -#include <unx/x11_cursors/salcursors.h> - -#include <vcl/svapp.hxx> -#include <chrono> - -using namespace vcl_sal; - -/*************************************************************** - * class GtkSalDisplay * - ***************************************************************/ -extern "C" { -static GdkFilterReturn call_filterGdkEvent( GdkXEvent* sys_event, - GdkEvent* /*event*/, - gpointer data ) -{ - GtkSalDisplay *pDisplay = static_cast<GtkSalDisplay *>(data); - return pDisplay->filterGdkEvent( sys_event ); -} -} - -GtkSalDisplay::GtkSalDisplay( GdkDisplay* pDisplay ) : - SalDisplay( gdk_x11_display_get_xdisplay( pDisplay ) ), - m_pSys( GtkSalSystem::GetSingleton() ), - m_pGdkDisplay( pDisplay ), - m_bStartupCompleted( false ) -{ - for(GdkCursor* & rpCsr : m_aCursors) - rpCsr = nullptr; - m_bUseRandRWrapper = false; // use gdk signal instead - Init (); - - // FIXME: unify this with SalInst's filter too ? - gdk_window_add_filter( nullptr, call_filterGdkEvent, this ); - - if ( getenv( "SAL_IGNOREXERRORS" ) ) - GetGenericUnixSalData()->ErrorTrapPush(); // and leak the trap - - m_bX11Display = true; - - gtk_widget_set_default_direction(AllSettings::GetLayoutRTL() ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); -} - -GtkSalDisplay::~GtkSalDisplay() -{ - gdk_window_remove_filter( nullptr, call_filterGdkEvent, this ); - - if( !m_bStartupCompleted ) - gdk_notify_startup_complete(); - - doDestruct(); - pDisp_ = nullptr; - - for(GdkCursor* & rpCsr : m_aCursors) - if( rpCsr ) - gdk_cursor_unref( rpCsr ); -} - -extern "C" { - -static void signalScreenSizeChanged( GdkScreen* pScreen, gpointer data ) -{ - GtkSalDisplay* pDisp = static_cast<GtkSalDisplay*>(data); - pDisp->screenSizeChanged( pScreen ); -} - -static void signalMonitorsChanged( GdkScreen* pScreen, gpointer data ) -{ - GtkSalDisplay* pDisp = static_cast<GtkSalDisplay*>(data); - pDisp->monitorsChanged( pScreen ); -} - -} - -GdkFilterReturn GtkSalDisplay::filterGdkEvent( GdkXEvent* sys_event ) -{ - GdkFilterReturn aFilterReturn = GDK_FILTER_CONTINUE; - XEvent *pEvent = static_cast<XEvent *>(sys_event); - - // dispatch all XEvents to event callback - if( GetSalData()->m_pInstance-> - CallEventCallback( pEvent, sizeof( XEvent ) ) ) - aFilterReturn = GDK_FILTER_REMOVE; - - if (GetDisplay() == pEvent->xany.display ) - { - // #i53471# gtk has no callback mechanism that lets us be notified - // when settings (as in XSETTING and opposed to styles) are changed. - // so we need to listen for corresponding property notifications here - // these should be rare enough so that we can assume that the settings - // actually change when a corresponding PropertyNotify occurs - SalFrame *pAnyFrame = anyFrame(); - if( pAnyFrame && pEvent->type == PropertyNotify && - pEvent->xproperty.atom == getWMAdaptor()->getAtom( WMAdaptor::XSETTINGS ) ) - { - PostEvent( pAnyFrame, nullptr, SalEvent::SettingsChanged ); - } - // let's see if one of our frames wants to swallow these events - // get the frame - for (auto pSalFrame : m_aFrames ) - { - GtkSalFrame* pFrame = static_cast<GtkSalFrame*>( pSalFrame ); - if( pFrame->GetSystemData()->aWindow == pEvent->xany.window || - ( pFrame->getForeignParent() && pFrame->getForeignParentWindow() == pEvent->xany.window ) || - ( pFrame->getForeignTopLevel() && pFrame->getForeignTopLevelWindow() == pEvent->xany.window ) - ) - { - if( ! pFrame->Dispatch( pEvent ) ) - aFilterReturn = GDK_FILTER_REMOVE; - break; - } - } - X11SalObject::Dispatch( pEvent ); - } - - return aFilterReturn; -} - -void GtkSalDisplay::screenSizeChanged( GdkScreen const * pScreen ) -{ - m_pSys->countScreenMonitors(); - if (pScreen) - emitDisplayChanged(); -} - -void GtkSalDisplay::monitorsChanged( GdkScreen const * pScreen ) -{ - m_pSys->countScreenMonitors(); - if (pScreen) - emitDisplayChanged(); -} - -SalDisplay::ScreenData * -GtkSalDisplay::initScreen( SalX11Screen nXScreen ) const -{ - // choose visual for screen - ScreenData *pSD; - if (!(pSD = SalDisplay::initScreen( nXScreen ))) - return nullptr; - - // now set a gdk default colormap matching the chosen visual to the screen - GdkScreen* pScreen = gdk_display_get_screen( m_pGdkDisplay, nXScreen.getXScreen() ); -// should really use this: -// GdkVisual* pVis = gdk_x11_screen_lookup_visual_get( screen, pSD->m_aVisual.visualid ); -// and not this: - GdkVisual* pVis = gdkx_visual_get( pSD->m_aVisual.visualid ); - if( pVis ) - { - GdkColormap* pDefCol = gdk_screen_get_default_colormap( pScreen ); - GdkVisual* pDefVis = gdk_colormap_get_visual( pDefCol ); - if( pDefVis != pVis ) - { - pDefCol = gdk_x11_colormap_foreign_new( pVis, pSD->m_aColormap.GetXColormap() ); - gdk_screen_set_default_colormap( pScreen, pDefCol ); - SAL_INFO( "vcl.gtk", "set new gdk color map for screen " << nXScreen.getXScreen() ); - } - } - else - SAL_INFO( "vcl.gtk", "not GdkVisual for visual id " << pSD->m_aVisual.visualid ); - - return pSD; -} - -bool GtkSalDisplay::Dispatch( XEvent* pEvent ) -{ - if( GetDisplay() == pEvent->xany.display ) - { - // let's see if one of our frames wants to swallow these events - // get the child frame - for (auto pSalFrame : m_aFrames ) - { - if (pSalFrame->GetSystemData()->aWindow == pEvent->xany.window) - return static_cast<GtkSalFrame*>( pSalFrame )->Dispatch( pEvent ); - } - } - - return false; -} - -GdkCursor* GtkSalDisplay::getFromXBM( const unsigned char *pBitmap, - const unsigned char *pMask, - int nWidth, int nHeight, - int nXHot, int nYHot ) -{ - GdkScreen *pScreen = gdk_display_get_default_screen( m_pGdkDisplay ); - GdkDrawable *pDrawable = GDK_DRAWABLE( gdk_screen_get_root_window (pScreen) ); - GdkBitmap *pBitmapPix = gdk_bitmap_create_from_data - ( pDrawable, reinterpret_cast<const char*>(pBitmap), nWidth, nHeight ); - GdkBitmap *pMaskPix = gdk_bitmap_create_from_data - ( pDrawable, reinterpret_cast<const char*>(pMask), nWidth, nHeight ); - GdkColormap *pColormap = gdk_drawable_get_colormap( pDrawable ); - - GdkColor aWhite = { 0, 0xffff, 0xffff, 0xffff }; - GdkColor aBlack = { 0, 0, 0, 0 }; - - gdk_colormap_alloc_color( pColormap, &aBlack, FALSE, TRUE); - gdk_colormap_alloc_color( pColormap, &aWhite, FALSE, TRUE); - - return gdk_cursor_new_from_pixmap - ( pBitmapPix, pMaskPix, - &aBlack, &aWhite, nXHot, nYHot); -} - -static unsigned char nullmask_bits[] = { 0x00, 0x00, 0x00, 0x00 }; -static unsigned char nullcurs_bits[] = { 0x00, 0x00, 0x00, 0x00 }; - -#define MAKE_CURSOR( vcl_name, name ) \ - case vcl_name: \ - pCursor = getFromXBM( name##curs##_bits, name##mask##_bits, \ - name##curs_width, name##curs_height, \ - name##curs_x_hot, name##curs_y_hot ); \ - break -#define MAP_BUILTIN( vcl_name, gdk_name ) \ - case vcl_name: \ - pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, gdk_name ); \ - break - -GdkCursor *GtkSalDisplay::getCursor( PointerStyle ePointerStyle ) -{ - if ( !m_aCursors[ ePointerStyle ] ) - { - GdkCursor *pCursor = nullptr; - - switch( ePointerStyle ) - { - MAP_BUILTIN( PointerStyle::Arrow, GDK_LEFT_PTR ); - MAP_BUILTIN( PointerStyle::Text, GDK_XTERM ); - MAP_BUILTIN( PointerStyle::Help, GDK_QUESTION_ARROW ); - MAP_BUILTIN( PointerStyle::Cross, GDK_CROSSHAIR ); - MAP_BUILTIN( PointerStyle::Wait, GDK_WATCH ); - - MAP_BUILTIN( PointerStyle::NSize, GDK_SB_V_DOUBLE_ARROW ); - MAP_BUILTIN( PointerStyle::SSize, GDK_SB_V_DOUBLE_ARROW ); - MAP_BUILTIN( PointerStyle::WSize, GDK_SB_H_DOUBLE_ARROW ); - MAP_BUILTIN( PointerStyle::ESize, GDK_SB_H_DOUBLE_ARROW ); - - MAP_BUILTIN( PointerStyle::NWSize, GDK_TOP_LEFT_CORNER ); - MAP_BUILTIN( PointerStyle::NESize, GDK_TOP_RIGHT_CORNER ); - MAP_BUILTIN( PointerStyle::SWSize, GDK_BOTTOM_LEFT_CORNER ); - MAP_BUILTIN( PointerStyle::SESize, GDK_BOTTOM_RIGHT_CORNER ); - - MAP_BUILTIN( PointerStyle::WindowNSize, GDK_TOP_SIDE ); - MAP_BUILTIN( PointerStyle::WindowSSize, GDK_BOTTOM_SIDE ); - MAP_BUILTIN( PointerStyle::WindowWSize, GDK_LEFT_SIDE ); - MAP_BUILTIN( PointerStyle::WindowESize, GDK_RIGHT_SIDE ); - - MAP_BUILTIN( PointerStyle::WindowNWSize, GDK_TOP_LEFT_CORNER ); - MAP_BUILTIN( PointerStyle::WindowNESize, GDK_TOP_RIGHT_CORNER ); - MAP_BUILTIN( PointerStyle::WindowSWSize, GDK_BOTTOM_LEFT_CORNER ); - MAP_BUILTIN( PointerStyle::WindowSESize, GDK_BOTTOM_RIGHT_CORNER ); - - MAP_BUILTIN( PointerStyle::HSizeBar, GDK_SB_H_DOUBLE_ARROW ); - MAP_BUILTIN( PointerStyle::VSizeBar, GDK_SB_V_DOUBLE_ARROW ); - - MAP_BUILTIN( PointerStyle::RefHand, GDK_HAND2 ); - MAP_BUILTIN( PointerStyle::Hand, GDK_HAND2 ); - MAP_BUILTIN( PointerStyle::Pen, GDK_PENCIL ); - - MAP_BUILTIN( PointerStyle::HSplit, GDK_SB_H_DOUBLE_ARROW ); - MAP_BUILTIN( PointerStyle::VSplit, GDK_SB_V_DOUBLE_ARROW ); - - MAP_BUILTIN( PointerStyle::Move, GDK_FLEUR ); - - MAKE_CURSOR( PointerStyle::Null, null ); - MAKE_CURSOR( PointerStyle::Magnify, magnify_ ); - MAKE_CURSOR( PointerStyle::Fill, fill_ ); - MAKE_CURSOR( PointerStyle::MoveData, movedata_ ); - MAKE_CURSOR( PointerStyle::CopyData, copydata_ ); - MAKE_CURSOR( PointerStyle::MoveFile, movefile_ ); - MAKE_CURSOR( PointerStyle::CopyFile, copyfile_ ); - MAKE_CURSOR( PointerStyle::MoveFiles, movefiles_ ); - MAKE_CURSOR( PointerStyle::CopyFiles, copyfiles_ ); - MAKE_CURSOR( PointerStyle::NotAllowed, nodrop_ ); - MAKE_CURSOR( PointerStyle::Rotate, rotate_ ); - MAKE_CURSOR( PointerStyle::HShear, hshear_ ); - MAKE_CURSOR( PointerStyle::VShear, vshear_ ); - MAKE_CURSOR( PointerStyle::DrawLine, drawline_ ); - MAKE_CURSOR( PointerStyle::DrawRect, drawrect_ ); - MAKE_CURSOR( PointerStyle::DrawPolygon, drawpolygon_ ); - MAKE_CURSOR( PointerStyle::DrawBezier, drawbezier_ ); - MAKE_CURSOR( PointerStyle::DrawArc, drawarc_ ); - MAKE_CURSOR( PointerStyle::DrawPie, drawpie_ ); - MAKE_CURSOR( PointerStyle::DrawCircleCut, drawcirclecut_ ); - MAKE_CURSOR( PointerStyle::DrawEllipse, drawellipse_ ); - MAKE_CURSOR( PointerStyle::DrawConnect, drawconnect_ ); - MAKE_CURSOR( PointerStyle::DrawText, drawtext_ ); - MAKE_CURSOR( PointerStyle::Mirror, mirror_ ); - MAKE_CURSOR( PointerStyle::Crook, crook_ ); - MAKE_CURSOR( PointerStyle::Crop, crop_ ); - MAKE_CURSOR( PointerStyle::MovePoint, movepoint_ ); - MAKE_CURSOR( PointerStyle::MoveBezierWeight, movebezierweight_ ); - MAKE_CURSOR( PointerStyle::DrawFreehand, drawfreehand_ ); - MAKE_CURSOR( PointerStyle::DrawCaption, drawcaption_ ); - MAKE_CURSOR( PointerStyle::LinkData, linkdata_ ); - MAKE_CURSOR( PointerStyle::MoveDataLink, movedlnk_ ); - MAKE_CURSOR( PointerStyle::CopyDataLink, copydlnk_ ); - MAKE_CURSOR( PointerStyle::LinkFile, linkfile_ ); - MAKE_CURSOR( PointerStyle::MoveFileLink, moveflnk_ ); - MAKE_CURSOR( PointerStyle::CopyFileLink, copyflnk_ ); - MAKE_CURSOR( PointerStyle::Chart, chart_ ); - MAKE_CURSOR( PointerStyle::Detective, detective_ ); - MAKE_CURSOR( PointerStyle::PivotCol, pivotcol_ ); - MAKE_CURSOR( PointerStyle::PivotRow, pivotrow_ ); - MAKE_CURSOR( PointerStyle::PivotField, pivotfld_ ); - MAKE_CURSOR( PointerStyle::PivotDelete, pivotdel_ ); - MAKE_CURSOR( PointerStyle::Chain, chain_ ); - MAKE_CURSOR( PointerStyle::ChainNotAllowed, chainnot_ ); - MAKE_CURSOR( PointerStyle::AutoScrollN, asn_ ); - MAKE_CURSOR( PointerStyle::AutoScrollS, ass_ ); - MAKE_CURSOR( PointerStyle::AutoScrollW, asw_ ); - MAKE_CURSOR( PointerStyle::AutoScrollE, ase_ ); - MAKE_CURSOR( PointerStyle::AutoScrollNW, asnw_ ); - MAKE_CURSOR( PointerStyle::AutoScrollNE, asne_ ); - MAKE_CURSOR( PointerStyle::AutoScrollSW, assw_ ); - MAKE_CURSOR( PointerStyle::AutoScrollSE, asse_ ); - MAKE_CURSOR( PointerStyle::AutoScrollNS, asns_ ); - MAKE_CURSOR( PointerStyle::AutoScrollWE, aswe_ ); - MAKE_CURSOR( PointerStyle::AutoScrollNSWE, asnswe_ ); - MAKE_CURSOR( PointerStyle::TextVertical, vertcurs_ ); - - // #i32329# - MAKE_CURSOR( PointerStyle::TabSelectS, tblsels_ ); - MAKE_CURSOR( PointerStyle::TabSelectE, tblsele_ ); - MAKE_CURSOR( PointerStyle::TabSelectSE, tblselse_ ); - MAKE_CURSOR( PointerStyle::TabSelectW, tblselw_ ); - MAKE_CURSOR( PointerStyle::TabSelectSW, tblselsw_ ); - - MAKE_CURSOR( PointerStyle::HideWhitespace, hidewhitespace_ ); - MAKE_CURSOR( PointerStyle::ShowWhitespace, showwhitespace_ ); - - default: - SAL_WARN( "vcl.gtk", "pointer " << static_cast<int>(ePointerStyle) << "not implemented" ); - break; - } - if( !pCursor ) - pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, GDK_LEFT_PTR ); - - m_aCursors[ ePointerStyle ] = pCursor; - } - - return m_aCursors[ ePointerStyle ]; -} - -int GtkSalDisplay::CaptureMouse( SalFrame* pSFrame ) -{ - GtkSalFrame* pFrame = static_cast<GtkSalFrame*>(pSFrame); - - if( !pFrame ) - { - if( m_pCapture ) - static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE ); - m_pCapture = nullptr; - return 0; - } - - if( m_pCapture ) - { - if( pFrame == m_pCapture ) - return 1; - static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE ); - } - - m_pCapture = pFrame; - pFrame->grabPointer( TRUE ); - return 1; -} - -/********************************************************************** - * class GtkSalData * - **********************************************************************/ - -GtkSalData::GtkSalData( SalInstance *pInstance ) - : GenericUnixSalData( SAL_DATA_GTK, pInstance ) - , m_aDispatchMutex() - , m_aDispatchCondition() - , m_pDocumentFocusListener(nullptr) -{ - m_pUserEvent = nullptr; -} - -static XIOErrorHandler aOrigXIOErrorHandler = nullptr; - -extern "C" { - -static int XIOErrorHdl(Display *) -{ - fprintf(stderr, "X IO Error\n"); - _exit(1); - // avoid crashes in unrelated threads that still run while atexit - // handlers are in progress -} - -} - -GtkSalData::~GtkSalData() -{ - Yield( true, true ); - g_warning ("TESTME: We used to have a stop-timer here, but the central code should do this"); - - // sanity check: at this point nobody should be yielding, but wake them - // up anyway before the condition they're waiting on gets destroyed. - m_aDispatchCondition.set(); - - osl::MutexGuard g( m_aDispatchMutex ); - if (m_pUserEvent) - { - g_source_destroy (m_pUserEvent); - g_source_unref (m_pUserEvent); - m_pUserEvent = nullptr; - } - XSetIOErrorHandler(aOrigXIOErrorHandler); -} - -void GtkSalData::Dispose() -{ - deInitNWF(); -} - -/// Allows events to be processed, returns true if we processed an event. -bool GtkSalData::Yield( bool bWait, bool bHandleAllCurrentEvents ) -{ - /* #i33212# only enter g_main_context_iteration in one thread at any one - * time, else one of them potentially will never end as long as there is - * another thread in there. Having only one yielding thread actually dispatch - * fits the vcl event model (see e.g. the generic plugin). - */ - bool bDispatchThread = false; - bool bWasEvent = false; - { - // release YieldMutex (and re-acquire at block end) - SolarMutexReleaser aReleaser; - if( m_aDispatchMutex.tryToAcquire() ) - bDispatchThread = true; - else if( ! bWait ) - { - return false; // someone else is waiting already, return - } - - if( bDispatchThread ) - { - int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1; - gboolean wasOneEvent = TRUE; - while( nMaxEvents-- && wasOneEvent ) - { - wasOneEvent = g_main_context_iteration( nullptr, bWait && !bWasEvent ); - if( wasOneEvent ) - bWasEvent = true; - } - } - else if( bWait ) - { - /* #i41693# in case the dispatch thread hangs in join - * for this thread the condition will never be set - * workaround: timeout of 1 second an emergency exit - */ - // we are the dispatch thread - m_aDispatchCondition.reset(); - m_aDispatchCondition.wait( std::chrono::seconds(1) ); - } - } - - if( bDispatchThread ) - { - m_aDispatchMutex.release(); - if( bWasEvent ) - m_aDispatchCondition.set(); // trigger non dispatch thread yields - } - - return bWasEvent; -} - -void GtkSalData::Init() -{ - int i; - SAL_INFO( "vcl.gtk", "GtkMainloop::Init()" ); - XrmInitialize(); - - gtk_set_locale(); - - /* - * open connection to X11 Display - * try in this order: - * o -display command line parameter, - * o $DISPLAY environment variable - * o default display - */ - - GdkDisplay *pGdkDisp = nullptr; - - // is there a -display command line parameter? - rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); - int nParams = osl_getCommandArgCount(); - OString aDisplay; - OUString aParam, aBin; - char** pCmdLineAry = new char*[ nParams+1 ]; - osl_getExecutableFile( &aParam.pData ); - osl_getSystemPathFromFileURL( aParam.pData, &aBin.pData ); - pCmdLineAry[0] = g_strdup( OUStringToOString( aBin, aEnc ).getStr() ); - for (i=0; i<nParams; i++) - { - osl_getCommandArg(i, &aParam.pData ); - OString aBParam( OUStringToOString( aParam, aEnc ) ); - - if( aParam == "-display" || aParam == "--display" ) - { - pCmdLineAry[i+1] = g_strdup( "--display" ); - osl_getCommandArg(i+1, &aParam.pData ); - aDisplay = OUStringToOString( aParam, aEnc ); - } - else - pCmdLineAry[i+1] = g_strdup( aBParam.getStr() ); - } - // add executable - nParams++; - - g_set_application_name(SalGenericSystem::getFrameClassName()); - - // Set consistent name of the root accessible - OUString aAppName = Application::GetAppName(); - if( !aAppName.isEmpty() ) - { - OString aPrgName = OUStringToOString(aAppName, aEnc); - g_set_prgname(aPrgName.getStr()); - } - - // init gtk/gdk - gtk_init_check( &nParams, &pCmdLineAry ); - gdk_error_trap_push(); - - for (i = 0; i < nParams; i++ ) - g_free( pCmdLineAry[i] ); - delete [] pCmdLineAry; - -#if OSL_DEBUG_LEVEL > 1 - if (g_getenv ("SAL_DEBUG_UPDATES")) - gdk_window_set_debug_updates (TRUE); -#endif - - pGdkDisp = gdk_display_get_default(); - if ( !pGdkDisp ) - { - OUString aProgramFileURL; - osl_getExecutableFile( &aProgramFileURL.pData ); - OUString aProgramSystemPath; - osl_getSystemPathFromFileURL (aProgramFileURL.pData, &aProgramSystemPath.pData); - OString aProgramName = OUStringToOString( - aProgramSystemPath, - osl_getThreadTextEncoding() ); - fprintf( stderr, "%s X11 error: Can't open display: %s\n", - aProgramName.getStr(), aDisplay.getStr()); - fprintf( stderr, " Set DISPLAY environment variable, use -display option\n"); - fprintf( stderr, " or check permissions of your X-Server\n"); - fprintf( stderr, " (See \"man X\" resp. \"man xhost\" for details)\n"); - fflush( stderr ); - exit(0); - } - - aOrigXIOErrorHandler = XSetIOErrorHandler(XIOErrorHdl); - - /* - * if a -display switch was used, we need - * to set the environment accordingly since - * the clipboard build another connection - * to the xserver using $DISPLAY - */ - OUString envVar("DISPLAY"); - const gchar *name = gdk_display_get_name( pGdkDisp ); - OUString envValue(name, strlen(name), aEnc); - osl_setEnvironment(envVar.pData, envValue.pData); - - GtkSalDisplay *pDisplay = new GtkSalDisplay( pGdkDisp ); - SetDisplay( pDisplay ); - - Display *pDisp = gdk_x11_display_get_xdisplay( pGdkDisp ); - - gdk_error_trap_push(); - SalI18N_KeyboardExtension *pKbdExtension = new SalI18N_KeyboardExtension( pDisp ); - bool bErrorOccured = gdk_error_trap_pop() != 0; - gdk_error_trap_push(); - pKbdExtension->UseExtension( bErrorOccured ); - gdk_error_trap_pop(); - GetGtkDisplay()->SetKbdExtension( pKbdExtension ); - - // add signal handler to notify screen size changes - int nScreens = gdk_display_get_n_screens( pGdkDisp ); - for( int n = 0; n < nScreens; n++ ) - { - GdkScreen *pScreen = gdk_display_get_screen( pGdkDisp, n ); - if( pScreen ) - { - pDisplay->screenSizeChanged( pScreen ); - pDisplay->monitorsChanged( pScreen ); - g_signal_connect( G_OBJECT(pScreen), "size-changed", - G_CALLBACK(signalScreenSizeChanged), pDisplay ); - g_signal_connect( G_OBJECT(pScreen), "monitors-changed", - G_CALLBACK(signalMonitorsChanged), GetGtkDisplay() ); - } - } -} - -void GtkSalData::ErrorTrapPush() -{ - gdk_error_trap_push (); -} - -bool GtkSalData::ErrorTrapPop( bool ) -{ - return gdk_error_trap_pop () != 0; -} - -#if !GLIB_CHECK_VERSION(2,32,0) -#define G_SOURCE_REMOVE FALSE -#endif - -extern "C" { - - struct SalGtkTimeoutSource { - GSource aParent; - GTimeVal aFireTime; - GtkSalTimer *pInstance; - }; - - static void sal_gtk_timeout_defer( SalGtkTimeoutSource *pTSource ) - { - g_get_current_time( &pTSource->aFireTime ); - g_time_val_add( &pTSource->aFireTime, pTSource->pInstance->m_nTimeoutMS * 1000 ); - } - - static gboolean sal_gtk_timeout_expired( SalGtkTimeoutSource *pTSource, - gint *nTimeoutMS, GTimeVal const *pTimeNow ) - { - glong nDeltaSec = pTSource->aFireTime.tv_sec - pTimeNow->tv_sec; - glong nDeltaUSec = pTSource->aFireTime.tv_usec - pTimeNow->tv_usec; - if( nDeltaSec < 0 || ( nDeltaSec == 0 && nDeltaUSec < 0) ) - { - *nTimeoutMS = 0; - return TRUE; - } - if( nDeltaUSec < 0 ) - { - nDeltaUSec += 1000000; - nDeltaSec -= 1; - } - // if the clock changes backwards we need to cope ... - if( static_cast<unsigned long>(nDeltaSec) > 1 + ( pTSource->pInstance->m_nTimeoutMS / 1000 ) ) - { - sal_gtk_timeout_defer( pTSource ); - return TRUE; - } - - *nTimeoutMS = MIN( G_MAXINT, ( nDeltaSec * 1000 + (nDeltaUSec + 999) / 1000 ) ); - - return *nTimeoutMS == 0; - } - - static gboolean sal_gtk_timeout_prepare( GSource *pSource, gint *nTimeoutMS ) - { - SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource); - - GTimeVal aTimeNow; - g_get_current_time( &aTimeNow ); - - return sal_gtk_timeout_expired( pTSource, nTimeoutMS, &aTimeNow ); - } - - static gboolean sal_gtk_timeout_check( GSource *pSource ) - { - SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource); - - GTimeVal aTimeNow; - g_get_current_time( &aTimeNow ); - - return ( pTSource->aFireTime.tv_sec < aTimeNow.tv_sec || - ( pTSource->aFireTime.tv_sec == aTimeNow.tv_sec && - pTSource->aFireTime.tv_usec < aTimeNow.tv_usec ) ); - } - - static gboolean sal_gtk_timeout_dispatch( GSource *pSource, GSourceFunc, gpointer ) - { - SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource); - - if( !pTSource->pInstance ) - return FALSE; - - SolarMutexGuard aGuard; - - sal_gtk_timeout_defer( pTSource ); - - ImplSVData* pSVData = ImplGetSVData(); - if( pSVData->maSchedCtx.mpSalTimer ) - pSVData->maSchedCtx.mpSalTimer->CallCallback(); - - return G_SOURCE_REMOVE; - } - - static GSourceFuncs sal_gtk_timeout_funcs = - { - sal_gtk_timeout_prepare, - sal_gtk_timeout_check, - sal_gtk_timeout_dispatch, - nullptr, nullptr, nullptr - }; -} - -static SalGtkTimeoutSource * -create_sal_gtk_timeout( GtkSalTimer *pTimer ) -{ - GSource *pSource = g_source_new( &sal_gtk_timeout_funcs, sizeof( SalGtkTimeoutSource ) ); - SalGtkTimeoutSource *pTSource = reinterpret_cast<SalGtkTimeoutSource *>(pSource); - pTSource->pInstance = pTimer; - - // #i36226# timers should be executed with lower priority - // than XEvents like in generic plugin - g_source_set_priority( pSource, G_PRIORITY_LOW ); - g_source_set_can_recurse( pSource, TRUE ); - g_source_set_callback( pSource, - /* unused dummy */ g_idle_remove_by_data, - nullptr, nullptr ); - g_source_attach( pSource, g_main_context_default() ); -#ifdef DBG_UTIL - g_source_set_name( pSource, "VCL timeout source" ); -#endif - - sal_gtk_timeout_defer( pTSource ); - - return pTSource; -} - -GtkSalTimer::GtkSalTimer() - : m_pTimeout(nullptr) - , m_nTimeoutMS(0) -{ -} - -GtkSalTimer::~GtkSalTimer() -{ - GtkInstance *pInstance = static_cast<GtkInstance *>(GetSalData()->m_pInstance); - pInstance->RemoveTimer(); - Stop(); -} - -bool GtkSalTimer::Expired() -{ - if( !m_pTimeout || g_source_is_destroyed( &m_pTimeout->aParent ) ) - return false; - - gint nDummy = 0; - GTimeVal aTimeNow; - g_get_current_time( &aTimeNow ); - return !!sal_gtk_timeout_expired( m_pTimeout, &nDummy, &aTimeNow); -} - -void GtkSalTimer::Start( sal_uInt64 nMS ) -{ - // glib is not 64bit safe in this regard. - assert( nMS <= G_MAXINT ); - if ( nMS > G_MAXINT ) - nMS = G_MAXINT; - m_nTimeoutMS = nMS; // for restarting - Stop(); // FIXME: ideally re-use an existing m_pTimeout - m_pTimeout = create_sal_gtk_timeout( this ); -} - -void GtkSalTimer::Stop() -{ - if( m_pTimeout ) - { - g_source_destroy( &m_pTimeout->aParent ); - g_source_unref( &m_pTimeout->aParent ); - m_pTimeout = nullptr; - } -} - -extern "C" { - static gboolean call_userEventFn( void *data ) - { - SolarMutexGuard aGuard; - const SalGenericDisplay *pDisplay = GetGenericUnixSalData()->GetDisplay(); - if ( pDisplay ) - { - GtkSalDisplay *pThisDisplay = static_cast<GtkSalData *>(data)->GetGtkDisplay(); - assert(static_cast<const SalGenericDisplay *>(pThisDisplay) == pDisplay); - pThisDisplay->DispatchInternalEvent(); - } - return TRUE; - } -} - -void GtkSalData::TriggerUserEventProcessing() -{ - if (m_pUserEvent) - g_main_context_wakeup (nullptr); // really needed ? - else // nothing pending anyway - { - m_pUserEvent = g_idle_source_new(); - // tdf#112890 some dialog don't appear under gtk2, use the same - // priority for user-events as gtk3 does since tdf#110737 - g_source_set_priority (m_pUserEvent, G_PRIORITY_HIGH_IDLE + 30); - g_source_set_can_recurse (m_pUserEvent, TRUE); - g_source_set_callback (m_pUserEvent, call_userEventFn, - static_cast<gpointer>(this), nullptr); - g_source_attach (m_pUserEvent, g_main_context_default ()); - } -} - -void GtkSalData::TriggerAllUserEventsProcessed() -{ - assert( m_pUserEvent ); - g_source_destroy( m_pUserEvent ); - g_source_unref( m_pUserEvent ); - m_pUserEvent = nullptr; -} - -void GtkSalDisplay::TriggerUserEventProcessing() -{ - GetGtkSalData()->TriggerUserEventProcessing(); -} - -void GtkSalDisplay::TriggerAllUserEventsProcessed() -{ - GetGtkSalData()->TriggerAllUserEventsProcessed(); -} - -GtkWidget* GtkSalDisplay::findGtkWidgetForNativeHandle(sal_uIntPtr hWindow) const -{ - for (auto pFrame : m_aFrames) - { - const SystemEnvData* pEnvData = pFrame->GetSystemData(); - if (pEnvData->aWindow == hWindow) - return GTK_WIDGET(pEnvData->pWidget); - } - return nullptr; -} - -void GtkSalDisplay::deregisterFrame( SalFrame* pFrame ) -{ - if( m_pCapture == pFrame ) - { - static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE ); - m_pCapture = nullptr; - } - SalGenericDisplay::deregisterFrame( pFrame ); -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/gtkinst.cxx b/vcl/unx/gtk/gtkinst.cxx deleted file mode 100644 index 2a31b87e45cf..000000000000 --- a/vcl/unx/gtk/gtkinst.cxx +++ /dev/null @@ -1,478 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 <stack> -#include <string.h> -#include <osl/process.h> -#include <unx/gtk/gtkdata.hxx> -#include <unx/gtk/gtkinst.hxx> -#include <unx/salobj.h> -#include <unx/gtk/gtkgdi.hxx> -#include <unx/gtk/gtkframe.hxx> -#include <unx/gtk/gtkobject.hxx> -#include <unx/gtk/atkbridge.hxx> -#include <unx/gtk/gtkprn.hxx> -#include <unx/gtk/gtksalmenu.hxx> -#include <headless/svpvd.hxx> -#include <headless/svpbmp.hxx> -#include <vcl/inputtypes.hxx> -#include <unx/genpspgraphics.h> -#include <rtl/strbuf.hxx> -#include <sal/log.hxx> -#include <rtl/uri.hxx> - -#include <vcl/settings.hxx> - -#include <dlfcn.h> -#include <fcntl.h> -#include <unistd.h> - -#include <unx/gtk/gtkprintwrapper.hxx> - -extern "C" -{ - #define GET_YIELD_MUTEX() static_cast<GtkYieldMutex*>(GetSalData()->m_pInstance->GetYieldMutex()) - static void GdkThreadsEnter() - { - GtkYieldMutex *pYieldMutex = GET_YIELD_MUTEX(); - pYieldMutex->ThreadsEnter(); - } - static void GdkThreadsLeave() - { - GtkYieldMutex *pYieldMutex = GET_YIELD_MUTEX(); - pYieldMutex->ThreadsLeave(); - } - - VCLPLUG_GTK_PUBLIC SalInstance* create_SalInstance() - { - SAL_INFO( - "vcl.gtk", - "create vcl plugin instance with gtk version " << gtk_major_version - << " " << gtk_minor_version << " " << gtk_micro_version); - -#if !GTK_CHECK_VERSION(3,0,0) - if( gtk_major_version < 2 || // very unlikely sanity check - ( gtk_major_version == 2 && gtk_minor_version < 4 ) ) - { - g_warning("require a newer gtk than %d.%d for gdk_threads_set_lock_functions", static_cast<int>(gtk_major_version), gtk_minor_version); - return nullptr; - } -#else - if (gtk_major_version == 3 && gtk_minor_version < 18) - { - g_warning("require gtk >= 3.18 for theme expectations"); - return nullptr; - } -#endif - - // for gtk2 it is always built with X support, so this is always called - // for gtk3 it is normally built with X and Wayland support, if - // X is supported GDK_WINDOWING_X11 is defined and this is always - // called, regardless of if we're running under X or Wayland. - // We can't use (DLSYM_GDK_IS_X11_DISPLAY(pDisplay)) to only do it under - // X, because we need to do it earlier than we have a display -#if !GTK_CHECK_VERSION(3,0,0) || defined(GDK_WINDOWING_X11) - /* #i92121# workaround deadlocks in the X11 implementation - */ - static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" ); - /* #i90094# - from now on we know that an X connection will be - established, so protect X against itself - */ - if( ! ( pNoXInitThreads && *pNoXInitThreads ) ) - XInitThreads(); -#endif - - // init gdk thread protection - bool const sup = g_thread_supported(); - // extracted from the 'if' to avoid Clang -Wunreachable-code - if ( !sup ) - g_thread_init( nullptr ); - - gdk_threads_set_lock_functions (GdkThreadsEnter, GdkThreadsLeave); - SAL_INFO("vcl.gtk", "Hooked gdk threads locks"); - - auto pYieldMutex = std::make_unique<GtkYieldMutex>(); - - gdk_threads_init(); - - GtkInstance* pInstance = new GtkInstance( std::move(pYieldMutex) ); - SAL_INFO("vcl.gtk", "creating GtkInstance " << pInstance); - - // Create SalData, this does not leak - new GtkSalData( pInstance ); - - return pInstance; - } -} - -#if GTK_CHECK_VERSION(3,0,0) -static VclInputFlags categorizeEvent(const GdkEvent *pEvent) -{ - VclInputFlags nType = VclInputFlags::NONE; - switch( pEvent->type ) - { - case GDK_MOTION_NOTIFY: - case GDK_BUTTON_PRESS: - case GDK_2BUTTON_PRESS: - case GDK_3BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - case GDK_ENTER_NOTIFY: - case GDK_LEAVE_NOTIFY: - case GDK_SCROLL: - nType = VclInputFlags::MOUSE; - break; - case GDK_KEY_PRESS: - // case GDK_KEY_RELEASE: //similar to the X11SalInstance one - nType = VclInputFlags::KEYBOARD; - break; - case GDK_EXPOSE: - nType = VclInputFlags::PAINT; - break; - default: - nType = VclInputFlags::OTHER; - break; - } - return nType; -} -#endif - -GtkInstance::GtkInstance( std::unique_ptr<SalYieldMutex> pMutex ) -#if GTK_CHECK_VERSION(3,0,0) - : SvpSalInstance( std::move(pMutex) ) -#else - : X11SalInstance( std::move(pMutex) ) -#endif - , m_pTimer(nullptr) - , bNeedsInit(true) - , m_pLastCairoFontOptions(nullptr) -{ -} - -//We want to defer initializing gtk until we are after uno has been -//bootstrapped so we can ask the config what the UI language is so that we can -//force that in as $LANGUAGE to get gtk to render widgets RTL if we have a RTL -//UI in a LTR locale -void GtkInstance::AfterAppInit() -{ - EnsureInit(); -} - -void GtkInstance::EnsureInit() -{ - if (!bNeedsInit) - return; - // initialize SalData - GtkSalData *pSalData = GetGtkSalData(); - pSalData->Init(); - GtkSalData::initNWF(); - - InitAtkBridge(); - - ImplSVData* pSVData = ImplGetSVData(); -#ifdef GTK_TOOLKIT_NAME - pSVData->maAppData.mxToolkitName = OUString(GTK_TOOLKIT_NAME); -#elif GTK_CHECK_VERSION(3,0,0) - pSVData->maAppData.mxToolkitName = OUString("gtk3"); -#else - pSVData->maAppData.mxToolkitName = OUString("gtk2"); -#endif - - bNeedsInit = false; -} - -GtkInstance::~GtkInstance() -{ - assert( nullptr == m_pTimer ); - DeInitAtkBridge(); - ResetLastSeenCairoFontOptions(nullptr); -} - -SalFrame* GtkInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle ) -{ - EnsureInit(); - return new GtkSalFrame( pParent, nStyle ); -} - -SalFrame* GtkInstance::CreateChildFrame( SystemParentData* pParentData, SalFrameStyleFlags ) -{ - EnsureInit(); - return new GtkSalFrame( pParentData ); -} - -SalObject* GtkInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, bool bShow ) -{ - EnsureInit(); -#if !GTK_CHECK_VERSION(3,0,0) - // there is no method to set a visual for a GtkWidget - // so we need the X11SalObject in that case - if( pWindowData ) - return X11SalObject::CreateObject( pParent, pWindowData, bShow ); -#else - (void)pWindowData; - //FIXME: Missing CreateObject functionality ... -#endif - - return new GtkSalObject( static_cast<GtkSalFrame*>(pParent), bShow ); -} - -extern "C" -{ - typedef void*(* getDefaultFnc)(); - typedef void(* addItemFnc)(void *, const char *); -} - -void GtkInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString&, const OUString&) -{ - EnsureInit(); - OString sGtkURL; - rtl_TextEncoding aSystemEnc = osl_getThreadTextEncoding(); - if ((aSystemEnc == RTL_TEXTENCODING_UTF8) || !rFileUrl.startsWith( "file://" )) - sGtkURL = OUStringToOString(rFileUrl, RTL_TEXTENCODING_UTF8); - else - { - //Non-utf8 locales are a bad idea if trying to work with non-ascii filenames - //Decode %XX components - OUString sDecodedUri = rtl::Uri::decode(rFileUrl.copy(7), rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8); - //Convert back to system locale encoding - OString sSystemUrl = OUStringToOString(sDecodedUri, aSystemEnc); - //Encode to an escaped ASCII-encoded URI - gchar *g_uri = g_filename_to_uri(sSystemUrl.getStr(), nullptr, nullptr); - sGtkURL = OString(g_uri); - g_free(g_uri); - } - GtkRecentManager *manager = gtk_recent_manager_get_default (); - gtk_recent_manager_add_item (manager, sGtkURL.getStr()); -} - -SalInfoPrinter* GtkInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo, - ImplJobSetup* pSetupData ) -{ - EnsureInit(); -#if defined ENABLE_GTK_PRINT || GTK_CHECK_VERSION(3,0,0) - mbPrinterInit = true; - // create and initialize SalInfoPrinter - PspSalInfoPrinter* pPrinter = new GtkSalInfoPrinter; - configurePspInfoPrinter(pPrinter, pQueueInfo, pSetupData); - return pPrinter; -#else - return Superclass_t::CreateInfoPrinter( pQueueInfo, pSetupData ); -#endif -} - -std::unique_ptr<SalPrinter> GtkInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) -{ - EnsureInit(); -#if defined ENABLE_GTK_PRINT || GTK_CHECK_VERSION(3,0,0) - mbPrinterInit = true; - return std::unique_ptr<SalPrinter>(new GtkSalPrinter( pInfoPrinter )); -#else - return Superclass_t::CreatePrinter( pInfoPrinter ); -#endif -} - -/* - * These methods always occur in pairs - * A ThreadsEnter is followed by a ThreadsLeave - * We need to queue up the recursive lock count - * for each pair, so we can accurately restore - * it later. - */ -thread_local std::stack<sal_uInt32> GtkYieldMutex::yieldCounts; - -void GtkYieldMutex::ThreadsEnter() -{ - acquire(); - if (!yieldCounts.empty()) { - auto n = yieldCounts.top(); - yieldCounts.pop(); - assert(n > 0); - n--; - if (n > 0) - acquire(n); - } -} - -void GtkYieldMutex::ThreadsLeave() -{ - assert(m_nCount != 0); - yieldCounts.push(m_nCount); - release(true); -} - -std::unique_ptr<SalVirtualDevice> GtkInstance::CreateVirtualDevice( SalGraphics *pG, - long &nDX, long &nDY, - DeviceFormat eFormat, - const SystemGraphicsData *pGd ) -{ - EnsureInit(); -#if GTK_CHECK_VERSION(3,0,0) - (void) pGd; - SvpSalGraphics *pSvpSalGraphics = dynamic_cast<SvpSalGraphics*>(pG); - assert(pSvpSalGraphics); - std::unique_ptr<SalVirtualDevice> pNew(new SvpSalVirtualDevice(eFormat, pSvpSalGraphics->getSurface())); - pNew->SetSize( nDX, nDY ); - return pNew; -#else - GtkSalGraphics *pGtkSalGraphics = dynamic_cast<GtkSalGraphics*>(pG); - assert(pGtkSalGraphics); - return CreateX11VirtualDevice(pG, nDX, nDY, eFormat, pGd, - std::make_unique<GtkSalGraphics>(pGtkSalGraphics->GetGtkFrame(), - pGtkSalGraphics->GetGtkWidget(), - pGtkSalGraphics->GetScreenNumber())); -#endif -} - -std::shared_ptr<SalBitmap> GtkInstance::CreateSalBitmap() -{ - EnsureInit(); -#if GTK_CHECK_VERSION(3,0,0) - return SvpSalInstance::CreateSalBitmap();//new SvpSalBitmap(); -#else - return X11SalInstance::CreateSalBitmap(); -#endif -} - -#ifdef ENABLE_GMENU_INTEGRATION - -std::unique_ptr<SalMenu> GtkInstance::CreateMenu( bool bMenuBar, Menu* pVCLMenu ) -{ - EnsureInit(); - GtkSalMenu* pSalMenu = new GtkSalMenu( bMenuBar ); - pSalMenu->SetMenu( pVCLMenu ); - return std::unique_ptr<SalMenu>(pSalMenu); -} - -std::unique_ptr<SalMenuItem> GtkInstance::CreateMenuItem( const SalItemParams & rItemData ) -{ - EnsureInit(); - return std::unique_ptr<SalMenuItem>(new GtkSalMenuItem( &rItemData )); -} - -#else // not ENABLE_GMENU_INTEGRATION - -std::unique_ptr<SalMenu> GtkInstance::CreateMenu( bool, Menu* ) { return nullptr; } -std::unique_ptr<SalMenuItem> GtkInstance::CreateMenuItem( const SalItemParams & ) { return nullptr; } - -#endif - -SalTimer* GtkInstance::CreateSalTimer() -{ - EnsureInit(); - assert( nullptr == m_pTimer ); - if ( nullptr == m_pTimer ) - m_pTimer = new GtkSalTimer(); - return m_pTimer; -} - -void GtkInstance::RemoveTimer () -{ - EnsureInit(); - m_pTimer = nullptr; -} - -bool GtkInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents) -{ - EnsureInit(); - return GetGtkSalData()->Yield( bWait, bHandleAllCurrentEvents ); -} - -bool GtkInstance::IsTimerExpired() -{ - EnsureInit(); - return (m_pTimer && m_pTimer->Expired()); -} - -bool GtkInstance::AnyInput( VclInputFlags nType ) -{ - EnsureInit(); - if( (nType & VclInputFlags::TIMER) && IsTimerExpired() ) - return true; -#if !GTK_CHECK_VERSION(3,0,0) - bool bRet = X11SalInstance::AnyInput(nType); -#else - if (!gdk_events_pending()) - return false; - - if (nType == VCL_INPUT_ANY) - return true; - - bool bRet = false; - std::stack<GdkEvent*> aEvents; - GdkEvent *pEvent = nullptr; - while ((pEvent = gdk_event_get())) - { - aEvents.push(pEvent); - VclInputFlags nEventType = categorizeEvent(pEvent); - if ( (nEventType & nType) || ( nEventType == VclInputFlags::NONE && (nType & VclInputFlags::OTHER) ) ) - { - bRet = true; - break; - } - } - - while (!aEvents.empty()) - { - pEvent = aEvents.top(); - gdk_event_put(pEvent); - gdk_event_free(pEvent); - aEvents.pop(); - } -#endif - return bRet; -} - -std::unique_ptr<GenPspGraphics> GtkInstance::CreatePrintGraphics() -{ - EnsureInit(); - return std::make_unique<GenPspGraphics>(); -} - -std::shared_ptr<vcl::unx::GtkPrintWrapper> const & -GtkInstance::getPrintWrapper() const -{ - if (!m_xPrintWrapper) - m_xPrintWrapper.reset(new vcl::unx::GtkPrintWrapper); - return m_xPrintWrapper; -} - -const cairo_font_options_t* GtkInstance::GetCairoFontOptions() -{ - const cairo_font_options_t* pCairoFontOptions = gdk_screen_get_font_options(gdk_screen_get_default()); - if (!m_pLastCairoFontOptions && pCairoFontOptions) - m_pLastCairoFontOptions = cairo_font_options_copy(pCairoFontOptions); - return pCairoFontOptions; -} - -const cairo_font_options_t* GtkInstance::GetLastSeenCairoFontOptions() const -{ - return m_pLastCairoFontOptions; -} - -void GtkInstance::ResetLastSeenCairoFontOptions(const cairo_font_options_t* pCairoFontOptions) -{ - if (m_pLastCairoFontOptions) - cairo_font_options_destroy(m_pLastCairoFontOptions); - if (pCairoFontOptions) - m_pLastCairoFontOptions = cairo_font_options_copy(pCairoFontOptions); - else - m_pLastCairoFontOptions = nullptr; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/gtkobject.cxx b/vcl/unx/gtk/gtkobject.cxx deleted file mode 100644 index 38302c60ef74..000000000000 --- a/vcl/unx/gtk/gtkobject.cxx +++ /dev/null @@ -1,192 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.apache.org/licenses/LICENSE-2.0 . - */ - -#ifdef AIX -#define _LINUX_SOURCE_COMPAT -#include <sys/timer.h> -#undef _LINUX_SOURCE_COMPAT -#endif - -#include <unx/gtk/gtkobject.hxx> -#include <unx/gtk/gtkframe.hxx> -#include <unx/gtk/gtkdata.hxx> -#include <unx/gtk/gtkinst.hxx> -#include <unx/gtk/gtkgdi.hxx> - -GtkSalObject::GtkSalObject( GtkSalFrame* pParent, bool bShow ) - : m_pSocket( nullptr ), - m_pRegion( nullptr ) -{ - if( !pParent ) - return; - - // our plug window - m_pSocket = gtk_drawing_area_new(); - Show( bShow ); - // insert into container - gtk_fixed_put( pParent->getFixedContainer(), - m_pSocket, - 0, 0 ); - // realize so we can get a window id - gtk_widget_realize( m_pSocket ); - - // make it transparent; some plugins may not insert - // their own window here but use the socket window itself - gtk_widget_set_app_paintable( m_pSocket, TRUE ); - - // system data - m_aSystemData.nSize = sizeof( SystemEnvData ); - SalDisplay* pDisp = vcl_sal::getSalDisplay(GetGenericUnixSalData()); - m_aSystemData.pDisplay = pDisp->GetDisplay(); - m_aSystemData.pVisual = pDisp->GetVisual(pParent->getXScreenNumber()).GetVisual(); - m_aSystemData.aWindow = GDK_WINDOW_XWINDOW(widget_get_window(m_pSocket)); - m_aSystemData.aShellWindow = GDK_WINDOW_XWINDOW(widget_get_window(GTK_WIDGET(pParent->getWindow()))); - m_aSystemData.pSalFrame = nullptr; - m_aSystemData.pWidget = m_pSocket; - m_aSystemData.nScreen = pParent->getXScreenNumber().getXScreen(); - m_aSystemData.pToolkit = "gtk2"; - m_aSystemData.pPlatformName = "xcb"; - - g_signal_connect( G_OBJECT(m_pSocket), "button-press-event", G_CALLBACK(signalButton), this ); - g_signal_connect( G_OBJECT(m_pSocket), "button-release-event", G_CALLBACK(signalButton), this ); - g_signal_connect( G_OBJECT(m_pSocket), "focus-in-event", G_CALLBACK(signalFocus), this ); - g_signal_connect( G_OBJECT(m_pSocket), "focus-out-event", G_CALLBACK(signalFocus), this ); - g_signal_connect( G_OBJECT(m_pSocket), "destroy", G_CALLBACK(signalDestroy), this ); - - // #i59255# necessary due to sync effects with java child windows - pParent->Flush(); - -} - -GtkSalObject::~GtkSalObject() -{ - if( m_pRegion ) - { - gdk_region_destroy( m_pRegion ); - } - if( m_pSocket ) - { - // remove socket from parent frame's fixed container - gtk_container_remove( GTK_CONTAINER(gtk_widget_get_parent(m_pSocket)), - m_pSocket ); - // get rid of the socket - // actually the gtk_container_remove should let the ref count - // of the socket sink to 0 and destroy it (see signalDestroy) - // this is just a sanity check - if( m_pSocket ) - gtk_widget_destroy( m_pSocket ); - } -} - -void GtkSalObject::ResetClipRegion() -{ - if( m_pSocket ) - gdk_window_shape_combine_region( widget_get_window(m_pSocket), nullptr, 0, 0 ); -} - -void GtkSalObject::BeginSetClipRegion( sal_uInt32 ) -{ - if( m_pRegion ) - gdk_region_destroy( m_pRegion ); - m_pRegion = gdk_region_new(); -} - -void GtkSalObject::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) -{ - GdkRectangle aRect; - aRect.x = nX; - aRect.y = nY; - aRect.width = nWidth; - aRect.height = nHeight; - - gdk_region_union_with_rect( m_pRegion, &aRect ); -} - -void GtkSalObject::EndSetClipRegion() -{ - if( m_pSocket ) - gdk_window_shape_combine_region( widget_get_window(m_pSocket), m_pRegion, 0, 0 ); -} - -void GtkSalObject::SetPosSize( long nX, long nY, long nWidth, long nHeight ) -{ - if( m_pSocket ) - { - GtkFixed* pContainer = GTK_FIXED(gtk_widget_get_parent(m_pSocket)); - gtk_fixed_move( pContainer, m_pSocket, nX, nY ); - gtk_widget_set_size_request( m_pSocket, nWidth, nHeight ); - gtk_container_resize_children( GTK_CONTAINER(pContainer) ); - } -} - -void GtkSalObject::Show( bool bVisible ) -{ - if( m_pSocket ) - { - if( bVisible ) - gtk_widget_show( m_pSocket ); - else - gtk_widget_hide( m_pSocket ); - } -} - -const SystemEnvData* GtkSalObject::GetSystemData() const -{ - return &m_aSystemData; -} - -gboolean GtkSalObject::signalButton( GtkWidget*, GdkEventButton* pEvent, gpointer object ) -{ - GtkSalObject* pThis = static_cast<GtkSalObject*>(object); - - if( pEvent->type == GDK_BUTTON_PRESS ) - { - pThis->CallCallback( SalObjEvent::ToTop ); - } - - return FALSE; -} - -gboolean GtkSalObject::signalFocus( GtkWidget*, GdkEventFocus* pEvent, gpointer object ) -{ - GtkSalObject* pThis = static_cast<GtkSalObject*>(object); - - pThis->CallCallback( pEvent->in ? SalObjEvent::GetFocus : SalObjEvent::LoseFocus ); - - return FALSE; -} - -void GtkSalObject::signalDestroy( GtkWidget* pObj, gpointer object ) -{ - GtkSalObject* pThis = static_cast<GtkSalObject*>(object); - if( pObj == pThis->m_pSocket ) - { - pThis->m_pSocket = nullptr; - } -} - -void GtkSalObject::SetForwardKey( bool bEnable ) -{ - if( bEnable ) - gtk_widget_add_events( GTK_WIDGET( m_pSocket ), GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE ); - else - gtk_widget_set_events( GTK_WIDGET( m_pSocket ), ~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE) & gtk_widget_get_events( GTK_WIDGET( m_pSocket ) ) ); -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/gtkprintwrapper.cxx b/vcl/unx/gtk/gtkprintwrapper.cxx deleted file mode 100644 index 7e2241304f26..000000000000 --- a/vcl/unx/gtk/gtkprintwrapper.cxx +++ /dev/null @@ -1,349 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.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 <cassert> - -#include <rtl/ustring.hxx> - -#include <unx/gtk/gtkprintwrapper.hxx> - -namespace vcl -{ -namespace unx -{ - -#if !GTK_CHECK_VERSION(3,0,0) - -GtkPrintWrapper::GtkPrintWrapper() - : m_page_setup_new(nullptr) - , m_print_job_new(nullptr) - , m_print_job_send(nullptr) - , m_print_job_set_source_file(nullptr) - , m_print_settings_get(nullptr) - , m_print_settings_get_collate(nullptr) - , m_print_settings_set_collate(nullptr) - , m_print_settings_get_n_copies(nullptr) - , m_print_settings_set_n_copies(nullptr) - , m_print_settings_get_page_ranges(nullptr) - , m_print_settings_set_print_pages(nullptr) - , m_print_unix_dialog_new(nullptr) - , m_print_unix_dialog_add_custom_tab(nullptr) - , m_print_unix_dialog_get_selected_printer(nullptr) - , m_print_unix_dialog_set_manual_capabilities(nullptr) - , m_print_unix_dialog_get_settings(nullptr) - , m_print_unix_dialog_set_settings(nullptr) - , m_print_unix_dialog_set_support_selection(nullptr) - , m_print_unix_dialog_set_has_selection(nullptr) -{ - impl_load(); -} - -#else - -GtkPrintWrapper::GtkPrintWrapper() -{ -} - -#endif - -GtkPrintWrapper::~GtkPrintWrapper() -{ -} - -#if !GTK_CHECK_VERSION(3,0,0) - -void GtkPrintWrapper::impl_load() -{ - m_aModule.load("libgtk-x11-2.0.so.0"); - if (!m_aModule.is()) - m_aModule.load("libgtk-x11-2.0.so"); - if (!m_aModule.is()) - return; - - m_page_setup_new = reinterpret_cast<page_setup_new_t>(m_aModule.getFunctionSymbol("gtk_page_setup_new")); - m_print_job_new = reinterpret_cast<print_job_new_t>(m_aModule.getFunctionSymbol("gtk_print_job_new")); - m_print_job_send = reinterpret_cast<print_job_send_t>(m_aModule.getFunctionSymbol("gtk_print_job_send")); - m_print_job_set_source_file = reinterpret_cast<print_job_set_source_file_t>(m_aModule.getFunctionSymbol("gtk_print_job_set_source_file")); - m_print_settings_get = reinterpret_cast<print_settings_get_t>(m_aModule.getFunctionSymbol("gtk_print_settings_get")); - m_print_settings_get_collate = reinterpret_cast<print_settings_get_collate_t>(m_aModule.getFunctionSymbol("gtk_print_settings_get_collate")); - m_print_settings_set_collate = reinterpret_cast<print_settings_set_collate_t>(m_aModule.getFunctionSymbol("gtk_print_settings_set_collate")); - m_print_settings_get_n_copies = reinterpret_cast<print_settings_get_n_copies_t>(m_aModule.getFunctionSymbol("gtk_print_settings_get_n_copies")); - m_print_settings_set_n_copies = reinterpret_cast<print_settings_set_n_copies_t>(m_aModule.getFunctionSymbol("gtk_print_settings_set_n_copies")); - m_print_settings_get_page_ranges = reinterpret_cast<print_settings_get_page_ranges_t>(m_aModule.getFunctionSymbol("gtk_print_settings_get_page_ranges")); - m_print_settings_set_print_pages = reinterpret_cast<print_settings_set_print_pages_t>(m_aModule.getFunctionSymbol("gtk_print_settings_set_print_pages")); - m_print_unix_dialog_new = reinterpret_cast<print_unix_dialog_new_t>(m_aModule.getFunctionSymbol("gtk_print_unix_dialog_new")); - m_print_unix_dialog_add_custom_tab = reinterpret_cast<print_unix_dialog_add_custom_tab_t>(m_aModule.getFunctionSymbol("gtk_print_unix_dialog_add_custom_tab")); - m_print_unix_dialog_get_selected_printer = reinterpret_cast<print_unix_dialog_get_selected_printer_t>(m_aModule.getFunctionSymbol("gtk_print_unix_dialog_get_selected_printer")); - m_print_unix_dialog_set_manual_capabilities = reinterpret_cast<print_unix_dialog_set_manual_capabilities_t>(m_aModule.getFunctionSymbol("gtk_print_unix_dialog_set_manual_capabilities")); - m_print_unix_dialog_get_settings = reinterpret_cast<print_unix_dialog_get_settings_t>(m_aModule.getFunctionSymbol("gtk_print_unix_dialog_get_settings")); - m_print_unix_dialog_set_settings = reinterpret_cast<print_unix_dialog_set_settings_t>(m_aModule.getFunctionSymbol("gtk_print_unix_dialog_set_settings")); - m_print_unix_dialog_set_support_selection = reinterpret_cast<print_unix_dialog_set_support_selection_t>(m_aModule.getFunctionSymbol("gtk_print_unix_dialog_set_support_selection")); - m_print_unix_dialog_set_has_selection = reinterpret_cast<print_unix_dialog_set_has_selection_t>(m_aModule.getFunctionSymbol("gtk_print_unix_dialog_set_has_selection")); -} - -#endif - -bool GtkPrintWrapper::supportsPrinting() const -{ -#if !GTK_CHECK_VERSION(3,0,0) - return - m_page_setup_new - && m_print_job_new - && m_print_job_send - && m_print_job_set_source_file - && m_print_settings_get - && m_print_settings_get_collate - && m_print_settings_set_collate - && m_print_settings_get_n_copies - && m_print_settings_set_n_copies - && m_print_settings_get_page_ranges - && m_print_settings_set_print_pages - && m_print_unix_dialog_new - && m_print_unix_dialog_add_custom_tab - && m_print_unix_dialog_get_selected_printer - && m_print_unix_dialog_set_manual_capabilities - && m_print_unix_dialog_get_settings - && m_print_unix_dialog_set_settings - ; -#else - (void) this; // loplugin:staticmethods - return true; -#endif -} - -bool GtkPrintWrapper::supportsPrintSelection() const -{ -#if !GTK_CHECK_VERSION(3,0,0) - return - supportsPrinting() - && m_print_unix_dialog_set_support_selection - && m_print_unix_dialog_set_has_selection - ; -#else - (void) this; // loplugin:staticmethods - return true; -#endif -} - -GtkPageSetup* GtkPrintWrapper::page_setup_new() const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_page_setup_new); - return (*m_page_setup_new)(); -#else - (void) this; // loplugin:staticmethods - return gtk_page_setup_new(); -#endif -} - -GtkPrintJob* GtkPrintWrapper::print_job_new(const gchar* title, GtkPrinter* printer, GtkPrintSettings* settings, GtkPageSetup* page_setup) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_job_new); - return (*m_print_job_new)(title, printer, settings, page_setup); -#else - (void) this; // loplugin:staticmethods - return gtk_print_job_new(title, printer, settings, page_setup); -#endif -} - -void GtkPrintWrapper::print_job_send(GtkPrintJob* job, GtkPrintJobCompleteFunc callback, gpointer user_data, GDestroyNotify dnotify) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_job_send); - (*m_print_job_send)(job, callback, user_data, dnotify); -#else - (void) this; // loplugin:staticmethods - gtk_print_job_send(job, callback, user_data, dnotify); -#endif -} - -gboolean GtkPrintWrapper::print_job_set_source_file(GtkPrintJob* job, const gchar* filename, GError** error) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_job_set_source_file); - return (*m_print_job_set_source_file)(job, filename, error); -#else - (void) this; // loplugin:staticmethods - return gtk_print_job_set_source_file(job, filename, error); -#endif -} - -const gchar* GtkPrintWrapper::print_settings_get(GtkPrintSettings* settings, const gchar* key) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_settings_get); - return (*m_print_settings_get)(settings, key); -#else - (void) this; // loplugin:staticmethods - return gtk_print_settings_get(settings, key); -#endif -} - -gboolean GtkPrintWrapper::print_settings_get_collate(GtkPrintSettings* settings) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_settings_get_collate); - return (*m_print_settings_get_collate)(settings); -#else - (void) this; // loplugin:staticmethods - return gtk_print_settings_get_collate(settings); -#endif -} - -void GtkPrintWrapper::print_settings_set_collate(GtkPrintSettings* settings, gboolean collate) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_settings_set_collate); - (*m_print_settings_set_collate)(settings, collate); -#else - (void) this; // loplugin:staticmethods - gtk_print_settings_set_collate(settings, collate); -#endif -} - -gint GtkPrintWrapper::print_settings_get_n_copies(GtkPrintSettings* settings) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_settings_get_n_copies); - return (*m_print_settings_get_n_copies)(settings); -#else - (void) this; // loplugin:staticmethods - return gtk_print_settings_get_n_copies(settings); -#endif -} - -void GtkPrintWrapper::print_settings_set_n_copies(GtkPrintSettings* settings, gint num_copies) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_settings_set_n_copies); - (*m_print_settings_set_n_copies)(settings, num_copies); -#else - (void) this; // loplugin:staticmethods - gtk_print_settings_set_n_copies(settings, num_copies); -#endif -} - -GtkPageRange* GtkPrintWrapper::print_settings_get_page_ranges(GtkPrintSettings* settings, gint* num_ranges) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_settings_get_page_ranges); - return (*m_print_settings_get_page_ranges)(settings, num_ranges); -#else - (void) this; // loplugin:staticmethods - return gtk_print_settings_get_page_ranges(settings, num_ranges); -#endif -} - -void GtkPrintWrapper::print_settings_set_print_pages(GtkPrintSettings* settings, GtkPrintPages pages) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_settings_set_print_pages); - (*m_print_settings_set_print_pages)(settings, pages); -#else - (void) this; // loplugin:staticmethods - gtk_print_settings_set_print_pages(settings, pages); -#endif -} - -GtkWidget* GtkPrintWrapper::print_unix_dialog_new() const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_unix_dialog_new); - return (*m_print_unix_dialog_new)(nullptr, nullptr); -#else - (void) this; // loplugin:staticmethods - return gtk_print_unix_dialog_new(nullptr, nullptr); -#endif -} - -void GtkPrintWrapper::print_unix_dialog_add_custom_tab(GtkPrintUnixDialog* dialog, GtkWidget* child, GtkWidget* tab_label) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_unix_dialog_add_custom_tab); - (*m_print_unix_dialog_add_custom_tab)(dialog, child, tab_label); -#else - (void) this; // loplugin:staticmethods - gtk_print_unix_dialog_add_custom_tab(dialog, child, tab_label); -#endif -} - -GtkPrinter* GtkPrintWrapper::print_unix_dialog_get_selected_printer(GtkPrintUnixDialog* dialog) const -{ - GtkPrinter* pRet = nullptr; -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_unix_dialog_get_selected_printer); - pRet = (*m_print_unix_dialog_get_selected_printer)(dialog); -#else - (void) this; // loplugin:staticmethods - pRet = gtk_print_unix_dialog_get_selected_printer(dialog); -#endif - g_object_ref(G_OBJECT(pRet)); - return pRet; -} - -void GtkPrintWrapper::print_unix_dialog_set_manual_capabilities(GtkPrintUnixDialog* dialog, GtkPrintCapabilities capabilities) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_unix_dialog_set_manual_capabilities); - (*m_print_unix_dialog_set_manual_capabilities)(dialog, capabilities); -#else - (void) this; // loplugin:staticmethods - gtk_print_unix_dialog_set_manual_capabilities(dialog, capabilities); -#endif -} - -GtkPrintSettings* GtkPrintWrapper::print_unix_dialog_get_settings(GtkPrintUnixDialog* dialog) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_unix_dialog_get_settings); - return (*m_print_unix_dialog_get_settings)(dialog); -#else - (void) this; // loplugin:staticmethods - return gtk_print_unix_dialog_get_settings(dialog); -#endif -} - -void GtkPrintWrapper::print_unix_dialog_set_settings(GtkPrintUnixDialog* dialog, GtkPrintSettings* settings) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_unix_dialog_set_settings); - (*m_print_unix_dialog_set_settings)(dialog, settings); -#else - (void) this; // loplugin:staticmethods - gtk_print_unix_dialog_set_settings(dialog, settings); -#endif -} - -void GtkPrintWrapper::print_unix_dialog_set_support_selection(GtkPrintUnixDialog* dialog, gboolean support_selection) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_unix_dialog_set_support_selection); - (*m_print_unix_dialog_set_support_selection)(dialog, support_selection); -#else - (void) this; // loplugin:staticmethods - gtk_print_unix_dialog_set_support_selection(dialog, support_selection); -#endif -} - -void GtkPrintWrapper::print_unix_dialog_set_has_selection(GtkPrintUnixDialog* dialog, gboolean has_selection) const -{ -#if !GTK_CHECK_VERSION(3,0,0) - assert(m_print_unix_dialog_set_has_selection); - (*m_print_unix_dialog_set_has_selection)(dialog, has_selection); -#else - (void) this; // loplugin:staticmethods - gtk_print_unix_dialog_set_has_selection(dialog, has_selection); -#endif -} - -} -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/gtksalframe.cxx b/vcl/unx/gtk/gtksalframe.cxx deleted file mode 100644 index 2601d7d675fb..000000000000 --- a/vcl/unx/gtk/gtksalframe.cxx +++ /dev/null @@ -1,3606 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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 <unx/gtk/gtkframe.hxx> -#include <unx/gtk/gtkdata.hxx> -#include <unx/gtk/gtkinst.hxx> -#include <unx/gtk/gtkgdi.hxx> -#include <unx/gtk/gtksalmenu.hxx> -#include <vcl/keycodes.hxx> -#include <vcl/layout.hxx> -#include <unx/wmadaptor.hxx> -#include <unx/sm.hxx> -#include <unx/salbmp.h> -#include <unx/genprn.h> -#include <unx/geninst.h> -#include <headless/svpgdi.hxx> -#include <osl/file.hxx> -#include <rtl/bootstrap.hxx> -#include <rtl/process.h> -#include <sal/log.hxx> -#include <tools/diagnose_ex.h> -#include <vcl/floatwin.hxx> -#include <vcl/svapp.hxx> -#include <vcl/window.hxx> -#include <vcl/settings.hxx> -#include <vcl/opengl/OpenGLHelper.hxx> - -#include <config_gio.h> - -#include <unx/x11/xlimits.hxx> -#if defined ENABLE_GMENU_INTEGRATION // defined in gtksalmenu.hxx above -# include <unx/gtk/hudawareness.h> -#endif - -#include <gtk/gtk.h> -#include <gdk/gdkx.h> -#include <X11/Xatom.h> - -#include <dlfcn.h> -#include <window.h> -#include <strings.hrc> -#include <bitmaps.hlst> -#include <sal/macros.h> - -#include <basegfx/range/b2ibox.hxx> -#include <basegfx/vector/b2ivector.hxx> - -#include <algorithm> -#include <glib/gprintf.h> - -#if OSL_DEBUG_LEVEL > 1 -# include <cstdio> -#endif - -#include <com/sun/star/accessibility/XAccessibleContext.hpp> -#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> - -#include <config_folders.h> - -#define IS_WIDGET_REALIZED GTK_WIDGET_REALIZED -#define IS_WIDGET_MAPPED GTK_WIDGET_MAPPED - -using namespace com::sun::star; - -int GtkSalFrame::m_nFloats = 0; - -#if defined ENABLE_GMENU_INTEGRATION -static GDBusConnection* pSessionBus = nullptr; -#endif - -sal_uInt16 GtkSalFrame::GetKeyModCode( guint state ) -{ - sal_uInt16 nCode = 0; - if( state & GDK_SHIFT_MASK ) - nCode |= KEY_SHIFT; - if( state & GDK_CONTROL_MASK ) - nCode |= KEY_MOD1; - if( state & GDK_MOD1_MASK ) - nCode |= KEY_MOD2; - - // Map Meta/Super keys to MOD3 modifier on all Unix systems - // except macOS - if ( (state & GDK_META_MASK ) || ( state & GDK_SUPER_MASK ) ) - nCode |= KEY_MOD3; - return nCode; -} - -sal_uInt16 GtkSalFrame::GetMouseModCode( guint state ) -{ - sal_uInt16 nCode = GetKeyModCode( state ); - if( state & GDK_BUTTON1_MASK ) - nCode |= MOUSE_LEFT; - if( state & GDK_BUTTON2_MASK ) - nCode |= MOUSE_MIDDLE; - if( state & GDK_BUTTON3_MASK ) - nCode |= MOUSE_RIGHT; - - return nCode; -} - -sal_uInt16 GtkSalFrame::GetKeyCode(guint keyval) -{ - sal_uInt16 nCode = 0; - if( keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9 ) - nCode = KEY_0 + (keyval-GDK_KEY_0); - else if( keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9 ) - nCode = KEY_0 + (keyval-GDK_KEY_KP_0); - else if( keyval >= GDK_KEY_A && keyval <= GDK_KEY_Z ) - nCode = KEY_A + (keyval-GDK_KEY_A ); - else if( keyval >= GDK_KEY_a && keyval <= GDK_KEY_z ) - nCode = KEY_A + (keyval-GDK_KEY_a ); - else if( keyval >= GDK_KEY_F1 && keyval <= GDK_KEY_F26 ) - // KEY_F26 is the last function key known to keycodes.hxx - { - if( GetGtkSalData()->GetGtkDisplay()->IsNumLockFromXS() ) - { - nCode = KEY_F1 + (keyval-GDK_KEY_F1); - } - else - { - switch( keyval ) - { - // - - - - - Sun keyboard, see vcl/unx/source/app/saldisp.cxx - // although GDK_KEY_F1 ... GDK_KEY_L10 are known to - // gdk/gdkkeysyms.h, they are unlikely to be generated - // except possibly by Solaris systems - // this whole section needs review - case GDK_KEY_L2: - if( GetGtkSalData()->GetGtkDisplay()->GetServerVendor() == vendor_sun ) - nCode = KEY_REPEAT; - else - nCode = KEY_F12; - break; - case GDK_KEY_L3: nCode = KEY_PROPERTIES; break; - case GDK_KEY_L4: nCode = KEY_UNDO; break; - case GDK_KEY_L6: nCode = KEY_COPY; break; // KEY_F16 - case GDK_KEY_L8: nCode = KEY_PASTE; break; // KEY_F18 - case GDK_KEY_L10: nCode = KEY_CUT; break; // KEY_F20 - default: - nCode = KEY_F1 + (keyval-GDK_KEY_F1); break; - } - } - } - else - { - switch( keyval ) - { - case GDK_KEY_KP_Down: - case GDK_KEY_Down: nCode = KEY_DOWN; break; - case GDK_KEY_KP_Up: - case GDK_KEY_Up: nCode = KEY_UP; break; - case GDK_KEY_KP_Left: - case GDK_KEY_Left: nCode = KEY_LEFT; break; - case GDK_KEY_KP_Right: - case GDK_KEY_Right: nCode = KEY_RIGHT; break; - case GDK_KEY_KP_Begin: - case GDK_KEY_KP_Home: - case GDK_KEY_Begin: - case GDK_KEY_Home: nCode = KEY_HOME; break; - case GDK_KEY_KP_End: - case GDK_KEY_End: nCode = KEY_END; break; - case GDK_KEY_KP_Page_Up: - case GDK_KEY_Page_Up: nCode = KEY_PAGEUP; break; - case GDK_KEY_KP_Page_Down: - case GDK_KEY_Page_Down: nCode = KEY_PAGEDOWN; break; - case GDK_KEY_KP_Enter: - case GDK_KEY_Return: nCode = KEY_RETURN; break; - case GDK_KEY_Escape: nCode = KEY_ESCAPE; break; - case GDK_KEY_ISO_Left_Tab: - case GDK_KEY_KP_Tab: - case GDK_KEY_Tab: nCode = KEY_TAB; break; - case GDK_KEY_BackSpace: nCode = KEY_BACKSPACE; break; - case GDK_KEY_KP_Space: - case GDK_KEY_space: nCode = KEY_SPACE; break; - case GDK_KEY_KP_Insert: - case GDK_KEY_Insert: nCode = KEY_INSERT; break; - case GDK_KEY_KP_Delete: - case GDK_KEY_Delete: nCode = KEY_DELETE; break; - case GDK_KEY_plus: - case GDK_KEY_KP_Add: nCode = KEY_ADD; break; - case GDK_KEY_minus: - case GDK_KEY_KP_Subtract: nCode = KEY_SUBTRACT; break; - case GDK_KEY_asterisk: - case GDK_KEY_KP_Multiply: nCode = KEY_MULTIPLY; break; - case GDK_KEY_slash: - case GDK_KEY_KP_Divide: nCode = KEY_DIVIDE; break; - case GDK_KEY_period: nCode = KEY_POINT; break; - case GDK_KEY_decimalpoint: nCode = KEY_POINT; break; - case GDK_KEY_comma: nCode = KEY_COMMA; break; - case GDK_KEY_less: nCode = KEY_LESS; break; - case GDK_KEY_greater: nCode = KEY_GREATER; break; - case GDK_KEY_KP_Equal: - case GDK_KEY_equal: nCode = KEY_EQUAL; break; - case GDK_KEY_Find: nCode = KEY_FIND; break; - case GDK_KEY_Menu: nCode = KEY_CONTEXTMENU;break; - case GDK_KEY_Help: nCode = KEY_HELP; break; - case GDK_KEY_Undo: nCode = KEY_UNDO; break; - case GDK_KEY_Redo: nCode = KEY_REPEAT; break; - // on a sun keyboard this actually is usually SunXK_Stop = 0x0000FF69 (XK_Cancel), - // but VCL doesn't have a key definition for that - case GDK_KEY_Cancel: nCode = KEY_F11; break; - case GDK_KEY_KP_Decimal: - case GDK_KEY_KP_Separator: nCode = KEY_DECIMAL; break; - case GDK_KEY_asciitilde: nCode = KEY_TILDE; break; - case GDK_KEY_leftsinglequotemark: - case GDK_KEY_quoteleft: nCode = KEY_QUOTELEFT; break; - case GDK_KEY_bracketleft: nCode = KEY_BRACKETLEFT; break; - case GDK_KEY_bracketright: nCode = KEY_BRACKETRIGHT; break; - case GDK_KEY_semicolon: nCode = KEY_SEMICOLON; break; - case GDK_KEY_quoteright: nCode = KEY_QUOTERIGHT; break; - // some special cases, also see saldisp.cxx - // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000 - // These can be found in ap_keysym.h - case 0x1000FF02: // apXK_Copy - nCode = KEY_COPY; - break; - case 0x1000FF03: // apXK_Cut - nCode = KEY_CUT; - break; - case 0x1000FF04: // apXK_Paste - nCode = KEY_PASTE; - break; - case 0x1000FF14: // apXK_Repeat - nCode = KEY_REPEAT; - break; - // Exit, Save - // - - - - - - - - - - - - - - D E C - - - - - - - - - - - - - 0x1000 - // These can be found in DECkeysym.h - case 0x1000FF00: - nCode = KEY_DELETE; - break; - // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000 - // These can be found in HPkeysym.h - case 0x1000FF73: // hpXK_DeleteChar - nCode = KEY_DELETE; - break; - case 0x1000FF74: // hpXK_BackTab - case 0x1000FF75: // hpXK_KP_BackTab - nCode = KEY_TAB; - break; - // - - - - - - - - - - - - - - I B M - - - - - - - - - - - - - - // - - - - - - - - - - - - - - O S F - - - - - - - - - - - - - 0x1004 - // These also can be found in HPkeysym.h - case 0x1004FF02: // osfXK_Copy - nCode = KEY_COPY; - break; - case 0x1004FF03: // osfXK_Cut - nCode = KEY_CUT; - break; - case 0x1004FF04: // osfXK_Paste - nCode = KEY_PASTE; - break; - case 0x1004FF07: // osfXK_BackTab - nCode = KEY_TAB; - break; - case 0x1004FF08: // osfXK_BackSpace - nCode = KEY_BACKSPACE; - break; - case 0x1004FF1B: // osfXK_Escape - nCode = KEY_ESCAPE; - break; - // Up, Down, Left, Right, PageUp, PageDown - // - - - - - - - - - - - - - - S C O - - - - - - - - - - - - - - // - - - - - - - - - - - - - - S G I - - - - - - - - - - - - - 0x1007 - // - - - - - - - - - - - - - - S N I - - - - - - - - - - - - - - // - - - - - - - - - - - - - - S U N - - - - - - - - - - - - - 0x1005 - // These can be found in Sunkeysym.h - case 0x1005FF10: // SunXK_F36 - nCode = KEY_F11; - break; - case 0x1005FF11: // SunXK_F37 - nCode = KEY_F12; - break; - case 0x1005FF70: // SunXK_Props - nCode = KEY_PROPERTIES; - break; - case 0x1005FF71: // SunXK_Front - nCode = KEY_FRONT; - break; - case 0x1005FF72: // SunXK_Copy - nCode = KEY_COPY; - break; - case 0x1005FF73: // SunXK_Open - nCode = KEY_OPEN; - break; - case 0x1005FF74: // SunXK_Paste - nCode = KEY_PASTE; - break; - case 0x1005FF75: // SunXK_Cut - nCode = KEY_CUT; - break; - // - - - - - - - - - - - - - X F 8 6 - - - - - - - - - - - - - 0x1008 - // These can be found in XF86keysym.h - // but more importantly they are also available GTK/Gdk version 3 - // and hence are already provided in gdk/gdkkeysyms.h, and hence - // in gdk/gdk.h - case GDK_KEY_Copy: nCode = KEY_COPY; break; // 0x1008ff57 - case GDK_KEY_Cut: nCode = KEY_CUT; break; // 0x1008ff58 - case GDK_KEY_Open: nCode = KEY_OPEN; break; // 0x1008ff6b - case GDK_KEY_Paste: nCode = KEY_PASTE; break; // 0x1008ff6d - } - } - - return nCode; -} - -guint GtkSalFrame::GetKeyValFor(GdkKeymap* pKeyMap, guint16 hardware_keycode, guint8 group) -{ - guint updated_keyval = 0; - gdk_keymap_translate_keyboard_state(pKeyMap, hardware_keycode, - GdkModifierType(0), group, &updated_keyval, nullptr, nullptr, nullptr); - return updated_keyval; -} - -// F10 means either KEY_F10 or KEY_MENU, which has to be decided -// in the independent part. -struct KeyAlternate -{ - sal_uInt16 nKeyCode; - sal_Unicode nCharCode; - KeyAlternate() : nKeyCode( 0 ), nCharCode( 0 ) {} - KeyAlternate( sal_uInt16 nKey, sal_Unicode nChar = 0 ) : nKeyCode( nKey ), nCharCode( nChar ) {} -}; - -static KeyAlternate -GetAlternateKeyCode( const sal_uInt16 nKeyCode ) -{ - KeyAlternate aAlternate; - - switch( nKeyCode ) - { - case KEY_F10: aAlternate = KeyAlternate( KEY_MENU );break; - case KEY_F24: aAlternate = KeyAlternate( KEY_SUBTRACT, '-' );break; - } - - return aAlternate; -} - -bool GtkSalFrame::doKeyCallback( guint state, - guint keyval, - guint16 hardware_keycode, - guint8 group, - sal_Unicode aOrigCode, - bool bDown, - bool bSendRelease - ) -{ - SalKeyEvent aEvent; - - aEvent.mnCharCode = aOrigCode; - aEvent.mnRepeat = 0; - - vcl::DeletionListener aDel( this ); - - /* - * #i42122# translate all keys with Ctrl and/or Alt to group 0 else - * shortcuts (e.g. Ctrl-o) will not work but be inserted by the - * application - * - * #i52338# do this for all keys that the independent part has no key code - * for - * - * fdo#41169 rather than use group 0, detect if there is a group which can - * be used to input Latin text and use that if possible - */ - aEvent.mnCode = GetKeyCode( keyval ); - if( aEvent.mnCode == 0 ) - { - gint best_group = SAL_MAX_INT32; - - // Try and find Latin layout - GdkKeymap* keymap = gdk_keymap_get_default(); - GdkKeymapKey *keys; - gint n_keys; - if (gdk_keymap_get_entries_for_keyval(keymap, GDK_KEY_A, &keys, &n_keys)) - { - // Find the lowest group that supports Latin layout - for (gint i = 0; i < n_keys; ++i) - { - if (keys[i].level != 0 && keys[i].level != 1) - continue; - best_group = std::min(best_group, keys[i].group); - if (best_group == 0) - break; - } - g_free(keys); - } - - //Unavailable, go with original group then I suppose - if (best_group == SAL_MAX_INT32) - best_group = group; - - guint updated_keyval = GetKeyValFor(keymap, hardware_keycode, best_group); - aEvent.mnCode = GetKeyCode(updated_keyval); - } - - aEvent.mnCode |= GetKeyModCode( state ); - - if( bDown ) - { - bool bHandled = CallCallback( SalEvent::KeyInput, &aEvent ); - // #i46889# copy AlternateKeyCode handling from generic plugin - if( ! bHandled ) - { - KeyAlternate aAlternate = GetAlternateKeyCode( aEvent.mnCode ); - if( aAlternate.nKeyCode ) - { - aEvent.mnCode = aAlternate.nKeyCode; - if( aAlternate.nCharCode ) - aEvent.mnCharCode = aAlternate.nCharCode; - CallCallback( SalEvent::KeyInput, &aEvent ); - } - } - if( bSendRelease && ! aDel.isDeleted() ) - { - CallCallback( SalEvent::KeyUp, &aEvent ); - } - } - else - CallCallback( SalEvent::KeyUp, &aEvent ); - - return false; -} - -GtkSalFrame::GtkSalFrame( SalFrame* pParent, SalFrameStyleFlags nStyle ) - : m_nXScreen( getDisplay()->GetDefaultXScreen() ) - , m_bGraphics(false) -{ - getDisplay()->registerFrame( this ); - m_bDefaultPos = true; - m_bDefaultSize = ( (nStyle & SalFrameStyleFlags::SIZEABLE) && ! pParent ); - m_bWindowIsGtkPlug = false; - Init( pParent, nStyle ); -} - -GtkSalFrame::GtkSalFrame( SystemParentData* pSysData ) - : m_nXScreen( getDisplay()->GetDefaultXScreen() ) - , m_bGraphics(false) -{ - getDisplay()->registerFrame( this ); - // permanently ignore errors from our unruly children ... - GetGenericUnixSalData()->ErrorTrapPush(); - m_bDefaultPos = true; - m_bDefaultSize = true; - Init( pSysData ); -} - -#ifdef ENABLE_GMENU_INTEGRATION - -static void -gdk_x11_window_set_utf8_property (GdkWindow *window, - const gchar *name, - const gchar *value) -{ - GdkDisplay* display = gdk_window_get_display (window); - - if (value != nullptr) - { - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_WINDOW_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, name), - gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, - PropModeReplace, reinterpret_cast<guchar const *>(value), strlen (value)); - } - else - { - XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_WINDOW_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, name)); - } -} - -// AppMenu watch functions. - -static void ObjectDestroyedNotify( gpointer data ) -{ - if ( data ) { - g_object_unref( data ); - } -} - -static void hud_activated( gboolean hud_active, gpointer user_data ) -{ - if ( hud_active ) - { - SolarMutexGuard aGuard; - GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data ); - GtkSalMenu* pSalMenu = reinterpret_cast< GtkSalMenu* >( pSalFrame->GetMenu() ); - - if ( pSalMenu ) - pSalMenu->UpdateFull(); - } -} - -static gboolean ensure_dbus_setup( gpointer data ) -{ - GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( data ); - GdkWindow* gdkWindow = widget_get_window( pSalFrame->getWindow() ); - - if ( gdkWindow != nullptr && g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) == nullptr ) - { - // Get a DBus session connection. - if(!pSessionBus) - pSessionBus = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); - if( !pSessionBus ) - { - return FALSE; - } - - // Create menu model and action group attached to this frame. - GMenuModel* pMenuModel = G_MENU_MODEL( g_lo_menu_new() ); - GActionGroup* pActionGroup = reinterpret_cast<GActionGroup*>(g_lo_action_group_new()); - - // Generate menu paths. - ::Window windowId = GDK_WINDOW_XID( gdkWindow ); - gchar* aDBusWindowPath = g_strdup_printf( "/org/libreoffice/window/%lu", windowId ); - gchar* aDBusMenubarPath = g_strdup_printf( "/org/libreoffice/window/%lu/menus/menubar", windowId ); - - // Set window properties. - g_object_set_data_full( G_OBJECT( gdkWindow ), "g-lo-menubar", pMenuModel, ObjectDestroyedNotify ); - g_object_set_data_full( G_OBJECT( gdkWindow ), "g-lo-action-group", pActionGroup, ObjectDestroyedNotify ); - - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_ID", "org.libreoffice" ); - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_UNIQUE_BUS_NAME", g_dbus_connection_get_unique_name( pSessionBus ) ); - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_OBJECT_PATH", "/org/libreoffice" ); - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_WINDOW_OBJECT_PATH", aDBusWindowPath ); - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_MENUBAR_OBJECT_PATH", aDBusMenubarPath ); - - // Publish the menu model and the action group. - SAL_INFO("vcl.unity", "exporting menu model at " << pMenuModel << " for window " << windowId); - pSalFrame->m_nMenuExportId = g_dbus_connection_export_menu_model (pSessionBus, aDBusMenubarPath, pMenuModel, nullptr); - SAL_INFO("vcl.unity", "exporting action group at " << pActionGroup << " for window " << windowId); - pSalFrame->m_nActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, aDBusWindowPath, pActionGroup, nullptr); - pSalFrame->m_nHudAwarenessId = hud_awareness_register( pSessionBus, aDBusMenubarPath, hud_activated, pSalFrame, nullptr, nullptr ); - - g_free( aDBusWindowPath ); - g_free( aDBusMenubarPath ); - } - - return FALSE; -} - -void on_registrar_available( GDBusConnection * /*connection*/, - const gchar * /*name*/, - const gchar * /*name_owner*/, - gpointer user_data ) -{ - SolarMutexGuard aGuard; - - GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data ); - - SalMenu* pSalMenu = pSalFrame->GetMenu(); - - if ( pSalMenu != nullptr ) - { - GtkSalMenu* pGtkSalMenu = static_cast<GtkSalMenu*>(pSalMenu); - pGtkSalMenu->EnableUnity(true); - } -} - -// This is called when the registrar becomes unavailable. It shows the menubar. -void on_registrar_unavailable( GDBusConnection * /*connection*/, - const gchar * /*name*/, - gpointer user_data ) -{ - SolarMutexGuard aGuard; - - SAL_INFO("vcl.unity", "on_registrar_unavailable"); - - //pSessionBus = NULL; - GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data ); - - SalMenu* pSalMenu = pSalFrame->GetMenu(); - - if ( pSalMenu ) { - GtkSalMenu* pGtkSalMenu = static_cast< GtkSalMenu* >( pSalMenu ); - pGtkSalMenu->EnableUnity(false); - } -} -#endif - -void GtkSalFrame::EnsureAppMenuWatch() -{ -#ifdef ENABLE_GMENU_INTEGRATION - if ( !m_nWatcherId ) - { - // Get a DBus session connection. - if ( pSessionBus == nullptr ) - { - pSessionBus = g_bus_get_sync( G_BUS_TYPE_SESSION, nullptr, nullptr ); - - if ( pSessionBus == nullptr ) - return; - } - - // Publish the menu only if AppMenu registrar is available. - m_nWatcherId = g_bus_watch_name_on_connection( pSessionBus, - "com.canonical.AppMenu.Registrar", - G_BUS_NAME_WATCHER_FLAGS_NONE, - on_registrar_available, - on_registrar_unavailable, - this, - nullptr ); - } -#else - (void) this; // loplugin:staticmethods -#endif -} - -void GtkSalFrame::InvalidateGraphics() -{ - if( m_pGraphics ) - { - m_pGraphics->DeInit(); - m_pGraphics->SetWindow(nullptr); - m_pGraphics.reset(); - } -} - -GtkSalFrame::~GtkSalFrame() -{ - InvalidateGraphics(); - - if( m_pParent ) - m_pParent->m_aChildren.remove( this ); - - getDisplay()->deregisterFrame( this ); - - if( m_pRegion ) - { - gdk_region_destroy( m_pRegion ); - } - - m_pIMHandler.reset(); - - GtkWidget *pEventWidget = getMouseEventWidget(); - for (auto handler_id : m_aMouseSignalIds) - g_signal_handler_disconnect(G_OBJECT(pEventWidget), handler_id); - if( m_pFixedContainer ) - gtk_widget_destroy( GTK_WIDGET( m_pFixedContainer ) ); - if( m_pEventBox ) - gtk_widget_destroy( GTK_WIDGET(m_pEventBox) ); - { - SolarMutexGuard aGuard; -#if defined ENABLE_GMENU_INTEGRATION - if(m_nWatcherId) - g_bus_unwatch_name(m_nWatcherId); -#endif - if( m_pWindow ) - { - g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", nullptr ); - -#if defined ENABLE_GMENU_INTEGRATION - if ( pSessionBus ) - { - if ( m_nHudAwarenessId ) - hud_awareness_unregister( pSessionBus, m_nHudAwarenessId ); - if ( m_nMenuExportId ) - g_dbus_connection_unexport_menu_model( pSessionBus, m_nMenuExportId ); - if ( m_nActionGroupExportId ) - g_dbus_connection_unexport_action_group( pSessionBus, m_nActionGroupExportId ); - } -#endif - gtk_widget_destroy( m_pWindow ); - } - } - if( m_pForeignParent ) - g_object_unref( G_OBJECT( m_pForeignParent ) ); - if( m_pForeignTopLevel ) - g_object_unref( G_OBJECT( m_pForeignTopLevel) ); -} - -void GtkSalFrame::moveWindow( long nX, long nY ) -{ - if( isChild( false ) ) - { - if( m_pParent ) - gtk_fixed_move( m_pParent->getFixedContainer(), - m_pWindow, - nX - m_pParent->maGeometry.nX, nY - m_pParent->maGeometry.nY ); - } - else - gtk_window_move( GTK_WINDOW(m_pWindow), nX, nY ); -} - -void GtkSalFrame::widget_set_size_request(long nWidth, long nHeight) -{ - gtk_widget_set_size_request(m_pWindow, nWidth, nHeight ); -} - -void GtkSalFrame::window_resize(long nWidth, long nHeight) -{ - gtk_window_resize(GTK_WINDOW(m_pWindow), nWidth, nHeight); -} - -void GtkSalFrame::resizeWindow( long nWidth, long nHeight ) -{ - if( isChild( false ) ) - { - widget_set_size_request(nWidth, nHeight); - } - else if( ! isChild( true, false ) ) - window_resize(nWidth, nHeight); -} - -// tdf#124694 GtkFixed takes the max size of all its children as its -// preferred size, causing it to not clip its child, but grow instead. - -static void -ooo_fixed_size_request(GtkWidget*, GtkRequisition* req) -{ - req->width = 0; - req->height = 0; -} - -static void -ooo_fixed_class_init(GtkFixedClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - widget_class->size_request = ooo_fixed_size_request; -} - -/* - * Always use a sub-class of GtkFixed we can tag for a11y. This allows us to - * utilize GAIL for the toplevel window and toolkit implementation incl. - * key event listener support .. - */ - -GType -ooo_fixed_get_type() -{ - static GType type = 0; - - if (!type) { - static const GTypeInfo tinfo = - { - sizeof (GtkFixedClass), - nullptr, /* base init */ - nullptr, /* base finalize */ - reinterpret_cast<GClassInitFunc>(ooo_fixed_class_init), /* class init */ - nullptr, /* class finalize */ - nullptr, /* class data */ - sizeof (GtkFixed), /* instance size */ - 0, /* nb preallocs */ - nullptr, /* instance init */ - nullptr /* value table */ - }; - - type = g_type_register_static( GTK_TYPE_FIXED, "OOoFixed", - &tinfo, GTypeFlags(0)); - } - - return type; -} - -void GtkSalFrame::updateScreenNumber() -{ - int nScreen = 0; - GdkScreen *pScreen = gtk_widget_get_screen( m_pWindow ); - if( pScreen ) - nScreen = getDisplay()->getSystem()->getScreenMonitorIdx( pScreen, maGeometry.nX, maGeometry.nY ); - maGeometry.nDisplayScreenNumber = nScreen; -} - -GtkWidget *GtkSalFrame::getMouseEventWidget() const -{ - return m_pWindow; -} - -void GtkSalFrame::InitCommon() -{ - m_pEventBox = nullptr; - // add the fixed container child, - // fixed is needed since we have to position plugin windows - m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), nullptr )); - gtk_container_add( GTK_CONTAINER(m_pWindow), GTK_WIDGET(m_pFixedContainer) ); - - GtkWidget *pEventWidget = getMouseEventWidget(); - - gtk_widget_set_app_paintable(GTK_WIDGET(m_pFixedContainer), true); - gtk_widget_set_double_buffered(GTK_WIDGET(m_pFixedContainer), false); - gtk_widget_set_redraw_on_allocate(GTK_WIDGET(m_pFixedContainer), false); - - - // connect signals - g_signal_connect( G_OBJECT(m_pWindow), "style-set", G_CALLBACK(signalStyleSet), this ); - m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-press-event", G_CALLBACK(signalButton), this )); - m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "motion-notify-event", G_CALLBACK(signalMotion), this )); - m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-release-event", G_CALLBACK(signalButton), this )); - g_signal_connect( G_OBJECT(m_pFixedContainer), "expose-event", G_CALLBACK(signalExpose), this ); - g_signal_connect( G_OBJECT(m_pWindow), "focus-in-event", G_CALLBACK(signalFocus), this ); - g_signal_connect( G_OBJECT(m_pWindow), "focus-out-event", G_CALLBACK(signalFocus), this ); - g_signal_connect( G_OBJECT(m_pWindow), "map-event", G_CALLBACK(signalMap), this ); - g_signal_connect( G_OBJECT(m_pWindow), "unmap-event", G_CALLBACK(signalUnmap), this ); - g_signal_connect( G_OBJECT(m_pWindow), "configure-event", G_CALLBACK(signalConfigure), this ); - g_signal_connect( G_OBJECT(m_pWindow), "key-press-event", G_CALLBACK(signalKey), this ); - g_signal_connect( G_OBJECT(m_pWindow), "key-release-event", G_CALLBACK(signalKey), this ); - g_signal_connect( G_OBJECT(m_pWindow), "delete-event", G_CALLBACK(signalDelete), this ); - g_signal_connect( G_OBJECT(m_pWindow), "window-state-event", G_CALLBACK(signalWindowState), this ); - g_signal_connect( G_OBJECT(m_pWindow), "scroll-event", G_CALLBACK(signalScroll), this ); - g_signal_connect( G_OBJECT(m_pWindow), "leave-notify-event", G_CALLBACK(signalCrossing), this ); - g_signal_connect( G_OBJECT(m_pWindow), "enter-notify-event", G_CALLBACK(signalCrossing), this ); - g_signal_connect( G_OBJECT(m_pWindow), "visibility-notify-event", G_CALLBACK(signalVisibility), this ); - g_signal_connect( G_OBJECT(m_pWindow), "destroy", G_CALLBACK(signalDestroy), this ); - - // init members - m_pCurrentCursor = nullptr; - m_nKeyModifiers = ModKeyFlags::NONE; - m_bFullscreen = false; - m_bSpanMonitorsWhenFullscreen = false; - m_nState = GDK_WINDOW_STATE_WITHDRAWN; - m_pIMHandler = nullptr; - m_pRegion = nullptr; - m_ePointerStyle = static_cast<PointerStyle>(0xffff); - m_bSetFocusOnMap = false; - m_pSalMenu = nullptr; - m_nWatcherId = 0; - m_nMenuExportId = 0; - m_nActionGroupExportId = 0; - m_nHudAwarenessId = 0; - - gtk_widget_add_events( m_pWindow, - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | - GDK_VISIBILITY_NOTIFY_MASK | GDK_SCROLL_MASK - ); - - // show the widgets - gtk_widget_show_all( GTK_WIDGET(m_pFixedContainer) ); - - // realize the window, we need an XWindow id - gtk_widget_realize( m_pWindow ); - - //system data - m_aSystemData.nSize = sizeof( SystemEnvData ); - GtkSalDisplay* pDisp = GetGtkSalData()->GetGtkDisplay(); - m_aSystemData.pDisplay = pDisp->GetDisplay(); - m_aSystemData.pVisual = pDisp->GetVisual( m_nXScreen ).GetVisual(); - m_aSystemData.aWindow = widget_get_xid(m_pWindow); - m_aSystemData.aShellWindow = m_aSystemData.aWindow; - m_aSystemData.pSalFrame = this; - m_aSystemData.pWidget = m_pWindow; - m_aSystemData.nScreen = m_nXScreen.getXScreen(); - m_aSystemData.pToolkit = "gtk2"; - m_aSystemData.pPlatformName = "xcb"; - - m_bGraphics = false; - m_pGraphics = nullptr; - - // fake an initial geometry, gets updated via configure event or SetPosSize - if( m_bDefaultPos || m_bDefaultSize ) - { - Size aDefSize = calcDefaultSize(); - maGeometry.nX = -1; - maGeometry.nY = -1; - maGeometry.nWidth = aDefSize.Width(); - maGeometry.nHeight = aDefSize.Height(); - if( m_pParent ) - { - // approximation - maGeometry.nTopDecoration = m_pParent->maGeometry.nTopDecoration; - maGeometry.nBottomDecoration = m_pParent->maGeometry.nBottomDecoration; - maGeometry.nLeftDecoration = m_pParent->maGeometry.nLeftDecoration; - maGeometry.nRightDecoration = m_pParent->maGeometry.nRightDecoration; - } - else - { - maGeometry.nTopDecoration = 0; - maGeometry.nBottomDecoration = 0; - maGeometry.nLeftDecoration = 0; - maGeometry.nRightDecoration = 0; - } - } - else - { - resizeWindow( maGeometry.nWidth, maGeometry.nHeight ); - moveWindow( maGeometry.nX, maGeometry.nY ); - } - updateScreenNumber(); - - SetIcon(SV_ICON_ID_OFFICE); - - m_nWorkArea = pDisp->getWMAdaptor()->getCurrentWorkArea(); - /* #i64117# gtk sets a nice background pixmap - * but we actually don't really want that, so save - * some time on the Xserver as well as prevent - * some paint issues - */ - XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(), - widget_get_xid(m_pWindow), - None ); -} - -static void lcl_set_accept_focus( GtkWindow* pWindow ) -{ - if (GetGtkSalData()->GetGtkDisplay()->getWMAdaptor()->getWindowManagerName().startsWith("Metacity")) - { - /* Metacity considers a toolbar type window as should not - * have focus on mapping, yet it believes it should unfocus - * the parent window... So convince Metacity to not do so, - * by disabling the focus until the window is mapped. We - * will restore the focus later in the map signal. - */ - gtk_window_set_accept_focus( pWindow, false ); - - /* remove WM_TAKE_FOCUS protocol; this would usually be the - * right thing, but gtk handles it internally whereas we - * want to handle it ourselves (as to sometimes not get - * the focus) - */ - Display* pDisplay = GetGtkSalData()->GetGtkDisplay()->GetDisplay(); - ::Window aWindow = widget_get_xid(GTK_WIDGET(pWindow)); - Atom* pProtocols = nullptr; - int nProtocols = 0; - XGetWMProtocols( pDisplay, - aWindow, - &pProtocols, &nProtocols ); - if( pProtocols ) - { - bool bSet = false; - Atom nTakeFocus = XInternAtom( pDisplay, "WM_TAKE_FOCUS", True ); - if( nTakeFocus ) - { - for( int i = 0; i < nProtocols; i++ ) - { - if( pProtocols[i] == nTakeFocus ) - { - for( int n = i; n < nProtocols-1; n++ ) - pProtocols[n] = pProtocols[n+1]; - nProtocols--; - i--; - bSet = true; - } - } - } - if( bSet ) - XSetWMProtocols( pDisplay, aWindow, pProtocols, nProtocols ); - XFree( pProtocols ); - } - } - else - { - // Only needed for Compiz. The toolbar type hint seems to be enough for other WMs. - gtk_window_set_focus_on_map( pWindow, false ); - } -} - -GtkSalFrame *GtkSalFrame::getFromWindow( GtkWindow *pWindow ) -{ - return static_cast<GtkSalFrame *>(g_object_get_data( G_OBJECT( pWindow ), "SalFrame" )); -} - -void GtkSalFrame::Init( SalFrame* pParent, SalFrameStyleFlags nStyle ) -{ - if( nStyle & SalFrameStyleFlags::DEFAULT ) // ensure default style - { - nStyle |= SalFrameStyleFlags::MOVEABLE | SalFrameStyleFlags::SIZEABLE | SalFrameStyleFlags::CLOSEABLE; - nStyle &= ~SalFrameStyleFlags::FLOAT; - } - - m_pParent = static_cast<GtkSalFrame*>(pParent); - m_pForeignParent = nullptr; - m_aForeignParentWindow = None; - m_pForeignTopLevel = nullptr; - m_aForeignTopLevelWindow = None; - m_nStyle = nStyle; - - GtkWindowType eWinType = ( (nStyle & SalFrameStyleFlags::FLOAT) && - ! (nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION) - ) - ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL; - - if( nStyle & SalFrameStyleFlags::SYSTEMCHILD ) - { - m_pWindow = gtk_event_box_new(); - if( m_pParent ) - { - // insert into container - gtk_fixed_put( m_pParent->getFixedContainer(), - m_pWindow, 0, 0 ); - - } - } - else - { - m_pWindow = gtk_widget_new( GTK_TYPE_WINDOW, "type", eWinType, - "visible", FALSE, nullptr ); - } - g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", this ); - g_object_set_data( G_OBJECT( m_pWindow ), "libo-version", const_cast<char *>(LIBO_VERSION_DOTTED)); - - // force wm class hint - if (!isChild()) - { - if (m_pParent) - m_sWMClass = m_pParent->m_sWMClass; - updateWMClass(); - } - - if( m_pParent && m_pParent->m_pWindow && ! isChild() ) - gtk_window_set_screen( GTK_WINDOW(m_pWindow), gtk_window_get_screen( GTK_WINDOW(m_pParent->m_pWindow) ) ); - - if (m_pParent) - { - if (!(m_pParent->m_nStyle & SalFrameStyleFlags::PLUG)) - gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), GTK_WINDOW(m_pParent->m_pWindow) ); - m_pParent->m_aChildren.push_back( this ); - } - - InitCommon(); - - // set window type - bool bDecoHandling = - ! isChild() && - ( ! (nStyle & SalFrameStyleFlags::FLOAT) || - (nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION) ); - - if( bDecoHandling ) - { - GdkWindowTypeHint eType = GDK_WINDOW_TYPE_HINT_NORMAL; - if( (nStyle & SalFrameStyleFlags::DIALOG) && m_pParent != nullptr ) - eType = GDK_WINDOW_TYPE_HINT_DIALOG; - if( nStyle & SalFrameStyleFlags::INTRO ) - { - gtk_window_set_role( GTK_WINDOW(m_pWindow), "splashscreen" ); - eType = GDK_WINDOW_TYPE_HINT_SPLASHSCREEN; - } - else if( nStyle & SalFrameStyleFlags::TOOLWINDOW ) - { - eType = GDK_WINDOW_TYPE_HINT_UTILITY; - gtk_window_set_skip_taskbar_hint( GTK_WINDOW(m_pWindow), true ); - } - else if( nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION ) - { - eType = GDK_WINDOW_TYPE_HINT_TOOLBAR; - lcl_set_accept_focus( GTK_WINDOW(m_pWindow) ); - gtk_window_set_decorated( GTK_WINDOW(m_pWindow), false ); - } - if( (nStyle & SalFrameStyleFlags::PARTIAL_FULLSCREEN ) - && getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) - { - eType = GDK_WINDOW_TYPE_HINT_TOOLBAR; - gtk_window_set_keep_above( GTK_WINDOW(m_pWindow), true ); - } - gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), eType ); - gtk_window_set_gravity( GTK_WINDOW(m_pWindow), GDK_GRAVITY_STATIC ); - gtk_window_set_resizable( GTK_WINDOW(m_pWindow), bool(nStyle & SalFrameStyleFlags::SIZEABLE) ); - } - else if( nStyle & SalFrameStyleFlags::FLOAT ) - gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), GDK_WINDOW_TYPE_HINT_POPUP_MENU ); - -#ifdef ENABLE_GMENU_INTEGRATION - if( eWinType == GTK_WINDOW_TOPLEVEL ) - { - // Enable DBus native menu if available. - ensure_dbus_setup( this ); - } -#endif -} - -GdkNativeWindow GtkSalFrame::findTopLevelSystemWindow(GdkNativeWindow aWindow) -{ - ::Window aRoot, aParent; - ::Window* pChildren; - unsigned int nChildren; - bool bBreak = false; - do - { - pChildren = nullptr; - nChildren = 0; - aParent = aRoot = None; - XQueryTree( getDisplay()->GetDisplay(), aWindow, - &aRoot, &aParent, &pChildren, &nChildren ); - XFree( pChildren ); - if( aParent != aRoot ) - aWindow = aParent; - int nCount = 0; - Atom* pProps = XListProperties( getDisplay()->GetDisplay(), - aWindow, - &nCount ); - for( int i = 0; i < nCount && ! bBreak; ++i ) - bBreak = (pProps[i] == XA_WM_HINTS); - if( pProps ) - XFree( pProps ); - } while( aParent != aRoot && ! bBreak ); - - return aWindow; -} - -void GtkSalFrame::Init( SystemParentData* pSysData ) -{ - m_pParent = nullptr; - m_aForeignParentWindow = pSysData->aWindow; - m_pForeignParent = nullptr; - m_aForeignTopLevelWindow = findTopLevelSystemWindow(pSysData->aWindow); - m_pForeignTopLevel = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignTopLevelWindow ); - gdk_window_set_events( m_pForeignTopLevel, GDK_STRUCTURE_MASK ); - - if( pSysData->nSize > sizeof(pSysData->nSize)+sizeof(pSysData->aWindow) && pSysData->bXEmbedSupport ) - { - m_pWindow = gtk_plug_new( pSysData->aWindow ); - m_bWindowIsGtkPlug = true; - widget_set_can_default( m_pWindow, true ); - widget_set_can_focus( m_pWindow, true ); - gtk_widget_set_sensitive( m_pWindow, true ); - } - else - { - m_pWindow = gtk_window_new( GTK_WINDOW_POPUP ); - m_bWindowIsGtkPlug = false; - } - m_nStyle = SalFrameStyleFlags::PLUG; - InitCommon(); - - m_pForeignParent = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignParentWindow ); - gdk_window_set_events( m_pForeignParent, GDK_STRUCTURE_MASK ); - - int x_ret, y_ret; - unsigned int w, h, bw, d; - ::Window aRoot; - XGetGeometry( getDisplay()->GetDisplay(), pSysData->aWindow, - &aRoot, &x_ret, &y_ret, &w, &h, &bw, &d ); - maGeometry.nWidth = w; - maGeometry.nHeight = h; - window_resize(w, h); - gtk_window_move( GTK_WINDOW(m_pWindow), 0, 0 ); - if( ! m_bWindowIsGtkPlug ) - { - XReparentWindow( getDisplay()->GetDisplay(), - widget_get_xid(m_pWindow), - static_cast<::Window>(pSysData->aWindow), - 0, 0 ); - } -} - -void GtkSalFrame::askForXEmbedFocus( sal_Int32 i_nTimeCode ) -{ - XEvent aEvent; - - memset( &aEvent, 0, sizeof(aEvent) ); - aEvent.xclient.window = m_aForeignParentWindow; - aEvent.xclient.type = ClientMessage; - aEvent.xclient.message_type = getDisplay()->getWMAdaptor()->getAtom( vcl_sal::WMAdaptor::XEMBED ); - aEvent.xclient.format = 32; - aEvent.xclient.data.l[0] = i_nTimeCode ? i_nTimeCode : CurrentTime; - aEvent.xclient.data.l[1] = 3; // XEMBED_REQUEST_FOCUS - aEvent.xclient.data.l[2] = 0; - aEvent.xclient.data.l[3] = 0; - aEvent.xclient.data.l[4] = 0; - - GetGenericUnixSalData()->ErrorTrapPush(); - XSendEvent( getDisplay()->GetDisplay(), - m_aForeignParentWindow, - False, NoEventMask, &aEvent ); - GetGenericUnixSalData()->ErrorTrapPop(); -} - -void GtkSalFrame::SetExtendedFrameStyle(SalExtStyle) -{ -} - -SalGraphics* GtkSalFrame::AcquireGraphics() -{ - if( m_bGraphics ) - return nullptr; - - if( !m_pGraphics ) - { - m_pGraphics.reset( new GtkSalGraphics( this, m_pWindow, m_nXScreen ) ); - } - m_bGraphics = true; - return m_pGraphics.get(); -} - -void GtkSalFrame::ReleaseGraphics( SalGraphics* pGraphics ) -{ - (void) pGraphics; - assert( pGraphics == m_pGraphics.get() ); - m_bGraphics = false; -} - -bool GtkSalFrame::PostEvent(std::unique_ptr<ImplSVEvent> pData) -{ - getDisplay()->SendInternalEvent( this, pData.release() ); - return true; -} - -void GtkSalFrame::SetTitle( const OUString& rTitle ) -{ - m_aTitle = rTitle; - if( m_pWindow && ! isChild() ) - gtk_window_set_title( GTK_WINDOW(m_pWindow), OUStringToOString( rTitle, RTL_TEXTENCODING_UTF8 ).getStr() ); -} - -void GtkSalFrame::SetIcon( sal_uInt16 nIcon ) -{ - if( (m_nStyle & (SalFrameStyleFlags::PLUG|SalFrameStyleFlags::SYSTEMCHILD|SalFrameStyleFlags::FLOAT|SalFrameStyleFlags::INTRO|SalFrameStyleFlags::OWNERDRAWDECORATION)) - || ! m_pWindow ) - return; - - gchar* appicon; - - if (nIcon == SV_ICON_ID_TEXT) - appicon = g_strdup ("libreoffice-writer"); - else if (nIcon == SV_ICON_ID_SPREADSHEET) - appicon = g_strdup ("libreoffice-calc"); - else if (nIcon == SV_ICON_ID_DRAWING) - appicon = g_strdup ("libreoffice-draw"); - else if (nIcon == SV_ICON_ID_PRESENTATION) - appicon = g_strdup ("libreoffice-impress"); - else if (nIcon == SV_ICON_ID_DATABASE) - appicon = g_strdup ("libreoffice-base"); - else if (nIcon == SV_ICON_ID_FORMULA) - appicon = g_strdup ("libreoffice-math"); - else - appicon = g_strdup ("libreoffice-startcenter"); - - gtk_window_set_icon_name (GTK_WINDOW (m_pWindow), appicon); -} - -void GtkSalFrame::SetMenu( SalMenu* pSalMenu ) -{ - m_pSalMenu = static_cast<GtkSalMenu*>(pSalMenu); -} - -SalMenu* GtkSalFrame::GetMenu() -{ - return m_pSalMenu; -} - -void GtkSalFrame::DrawMenuBar() -{ -} - -void GtkSalFrame::Center() -{ - long nX, nY; - - if( m_pParent ) - { - nX = (static_cast<long>(m_pParent->maGeometry.nWidth) - static_cast<long>(maGeometry.nWidth))/2; - nY = (static_cast<long>(m_pParent->maGeometry.nHeight) - static_cast<long>(maGeometry.nHeight))/2; - } - else - { - GdkScreen *pScreen = nullptr; - gint px, py; - GdkModifierType nMask; - gdk_display_get_pointer( getGdkDisplay(), &pScreen, &px, &py, &nMask ); - if( !pScreen ) - pScreen = gtk_widget_get_screen( m_pWindow ); - - gint nMonitor; - nMonitor = gdk_screen_get_monitor_at_point( pScreen, px, py ); - - GdkRectangle aMonitor; - gdk_screen_get_monitor_geometry( pScreen, nMonitor, &aMonitor ); - - nX = aMonitor.x + (aMonitor.width - static_cast<long>(maGeometry.nWidth))/2; - nY = aMonitor.y + (aMonitor.height - static_cast<long>(maGeometry.nHeight))/2; - } - SetPosSize( nX, nY, 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ); -} - -Size GtkSalFrame::calcDefaultSize() -{ - return bestmaxFrameSizeForScreenSize(getDisplay()->GetScreenSize(GetDisplayScreen())); -} - -void GtkSalFrame::SetDefaultSize() -{ - Size aDefSize = calcDefaultSize(); - - SetPosSize( 0, 0, aDefSize.Width(), aDefSize.Height(), - SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); - - if( (m_nStyle & SalFrameStyleFlags::DEFAULT) && m_pWindow ) - gtk_window_maximize( GTK_WINDOW(m_pWindow) ); -} - -static void initClientId() -{ - static bool bOnce = false; - if (!bOnce) - { - bOnce = true; - const OString& rID = SessionManagerClient::getSessionID(); - if (!rID.isEmpty()) - gdk_set_sm_client_id(rID.getStr()); - } -} - -void GtkSalFrame::Show( bool bVisible, bool bNoActivate ) -{ - if( m_pWindow ) - { - if( m_pParent && (m_pParent->m_nStyle & SalFrameStyleFlags::PARTIAL_FULLSCREEN) - && getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) - gtk_window_set_keep_above( GTK_WINDOW(m_pWindow), bVisible ); - if( bVisible ) - { - initClientId(); - getDisplay()->startupNotificationCompleted(); - - if( m_bDefaultPos ) - Center(); - if( m_bDefaultSize ) - SetDefaultSize(); - setMinMaxSize(); - - // #i45160# switch to desktop where a dialog with parent will appear - if( m_pParent && m_pParent->m_nWorkArea != m_nWorkArea && IS_WIDGET_MAPPED(m_pParent->m_pWindow) ) - getDisplay()->getWMAdaptor()->switchToWorkArea( m_pParent->m_nWorkArea ); - - if( isFloatGrabWindow() && - m_pParent && - m_nFloats == 0 && - ! getDisplay()->GetCaptureFrame() ) - { - /* #i63086# - * outsmart Metacity's "focus:mouse" mode - * which insists on taking the focus from the document - * to the new float. Grab focus to parent frame BEFORE - * showing the float (cannot grab it to the float - * before show). - */ - m_pParent->grabPointer( true, true ); - } - - if( ! bNoActivate && (m_nStyle & SalFrameStyleFlags::TOOLWINDOW) ) - m_bSetFocusOnMap = true; - - gtk_widget_show( m_pWindow ); - - if( isFloatGrabWindow() ) - { - m_nFloats++; - if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 1 ) - { - grabPointer(true, true); - GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this; - pKeyboardFrame->grabKeyboard(true); - } - // #i44068# reset parent's IM context - if( m_pParent ) - m_pParent->EndExtTextInput(EndExtTextInputFlags::NONE); - } - if( m_bWindowIsGtkPlug ) - askForXEmbedFocus( 0 ); - } - else - { - if( isFloatGrabWindow() ) - { - m_nFloats--; - if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 0) - { - GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this; - pKeyboardFrame->grabKeyboard(false); - grabPointer(false); - } - } - gtk_widget_hide( m_pWindow ); - if( m_pIMHandler ) - m_pIMHandler->focusChanged( false ); - // flush here; there may be a very seldom race between - // the display connection used for clipboard and our connection - Flush(); - } - CallCallback( SalEvent::Resize, nullptr ); - TriggerPaintEvent(); - } -} - -void GtkSalFrame::setMinMaxSize() -{ - /* #i34504# metacity (and possibly others) do not treat - * _NET_WM_STATE_FULLSCREEN and max_width/height independently; - * whether they should is undefined. So don't set the max size hint - * for a full screen window. - */ - if( m_pWindow && ! isChild() ) - { - GdkGeometry aGeo; - int aHints = 0; - if( m_nStyle & SalFrameStyleFlags::SIZEABLE ) - { - if( m_aMinSize.Width() && m_aMinSize.Height() && ! m_bFullscreen ) - { - aGeo.min_width = m_aMinSize.Width(); - aGeo.min_height = m_aMinSize.Height(); - aHints |= GDK_HINT_MIN_SIZE; - } - if( m_aMaxSize.Width() && m_aMaxSize.Height() && ! m_bFullscreen ) - { - aGeo.max_width = m_aMaxSize.Width(); - aGeo.max_height = m_aMaxSize.Height(); - aHints |= GDK_HINT_MAX_SIZE; - } - } - else - { - if( ! m_bFullscreen ) - { - aGeo.min_width = maGeometry.nWidth; - aGeo.min_height = maGeometry.nHeight; - aHints |= GDK_HINT_MIN_SIZE; - - aGeo.max_width = maGeometry.nWidth; - aGeo.max_height = maGeometry.nHeight; - aHints |= GDK_HINT_MAX_SIZE; - } - } - if( m_bFullscreen && m_aMaxSize.Width() && m_aMaxSize.Height() ) - { - aGeo.max_width = m_aMaxSize.Width(); - aGeo.max_height = m_aMaxSize.Height(); - aHints |= GDK_HINT_MAX_SIZE; - } - if( aHints ) - { - gtk_window_set_geometry_hints( GTK_WINDOW(m_pWindow), - nullptr, - &aGeo, - GdkWindowHints( aHints ) ); - } - } -} - -void GtkSalFrame::SetMaxClientSize( long nWidth, long nHeight ) -{ - if( ! isChild() ) - { - m_aMaxSize = Size( nWidth, nHeight ); - // Show does a setMinMaxSize - if( IS_WIDGET_MAPPED( m_pWindow ) ) - setMinMaxSize(); - } -} -void GtkSalFrame::SetMinClientSize( long nWidth, long nHeight ) -{ - if( ! isChild() ) - { - m_aMinSize = Size( nWidth, nHeight ); - if( m_pWindow ) - { - widget_set_size_request(nWidth, nHeight ); - // Show does a setMinMaxSize - if( IS_WIDGET_MAPPED( m_pWindow ) ) - setMinMaxSize(); - } - } -} - -void GtkSalFrame::AllocateFrame() -{ -} - -void GtkSalFrame::SetPosSize( long nX, long nY, long nWidth, long nHeight, sal_uInt16 nFlags ) -{ - if( !m_pWindow || isChild( true, false ) ) - return; - - bool bSized = false, bMoved = false; - - if( (nFlags & ( SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT )) && - (nWidth > 0 && nHeight > 0 ) // sometimes stupid things happen - ) - { - m_bDefaultSize = false; - - if( static_cast<unsigned long>(nWidth) != maGeometry.nWidth || static_cast<unsigned long>(nHeight) != maGeometry.nHeight ) - bSized = true; - maGeometry.nWidth = nWidth; - maGeometry.nHeight = nHeight; - - if( isChild( false ) ) - widget_set_size_request(nWidth, nHeight); - else if( ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) ) - window_resize(nWidth, nHeight); - setMinMaxSize(); - } - else if( m_bDefaultSize ) - SetDefaultSize(); - - m_bDefaultSize = false; - - if( nFlags & ( SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ) ) - { - if( m_pParent ) - { - if( AllSettings::GetLayoutRTL() ) - nX = m_pParent->maGeometry.nWidth-maGeometry.nWidth-1-nX; - nX += m_pParent->maGeometry.nX; - nY += m_pParent->maGeometry.nY; - } - - if( nX != maGeometry.nX || nY != maGeometry.nY ) - bMoved = true; - maGeometry.nX = nX; - maGeometry.nY = nY; - - m_bDefaultPos = false; - - moveWindow( maGeometry.nX, maGeometry.nY ); - - updateScreenNumber(); - } - else if( m_bDefaultPos ) - Center(); - - m_bDefaultPos = false; - - if( bSized ) - AllocateFrame(); - - if( bSized && ! bMoved ) - CallCallback( SalEvent::Resize, nullptr ); - else if( bMoved && ! bSized ) - CallCallback( SalEvent::Move, nullptr ); - else if( bMoved && bSized ) - CallCallback( SalEvent::MoveResize, nullptr ); - - if (bSized) - TriggerPaintEvent(); -} - -void GtkSalFrame::GetClientSize( long& rWidth, long& rHeight ) -{ - if( m_pWindow && !(m_nState & GDK_WINDOW_STATE_ICONIFIED) ) - { - rWidth = maGeometry.nWidth; - rHeight = maGeometry.nHeight; - } - else - rWidth = rHeight = 0; -} - -void GtkSalFrame::GetWorkArea( tools::Rectangle& rRect ) -{ - rRect = GetGtkSalData()->GetGtkDisplay()->getWMAdaptor()->getWorkArea( 0 ); -} - -SalFrame* GtkSalFrame::GetParent() const -{ - return m_pParent; -} - -void GtkSalFrame::SetWindowState( const SalFrameState* pState ) -{ - if( ! m_pWindow || ! pState || isChild( true, false ) ) - return; - - const WindowStateMask nMaxGeometryMask = - WindowStateMask::X | WindowStateMask::Y | - WindowStateMask::Width | WindowStateMask::Height | - WindowStateMask::MaximizedX | WindowStateMask::MaximizedY | - WindowStateMask::MaximizedWidth | WindowStateMask::MaximizedHeight; - - if( (pState->mnMask & WindowStateMask::State) && - ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) && - (pState->mnState & WindowStateState::Maximized) && - (pState->mnMask & nMaxGeometryMask) == nMaxGeometryMask ) - { - resizeWindow( pState->mnWidth, pState->mnHeight ); - moveWindow( pState->mnX, pState->mnY ); - m_bDefaultPos = m_bDefaultSize = false; - - maGeometry.nX = pState->mnMaximizedX; - maGeometry.nY = pState->mnMaximizedY; - maGeometry.nWidth = pState->mnMaximizedWidth; - maGeometry.nHeight = pState->mnMaximizedHeight; - updateScreenNumber(); - - m_nState = GdkWindowState( m_nState | GDK_WINDOW_STATE_MAXIMIZED ); - m_aRestorePosSize = tools::Rectangle( Point( pState->mnX, pState->mnY ), - Size( pState->mnWidth, pState->mnHeight ) ); - CallCallback( SalEvent::Resize, nullptr ); - } - else if( pState->mnMask & (WindowStateMask::X | WindowStateMask::Y | - WindowStateMask::Width | WindowStateMask::Height ) ) - { - sal_uInt16 nPosSizeFlags = 0; - long nX = pState->mnX - (m_pParent ? m_pParent->maGeometry.nX : 0); - long nY = pState->mnY - (m_pParent ? m_pParent->maGeometry.nY : 0); - if( pState->mnMask & WindowStateMask::X ) - nPosSizeFlags |= SAL_FRAME_POSSIZE_X; - else - nX = maGeometry.nX - (m_pParent ? m_pParent->maGeometry.nX : 0); - if( pState->mnMask & WindowStateMask::Y ) - nPosSizeFlags |= SAL_FRAME_POSSIZE_Y; - else - nY = maGeometry.nY - (m_pParent ? m_pParent->maGeometry.nY : 0); - if( pState->mnMask & WindowStateMask::Width ) - nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH; - if( pState->mnMask & WindowStateMask::Height ) - nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT; - SetPosSize( nX, nY, pState->mnWidth, pState->mnHeight, nPosSizeFlags ); - } - if( pState->mnMask & WindowStateMask::State && ! isChild() ) - { - if( pState->mnState & WindowStateState::Maximized ) - gtk_window_maximize( GTK_WINDOW(m_pWindow) ); - else - gtk_window_unmaximize( GTK_WINDOW(m_pWindow) ); - /* #i42379# there is no rollup state in GDK; and rolled up windows are - * (probably depending on the WM) reported as iconified. If we iconify a - * window here that was e.g. a dialog, then it will be unmapped but still - * not be displayed in the task list, so it's an iconified window that - * the user cannot get out of this state. So do not set the iconified state - * on windows with a parent (that is transient frames) since these tend - * to not be represented in an icon task list. - */ - if( (pState->mnState & WindowStateState::Minimized) - && ! m_pParent ) - gtk_window_iconify( GTK_WINDOW(m_pWindow) ); - else - gtk_window_deiconify( GTK_WINDOW(m_pWindow) ); - } - TriggerPaintEvent(); -} - -bool GtkSalFrame::GetWindowState( SalFrameState* pState ) -{ - pState->mnState = WindowStateState::Normal; - pState->mnMask = WindowStateMask::State; - // rollup ? gtk 2.2 does not seem to support the shaded state - if( m_nState & GDK_WINDOW_STATE_ICONIFIED ) - pState->mnState |= WindowStateState::Minimized; - if( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) - { - pState->mnState |= WindowStateState::Maximized; - pState->mnX = m_aRestorePosSize.Left(); - pState->mnY = m_aRestorePosSize.Top(); - pState->mnWidth = m_aRestorePosSize.GetWidth(); - pState->mnHeight = m_aRestorePosSize.GetHeight(); - pState->mnMaximizedX = maGeometry.nX; - pState->mnMaximizedY = maGeometry.nY; - pState->mnMaximizedWidth = maGeometry.nWidth; - pState->mnMaximizedHeight = maGeometry.nHeight; - pState->mnMask |= WindowStateMask::MaximizedX | - WindowStateMask::MaximizedY | - WindowStateMask::MaximizedWidth | - WindowStateMask::MaximizedHeight; - } - else - { - pState->mnX = maGeometry.nX; - pState->mnY = maGeometry.nY; - pState->mnWidth = maGeometry.nWidth; - pState->mnHeight = maGeometry.nHeight; - } - pState->mnMask |= WindowStateMask::X | - WindowStateMask::Y | - WindowStateMask::Width | - WindowStateMask::Height; - - return true; -} - -void GtkSalFrame::SetScreen( unsigned int nNewScreen, SetType eType, tools::Rectangle const *pSize ) -{ - if( !m_pWindow ) - return; - - if (maGeometry.nDisplayScreenNumber == nNewScreen && eType == SetType::RetainSize) - return; - - GdkScreen *pScreen = nullptr; - GdkRectangle aNewMonitor; - - bool bSpanAllScreens = nNewScreen == static_cast<unsigned int>(-1); - m_bSpanMonitorsWhenFullscreen = bSpanAllScreens && getDisplay()->getSystem()->GetDisplayScreenCount() > 1; - - if (m_bSpanMonitorsWhenFullscreen) //span all screens - { - pScreen = gtk_widget_get_screen( m_pWindow ); - aNewMonitor.x = 0; - aNewMonitor.y = 0; - aNewMonitor.width = gdk_screen_get_width(pScreen); - aNewMonitor.height = gdk_screen_get_height(pScreen); - } - else - { - gint nMonitor; - bool bSameMonitor = false; - - if (!bSpanAllScreens) - { - pScreen = getDisplay()->getSystem()->getScreenMonitorFromIdx( nNewScreen, nMonitor ); - if (!pScreen) - { - g_warning ("Attempt to move GtkSalFrame to invalid screen %d => " - "fallback to current\n", nNewScreen); - } - } - - if (!pScreen) - { - pScreen = gtk_widget_get_screen( m_pWindow ); - bSameMonitor = true; - } - - // Heavy lifting, need to move screen ... - if( pScreen != gtk_widget_get_screen( m_pWindow )) - gtk_window_set_screen( GTK_WINDOW( m_pWindow ), pScreen ); - - gint nOldMonitor = gdk_screen_get_monitor_at_window( - pScreen, widget_get_window( m_pWindow ) ); - if (bSameMonitor) - nMonitor = nOldMonitor; - - #if OSL_DEBUG_LEVEL > 1 - if( nMonitor == nOldMonitor ) - g_warning( "An apparently pointless SetScreen - should we elide it ?" ); - #endif - - GdkRectangle aOldMonitor; - gdk_screen_get_monitor_geometry( pScreen, nOldMonitor, &aOldMonitor ); - gdk_screen_get_monitor_geometry( pScreen, nMonitor, &aNewMonitor ); - - maGeometry.nX = aNewMonitor.x + maGeometry.nX - aOldMonitor.x; - maGeometry.nY = aNewMonitor.y + maGeometry.nY - aOldMonitor.y; - } - - bool bResize = false; - bool bVisible = IS_WIDGET_MAPPED( m_pWindow ); - if( bVisible ) - Show( false ); - - if( eType == SetType::Fullscreen ) - { - maGeometry.nX = aNewMonitor.x; - maGeometry.nY = aNewMonitor.y; - maGeometry.nWidth = aNewMonitor.width; - maGeometry.nHeight = aNewMonitor.height; - m_nStyle |= SalFrameStyleFlags::PARTIAL_FULLSCREEN; - bResize = true; - - // #i110881# for the benefit of compiz set a max size here - // else setting to fullscreen fails for unknown reasons - m_aMaxSize.setWidth( aNewMonitor.width ); - m_aMaxSize.setHeight( aNewMonitor.height ); - } - - if( pSize && eType == SetType::UnFullscreen ) - { - maGeometry.nX = pSize->Left(); - maGeometry.nY = pSize->Top(); - maGeometry.nWidth = pSize->GetWidth(); - maGeometry.nHeight = pSize->GetHeight(); - m_nStyle &= ~SalFrameStyleFlags::PARTIAL_FULLSCREEN; - bResize = true; - } - - if (bResize) - { - // temporarily re-sizeable - if( !(m_nStyle & SalFrameStyleFlags::SIZEABLE) ) - gtk_window_set_resizable( GTK_WINDOW(m_pWindow), TRUE ); - window_resize(maGeometry.nWidth, maGeometry.nHeight); - //I wonder if we should instead leave maGeometry alone and rely on - //configure-event to trigger signalConfigure and set it there - AllocateFrame(); - TriggerPaintEvent(); - } - - gtk_window_move( GTK_WINDOW( m_pWindow ), maGeometry.nX, maGeometry.nY ); - - // _NET_WM_STATE_FULLSCREEN (Metacity <-> KWin) - if( ! getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) - { - if( eType == SetType::Fullscreen ) - gtk_window_fullscreen( GTK_WINDOW( m_pWindow ) ); - else if( eType == SetType::UnFullscreen ) - gtk_window_unfullscreen( GTK_WINDOW( m_pWindow ) ); - } - - if( eType == SetType::UnFullscreen && - !(m_nStyle & SalFrameStyleFlags::SIZEABLE) ) - gtk_window_set_resizable( GTK_WINDOW( m_pWindow ), FALSE ); - - // FIXME: we should really let gtk+ handle our widget hierarchy ... - if( m_pParent && gtk_widget_get_screen( m_pParent->m_pWindow ) != pScreen ) - SetParent( nullptr ); - std::list< GtkSalFrame* > aChildren = m_aChildren; - for (auto const& child : aChildren) - child->SetScreen( nNewScreen, SetType::RetainSize ); - - m_bDefaultPos = m_bDefaultSize = false; - updateScreenNumber(); - CallCallback( SalEvent::MoveResize, nullptr ); - - if( bVisible ) - Show( true ); -} - -void GtkSalFrame::SetScreenNumber( unsigned int nNewScreen ) -{ - SetScreen( nNewScreen, SetType::RetainSize ); -} - -void GtkSalFrame::updateWMClass() -{ - OString aResClass = OUStringToOString(m_sWMClass, RTL_TEXTENCODING_ASCII_US); - const char *pResClass = !aResClass.isEmpty() ? aResClass.getStr() : - SalGenericSystem::getFrameClassName(); - Display *display; - - if (!getDisplay()->IsX11Display()) - return; - - display = getDisplay()->GetDisplay(); - - if( IS_WIDGET_REALIZED( m_pWindow ) ) - { - XClassHint* pClass = XAllocClassHint(); - OString aResName = SalGenericSystem::getFrameResName(); - pClass->res_name = const_cast<char*>(aResName.getStr()); - pClass->res_class = const_cast<char*>(pResClass); - XSetClassHint( display, - widget_get_xid(m_pWindow), - pClass ); - XFree( pClass ); - } -} - -void GtkSalFrame::SetApplicationID( const OUString &rWMClass ) -{ - if( rWMClass != m_sWMClass && ! isChild() ) - { - m_sWMClass = rWMClass; - updateWMClass(); - - for (auto const& child : m_aChildren) - child->SetApplicationID(rWMClass); - } -} - -void GtkSalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nScreen ) -{ - m_bFullscreen = bFullScreen; - - if( !m_pWindow || isChild() ) - return; - - if( bFullScreen ) - { - m_aRestorePosSize = tools::Rectangle( Point( maGeometry.nX, maGeometry.nY ), - Size( maGeometry.nWidth, maGeometry.nHeight ) ); - SetScreen( nScreen, SetType::Fullscreen ); - } - else - { - SetScreen( nScreen, SetType::UnFullscreen, - !m_aRestorePosSize.IsEmpty() ? &m_aRestorePosSize : nullptr ); - m_aRestorePosSize = tools::Rectangle(); - } -} - -void GtkSalFrame::StartPresentation( bool bStart ) -{ - boost::optional<guint> aWindow; - boost::optional<Display*> aDisplay; - if( getDisplay()->IsX11Display() ) - { - aWindow = widget_get_xid(m_pWindow); - aDisplay = GDK_DISPLAY_XDISPLAY( getGdkDisplay() ); - } - - m_ScreenSaverInhibitor.inhibit( bStart, - "presentation", - getDisplay()->IsX11Display(), - aWindow, - aDisplay ); -} - -void GtkSalFrame::SetAlwaysOnTop( bool bOnTop ) -{ - if( m_pWindow ) - gtk_window_set_keep_above( GTK_WINDOW( m_pWindow ), bOnTop ); -} - -void GtkSalFrame::ToTop( SalFrameToTop nFlags ) -{ - if( m_pWindow ) - { - if( isChild( false ) ) - gtk_widget_grab_focus( m_pWindow ); - else if( IS_WIDGET_MAPPED( m_pWindow ) ) - { - if( ! (nFlags & SalFrameToTop::GrabFocusOnly) ) - gtk_window_present( GTK_WINDOW(m_pWindow) ); - else - { - guint32 nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); - gdk_window_focus( widget_get_window(m_pWindow), nUserTime ); - } - } - else - { - if( nFlags & SalFrameToTop::RestoreWhenMin ) - gtk_window_present( GTK_WINDOW(m_pWindow) ); - } - } -} - -void GtkSalFrame::SetPointer( PointerStyle ePointerStyle ) -{ - if( m_pWindow && ePointerStyle != m_ePointerStyle ) - { - m_ePointerStyle = ePointerStyle; - GdkCursor *pCursor = getDisplay()->getCursor( ePointerStyle ); - gdk_window_set_cursor( widget_get_window(m_pWindow), pCursor ); - m_pCurrentCursor = pCursor; - - // #i80791# use grabPointer the same way as CaptureMouse, respective float grab - if( getDisplay()->MouseCaptured( this ) ) - grabPointer( true ); - else if( m_nFloats > 0 ) - grabPointer( true, true ); - } -} - -void GtkSalFrame::grabPointer( bool bGrab, bool bOwnerEvents ) -{ - static const char* pEnv = getenv( "SAL_NO_MOUSEGRABS" ); - if (pEnv && *pEnv) - return; - - if (!m_pWindow) - return; - - const int nMask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); - - if( bGrab ) - { - bool bUseGdkGrab = true; - for (auto pSalFrame : getDisplay()->getFrames() ) - { - const GtkSalFrame* pFrame = static_cast< const GtkSalFrame* >( pSalFrame ); - if( pFrame->m_bWindowIsGtkPlug ) - { - bUseGdkGrab = false; - break; - } - } - if( bUseGdkGrab ) - { - gdk_pointer_grab( widget_get_window( m_pWindow ), bOwnerEvents, - GdkEventMask(nMask), nullptr, m_pCurrentCursor, - GDK_CURRENT_TIME ); - } - else - { - // FIXME: for some unknown reason gdk_pointer_grab does not - // really produce owner events for GtkPlug windows - // the cause is yet unknown - - // this is of course a bad hack, especially as we cannot - // set the right cursor this way - XGrabPointer( getDisplay()->GetDisplay(), - widget_get_xid( m_pWindow ), - bOwnerEvents, - PointerMotionMask | ButtonPressMask | ButtonReleaseMask, - GrabModeAsync, - GrabModeAsync, - None, - None, - CurrentTime - ); - } - } - else - { - // Two GdkDisplays may be open - gdk_display_pointer_ungrab( getGdkDisplay(), GDK_CURRENT_TIME); - } -} - -void GtkSalFrame::grabKeyboard( bool bGrab ) -{ - static const char* pEnv = getenv("SAL_NO_MOUSEGRABS"); // let's not introduce a special var for this - if (pEnv && *pEnv) - return; - - if (!m_pWindow) - return; - - if( bGrab ) - { - gdk_keyboard_grab(widget_get_window(m_pWindow), true, - GDK_CURRENT_TIME); - } - else - { - gdk_keyboard_ungrab(GDK_CURRENT_TIME); - } -} - -void GtkSalFrame::CaptureMouse( bool bCapture ) -{ - getDisplay()->CaptureMouse( bCapture ? this : nullptr ); -} - -void GtkSalFrame::SetPointerPos( long nX, long nY ) -{ - GtkSalFrame* pFrame = this; - while( pFrame && pFrame->isChild( false ) ) - pFrame = pFrame->m_pParent; - if( ! pFrame ) - return; - - GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(pFrame->m_pWindow) ); - GdkDisplay *pDisplay = gdk_screen_get_display( pScreen ); - - /* when the application tries to center the mouse in the dialog the - * window isn't mapped already. So use coordinates relative to the root window. - */ - unsigned int nWindowLeft = maGeometry.nX + nX; - unsigned int nWindowTop = maGeometry.nY + nY; - - XWarpPointer( GDK_DISPLAY_XDISPLAY (pDisplay), None, - GDK_WINDOW_XID (gdk_screen_get_root_window( pScreen ) ), - 0, 0, 0, 0, nWindowLeft, nWindowTop); - // #i38648# ask for the next motion hint - gint x, y; - GdkModifierType mask; - gdk_window_get_pointer( widget_get_window(pFrame->m_pWindow) , &x, &y, &mask ); -} - -void GtkSalFrame::Flush() -{ - XFlush (GDK_DISPLAY_XDISPLAY (getGdkDisplay())); -} - -void GtkSalFrame::KeyCodeToGdkKey(const vcl::KeyCode& rKeyCode, - guint* pGdkKeyCode, GdkModifierType *pGdkModifiers) -{ - if ( pGdkKeyCode == nullptr || pGdkModifiers == nullptr ) - return; - - // Get GDK key modifiers - GdkModifierType nModifiers = GdkModifierType(0); - - if ( rKeyCode.IsShift() ) - nModifiers = static_cast<GdkModifierType>( nModifiers | GDK_SHIFT_MASK ); - - if ( rKeyCode.IsMod1() ) - nModifiers = static_cast<GdkModifierType>( nModifiers | GDK_CONTROL_MASK ); - - if ( rKeyCode.IsMod2() ) - nModifiers = static_cast<GdkModifierType>( nModifiers | GDK_MOD1_MASK ); - - *pGdkModifiers = nModifiers; - - // Get GDK keycode. - guint nKeyCode = 0; - - guint nCode = rKeyCode.GetCode(); - - if ( nCode >= KEY_0 && nCode <= KEY_9 ) - nKeyCode = ( nCode - KEY_0 ) + GDK_KEY_0; - else if ( nCode >= KEY_A && nCode <= KEY_Z ) - nKeyCode = ( nCode - KEY_A ) + GDK_KEY_A; - else if ( nCode >= KEY_F1 && nCode <= KEY_F26 ) - nKeyCode = ( nCode - KEY_F1 ) + GDK_KEY_F1; - else - { - switch (nCode) - { - case KEY_DOWN: nKeyCode = GDK_KEY_Down; break; - case KEY_UP: nKeyCode = GDK_KEY_Up; break; - case KEY_LEFT: nKeyCode = GDK_KEY_Left; break; - case KEY_RIGHT: nKeyCode = GDK_KEY_Right; break; - case KEY_HOME: nKeyCode = GDK_KEY_Home; break; - case KEY_END: nKeyCode = GDK_KEY_End; break; - case KEY_PAGEUP: nKeyCode = GDK_KEY_Page_Up; break; - case KEY_PAGEDOWN: nKeyCode = GDK_KEY_Page_Down; break; - case KEY_RETURN: nKeyCode = GDK_KEY_Return; break; - case KEY_ESCAPE: nKeyCode = GDK_KEY_Escape; break; - case KEY_TAB: nKeyCode = GDK_KEY_Tab; break; - case KEY_BACKSPACE: nKeyCode = GDK_KEY_BackSpace; break; - case KEY_SPACE: nKeyCode = GDK_KEY_space; break; - case KEY_INSERT: nKeyCode = GDK_KEY_Insert; break; - case KEY_DELETE: nKeyCode = GDK_KEY_Delete; break; - case KEY_ADD: nKeyCode = GDK_KEY_plus; break; - case KEY_SUBTRACT: nKeyCode = GDK_KEY_minus; break; - case KEY_MULTIPLY: nKeyCode = GDK_KEY_asterisk; break; - case KEY_DIVIDE: nKeyCode = GDK_KEY_slash; break; - case KEY_POINT: nKeyCode = GDK_KEY_period; break; - case KEY_COMMA: nKeyCode = GDK_KEY_comma; break; - case KEY_LESS: nKeyCode = GDK_KEY_less; break; - case KEY_GREATER: nKeyCode = GDK_KEY_greater; break; - case KEY_EQUAL: nKeyCode = GDK_KEY_equal; break; - case KEY_FIND: nKeyCode = GDK_KEY_Find; break; - case KEY_CONTEXTMENU: nKeyCode = GDK_KEY_Menu; break; - case KEY_HELP: nKeyCode = GDK_KEY_Help; break; - case KEY_UNDO: nKeyCode = GDK_KEY_Undo; break; - case KEY_REPEAT: nKeyCode = GDK_KEY_Redo; break; - case KEY_DECIMAL: nKeyCode = GDK_KEY_KP_Decimal; break; - case KEY_TILDE: nKeyCode = GDK_KEY_asciitilde; break; - case KEY_QUOTELEFT: nKeyCode = GDK_KEY_quoteleft; break; - case KEY_BRACKETLEFT: nKeyCode = GDK_KEY_bracketleft; break; - case KEY_BRACKETRIGHT: nKeyCode = GDK_KEY_bracketright; break; - case KEY_SEMICOLON: nKeyCode = GDK_KEY_semicolon; break; - case KEY_QUOTERIGHT: nKeyCode = GDK_KEY_quoteright; break; - - // Special cases - case KEY_COPY: nKeyCode = GDK_KEY_Copy; break; - case KEY_CUT: nKeyCode = GDK_KEY_Cut; break; - case KEY_PASTE: nKeyCode = GDK_KEY_Paste; break; - case KEY_OPEN: nKeyCode = GDK_KEY_Open; break; - } - } - - *pGdkKeyCode = nKeyCode; -} - -OUString GtkSalFrame::GetKeyName( sal_uInt16 nKeyCode ) -{ - return getDisplay()->GetKeyName( nKeyCode ); -} - -GdkDisplay *GtkSalFrame::getGdkDisplay() -{ - return GetGtkSalData()->GetGdkDisplay(); -} - -GtkSalDisplay *GtkSalFrame::getDisplay() -{ - return GetGtkSalData()->GetGtkDisplay(); -} - -SalFrame::SalPointerState GtkSalFrame::GetPointerState() -{ - SalPointerState aState; - GdkScreen* pScreen; - gint x, y; - GdkModifierType aMask; - gdk_display_get_pointer( getGdkDisplay(), &pScreen, &x, &y, &aMask ); - aState.maPos = Point( x - maGeometry.nX, y - maGeometry.nY ); - aState.mnState = GetMouseModCode( aMask ); - return aState; -} - -KeyIndicatorState GtkSalFrame::GetIndicatorState() -{ - return GetGtkSalData()->GetGtkDisplay()->GetIndicatorState(); -} - -void GtkSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode ) -{ - GetGtkSalData()->GetGtkDisplay()->SimulateKeyPress(nKeyCode); -} - -void GtkSalFrame::SetInputContext( SalInputContext* pContext ) -{ - if( ! pContext ) - return; - - if( ! (pContext->mnOptions & InputContextFlags::Text) ) - return; - - // create a new im context - if( ! m_pIMHandler ) - m_pIMHandler.reset( new IMHandler( this ) ); -} - -void GtkSalFrame::EndExtTextInput( EndExtTextInputFlags nFlags ) -{ - if( m_pIMHandler ) - m_pIMHandler->endExtTextInput( nFlags ); -} - -bool GtkSalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , vcl::KeyCode& ) -{ - // not supported yet - return false; -} - -LanguageType GtkSalFrame::GetInputLanguage() -{ - return LANGUAGE_DONTKNOW; -} - -void GtkSalFrame::UpdateSettings( AllSettings& rSettings ) -{ - if( ! m_pWindow ) - return; - - GtkSalGraphics* pGraphics = m_pGraphics.get(); - bool bFreeGraphics = false; - if( ! pGraphics ) - { - pGraphics = static_cast<GtkSalGraphics*>(AcquireGraphics()); - if ( !pGraphics ) - { - SAL_WARN("vcl.gtk", "Could not get graphics - unable to update settings"); - return; - } - bFreeGraphics = true; - } - - pGraphics->UpdateSettings( rSettings ); - - if( bFreeGraphics ) - ReleaseGraphics( pGraphics ); -} - -void GtkSalFrame::Beep() -{ - gdk_display_beep( getGdkDisplay() ); -} - -const SystemEnvData* GtkSalFrame::GetSystemData() const -{ - return &m_aSystemData; -} - -void GtkSalFrame::SetParent( SalFrame* pNewParent ) -{ - if( m_pParent ) - m_pParent->m_aChildren.remove( this ); - m_pParent = static_cast<GtkSalFrame*>(pNewParent); - if( m_pParent ) - m_pParent->m_aChildren.push_back( this ); - if( ! isChild() ) - gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), - (m_pParent && ! m_pParent->isChild(true,false)) ? GTK_WINDOW(m_pParent->m_pWindow) : nullptr - ); -} - -void GtkSalFrame::createNewWindow( ::Window aNewParent, bool bXEmbed, SalX11Screen nXScreen ) -{ - bool bWasVisible = m_pWindow && IS_WIDGET_MAPPED(m_pWindow); - if( bWasVisible ) - Show( false ); - - if( static_cast<int>(nXScreen.getXScreen()) >= getDisplay()->GetXScreenCount() ) - nXScreen = m_nXScreen; - - SystemParentData aParentData; - aParentData.nSize = sizeof(SystemParentData); - aParentData.aWindow = aNewParent; - aParentData.bXEmbedSupport = bXEmbed; - if( aNewParent == None ) - { - aParentData.aWindow = None; - aParentData.bXEmbedSupport = false; - } - else - { - // is new parent a root window ? - Display* pDisp = getDisplay()->GetDisplay(); - int nScreens = getDisplay()->GetXScreenCount(); - for( int i = 0; i < nScreens; i++ ) - { - if( aNewParent == RootWindow( pDisp, i ) ) - { - nXScreen = SalX11Screen( i ); - aParentData.aWindow = None; - aParentData.bXEmbedSupport = false; - break; - } - } - } - - // free xrender resources - if( m_pGraphics ) - m_pGraphics->SetDrawable( None, m_nXScreen ); - - // first deinit frame - m_pIMHandler.reset(); - if( m_pRegion ) - { - gdk_region_destroy( m_pRegion ); - } - - GtkWidget *pEventWidget = getMouseEventWidget(); - for (auto handler_id : m_aMouseSignalIds) - g_signal_handler_disconnect(G_OBJECT(pEventWidget), handler_id); - if( m_pFixedContainer ) - gtk_widget_destroy( GTK_WIDGET(m_pFixedContainer) ); - if( m_pEventBox ) - gtk_widget_destroy( GTK_WIDGET(m_pEventBox) ); - if( m_pWindow ) - gtk_widget_destroy( m_pWindow ); - if( m_pForeignParent ) - g_object_unref( G_OBJECT( m_pForeignParent ) ); - if( m_pForeignTopLevel ) - g_object_unref( G_OBJECT( m_pForeignTopLevel ) ); - - // init new window - m_bDefaultPos = m_bDefaultSize = false; - if( aParentData.aWindow != None ) - { - m_nStyle |= SalFrameStyleFlags::PLUG; - Init( &aParentData ); - } - else - { - m_nStyle &= ~SalFrameStyleFlags::PLUG; - Init( (m_pParent && m_pParent->m_nXScreen == m_nXScreen) ? m_pParent : nullptr, m_nStyle ); - } - - // update graphics - if( m_pGraphics ) - { - m_pGraphics->SetDrawable( widget_get_xid(m_pWindow), m_nXScreen ); - m_pGraphics->SetWindow( m_pWindow ); - } - - if( ! m_aTitle.isEmpty() ) - SetTitle( m_aTitle ); - - if( bWasVisible ) - Show( true ); - - std::list< GtkSalFrame* > aChildren; - aChildren.swap(m_aChildren); - for (auto const& child : aChildren) - child->createNewWindow( None, false, m_nXScreen ); - - // FIXME: SalObjects -} - -bool GtkSalFrame::SetPluginParent( SystemParentData* pSysParent ) -{ - GetGenericUnixSalData()->ErrorTrapPush(); // permanently ignore unruly children's errors - createNewWindow( pSysParent->aWindow, (pSysParent->nSize > sizeof(long)) && pSysParent->bXEmbedSupport, m_nXScreen ); - return true; -} - -void GtkSalFrame::ResetClipRegion() -{ - if( m_pWindow ) - gdk_window_shape_combine_region( widget_get_window( m_pWindow ), nullptr, 0, 0 ); -} - -void GtkSalFrame::BeginSetClipRegion( sal_uInt32 ) -{ - if( m_pRegion ) - gdk_region_destroy( m_pRegion ); - m_pRegion = gdk_region_new(); -} - -void GtkSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) -{ - if( m_pRegion ) - { - GdkRectangle aRect; - aRect.x = nX; - aRect.y = nY; - aRect.width = nWidth; - aRect.height = nHeight; - gdk_region_union_with_rect( m_pRegion, &aRect ); - } -} - -void GtkSalFrame::EndSetClipRegion() -{ - if( m_pWindow && m_pRegion ) - gdk_window_shape_combine_region( widget_get_window(m_pWindow), m_pRegion, 0, 0 ); -} - -bool GtkSalFrame::Dispatch( const XEvent* pEvent ) -{ - bool bContinueDispatch = true; - - if( pEvent->type == PropertyNotify ) - { - vcl_sal::WMAdaptor* pAdaptor = getDisplay()->getWMAdaptor(); - Atom nDesktopAtom = pAdaptor->getAtom( vcl_sal::WMAdaptor::NET_WM_DESKTOP ); - if( pEvent->xproperty.atom == nDesktopAtom && - pEvent->xproperty.state == PropertyNewValue ) - { - m_nWorkArea = pAdaptor->getWindowWorkArea( widget_get_xid(m_pWindow) ); - } - } - else if( pEvent->type == ConfigureNotify ) - { - if( m_pForeignParent && pEvent->xconfigure.window == m_aForeignParentWindow ) - { - bContinueDispatch = false; - gtk_window_resize( GTK_WINDOW(m_pWindow), pEvent->xconfigure.width, pEvent->xconfigure.height ); - if( ( sal::static_int_cast< int >(maGeometry.nWidth) != - pEvent->xconfigure.width ) || - ( sal::static_int_cast< int >(maGeometry.nHeight) != - pEvent->xconfigure.height ) ) - { - maGeometry.nWidth = pEvent->xconfigure.width; - maGeometry.nHeight = pEvent->xconfigure.height; - setMinMaxSize(); - getDisplay()->SendInternalEvent( this, nullptr, SalEvent::Resize ); - } - } - else if( m_pForeignTopLevel && pEvent->xconfigure.window == m_aForeignTopLevelWindow ) - { - bContinueDispatch = false; - // update position - int x = 0, y = 0; - ::Window aChild; - XTranslateCoordinates( getDisplay()->GetDisplay(), - widget_get_xid(m_pWindow), - getDisplay()->GetRootWindow( getDisplay()->GetDefaultXScreen() ), - 0, 0, - &x, &y, - &aChild ); - if( x != maGeometry.nX || y != maGeometry.nY ) - { - maGeometry.nX = x; - maGeometry.nY = y; - getDisplay()->SendInternalEvent( this, nullptr, SalEvent::Move ); - } - } - } - else if( pEvent->type == ClientMessage && - pEvent->xclient.message_type == getDisplay()->getWMAdaptor()->getAtom( vcl_sal::WMAdaptor::XEMBED ) && - pEvent->xclient.window == widget_get_xid(m_pWindow) && - m_bWindowIsGtkPlug - ) - { - // FIXME: this should not be necessary, GtkPlug should do this - // transparently for us - if( pEvent->xclient.data.l[1] == 1 || // XEMBED_WINDOW_ACTIVATE - pEvent->xclient.data.l[1] == 2 // XEMBED_WINDOW_DEACTIVATE - ) - { - GdkEventFocus aEvent; - aEvent.type = GDK_FOCUS_CHANGE; - aEvent.window = widget_get_window( m_pWindow ); - aEvent.send_event = gint8(TRUE); - aEvent.in = gint16(pEvent->xclient.data.l[1] == 1); - signalFocus( m_pWindow, &aEvent, this ); - } - } - - return bContinueDispatch; -} - -gboolean GtkSalFrame::signalButton( GtkWidget*, GdkEventButton* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - SalMouseEvent aEvent; - SalEvent nEventType = SalEvent::NONE; - switch( pEvent->type ) - { - case GDK_BUTTON_PRESS: - nEventType = SalEvent::MouseButtonDown; - break; - case GDK_BUTTON_RELEASE: - nEventType = SalEvent::MouseButtonUp; - break; - default: - return false; - } - switch( pEvent->button ) - { - case 1: aEvent.mnButton = MOUSE_LEFT; break; - case 2: aEvent.mnButton = MOUSE_MIDDLE; break; - case 3: aEvent.mnButton = MOUSE_RIGHT; break; - default: return false; - } - aEvent.mnTime = pEvent->time; - aEvent.mnX = static_cast<long>(pEvent->x_root) - pThis->maGeometry.nX; - aEvent.mnY = static_cast<long>(pEvent->y_root) - pThis->maGeometry.nY; - aEvent.mnCode = GetMouseModCode( pEvent->state ); - - bool bClosePopups = false; - if( pEvent->type == GDK_BUTTON_PRESS && - !(pThis->m_nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION) - ) - { - if( m_nFloats > 0 ) - { - // close popups if user clicks outside our application - gint x, y; - bClosePopups = (gdk_display_get_window_at_pointer( GtkSalFrame::getGdkDisplay(), &x, &y ) == nullptr); - } - /* #i30306# release implicit pointer grab if no popups are open; else - * Drag cannot grab the pointer and will fail. - */ - if( m_nFloats < 1 || bClosePopups ) - gdk_display_pointer_ungrab( GtkSalFrame::getGdkDisplay(), GDK_CURRENT_TIME ); - } - - if( pThis->m_bWindowIsGtkPlug && - pEvent->type == GDK_BUTTON_PRESS && - pEvent->button == 1 ) - { - pThis->askForXEmbedFocus( pEvent->time ); - } - - if( AllSettings::GetLayoutRTL() ) - aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; - - vcl::DeletionListener aDel( pThis ); - - pThis->CallCallback( nEventType, &aEvent ); - - if( ! aDel.isDeleted() ) - { - if( bClosePopups ) - { - ImplSVData* pSVData = ImplGetSVData(); - if ( pSVData->maWinData.mpFirstFloat ) - { - if (!(pSVData->maWinData.mpFirstFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoAppFocusClose)) - pSVData->maWinData.mpFirstFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); - } - } - - if( ! aDel.isDeleted() ) - { - int frame_x = static_cast<int>(pEvent->x_root - pEvent->x); - int frame_y = static_cast<int>(pEvent->y_root - pEvent->y); - if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY ) - { - pThis->maGeometry.nX = frame_x; - pThis->maGeometry.nY = frame_y; - pThis->CallCallback( SalEvent::Move, nullptr ); - } - } - } - - return true; -} - -gboolean GtkSalFrame::signalScroll(GtkWidget*, GdkEvent* pInEvent, gpointer frame) -{ - GdkEventScroll& rEvent = pInEvent->scroll; - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - static sal_uLong nLines = 0; - if( ! nLines ) - { - char* pEnv = getenv( "SAL_WHEELLINES" ); - nLines = pEnv ? atoi( pEnv ) : 3; - if( nLines > 10 ) - nLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL; - } - - bool bNeg = (rEvent.direction == GDK_SCROLL_DOWN || rEvent.direction == GDK_SCROLL_RIGHT ); - SalWheelMouseEvent aEvent; - aEvent.mnTime = rEvent.time; - aEvent.mnX = static_cast<sal_uLong>(rEvent.x); - aEvent.mnY = static_cast<sal_uLong>(rEvent.y); - aEvent.mnDelta = bNeg ? -120 : 120; - aEvent.mnNotchDelta = bNeg ? -1 : 1; - aEvent.mnScrollLines = nLines; - aEvent.mnCode = GetMouseModCode( rEvent.state ); - aEvent.mbHorz = (rEvent.direction == GDK_SCROLL_LEFT || rEvent.direction == GDK_SCROLL_RIGHT); - - if( AllSettings::GetLayoutRTL() ) - aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; - - pThis->CallCallback( SalEvent::WheelMouse, &aEvent ); - - return true; -} - -gboolean GtkSalFrame::signalMotion( GtkWidget*, GdkEventMotion* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - SalMouseEvent aEvent; - aEvent.mnTime = pEvent->time; - aEvent.mnX = static_cast<long>(pEvent->x_root) - pThis->maGeometry.nX; - aEvent.mnY = static_cast<long>(pEvent->y_root) - pThis->maGeometry.nY; - aEvent.mnCode = GetMouseModCode( pEvent->state ); - aEvent.mnButton = 0; - - if( AllSettings::GetLayoutRTL() ) - aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; - - vcl::DeletionListener aDel( pThis ); - - pThis->CallCallback( SalEvent::MouseMove, &aEvent ); - - if( ! aDel.isDeleted() ) - { - int frame_x = static_cast<int>(pEvent->x_root - pEvent->x); - int frame_y = static_cast<int>(pEvent->y_root - pEvent->y); - if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY ) - { - pThis->maGeometry.nX = frame_x; - pThis->maGeometry.nY = frame_y; - pThis->CallCallback( SalEvent::Move, nullptr ); - } - - if( ! aDel.isDeleted() ) - { - // ask for the next hint - gint x, y; - GdkModifierType mask; - gdk_window_get_pointer( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &x, &y, &mask ); - } - } - - return true; -} - -gboolean GtkSalFrame::signalCrossing( GtkWidget*, GdkEventCrossing* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - SalMouseEvent aEvent; - aEvent.mnTime = pEvent->time; - aEvent.mnX = static_cast<long>(pEvent->x_root) - pThis->maGeometry.nX; - aEvent.mnY = static_cast<long>(pEvent->y_root) - pThis->maGeometry.nY; - aEvent.mnCode = GetMouseModCode( pEvent->state ); - aEvent.mnButton = 0; - - if (AllSettings::GetLayoutRTL()) - aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; - - pThis->CallCallback( (pEvent->type == GDK_ENTER_NOTIFY) ? SalEvent::MouseMove : SalEvent::MouseLeave, &aEvent ); - - return true; -} - -gboolean GtkSalFrame::signalExpose( GtkWidget*, GdkEventExpose* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - struct SalPaintEvent aEvent( pEvent->area.x, pEvent->area.y, pEvent->area.width, pEvent->area.height, OpenGLHelper::isVCLOpenGLEnabled() ); - - pThis->CallCallback( SalEvent::Paint, &aEvent ); - - return false; -} - -gboolean GtkSalFrame::signalConfigure( GtkWidget*, GdkEventConfigure* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - bool bMoved = false, bSized = false; - int x = pEvent->x, y = pEvent->y; - - /* HACK: during sizing/moving a toolbar pThis->maGeometry is actually - * already exact; even worse: due to the asynchronicity of configure - * events the borderwindow which would evaluate this event - * would size/move based on wrong data if we would actually evaluate - * this event. So let's swallow it. - */ - if( (pThis->m_nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION) && - GtkSalFrame::getDisplay()->GetCaptureFrame() == pThis ) - return false; - - /* #i31785# claims we cannot trust the x,y members of the event; - * they are e.g. not set correctly on maximize/demaximize; - * yet the gdkdisplay-x11.c code handling configure_events has - * done this XTranslateCoordinates work since the day ~zero. - */ - if( x != pThis->maGeometry.nX || y != pThis->maGeometry.nY ) - { - bMoved = true; - pThis->maGeometry.nX = x; - pThis->maGeometry.nY = y; - } - /* #i86302# - * for non sizeable windows we set the min and max hint for the window manager to - * achieve correct sizing. However this is asynchronous and e.g. on Compiz - * it sometimes happens that the window gets resized to another size (some default) - * if we update the size here, subsequent setMinMaxSize will use this wrong size - * - which is not good since the window manager will now size the window back to this - * wrong size at some point. - */ - if( pThis->m_bFullscreen || (pThis->m_nStyle & (SalFrameStyleFlags::SIZEABLE | SalFrameStyleFlags::PLUG)) == SalFrameStyleFlags::SIZEABLE ) - { - if( pEvent->width != static_cast<int>(pThis->maGeometry.nWidth) || pEvent->height != static_cast<int>(pThis->maGeometry.nHeight) ) - { - bSized = true; - pThis->maGeometry.nWidth = pEvent->width; - pThis->maGeometry.nHeight = pEvent->height; - } - } - - // update decoration hints - if( ! (pThis->m_nStyle & SalFrameStyleFlags::PLUG) ) - { - GdkRectangle aRect; - gdk_window_get_frame_extents( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &aRect ); - pThis->maGeometry.nTopDecoration = y - aRect.y; - pThis->maGeometry.nBottomDecoration = aRect.y + aRect.height - y - pEvent->height; - pThis->maGeometry.nLeftDecoration = x - aRect.x; - pThis->maGeometry.nRightDecoration = aRect.x + aRect.width - x - pEvent->width; - } - else - { - pThis->maGeometry.nTopDecoration = - pThis->maGeometry.nBottomDecoration = - pThis->maGeometry.nLeftDecoration = - pThis->maGeometry.nRightDecoration = 0; - } - - pThis->updateScreenNumber(); - if( bSized ) - pThis->AllocateFrame(); - - if( bMoved && bSized ) - pThis->CallCallback( SalEvent::MoveResize, nullptr ); - else if( bMoved ) - pThis->CallCallback( SalEvent::Move, nullptr ); - else if( bSized ) - pThis->CallCallback( SalEvent::Resize, nullptr ); - - if (bSized) - pThis->TriggerPaintEvent(); - return false; -} - -void GtkSalFrame::TriggerPaintEvent() -{ -} - -gboolean GtkSalFrame::signalFocus( GtkWidget*, GdkEventFocus* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - SalGenericInstance *pSalInstance = - static_cast< SalGenericInstance* >(GetSalData()->m_pInstance); - - // check if printers have changed (analogous to salframe focus handler) - pSalInstance->updatePrinterUpdate(); - - if( !pEvent->in ) - pThis->m_nKeyModifiers = ModKeyFlags::NONE; - - if( pThis->m_pIMHandler ) - pThis->m_pIMHandler->focusChanged( pEvent->in != 0 ); - - // ask for changed printers like generic implementation - if( pEvent->in && pSalInstance->isPrinterInit() ) - pSalInstance->updatePrinterUpdate(); - - // FIXME: find out who the hell steals the focus from our frame - // while we have the pointer grabbed, this should not come from - // the window manager. Is this an event that was still queued ? - // The focus does not seem to get set inside our process - - // in the meantime do not propagate focus get/lose if floats are open - if( m_nFloats == 0 ) - pThis->CallCallback( pEvent->in ? SalEvent::GetFocus : SalEvent::LoseFocus, nullptr ); - - return false; -} - -static OString getDisplayString() -{ - int nParams = rtl_getAppCommandArgCount(); - OUString aParam; - for( int i = 0; i < nParams; i++ ) - { - rtl_getAppCommandArg( i, &aParam.pData ); - if( i < nParams-1 && (aParam == "-display" || aParam == "--display" ) ) - { - rtl_getAppCommandArg( i+1, &aParam.pData ); - return OUStringToOString( aParam, osl_getThreadTextEncoding() ); - } - } - return OString(); -} - -gboolean GtkSalFrame::signalMap( GtkWidget *pWidget, GdkEvent*, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - //Spawn off a helper program that will attempt to set this fullscreen - //window either to span all displays or the current monitor - if (pThis->m_bFullscreen) - { - GdkWindow* gdkwin = widget_get_window(pThis->m_pWindow); - if (gdkwin) - { - OUString sProgramURL( "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/xid-fullscreen-on-all-monitors"); - rtl::Bootstrap::expandMacros(sProgramURL); - OUString sProgram; - if (osl::FileBase::getSystemPathFromFileURL(sProgramURL, sProgram) == osl::File::E_None) - { - OString sFinalProgram(OUStringToOString(sProgram, osl_getThreadTextEncoding()) - + " " + OString::number(static_cast<int>(GDK_WINDOW_XID(gdkwin))) - + " " + OString::number(static_cast<int>(pThis->m_bSpanMonitorsWhenFullscreen))); - OString sDisplay(getDisplayString()); - if (!sDisplay.isEmpty()) - { - sFinalProgram += "--display " + sDisplay; - } - (void) system(sFinalProgram.getStr()); - } - } - } - - if ( pThis->m_nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION ) - gtk_window_set_accept_focus( GTK_WINDOW(pWidget), true ); - - bool bSetFocus = pThis->m_bSetFocusOnMap; - pThis->m_bSetFocusOnMap = false; - - if( bSetFocus ) - pThis->ToTop( SalFrameToTop::GrabFocus ); - - pThis->CallCallback( SalEvent::Resize, nullptr ); - pThis->TriggerPaintEvent(); - - return false; -} - -gboolean GtkSalFrame::signalUnmap( GtkWidget*, GdkEvent*, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - pThis->CallCallback( SalEvent::Resize, nullptr ); - - return false; -} - -gboolean GtkSalFrame::signalKey( GtkWidget*, GdkEventKey* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - vcl::DeletionListener aDel( pThis ); - - if( pThis->m_pIMHandler ) - { - if( pThis->m_pIMHandler->handleKeyEvent( pEvent ) ) - return true; - } - - // handle modifiers - if( pEvent->keyval == GDK_KEY_Shift_L || pEvent->keyval == GDK_KEY_Shift_R || - pEvent->keyval == GDK_KEY_Control_L || pEvent->keyval == GDK_KEY_Control_R || - pEvent->keyval == GDK_KEY_Alt_L || pEvent->keyval == GDK_KEY_Alt_R || - pEvent->keyval == GDK_KEY_Meta_L || pEvent->keyval == GDK_KEY_Meta_R || - pEvent->keyval == GDK_KEY_Super_L || pEvent->keyval == GDK_KEY_Super_R ) - { - sal_uInt16 nModCode = GetKeyModCode( pEvent->state ); - ModKeyFlags nExtModMask = ModKeyFlags::NONE; - sal_uInt16 nModMask = 0; - // pressing just the ctrl key leads to a keysym of XK_Control but - // the event state does not contain ControlMask. In the release - // event it's the other way round: it does contain the Control mask. - // The modifier mode therefore has to be adapted manually. - switch( pEvent->keyval ) - { - case GDK_KEY_Control_L: - nExtModMask = ModKeyFlags::LeftMod1; - nModMask = KEY_MOD1; - break; - case GDK_KEY_Control_R: - nExtModMask = ModKeyFlags::RightMod1; - nModMask = KEY_MOD1; - break; - case GDK_KEY_Alt_L: - nExtModMask = ModKeyFlags::LeftMod2; - nModMask = KEY_MOD2; - break; - case GDK_KEY_Alt_R: - nExtModMask = ModKeyFlags::RightMod2; - nModMask = KEY_MOD2; - break; - case GDK_KEY_Shift_L: - nExtModMask = ModKeyFlags::LeftShift; - nModMask = KEY_SHIFT; - break; - case GDK_KEY_Shift_R: - nExtModMask = ModKeyFlags::RightShift; - nModMask = KEY_SHIFT; - break; - // Map Meta/Super to MOD3 modifier on all Unix systems - // except macOS - case GDK_KEY_Meta_L: - case GDK_KEY_Super_L: - nExtModMask = ModKeyFlags::LeftMod3; - nModMask = KEY_MOD3; - break; - case GDK_KEY_Meta_R: - case GDK_KEY_Super_R: - nExtModMask = ModKeyFlags::RightMod3; - nModMask = KEY_MOD3; - break; - } - - SalKeyModEvent aModEvt; - aModEvt.mbDown = pEvent->type == GDK_KEY_PRESS; - aModEvt.mnCode = nModCode; - - if( pEvent->type == GDK_KEY_RELEASE ) - { - aModEvt.mnModKeyCode = pThis->m_nKeyModifiers; - nModCode &= ~nModMask; - pThis->m_nKeyModifiers &= ~nExtModMask; - } - else - { - nModCode |= nModMask; - pThis->m_nKeyModifiers |= nExtModMask; - aModEvt.mnModKeyCode = pThis->m_nKeyModifiers; - } - - pThis->CallCallback( SalEvent::KeyModChange, &aModEvt ); - } - else - { - pThis->doKeyCallback( pEvent->state, - pEvent->keyval, - pEvent->hardware_keycode, - pEvent->group, - sal_Unicode(gdk_keyval_to_unicode( pEvent->keyval )), - (pEvent->type == GDK_KEY_PRESS), - false ); - if( ! aDel.isDeleted() ) - pThis->m_nKeyModifiers = ModKeyFlags::NONE; - } - - if( !aDel.isDeleted() && pThis->m_pIMHandler ) - pThis->m_pIMHandler->updateIMSpotLocation(); - - return true; -} - -gboolean GtkSalFrame::signalDelete( GtkWidget*, GdkEvent*, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - pThis->CallCallback( SalEvent::Close, nullptr ); - - return true; -} - -void GtkSalFrame::signalStyleSet( GtkWidget*, GtkStyle* pPrevious, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - - // every frame gets an initial style set on creation - // do not post these as the whole application tends to - // redraw itself to adjust to the new style - // where there IS no new style resulting in tremendous unnecessary flickering - if( pPrevious != nullptr ) - { - // signalStyleSet does NOT usually have the gdk lock - // so post user event to safely dispatch the SalEvent::SettingsChanged - // note: settings changed for multiple frames is avoided in winproc.cxx ImplHandleSettings - GtkSalFrame::getDisplay()->SendInternalEvent( pThis, nullptr, SalEvent::SettingsChanged ); - - // fire off font-changed when the system cairo font hints change - GtkInstance *pInstance = static_cast<GtkInstance*>(GetSalData()->m_pInstance); - const cairo_font_options_t* pLastCairoFontOptions = pInstance->GetLastSeenCairoFontOptions(); - const cairo_font_options_t* pCurrentCairoFontOptions = gdk_screen_get_font_options(gdk_screen_get_default()); - bool bFontSettingsChanged = true; - if (pLastCairoFontOptions && pCurrentCairoFontOptions) - bFontSettingsChanged = !cairo_font_options_equal(pLastCairoFontOptions, pCurrentCairoFontOptions); - else if (!pLastCairoFontOptions && !pCurrentCairoFontOptions) - bFontSettingsChanged = false; - if (bFontSettingsChanged) - { - pInstance->ResetLastSeenCairoFontOptions(pCurrentCairoFontOptions); - GtkSalFrame::getDisplay()->SendInternalEvent( pThis, nullptr, SalEvent::FontChanged ); - } - } - - /* #i64117# gtk sets a nice background pixmap - * but we actually don't really want that, so save - * some time on the Xserver as well as prevent - * some paint issues - */ - GdkWindow* pWin = widget_get_window(GTK_WIDGET(pThis->getWindow())); - if( pWin ) - { - ::Window aWin = GDK_WINDOW_XWINDOW(pWin); - if( aWin != None ) - XSetWindowBackgroundPixmap( GtkSalFrame::getDisplay()->GetDisplay(), - aWin, - None ); - } - if( ! pThis->m_pParent ) - { - // signalize theme changed for NWF caches - // FIXME: should be called only once for a style change - GtkSalGraphics::bThemeChanged = true; - } -} - -gboolean GtkSalFrame::signalWindowState( GtkWidget*, GdkEvent* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - if( (pThis->m_nState & GDK_WINDOW_STATE_ICONIFIED) != (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_ICONIFIED ) ) - { - GtkSalFrame::getDisplay()->SendInternalEvent( pThis, nullptr, SalEvent::Resize ); - pThis->TriggerPaintEvent(); - } - - if( (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_MAXIMIZED) && - ! (pThis->m_nState & GDK_WINDOW_STATE_MAXIMIZED) ) - { - pThis->m_aRestorePosSize = - tools::Rectangle( Point( pThis->maGeometry.nX, pThis->maGeometry.nY ), - Size( pThis->maGeometry.nWidth, pThis->maGeometry.nHeight ) ); - } - pThis->m_nState = pEvent->window_state.new_window_state; - - #if OSL_DEBUG_LEVEL > 1 - if( (pEvent->window_state.changed_mask & GDK_WINDOW_STATE_FULLSCREEN) ) - { - fprintf( stderr, "window %p %s full screen state\n", - pThis, - (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_FULLSCREEN) ? "enters" : "leaves"); - } - #endif - - return false; -} - -gboolean GtkSalFrame::signalVisibility( GtkWidget*, GdkEventVisibility* /*pEvent*/, gpointer /*frame*/ ) -{ - return true; -} - -void GtkSalFrame::signalDestroy( GtkWidget* pObj, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); - if( pObj == pThis->m_pWindow ) - { - pThis->m_pFixedContainer = nullptr; - pThis->m_pEventBox = nullptr; - pThis->m_pWindow = nullptr; - pThis->InvalidateGraphics(); - } -} - -// GtkSalFrame::IMHandler - -GtkSalFrame::IMHandler::IMHandler( GtkSalFrame* pFrame ) -: m_pFrame(pFrame), - m_nPrevKeyPresses( 0 ), - m_pIMContext( nullptr ), - m_bFocused( true ), - m_bPreeditJustChanged( false ) -{ - m_aInputEvent.mpTextAttr = nullptr; - createIMContext(); -} - -GtkSalFrame::IMHandler::~IMHandler() -{ - // cancel an eventual event posted to begin preedit again - GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SalEvent::ExtTextInput ); - deleteIMContext(); -} - -void GtkSalFrame::IMHandler::createIMContext() -{ - if( m_pIMContext ) - return; - - m_pIMContext = gtk_im_multicontext_new (); - g_signal_connect( m_pIMContext, "commit", - G_CALLBACK (signalIMCommit), this ); - g_signal_connect( m_pIMContext, "preedit_changed", - G_CALLBACK (signalIMPreeditChanged), this ); - g_signal_connect( m_pIMContext, "retrieve_surrounding", - G_CALLBACK (signalIMRetrieveSurrounding), this ); - g_signal_connect( m_pIMContext, "delete_surrounding", - G_CALLBACK (signalIMDeleteSurrounding), this ); - g_signal_connect( m_pIMContext, "preedit_start", - G_CALLBACK (signalIMPreeditStart), this ); - g_signal_connect( m_pIMContext, "preedit_end", - G_CALLBACK (signalIMPreeditEnd), this ); - - GetGenericUnixSalData()->ErrorTrapPush(); - gtk_im_context_set_client_window( m_pIMContext, widget_get_window(GTK_WIDGET(m_pFrame->m_pWindow)) ); - gtk_im_context_focus_in( m_pIMContext ); - GetGenericUnixSalData()->ErrorTrapPop(); - m_bFocused = true; - -} - -void GtkSalFrame::IMHandler::deleteIMContext() -{ - if( m_pIMContext ) - { - // first give IC a chance to deinitialize - GetGenericUnixSalData()->ErrorTrapPush(); - gtk_im_context_set_client_window( m_pIMContext, nullptr ); - GetGenericUnixSalData()->ErrorTrapPop(); - // destroy old IC - g_object_unref( m_pIMContext ); - m_pIMContext = nullptr; - } -} - -void GtkSalFrame::IMHandler::doCallEndExtTextInput() -{ - m_aInputEvent.mpTextAttr = nullptr; - m_pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr ); -} - -void GtkSalFrame::IMHandler::updateIMSpotLocation() -{ - SalExtTextInputPosEvent aPosEvent; - m_pFrame->CallCallback( SalEvent::ExtTextInputPos, static_cast<void*>(&aPosEvent) ); - GdkRectangle aArea; - aArea.x = aPosEvent.mnX; - aArea.y = aPosEvent.mnY; - aArea.width = aPosEvent.mnWidth; - aArea.height = aPosEvent.mnHeight; - GetGenericUnixSalData()->ErrorTrapPush(); - gtk_im_context_set_cursor_location( m_pIMContext, &aArea ); - GetGenericUnixSalData()->ErrorTrapPop(); -} - -void GtkSalFrame::IMHandler::sendEmptyCommit() -{ - vcl::DeletionListener aDel( m_pFrame ); - - SalExtTextInputEvent aEmptyEv; - aEmptyEv.mpTextAttr = nullptr; - aEmptyEv.maText.clear(); - aEmptyEv.mnCursorPos = 0; - aEmptyEv.mnCursorFlags = 0; - m_pFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void*>(&aEmptyEv) ); - if( ! aDel.isDeleted() ) - m_pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr ); -} - -void GtkSalFrame::IMHandler::endExtTextInput( EndExtTextInputFlags /*nFlags*/ ) -{ - gtk_im_context_reset ( m_pIMContext ); - - if( m_aInputEvent.mpTextAttr ) - { - vcl::DeletionListener aDel( m_pFrame ); - // delete preedit in sal (commit an empty string) - sendEmptyCommit(); - if( ! aDel.isDeleted() ) - { - // mark previous preedit state again (will e.g. be sent at focus gain) - m_aInputEvent.mpTextAttr = m_aInputFlags.data(); - if( m_bFocused ) - { - // begin preedit again - GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SalEvent::ExtTextInput ); - } - } - } -} - -void GtkSalFrame::IMHandler::focusChanged( bool bFocusIn ) -{ - m_bFocused = bFocusIn; - if( bFocusIn ) - { - GetGenericUnixSalData()->ErrorTrapPush(); - gtk_im_context_focus_in( m_pIMContext ); - GetGenericUnixSalData()->ErrorTrapPop(); - if( m_aInputEvent.mpTextAttr ) - { - sendEmptyCommit(); - // begin preedit again - GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SalEvent::ExtTextInput ); - } - } - else - { - GetGenericUnixSalData()->ErrorTrapPush(); - gtk_im_context_focus_out( m_pIMContext ); - GetGenericUnixSalData()->ErrorTrapPop(); - // cancel an eventual event posted to begin preedit again - GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SalEvent::ExtTextInput ); - } -} - -bool GtkSalFrame::IMHandler::handleKeyEvent( GdkEventKey* pEvent ) -{ - vcl::DeletionListener aDel( m_pFrame ); - - if( pEvent->type == GDK_KEY_PRESS ) - { - // Add this key press event to the list of previous key presses - // to which we compare key release events. If a later key release - // event has a matching key press event in this list, we swallow - // the key release because some GTK Input Methods don't swallow it - // for us. - m_aPrevKeyPresses.emplace_back(pEvent ); - m_nPrevKeyPresses++; - - // Also pop off the earliest key press event if there are more than 10 - // already. - while (m_nPrevKeyPresses > 10) - { - m_aPrevKeyPresses.pop_front(); - m_nPrevKeyPresses--; - } - - GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); - - // #i51353# update spot location on every key input since we cannot - // know which key may activate a preedit choice window - updateIMSpotLocation(); - if( aDel.isDeleted() ) - return true; - - gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); - g_object_unref( pRef ); - - if( aDel.isDeleted() ) - return true; - - m_bPreeditJustChanged = false; - - if( bResult ) - return true; - else - { - SAL_WARN_IF( m_nPrevKeyPresses <= 0, "vcl.gtk", "key press has vanished !" ); - if( ! m_aPrevKeyPresses.empty() ) // sanity check - { - // event was not swallowed, do not filter a following - // key release event - // note: this relies on gtk_im_context_filter_keypress - // returning without calling a handler (in the "not swallowed" - // case ) which might change the previous key press list so - // we would pop the wrong event here - m_aPrevKeyPresses.pop_back(); - m_nPrevKeyPresses--; - } - } - } - - // Determine if we got an earlier key press event corresponding to this key release - if (pEvent->type == GDK_KEY_RELEASE) - { - GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); - gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); - g_object_unref( pRef ); - - if( aDel.isDeleted() ) - return true; - - m_bPreeditJustChanged = false; - - auto iter = std::find(m_aPrevKeyPresses.begin(), m_aPrevKeyPresses.end(), pEvent); - // If we found a corresponding previous key press event, swallow the release - // and remove the earlier key press from our list - if (iter != m_aPrevKeyPresses.end()) - { - m_aPrevKeyPresses.erase(iter); - m_nPrevKeyPresses--; - return true; - } - - if( bResult ) - return true; - } - - return false; -} - -/* FIXME: -* #122282# still more hacking: some IMEs never start a preedit but simply commit -* in this case we cannot commit a single character. Workaround: do not do the -* single key hack for enter or space if the unicode committed does not match -*/ - -static bool checkSingleKeyCommitHack( guint keyval, sal_Unicode cCode ) -{ - bool bRet = true; - switch( keyval ) - { - case GDK_KEY_KP_Enter: - case GDK_KEY_Return: - if( cCode != '\n' && cCode != '\r' ) - bRet = false; - break; - case GDK_KEY_space: - case GDK_KEY_KP_Space: - if( cCode != ' ' ) - bRet = false; - break; - default: - break; - } - return bRet; -} - -void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext* pContext, gchar* pText, gpointer im_handler ) -{ - GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler); - - SolarMutexGuard aGuard; - vcl::DeletionListener aDel( pThis->m_pFrame ); - { - const bool bWasPreedit = - (pThis->m_aInputEvent.mpTextAttr != nullptr) || - pThis->m_bPreeditJustChanged; - - pThis->m_aInputEvent.mpTextAttr = nullptr; - pThis->m_aInputEvent.maText = OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ); - pThis->m_aInputEvent.mnCursorPos = pThis->m_aInputEvent.maText.getLength(); - pThis->m_aInputEvent.mnCursorFlags = 0; - - pThis->m_aInputFlags.clear(); - - /* necessary HACK: all keyboard input comes in here as soon as an IMContext is set - * which is logical and consequent. But since even simple input like - * <space> comes through the commit signal instead of signalKey - * and all kinds of windows only implement KeyInput (e.g. PushButtons, - * RadioButtons and a lot of other Controls), will send a single - * KeyInput/KeyUp sequence instead of an ExtText event if there - * never was a preedit and the text is only one character. - * - * In this case there the last ExtText event must have been - * SalEvent::EndExtTextInput, either because of a regular commit - * or because there never was a preedit. - */ - bool bSingleCommit = false; - if( ! bWasPreedit - && pThis->m_aInputEvent.maText.getLength() == 1 - && ! pThis->m_aPrevKeyPresses.empty() - ) - { - const PreviousKeyPress& rKP = pThis->m_aPrevKeyPresses.back(); - sal_Unicode aOrigCode = pThis->m_aInputEvent.maText[0]; - - if( checkSingleKeyCommitHack( rKP.keyval, aOrigCode ) ) - { - pThis->m_pFrame->doKeyCallback( rKP.state, rKP.keyval, rKP.hardware_keycode, rKP.group, aOrigCode, true, true ); - bSingleCommit = true; - } - } - if( ! bSingleCommit ) - { - pThis->m_pFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void*>(&pThis->m_aInputEvent)); - if( ! aDel.isDeleted() ) - pThis->doCallEndExtTextInput(); - } - if( ! aDel.isDeleted() ) - { - // reset input event - pThis->m_aInputEvent.maText.clear(); - pThis->m_aInputEvent.mnCursorPos = 0; - pThis->updateIMSpotLocation(); - } - } -#ifdef __sun - // #i51356# workaround a solaris IIIMP bug - // in case of partial commits the preedit changed signal - // and commit signal come in wrong order - if( ! aDel.isDeleted() ) - signalIMPreeditChanged( pContext, im_handler ); -#else - (void) pContext; -#endif -} - -void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext*, gpointer im_handler ) -{ - GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler); - - char* pText = nullptr; - PangoAttrList* pAttrs = nullptr; - gint nCursorPos = 0; - - gtk_im_context_get_preedit_string( pThis->m_pIMContext, - &pText, - &pAttrs, - &nCursorPos ); - if( pText && ! *pText ) // empty string - { - // change from nothing to nothing -> do not start preedit - // e.g. this will activate input into a calc cell without - // user input - if( pThis->m_aInputEvent.maText.getLength() == 0 ) - { - g_free( pText ); - pango_attr_list_unref( pAttrs ); - return; - } - } - - pThis->m_bPreeditJustChanged = true; - - bool bEndPreedit = (!pText || !*pText) && pThis->m_aInputEvent.mpTextAttr != nullptr; - pThis->m_aInputEvent.maText = pText ? OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ) : OUString(); - pThis->m_aInputEvent.mnCursorPos = nCursorPos; - pThis->m_aInputEvent.mnCursorFlags = 0; - - pThis->m_aInputFlags = std::vector<ExtTextInputAttr>( std::max( 1, static_cast<int>(pThis->m_aInputEvent.maText.getLength()) ), ExtTextInputAttr::NONE ); - - PangoAttrIterator *iter = pango_attr_list_get_iterator(pAttrs); - do - { - GSList *attr_list = nullptr; - GSList *tmp_list = nullptr; - gint start, end; - ExtTextInputAttr sal_attr = ExtTextInputAttr::NONE; - - pango_attr_iterator_range (iter, &start, &end); - if (start == G_MAXINT || end == G_MAXINT) - { - auto len = pText ? g_utf8_strlen(pText, -1) : 0; - if (end == G_MAXINT) - end = len; - if (start == G_MAXINT) - start = len; - } - if (end == start) - continue; - - start = g_utf8_pointer_to_offset (pText, pText + start); - end = g_utf8_pointer_to_offset (pText, pText + end); - - tmp_list = attr_list = pango_attr_iterator_get_attrs (iter); - while (tmp_list) - { - PangoAttribute *pango_attr = static_cast<PangoAttribute *>(tmp_list->data); - - switch (pango_attr->klass->type) - { - case PANGO_ATTR_BACKGROUND: - sal_attr |= ExtTextInputAttr::Highlight; - pThis->m_aInputEvent.mnCursorFlags |= EXTTEXTINPUT_CURSOR_INVISIBLE; - break; - case PANGO_ATTR_UNDERLINE: - sal_attr |= ExtTextInputAttr::Underline; - break; - case PANGO_ATTR_STRIKETHROUGH: - sal_attr |= ExtTextInputAttr::RedText; - break; - default: - break; - } - pango_attribute_destroy (pango_attr); - tmp_list = tmp_list->next; - } - if (sal_attr == ExtTextInputAttr::NONE) - sal_attr |= ExtTextInputAttr::Underline; - g_slist_free (attr_list); - - // Set the sal attributes on our text - for (int i = start; i < end; ++i) - { - SAL_WARN_IF(i >= static_cast<int>(pThis->m_aInputFlags.size()), - "vcl.gtk", "pango attrib out of range. Broken range: " - << start << "," << end << " Legal range: 0," - << pThis->m_aInputFlags.size()); - if (i >= static_cast<int>(pThis->m_aInputFlags.size())) - continue; - pThis->m_aInputFlags[i] |= sal_attr; - } - } while (pango_attr_iterator_next (iter)); - pango_attr_iterator_destroy(iter); - - pThis->m_aInputEvent.mpTextAttr = pThis->m_aInputFlags.data(); - - g_free( pText ); - pango_attr_list_unref( pAttrs ); - - SolarMutexGuard aGuard; - vcl::DeletionListener aDel( pThis->m_pFrame ); - - pThis->m_pFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void*>(&pThis->m_aInputEvent)); - if( bEndPreedit && ! aDel.isDeleted() ) - pThis->doCallEndExtTextInput(); - if( ! aDel.isDeleted() ) - pThis->updateIMSpotLocation(); -} - -void GtkSalFrame::IMHandler::signalIMPreeditStart( GtkIMContext*, gpointer /*im_handler*/ ) -{ -} - -void GtkSalFrame::IMHandler::signalIMPreeditEnd( GtkIMContext*, gpointer im_handler ) -{ - GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler); - - pThis->m_bPreeditJustChanged = true; - - SolarMutexGuard aGuard; - vcl::DeletionListener aDel( pThis->m_pFrame ); - pThis->doCallEndExtTextInput(); - if( ! aDel.isDeleted() ) - pThis->updateIMSpotLocation(); -} - -static uno::Reference<accessibility::XAccessibleEditableText> lcl_GetxText(vcl::Window *pFocusWin) -{ - uno::Reference<accessibility::XAccessibleEditableText> xText; - try - { - uno::Reference< accessibility::XAccessible > xAccessible( pFocusWin->GetAccessible() ); - if (xAccessible.is()) - xText = FindFocusedEditableText(xAccessible->getAccessibleContext()); - } - catch(const uno::Exception&) - { - TOOLS_WARN_EXCEPTION( "vcl.gtk", "Exception in getting input method surrounding text"); - } - return xText; -} - -gboolean GtkSalFrame::IMHandler::signalIMRetrieveSurrounding( GtkIMContext* pContext, gpointer /*im_handler*/ ) -{ - vcl::Window *pFocusWin = Application::GetFocusWindow(); - if (!pFocusWin) - return true; - - uno::Reference<accessibility::XAccessibleEditableText> xText = lcl_GetxText(pFocusWin); - if (xText.is()) - { - sal_Int32 nPosition = xText->getCaretPosition(); - OUString sAllText = xText->getText(); - OString sUTF = OUStringToOString(sAllText, RTL_TEXTENCODING_UTF8); - OUString sCursorText(sAllText.copy(0, nPosition)); - gtk_im_context_set_surrounding(pContext, sUTF.getStr(), sUTF.getLength(), - OUStringToOString(sCursorText, RTL_TEXTENCODING_UTF8).getLength()); - return true; - } - - return false; -} - -gboolean GtkSalFrame::IMHandler::signalIMDeleteSurrounding( GtkIMContext*, gint offset, gint nchars, - gpointer /*im_handler*/ ) -{ - vcl::Window *pFocusWin = Application::GetFocusWindow(); - if (!pFocusWin) - return true; - - uno::Reference<accessibility::XAccessibleEditableText> xText = lcl_GetxText(pFocusWin); - if (xText.is()) - { - sal_Int32 nPosition = xText->getCaretPosition(); - // #i111768# range checking - sal_Int32 nDeletePos = nPosition + offset; - sal_Int32 nDeleteEnd = nDeletePos + nchars; - if (nDeletePos < 0) - nDeletePos = 0; - if (nDeleteEnd < 0) - nDeleteEnd = 0; - if (nDeleteEnd > xText->getCharacterCount()) - nDeleteEnd = xText->getCharacterCount(); - - xText->deleteText(nDeletePos, nDeleteEnd); - //tdf91641 adjust cursor if deleted chars shift it forward (normal case) - if (nDeletePos < nPosition) - { - if (nDeleteEnd <= nPosition) - nPosition = nPosition - (nDeleteEnd - nDeletePos); - else - nPosition = nDeletePos; - - if (xText->getCharacterCount() >= nPosition) - xText->setCaretPosition( nPosition ); - } - return true; - } - - return false; -} - -Size GtkSalDisplay::GetScreenSize( int nDisplayScreen ) -{ - tools::Rectangle aRect = m_pSys->GetDisplayScreenPosSizePixel( nDisplayScreen ); - return Size( aRect.GetWidth(), aRect.GetHeight() ); -} - -sal_uIntPtr GtkSalFrame::GetNativeWindowHandle() -{ - return widget_get_xid(m_pWindow); -} - -GdkEvent* GtkSalFrame::makeFakeKeyPress(GtkWidget* pWidget) -{ - GdkEvent *event = gdk_event_new(GDK_KEY_PRESS); - event->key.window = GDK_WINDOW(g_object_ref(gtk_widget_get_window(pWidget))); - event->key.send_event = 1 /* TRUE */; - event->key.time = gtk_get_current_event_time(); - event->key.state = 0; - event->key.keyval = 0; - event->key.length = 0; - event->key.string = nullptr; - event->key.hardware_keycode = 0; - event->key.group = 0; - event->key.is_modifier = false; - return event; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx deleted file mode 100644 index d0503519a084..000000000000 --- a/vcl/unx/gtk/gtksalmenu.cxx +++ /dev/null @@ -1,1444 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.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 <unx/gtk/gtksalmenu.hxx> - -#ifdef ENABLE_GMENU_INTEGRATION - -#include <unx/gendata.hxx> -#include <unx/saldisp.hxx> -#include <unx/gtk/gtkdata.hxx> -#include <unx/gtk/glomenu.h> -#include <unx/gtk/gloactiongroup.h> -#include <vcl/floatwin.hxx> -#include <vcl/menu.hxx> -#include <vcl/pngwrite.hxx> -#include <unx/gtk/gtkinst.hxx> - -#include <sal/log.hxx> -#include <tools/stream.hxx> -#include <window.h> -#include <strings.hrc> - -static bool bUnityMode = false; - -/* - * This function generates a unique command name for each menu item - */ -static gchar* GetCommandForItem(GtkSalMenu* pParentMenu, sal_uInt16 nItemId) -{ - OString aCommand("window-"); - aCommand = aCommand + OString::number(reinterpret_cast<unsigned long>(pParentMenu)); - aCommand = aCommand + "-" + OString::number(nItemId); - return g_strdup(aCommand.getStr()); -} - -static gchar* GetCommandForItem(GtkSalMenuItem* pSalMenuItem) -{ - return GetCommandForItem(pSalMenuItem->mpParentMenu, - pSalMenuItem->mnId); -} - -bool GtkSalMenu::PrepUpdate() -{ -#if GTK_CHECK_VERSION(3,0,0) - return mpMenuModel && mpActionGroup; -#else - return bUnityMode && mpMenuModel && mpActionGroup; -#endif -} - -/* - * Menu updating methods - */ - -static void RemoveSpareItemsFromNativeMenu( GLOMenu* pMenu, GList** pOldCommandList, unsigned nSection, unsigned nValidItems ) -{ - sal_Int32 nSectionItems = g_lo_menu_get_n_items_from_section( pMenu, nSection ); - - while ( nSectionItems > static_cast<sal_Int32>(nValidItems) ) - { - gchar* aCommand = g_lo_menu_get_command_from_item_in_section( pMenu, nSection, --nSectionItems ); - - if ( aCommand != nullptr && pOldCommandList != nullptr ) - *pOldCommandList = g_list_append( *pOldCommandList, g_strdup( aCommand ) ); - - g_free( aCommand ); - - g_lo_menu_remove_from_section( pMenu, nSection, nSectionItems ); - } -} - -typedef std::pair<GtkSalMenu*, sal_uInt16> MenuAndId; - -namespace -{ - MenuAndId decode_command(const gchar *action_name) - { - OString sCommand(action_name); - - sal_Int32 nIndex = 0; - OString sWindow = sCommand.getToken(0, '-', nIndex); - OString sGtkSalMenu = sCommand.getToken(0, '-', nIndex); - OString sItemId = sCommand.getToken(0, '-', nIndex); - - GtkSalMenu* pSalSubMenu = reinterpret_cast<GtkSalMenu*>(sGtkSalMenu.toInt64()); - - assert(sWindow == "window" && pSalSubMenu); - (void) sWindow; - - return MenuAndId(pSalSubMenu, sItemId.toInt32()); - } -} - -static void RemoveDisabledItemsFromNativeMenu(GLOMenu* pMenu, GList** pOldCommandList, - sal_Int32 nSection, GActionGroup* pActionGroup) -{ - while (nSection >= 0) - { - sal_Int32 nSectionItems = g_lo_menu_get_n_items_from_section( pMenu, nSection ); - while (nSectionItems--) - { - gchar* pCommand = g_lo_menu_get_command_from_item_in_section(pMenu, nSection, nSectionItems); - // remove disabled entries - bool bRemove = !g_action_group_get_action_enabled(pActionGroup, pCommand); - if (!bRemove) - { - //also remove any empty submenus - GLOMenu* pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section(pMenu, nSection, nSectionItems); - if (pSubMenuModel) - { - gint nSubMenuSections = g_menu_model_get_n_items(G_MENU_MODEL(pSubMenuModel)); - if (nSubMenuSections == 0) - bRemove = true; - else if (nSubMenuSections == 1) - { - gint nItems = g_lo_menu_get_n_items_from_section(pSubMenuModel, 0); - if (nItems == 0) - bRemove = true; - else if (nItems == 1) - { - //If the only entry is the "No Selection Possible" entry, then we are allowed - //to removed it - gchar* pSubCommand = g_lo_menu_get_command_from_item_in_section(pSubMenuModel, 0, 0); - MenuAndId aMenuAndId(decode_command(pSubCommand)); - bRemove = aMenuAndId.second == 0xFFFF; - g_free(pSubCommand); - } - } - } - } - - if (bRemove) - { - //but tdf#86850 Always display clipboard functions - bRemove = g_strcmp0(pCommand, ".uno:Cut") && - g_strcmp0(pCommand, ".uno:Copy") && - g_strcmp0(pCommand, ".uno:Paste"); - } - - if (bRemove) - { - if (pCommand != nullptr && pOldCommandList != nullptr) - *pOldCommandList = g_list_append(*pOldCommandList, g_strdup(pCommand)); - g_lo_menu_remove_from_section(pMenu, nSection, nSectionItems); - } - - g_free(pCommand); - } - --nSection; - } -} - -static void RemoveSpareSectionsFromNativeMenu( GLOMenu* pMenu, GList** pOldCommandList, sal_Int32 nLastSection ) -{ - if ( pMenu == nullptr || pOldCommandList == nullptr ) - return; - - sal_Int32 n = g_menu_model_get_n_items( G_MENU_MODEL( pMenu ) ) - 1; - - for ( ; n > nLastSection; n--) - { - RemoveSpareItemsFromNativeMenu( pMenu, pOldCommandList, n, 0 ); - g_lo_menu_remove( pMenu, n ); - } -} - -static gint CompareStr( gpointer str1, gpointer str2 ) -{ - return g_strcmp0( static_cast<const gchar*>(str1), static_cast<const gchar*>(str2) ); -} - -static void RemoveUnusedCommands( GLOActionGroup* pActionGroup, GList* pOldCommandList, GList* pNewCommandList ) -{ - if ( pActionGroup == nullptr || pOldCommandList == nullptr ) - { - g_list_free_full( pOldCommandList, g_free ); - g_list_free_full( pNewCommandList, g_free ); - return; - } - - while ( pNewCommandList != nullptr ) - { - GList* pNewCommand = g_list_first( pNewCommandList ); - pNewCommandList = g_list_remove_link( pNewCommandList, pNewCommand ); - - gpointer aCommand = g_list_nth_data( pNewCommand, 0 ); - - GList* pOldCommand = g_list_find_custom( pOldCommandList, aCommand, reinterpret_cast<GCompareFunc>(CompareStr) ); - - if ( pOldCommand != nullptr ) - { - pOldCommandList = g_list_remove_link( pOldCommandList, pOldCommand ); - g_list_free_full( pOldCommand, g_free ); - } - - g_list_free_full( pNewCommand, g_free ); - } - - while ( pOldCommandList != nullptr ) - { - GList* pCommand = g_list_first( pOldCommandList ); - pOldCommandList = g_list_remove_link( pOldCommandList, pCommand ); - - gchar* aCommand = static_cast<gchar*>(g_list_nth_data( pCommand, 0 )); - - g_lo_action_group_remove( pActionGroup, aCommand ); - - g_list_free_full( pCommand, g_free ); - } -} - -void GtkSalMenu::ImplUpdate(bool bRecurse, bool bRemoveDisabledEntries) -{ - SolarMutexGuard aGuard; - - SAL_INFO("vcl.unity", "ImplUpdate pre PrepUpdate"); - if( !PrepUpdate() ) - return; - - if (mbNeedsUpdate) - { - mbNeedsUpdate = false; - if (mbMenuBar && maUpdateMenuBarIdle.IsActive()) - { - maUpdateMenuBarIdle.Stop(); - maUpdateMenuBarIdle.Invoke(); - return; - } - } - - Menu* pVCLMenu = mpVCLMenu; - GLOMenu* pLOMenu = G_LO_MENU( mpMenuModel ); - GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup ); - SAL_INFO("vcl.unity", "Syncing vcl menu " << pVCLMenu << " to menu model " << pLOMenu << " and action group " << pActionGroup); - GList *pOldCommandList = nullptr; - GList *pNewCommandList = nullptr; - - sal_uInt16 nLOMenuSize = g_menu_model_get_n_items( G_MENU_MODEL( pLOMenu ) ); - - if ( nLOMenuSize == 0 ) - g_lo_menu_new_section( pLOMenu, 0, nullptr ); - - sal_Int32 nSection = 0; - sal_Int32 nItemPos = 0; - sal_Int32 validItems = 0; - sal_Int32 nItem; - - for ( nItem = 0; nItem < static_cast<sal_Int32>(GetItemCount()); nItem++ ) { - if ( !IsItemVisible( nItem ) ) - continue; - - GtkSalMenuItem *pSalMenuItem = GetItemAtPos( nItem ); - sal_uInt16 nId = pSalMenuItem->mnId; - - // PopupMenu::ImplExecute might add <No Selection Possible> entry to top-level - // popup menu, but we have our own implementation below, so skip that one. - if ( nId == 0xFFFF ) - continue; - - if ( pSalMenuItem->mnType == MenuItemType::SEPARATOR ) - { - // Delete extra items from current section. - RemoveSpareItemsFromNativeMenu( pLOMenu, &pOldCommandList, nSection, validItems ); - - nSection++; - nItemPos = 0; - validItems = 0; - - if ( nLOMenuSize <= nSection ) - { - g_lo_menu_new_section( pLOMenu, nSection, nullptr ); - nLOMenuSize++; - } - - continue; - } - - if ( nItemPos >= g_lo_menu_get_n_items_from_section( pLOMenu, nSection ) ) - g_lo_menu_insert_in_section( pLOMenu, nSection, nItemPos, "EMPTY STRING" ); - - // Get internal menu item values. - OUString aText = pVCLMenu->GetItemText( nId ); - Image aImage = pVCLMenu->GetItemImage( nId ); - bool bEnabled = pVCLMenu->IsItemEnabled( nId ); - vcl::KeyCode nAccelKey = pVCLMenu->GetAccelKey( nId ); - bool bChecked = pVCLMenu->IsItemChecked( nId ); - MenuItemBits itemBits = pVCLMenu->GetItemBits( nId ); - - // Store current item command in command list. - gchar *aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pLOMenu, nSection, nItemPos ); - - if ( aCurrentCommand != nullptr ) - pOldCommandList = g_list_append( pOldCommandList, aCurrentCommand ); - - // Get the new command for the item. - gchar* aNativeCommand = GetCommandForItem(pSalMenuItem); - - // Force updating of native menu labels. - NativeSetItemText( nSection, nItemPos, aText ); - NativeSetItemIcon( nSection, nItemPos, aImage ); - NativeSetAccelerator( nSection, nItemPos, nAccelKey, nAccelKey.GetName( GetFrame()->GetWindow() ) ); - - if ( g_strcmp0( aNativeCommand, "" ) != 0 && pSalMenuItem->mpSubMenu == nullptr ) - { - NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, bChecked, false ); - NativeCheckItem( nSection, nItemPos, itemBits, bChecked ); - NativeSetEnableItem( aNativeCommand, bEnabled ); - - pNewCommandList = g_list_append( pNewCommandList, g_strdup( aNativeCommand ) ); - } - - GtkSalMenu* pSubmenu = pSalMenuItem->mpSubMenu; - - if ( pSubmenu && pSubmenu->GetMenu() ) - { - bool bNonMenuChangedToMenu = NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, false, true ); - pNewCommandList = g_list_append( pNewCommandList, g_strdup( aNativeCommand ) ); - - GLOMenu* pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section( pLOMenu, nSection, nItemPos ); - - if ( pSubMenuModel == nullptr ) - { - g_lo_menu_new_submenu_in_item_in_section( pLOMenu, nSection, nItemPos ); - pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section( pLOMenu, nSection, nItemPos ); - } - - g_object_unref( pSubMenuModel ); - - if (bRecurse || bNonMenuChangedToMenu) - { - SAL_INFO("vcl.unity", "preparing submenu " << pSubMenuModel << " to menu model " << G_MENU_MODEL(pSubMenuModel) << " and action group " << G_ACTION_GROUP(pActionGroup)); - pSubmenu->SetMenuModel( G_MENU_MODEL( pSubMenuModel ) ); - pSubmenu->SetActionGroup( G_ACTION_GROUP( pActionGroup ) ); - pSubmenu->ImplUpdate(true, bRemoveDisabledEntries); - } - } - - g_free( aNativeCommand ); - - ++nItemPos; - ++validItems; - } - - if (bRemoveDisabledEntries) - { - // Delete disabled items in last section. - RemoveDisabledItemsFromNativeMenu(pLOMenu, &pOldCommandList, nSection, G_ACTION_GROUP(pActionGroup)); - } - - // Delete extra items in last section. - RemoveSpareItemsFromNativeMenu( pLOMenu, &pOldCommandList, nSection, validItems ); - - // Delete extra sections. - RemoveSpareSectionsFromNativeMenu( pLOMenu, &pOldCommandList, nSection ); - - // Delete unused commands. - RemoveUnusedCommands( pActionGroup, pOldCommandList, pNewCommandList ); - - // Resolves: tdf#103166 if the menu is empty, add a disabled - // <No Selection Possible> placeholder. - sal_Int32 nSectionsCount = g_menu_model_get_n_items(G_MENU_MODEL(pLOMenu)); - gint nItemsCount = 0; - for (nSection = 0; nSection < nSectionsCount; ++nSection) - { - nItemsCount += g_lo_menu_get_n_items_from_section(pLOMenu, nSection); - if (nItemsCount) - break; - } - if (!nItemsCount) - { - gchar* aNativeCommand = GetCommandForItem(this, 0xFFFF); - OUString aPlaceholderText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE)); - g_lo_menu_insert_in_section(pLOMenu, nSection-1, 0, - OUStringToOString(aPlaceholderText, RTL_TEXTENCODING_UTF8).getStr()); - NativeSetItemCommand(nSection-1, 0, 0xFFFF, aNativeCommand, MenuItemBits::NONE, false, false); - NativeSetEnableItem(aNativeCommand, false); - g_free(aNativeCommand); - } -} - -void GtkSalMenu::Update() -{ - //find out if top level is a menubar or not, if not, then it's a popup menu - //hierarchy and in those we hide (most) disabled entries - const GtkSalMenu* pMenu = this; - while (pMenu->mpParentSalMenu) - pMenu = pMenu->mpParentSalMenu; - ImplUpdate(false, !pMenu->mbMenuBar); -} - -#if GTK_CHECK_VERSION(3,0,0) -static void MenuPositionFunc(GtkMenu* menu, gint* x, gint* y, gboolean* push_in, gpointer user_data) -{ - Point *pPos = static_cast<Point*>(user_data); - *x = pPos->X(); - if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL) - { - GtkRequisition natural_size; - gtk_widget_get_preferred_size(GTK_WIDGET(menu), nullptr, &natural_size); - *x -= natural_size.width; - } - *y = pPos->Y(); - *push_in = false; -} -#endif - -bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangle& rRect, - FloatWinPopupFlags nFlags) -{ -#if GTK_CHECK_VERSION(3,0,0) - VclPtr<vcl::Window> xParent = pWin->ImplGetWindowImpl()->mpRealParent; - mpFrame = static_cast<GtkSalFrame*>(xParent->ImplGetFrame()); - - GLOActionGroup* pActionGroup = g_lo_action_group_new(); - mpActionGroup = G_ACTION_GROUP(pActionGroup); - mpMenuModel = G_MENU_MODEL(g_lo_menu_new()); - // Generate the main menu structure, populates mpMenuModel - UpdateFull(); - - GtkWidget *pWidget = gtk_menu_new_from_model(mpMenuModel); - gtk_menu_attach_to_widget(GTK_MENU(pWidget), mpFrame->getMouseEventWidget(), nullptr); - gtk_widget_insert_action_group(mpFrame->getMouseEventWidget(), "win", mpActionGroup); - - //run in a sub main loop because we need to keep vcl PopupMenu alive to use - //it during DispatchCommand, returning now to the outer loop causes the - //launching PopupMenu to be destroyed, instead run the subloop here - //until the gtk menu is destroyed - GMainLoop* pLoop = g_main_loop_new(nullptr, true); - g_signal_connect_swapped(G_OBJECT(pWidget), "deactivate", G_CALLBACK(g_main_loop_quit), pLoop); - -#if GTK_CHECK_VERSION(3,22,0) - if (gtk_check_version(3, 22, 0) == nullptr) - { - GdkGravity rect_anchor = GDK_GRAVITY_SOUTH_WEST, menu_anchor = GDK_GRAVITY_NORTH_WEST; - - if (nFlags & FloatWinPopupFlags::Left) - { - rect_anchor = GDK_GRAVITY_NORTH_WEST; - menu_anchor = GDK_GRAVITY_NORTH_EAST; - } - else if (nFlags & FloatWinPopupFlags::Up) - { - rect_anchor = GDK_GRAVITY_NORTH_WEST; - menu_anchor = GDK_GRAVITY_SOUTH_WEST; - } - else if (nFlags & FloatWinPopupFlags::Right) - { - rect_anchor = GDK_GRAVITY_NORTH_EAST; - } - - tools::Rectangle aFloatRect = FloatingWindow::ImplConvertToAbsPos(xParent, rRect); - aFloatRect.Move(-mpFrame->maGeometry.nX, -mpFrame->maGeometry.nY); - GdkRectangle rect {static_cast<int>(aFloatRect.Left()), static_cast<int>(aFloatRect.Top()), - static_cast<int>(aFloatRect.GetWidth()), static_cast<int>(aFloatRect.GetHeight())}; - - GdkWindow* gdkWindow = widget_get_window(mpFrame->getMouseEventWidget()); - gtk_menu_popup_at_rect(GTK_MENU(pWidget), gdkWindow, &rect, rect_anchor, menu_anchor, nullptr); - } - else -#endif - { - guint nButton; - guint32 nTime; - - //typically there is an event, and we can then distinguish if this was - //launched from the keyboard (gets auto-mnemoniced) or the mouse (which - //doesn't) - GdkEvent *pEvent = gtk_get_current_event(); - if (pEvent) - { - gdk_event_get_button(pEvent, &nButton); - nTime = gdk_event_get_time(pEvent); - } - else - { - nButton = 0; - nTime = GtkSalFrame::GetLastInputEventTime(); - } - - // do the same strange semantics as vcl popup windows to arrive at a frame geometry - // in mirrored UI case; best done by actually executing the same code - sal_uInt16 nArrangeIndex; - Point aPos = FloatingWindow::ImplCalcPos(pWin, rRect, nFlags, nArrangeIndex); - aPos = FloatingWindow::ImplConvertToAbsPos(xParent, aPos); - - gtk_menu_popup(GTK_MENU(pWidget), nullptr, nullptr, MenuPositionFunc, - &aPos, nButton, nTime); - } - - if (g_main_loop_is_running(pLoop)) - { - gdk_threads_leave(); - g_main_loop_run(pLoop); - gdk_threads_enter(); - } - g_main_loop_unref(pLoop); - - mpVCLMenu->Deactivate(); - - gtk_widget_insert_action_group(mpFrame->getMouseEventWidget(), "win", nullptr); - - gtk_widget_destroy(pWidget); - - g_object_unref(mpActionGroup); - ClearActionGroupAndMenuModel(); - - mpFrame = nullptr; - - return true; -#else - (void)pWin; - (void)rRect; - (void)nFlags; - return false; -#endif -} - -/* - * GtkSalMenu - */ - -GtkSalMenu::GtkSalMenu( bool bMenuBar ) : - mbInActivateCallback( false ), - mbMenuBar( bMenuBar ), - mbNeedsUpdate( false ), - mbReturnFocusToDocument( false ), - mbAddedGrab( false ), - mpMenuBarContainerWidget( nullptr ), - mpMenuAllowShrinkWidget( nullptr ), - mpMenuBarWidget( nullptr ), - mpMenuBarContainerProvider( nullptr ), - mpMenuBarProvider( nullptr ), - mpCloseButton( nullptr ), - mpVCLMenu( nullptr ), - mpParentSalMenu( nullptr ), - mpFrame( nullptr ), - mpMenuModel( nullptr ), - mpActionGroup( nullptr ) -{ - //typically this only gets called after the menu has been customized on the - //next idle slot, in the normal case of a new menubar SetFrame is called - //directly long before this idle would get called. - maUpdateMenuBarIdle.SetPriority(TaskPriority::HIGHEST); - maUpdateMenuBarIdle.SetInvokeHandler(LINK(this, GtkSalMenu, MenuBarHierarchyChangeHandler)); - maUpdateMenuBarIdle.SetDebugName("Native Gtk Menu Update Idle"); -} - -IMPL_LINK_NOARG(GtkSalMenu, MenuBarHierarchyChangeHandler, Timer *, void) -{ - SAL_WARN_IF(!mpFrame, "vcl.gtk", "MenuBar layout changed, but no frame for some reason!"); - if (!mpFrame) - return; - SetFrame(mpFrame); -} - -void GtkSalMenu::SetNeedsUpdate() -{ - GtkSalMenu* pMenu = this; - // start that the menu and its parents are in need of an update - // on the next activation - while (pMenu && !pMenu->mbNeedsUpdate) - { - pMenu->mbNeedsUpdate = true; - pMenu = pMenu->mpParentSalMenu; - } - // only if a menubar is directly updated do we force in a full - // structure update - if (mbMenuBar && !maUpdateMenuBarIdle.IsActive()) - maUpdateMenuBarIdle.Start(); -} - -void GtkSalMenu::SetMenuModel(GMenuModel* pMenuModel) -{ - if (mpMenuModel) - g_object_unref(mpMenuModel); - mpMenuModel = pMenuModel; - if (mpMenuModel) - g_object_ref(mpMenuModel); -} - -GtkSalMenu::~GtkSalMenu() -{ - SolarMutexGuard aGuard; - - DestroyMenuBarWidget(); - - if (mpMenuModel) - g_object_unref(mpMenuModel); - - maItems.clear(); - - if (mpFrame) - mpFrame->SetMenu(nullptr); -} - -bool GtkSalMenu::VisibleMenuBar() -{ - return mbMenuBar && (bUnityMode || mpMenuBarContainerWidget); -} - -void GtkSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos ) -{ - SolarMutexGuard aGuard; - GtkSalMenuItem *pItem = static_cast<GtkSalMenuItem*>( pSalMenuItem ); - - if ( nPos == MENU_APPEND ) - maItems.push_back( pItem ); - else - maItems.insert( maItems.begin() + nPos, pItem ); - - pItem->mpParentMenu = this; - - SetNeedsUpdate(); -} - -void GtkSalMenu::RemoveItem( unsigned nPos ) -{ - SolarMutexGuard aGuard; - maItems.erase( maItems.begin() + nPos ); - SetNeedsUpdate(); -} - -void GtkSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned ) -{ - SolarMutexGuard aGuard; - GtkSalMenuItem *pItem = static_cast< GtkSalMenuItem* >( pSalMenuItem ); - GtkSalMenu *pGtkSubMenu = static_cast< GtkSalMenu* >( pSubMenu ); - - if ( pGtkSubMenu == nullptr ) - return; - - pGtkSubMenu->mpParentSalMenu = this; - pItem->mpSubMenu = pGtkSubMenu; - - SetNeedsUpdate(); -} - -#if GTK_CHECK_VERSION(3,0,0) -static void CloseMenuBar(GtkWidget *, gpointer pMenu) -{ - Application::PostUserEvent(static_cast<MenuBar*>(pMenu)->GetCloseButtonClickHdl()); -} -#endif - -void GtkSalMenu::ShowCloseButton(bool bShow) -{ -#if GTK_CHECK_VERSION(3,0,0) - assert(mbMenuBar); - if (!mpMenuBarContainerWidget) - return; - - if (!bShow) - { - if (mpCloseButton) - gtk_widget_destroy(mpCloseButton); - return; - } - - MenuBar *pVclMenuBar = static_cast<MenuBar*>(mpVCLMenu.get()); - mpCloseButton = gtk_button_new(); - g_signal_connect(mpCloseButton, "clicked", G_CALLBACK(CloseMenuBar), pVclMenuBar); - - gtk_button_set_relief(GTK_BUTTON(mpCloseButton), GTK_RELIEF_NONE); - gtk_button_set_focus_on_click(GTK_BUTTON(mpCloseButton), false); - gtk_widget_set_can_focus(mpCloseButton, false); - - GtkStyleContext *pButtonContext = gtk_widget_get_style_context(GTK_WIDGET(mpCloseButton)); - - GtkCssProvider *pProvider = gtk_css_provider_new(); - static const gchar data[] = "* { " - "padding: 0;" - "margin-left: 8px;" - "margin-right: 8px;" - "min-width: 18px;" - "min-height: 18px;" - "}"; - const gchar olddata[] = "* { " - "padding: 0;" - "margin-left: 8px;" - "margin-right: 8px;" - "}"; - gtk_css_provider_load_from_data(pProvider, gtk_check_version(3, 20, 0) == nullptr ? data : olddata, -1, nullptr); - gtk_style_context_add_provider(pButtonContext, - GTK_STYLE_PROVIDER(pProvider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - - gtk_style_context_add_class(pButtonContext, "flat"); - gtk_style_context_add_class(pButtonContext, "small-button"); - - GIcon* icon = g_themed_icon_new_with_default_fallbacks("window-close-symbolic"); - GtkWidget* image = gtk_image_new_from_gicon(icon, GTK_ICON_SIZE_MENU); - gtk_widget_show(image); - g_object_unref(icon); - - OUString sToolTip(VclResId(SV_HELPTEXT_CLOSEDOCUMENT)); - gtk_widget_set_tooltip_text(mpCloseButton, - OUStringToOString(sToolTip, RTL_TEXTENCODING_UTF8).getStr()); - - gtk_widget_set_valign(mpCloseButton, GTK_ALIGN_CENTER); - - gtk_container_add(GTK_CONTAINER(mpCloseButton), image); - gtk_grid_attach(GTK_GRID(mpMenuBarContainerWidget), GTK_WIDGET(mpCloseButton), 1, 0, 1, 1); - gtk_widget_show_all(mpCloseButton); -#else - (void)bShow; - (void)mpMenuBarContainerWidget; - (void)mpCloseButton; -#endif -} - -//Typically when the menubar is deactivated we want the focus to return -//to where it came from. If the menubar was activated because of F6 -//moving focus into the associated VCL menubar then on pressing ESC -//or any other normal reason for deactivation we want focus to return -//to the document, definitely not still stuck in the associated -//VCL menubar. But if F6 is pressed while the menubar is activated -//we want to pass that F6 back to the VCL menubar which will move -//focus to the next pane by itself. -void GtkSalMenu::ReturnFocus() -{ - if (mbAddedGrab) - { - gtk_grab_remove(mpMenuBarWidget); - mbAddedGrab = false; - } - if (!mbReturnFocusToDocument) - gtk_widget_grab_focus(GTK_WIDGET(mpFrame->getEventBox())); - else - mpFrame->GetWindow()->GrabFocusToDocument(); - mbReturnFocusToDocument = false; -} - -gboolean GtkSalMenu::SignalKey(GdkEventKey const * pEvent) -{ - if (pEvent->keyval == GDK_KEY_F6) - { - mbReturnFocusToDocument = false; - gtk_menu_shell_cancel(GTK_MENU_SHELL(mpMenuBarWidget)); - //because we return false here, the keypress will continue - //to propagate and in the case that vcl focus is in - //the vcl menubar then that will also process F6 and move - //to the next pane - } - return false; -} - -//The GtkSalMenu is owner by a Vcl Menu/MenuBar. In the menubar -//case the vcl menubar is present and "visible", but with a 0 height -//so it not apparent. Normally it acts as though it is not there when -//a Native menubar is active. If we return true here, then for keyboard -//activation and traversal with F6 through panes then the vcl menubar -//acts as though it *is* present and we translate its take focus and F6 -//traversal key events into the gtk menubar equivalents. -bool GtkSalMenu::CanGetFocus() const -{ - return mpMenuBarWidget != nullptr; -} - -bool GtkSalMenu::TakeFocus() -{ - if (!mpMenuBarWidget) - return false; - - //Send a keyboard event to the gtk menubar to let it know it has been - //activated via the keyboard. Doesn't do anything except cause the gtk - //menubar "keyboard_mode" member to get set to true, so typically mnemonics - //are shown which will serve as indication that the menubar has focus - //(given that we want to show it with no menus popped down) - GdkEvent *event = GtkSalFrame::makeFakeKeyPress(mpMenuBarWidget); - gtk_widget_event(mpMenuBarWidget, event); - gdk_event_free(event); - - //this pairing results in a menubar with keyboard focus with no menus - //auto-popped down - gtk_grab_add(mpMenuBarWidget); - mbAddedGrab = true; - gtk_menu_shell_select_first(GTK_MENU_SHELL(mpMenuBarWidget), false); - gtk_menu_shell_deselect(GTK_MENU_SHELL(mpMenuBarWidget)); - mbReturnFocusToDocument = true; - return true; -} - -#if GTK_CHECK_VERSION(3,0,0) - -static void MenuBarReturnFocus(GtkMenuShell*, gpointer menu) -{ - GtkSalFrame::UpdateLastInputEventTime(gtk_get_current_event_time()); - GtkSalMenu* pMenu = static_cast<GtkSalMenu*>(menu); - pMenu->ReturnFocus(); -} - -static gboolean MenuBarSignalKey(GtkWidget*, GdkEventKey* pEvent, gpointer menu) -{ - GtkSalMenu* pMenu = static_cast<GtkSalMenu*>(menu); - return pMenu->SignalKey(pEvent); -} - -#endif - -void GtkSalMenu::CreateMenuBarWidget() -{ -#if GTK_CHECK_VERSION(3,0,0) - if (mpMenuBarContainerWidget) - return; - - GtkGrid* pGrid = mpFrame->getTopLevelGridWidget(); - mpMenuBarContainerWidget = gtk_grid_new(); - - gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarContainerWidget), true); - gtk_grid_insert_row(pGrid, 0); - gtk_grid_attach(pGrid, mpMenuBarContainerWidget, 0, 0, 1, 1); - - mpMenuAllowShrinkWidget = gtk_scrolled_window_new(nullptr, nullptr); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(mpMenuAllowShrinkWidget), GTK_SHADOW_NONE); - // tdf#116290 external policy on scrolledwindow will not show a scrollbar, - // but still allow scrolled window to not be sized to the child content. - // So the menubar can be shrunk past its nominal smallest width. - // Unlike a hack using GtkFixed/GtkLayout the correct placement of the menubar occurs under RTL - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mpMenuAllowShrinkWidget), GTK_POLICY_EXTERNAL, GTK_POLICY_NEVER); - gtk_grid_attach(GTK_GRID(mpMenuBarContainerWidget), mpMenuAllowShrinkWidget, 0, 0, 1, 1); - - mpMenuBarWidget = gtk_menu_bar_new_from_model(mpMenuModel); - - gtk_widget_insert_action_group(mpMenuBarWidget, "win", mpActionGroup); - gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarWidget), true); - gtk_widget_set_hexpand(mpMenuAllowShrinkWidget, true); - gtk_container_add(GTK_CONTAINER(mpMenuAllowShrinkWidget), mpMenuBarWidget); - - g_signal_connect(G_OBJECT(mpMenuBarWidget), "deactivate", G_CALLBACK(MenuBarReturnFocus), this); - g_signal_connect(G_OBJECT(mpMenuBarWidget), "key-press-event", G_CALLBACK(MenuBarSignalKey), this); - - gtk_widget_show_all(mpMenuBarContainerWidget); - - ShowCloseButton( static_cast<MenuBar*>(mpVCLMenu.get())->HasCloseButton() ); - - ApplyPersona(); -#else - (void)mpMenuAllowShrinkWidget; - (void)mpMenuBarContainerWidget; -#endif -} - -void GtkSalMenu::ApplyPersona() -{ -#if GTK_CHECK_VERSION(3,0,0) - if (!mpMenuBarContainerWidget) - return; - assert(mbMenuBar); - // I'm dubious about the persona theming feature, but as it exists, lets try and support - // it, apply the image to the mpMenuBarContainerWidget - const BitmapEx& rPersonaBitmap = Application::GetSettings().GetStyleSettings().GetPersonaHeader(); - - GtkStyleContext *pMenuBarContainerContext = gtk_widget_get_style_context(GTK_WIDGET(mpMenuBarContainerWidget)); - if (mpMenuBarContainerProvider) - { - gtk_style_context_remove_provider(pMenuBarContainerContext, GTK_STYLE_PROVIDER(mpMenuBarContainerProvider)); - mpMenuBarContainerProvider = nullptr; - } - GtkStyleContext *pMenuBarContext = gtk_widget_get_style_context(GTK_WIDGET(mpMenuBarWidget)); - if (mpMenuBarProvider) - { - gtk_style_context_remove_provider(pMenuBarContext, GTK_STYLE_PROVIDER(mpMenuBarProvider)); - mpMenuBarProvider = nullptr; - } - - if (!rPersonaBitmap.IsEmpty()) - { - if (maPersonaBitmap != rPersonaBitmap) - { - vcl::PNGWriter aPNGWriter(rPersonaBitmap); - mxPersonaImage.reset(new utl::TempFile); - mxPersonaImage->EnableKillingFile(true); - SvStream* pStream = mxPersonaImage->GetStream(StreamMode::WRITE); - aPNGWriter.Write(*pStream); - mxPersonaImage->CloseStream(); - } - - mpMenuBarContainerProvider = gtk_css_provider_new(); - OUString aBuffer = "* { background-image: url(\"" + mxPersonaImage->GetURL() + "\"); background-position: top right; }"; - OString aResult = OUStringToOString(aBuffer, RTL_TEXTENCODING_UTF8); - gtk_css_provider_load_from_data(mpMenuBarContainerProvider, aResult.getStr(), aResult.getLength(), nullptr); - gtk_style_context_add_provider(pMenuBarContainerContext, GTK_STYLE_PROVIDER(mpMenuBarContainerProvider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - - - // force the menubar to be transparent when persona is active otherwise for - // me the menubar becomes gray when its in the backdrop - mpMenuBarProvider = gtk_css_provider_new(); - static const gchar data[] = "* { " - "background-image: none;" - "background-color: transparent;" - "}"; - gtk_css_provider_load_from_data(mpMenuBarProvider, data, -1, nullptr); - gtk_style_context_add_provider(pMenuBarContext, - GTK_STYLE_PROVIDER(mpMenuBarProvider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - } - maPersonaBitmap = rPersonaBitmap; -#else - (void)maPersonaBitmap; - (void)mpMenuBarContainerProvider; - (void)mpMenuBarProvider; -#endif -} - -void GtkSalMenu::DestroyMenuBarWidget() -{ -#if GTK_CHECK_VERSION(3,0,0) - if (mpMenuBarContainerWidget) - { - gtk_widget_destroy(mpMenuBarContainerWidget); - mpMenuBarContainerWidget = nullptr; - mpCloseButton = nullptr; - } -#else - (void)mpMenuBarContainerWidget; -#endif -} - -void GtkSalMenu::SetFrame(const SalFrame* pFrame) -{ - SolarMutexGuard aGuard; - assert(mbMenuBar); - SAL_INFO("vcl.unity", "GtkSalMenu set to frame"); - mpFrame = const_cast<GtkSalFrame*>(static_cast<const GtkSalFrame*>(pFrame)); - - // if we had a menu on the GtkSalMenu we have to free it as we generate a - // full menu anyway and we might need to reuse an existing model and - // actiongroup - mpFrame->SetMenu( this ); - mpFrame->EnsureAppMenuWatch(); - - // Clean menu model and action group if needed. - GtkWidget* pWidget = mpFrame->getWindow(); - GdkWindow* gdkWindow = gtk_widget_get_window( pWidget ); - - GLOMenu* pMenuModel = G_LO_MENU( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) ); - GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-action-group" ) ); - SAL_INFO("vcl.unity", "Found menu model: " << pMenuModel << " and action group: " << pActionGroup); - - if ( pMenuModel ) - { - if ( g_menu_model_get_n_items( G_MENU_MODEL( pMenuModel ) ) > 0 ) - g_lo_menu_remove( pMenuModel, 0 ); - - mpMenuModel = G_MENU_MODEL( g_lo_menu_new() ); - } - - if ( pActionGroup ) - { - g_lo_action_group_clear( pActionGroup ); - mpActionGroup = G_ACTION_GROUP( pActionGroup ); - } - - // Generate the main menu structure. - if ( PrepUpdate() ) - UpdateFull(); - - g_lo_menu_insert_section( pMenuModel, 0, nullptr, mpMenuModel ); - - if (!bUnityMode && static_cast<MenuBar*>(mpVCLMenu.get())->IsDisplayable()) - { - DestroyMenuBarWidget(); - CreateMenuBarWidget(); - } -} - -const GtkSalFrame* GtkSalMenu::GetFrame() const -{ - SolarMutexGuard aGuard; - const GtkSalMenu* pMenu = this; - while( pMenu && ! pMenu->mpFrame ) - pMenu = pMenu->mpParentSalMenu; - return pMenu ? pMenu->mpFrame : nullptr; -} - -void GtkSalMenu::NativeCheckItem( unsigned nSection, unsigned nItemPos, MenuItemBits bits, gboolean bCheck ) -{ - SolarMutexGuard aGuard; - - if ( mpActionGroup == nullptr ) - return; - - gchar* aCommand = g_lo_menu_get_command_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos ); - - if ( aCommand != nullptr || g_strcmp0( aCommand, "" ) != 0 ) - { - GVariant *pCheckValue = nullptr; - GVariant *pCurrentState = g_action_group_get_action_state( mpActionGroup, aCommand ); - - if ( bits & MenuItemBits::RADIOCHECK ) - pCheckValue = bCheck ? g_variant_new_string( aCommand ) : g_variant_new_string( "" ); - else - { - // By default, all checked items are checkmark buttons. - if (bCheck || pCurrentState != nullptr) - pCheckValue = g_variant_new_boolean( bCheck ); - } - - if ( pCheckValue != nullptr ) - { - if ( pCurrentState == nullptr || g_variant_equal( pCurrentState, pCheckValue ) == FALSE ) - { - g_action_group_change_action_state( mpActionGroup, aCommand, pCheckValue ); - } - else - { - g_variant_unref (pCheckValue); - } - } - - if ( pCurrentState != nullptr ) - g_variant_unref( pCurrentState ); - } - - if ( aCommand ) - g_free( aCommand ); -} - -void GtkSalMenu::NativeSetEnableItem( gchar const * aCommand, gboolean bEnable ) -{ - SolarMutexGuard aGuard; - GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup ); - - if ( g_action_group_get_action_enabled( G_ACTION_GROUP( pActionGroup ), aCommand ) != bEnable ) - g_lo_action_group_set_action_enabled( pActionGroup, aCommand, bEnable ); -} - -void GtkSalMenu::NativeSetItemText( unsigned nSection, unsigned nItemPos, const OUString& rText ) -{ - SolarMutexGuard aGuard; - // Escape all underscores so that they don't get interpreted as hotkeys - OUString aText = rText.replaceAll( "_", "__" ); - // Replace the LibreOffice hotkey identifier with an underscore - aText = aText.replace( '~', '_' ); - OString aConvertedText = OUStringToOString( aText, RTL_TEXTENCODING_UTF8 ); - - // Update item text only when necessary. - gchar* aLabel = g_lo_menu_get_label_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos ); - - if ( aLabel == nullptr || g_strcmp0( aLabel, aConvertedText.getStr() ) != 0 ) - g_lo_menu_set_label_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aConvertedText.getStr() ); - - if ( aLabel ) - g_free( aLabel ); -} - -namespace -{ - void DestroyMemoryStream(gpointer data) - { - SvMemoryStream* pMemStm = static_cast<SvMemoryStream*>(data); - delete pMemStm; - } -} - -void GtkSalMenu::NativeSetItemIcon( unsigned nSection, unsigned nItemPos, const Image& rImage ) -{ -#if GLIB_CHECK_VERSION(2,38,0) - if (!rImage && mbHasNullItemIcon) - return; - - SolarMutexGuard aGuard; - - if (!!rImage) - { - SvMemoryStream* pMemStm = new SvMemoryStream; - vcl::PNGWriter aWriter(rImage.GetBitmapEx()); - aWriter.Write(*pMemStm); - - GBytes *pBytes = g_bytes_new_with_free_func(pMemStm->GetData(), - pMemStm->TellEnd(), - DestroyMemoryStream, - pMemStm); - - GIcon *pIcon = g_bytes_icon_new(pBytes); - - g_lo_menu_set_icon_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, pIcon ); - g_object_unref(pIcon); - g_bytes_unref(pBytes); - mbHasNullItemIcon = false; - } - else - { - g_lo_menu_set_icon_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, nullptr ); - mbHasNullItemIcon = true; - } -#else - (void)nSection; - (void)nItemPos; - (void)rImage; -#endif -} - -void GtkSalMenu::NativeSetAccelerator( unsigned nSection, unsigned nItemPos, const vcl::KeyCode& rKeyCode, const OUString& rKeyName ) -{ - SolarMutexGuard aGuard; - - if ( rKeyName.isEmpty() ) - return; - - guint nKeyCode; - GdkModifierType nModifiers; - GtkSalFrame::KeyCodeToGdkKey(rKeyCode, &nKeyCode, &nModifiers); - - gchar* aAccelerator = gtk_accelerator_name( nKeyCode, nModifiers ); - - gchar* aCurrentAccel = g_lo_menu_get_accelerator_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos ); - - if ( aCurrentAccel == nullptr && g_strcmp0( aCurrentAccel, aAccelerator ) != 0 ) - g_lo_menu_set_accelerator_to_item_in_section ( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aAccelerator ); - - g_free( aAccelerator ); - g_free( aCurrentAccel ); -} - -bool GtkSalMenu::NativeSetItemCommand( unsigned nSection, - unsigned nItemPos, - sal_uInt16 nId, - const gchar* aCommand, - MenuItemBits nBits, - bool bChecked, - bool bIsSubmenu ) -{ - bool bSubMenuAddedOrRemoved = false; - - SolarMutexGuard aGuard; - GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup ); - - GVariant *pTarget = nullptr; - - if (g_action_group_has_action(mpActionGroup, aCommand)) - g_lo_action_group_remove(pActionGroup, aCommand); - - if ( ( nBits & MenuItemBits::CHECKABLE ) || bIsSubmenu ) - { - // Item is a checkmark button. - GVariantType* pStateType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_BOOLEAN) ); - GVariant* pState = g_variant_new_boolean( bChecked ); - - g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, bIsSubmenu, nullptr, pStateType, nullptr, pState ); - } - else if ( nBits & MenuItemBits::RADIOCHECK ) - { - // Item is a radio button. - GVariantType* pParameterType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_STRING) ); - GVariantType* pStateType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_STRING) ); - GVariant* pState = g_variant_new_string( "" ); - pTarget = g_variant_new_string( aCommand ); - - g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, FALSE, pParameterType, pStateType, nullptr, pState ); - } - else - { - // Item is not special, so insert a stateless action. - g_lo_action_group_insert( pActionGroup, aCommand, nId, FALSE ); - } - - GLOMenu* pMenu = G_LO_MENU( mpMenuModel ); - - // Menu item is not updated unless it's necessary. - gchar* aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pMenu, nSection, nItemPos ); - - if ( aCurrentCommand == nullptr || g_strcmp0( aCurrentCommand, aCommand ) != 0 ) - { - bool bOldHasSubmenu = g_lo_menu_get_submenu_from_item_in_section(pMenu, nSection, nItemPos) != nullptr; - bSubMenuAddedOrRemoved = bOldHasSubmenu != bIsSubmenu; - if (bSubMenuAddedOrRemoved) - { - //tdf#98636 it's not good enough to unset the "submenu-action" attribute to change something - //from a submenu to a non-submenu item, so remove the old one entirely and re-add it to - //support achieving that - gchar* pLabel = g_lo_menu_get_label_from_item_in_section(pMenu, nSection, nItemPos); - g_lo_menu_remove_from_section(pMenu, nSection, nItemPos); - g_lo_menu_insert_in_section(pMenu, nSection, nItemPos, pLabel); - g_free(pLabel); - } - - g_lo_menu_set_command_to_item_in_section( pMenu, nSection, nItemPos, aCommand ); - - gchar* aItemCommand = g_strconcat("win.", aCommand, nullptr ); - - if ( bIsSubmenu ) - g_lo_menu_set_submenu_action_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand ); - else - { - g_lo_menu_set_action_and_target_value_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand, pTarget ); - pTarget = nullptr; - } - - g_free( aItemCommand ); - } - - if ( aCurrentCommand ) - g_free( aCurrentCommand ); - - if (pTarget) - g_variant_unref(pTarget); - - return bSubMenuAddedOrRemoved; -} - -GtkSalMenu* GtkSalMenu::GetTopLevel() -{ - GtkSalMenu *pMenu = this; - while (pMenu->mpParentSalMenu) - pMenu = pMenu->mpParentSalMenu; - return pMenu; -} - -void GtkSalMenu::DispatchCommand(const gchar *pCommand) -{ - SolarMutexGuard aGuard; - MenuAndId aMenuAndId = decode_command(pCommand); - GtkSalMenu* pSalSubMenu = aMenuAndId.first; - GtkSalMenu* pTopLevel = pSalSubMenu->GetTopLevel(); - if (pTopLevel->mpMenuBarWidget) - { - // tdf#125803 spacebar will toggle radios and checkbuttons without automatically - // closing the menu. To handle this properly I imagine we need to set groups for the - // radiobuttons so the others visually untoggle when the active one is toggled and - // we would further need to teach vcl that the state can change more than once. - // - // or we could unconditionally deactivate the menus if regardless of what particular - // type of menu item got activated - gtk_menu_shell_deactivate(GTK_MENU_SHELL(pTopLevel->mpMenuBarWidget)); - } - pTopLevel->GetMenu()->HandleMenuCommandEvent(pSalSubMenu->GetMenu(), aMenuAndId.second); -} - -void GtkSalMenu::ActivateAllSubmenus(Menu* pMenuBar) -{ - for (GtkSalMenuItem* pSalItem : maItems) - { - if ( pSalItem->mpSubMenu != nullptr ) - { - // We can re-enter this method via the new event loop that gets created - // in GtkClipboardTransferable::getTransferDataFlavorsAsVector, so use the InActivateCallback - // flag to detect that and skip some startup work. - if (!pSalItem->mpSubMenu->mbInActivateCallback) - { - pSalItem->mpSubMenu->mbInActivateCallback = true; - pMenuBar->HandleMenuActivateEvent(pSalItem->mpSubMenu->GetMenu()); - pSalItem->mpSubMenu->mbInActivateCallback = false; - pSalItem->mpSubMenu->ActivateAllSubmenus(pMenuBar); - pSalItem->mpSubMenu->Update(); - pMenuBar->HandleMenuDeActivateEvent(pSalItem->mpSubMenu->GetMenu()); - } - } - } -} - -void GtkSalMenu::ClearActionGroupAndMenuModel() -{ - SetMenuModel(nullptr); - mpActionGroup = nullptr; - for (GtkSalMenuItem* pSalItem : maItems) - { - if ( pSalItem->mpSubMenu != nullptr ) - { - pSalItem->mpSubMenu->ClearActionGroupAndMenuModel(); - } - } -} - -void GtkSalMenu::Activate(const gchar* pCommand) -{ - MenuAndId aMenuAndId = decode_command(pCommand); - GtkSalMenu* pSalMenu = aMenuAndId.first; - GtkSalMenu* pTopLevel = pSalMenu->GetTopLevel(); - Menu* pVclMenu = pSalMenu->GetMenu(); - Menu* pVclSubMenu = pVclMenu->GetPopupMenu(aMenuAndId.second); - GtkSalMenu* pSubMenu = pSalMenu->GetItemAtPos(pVclMenu->GetItemPos(aMenuAndId.second))->mpSubMenu; - - pSubMenu->mbInActivateCallback = true; - pTopLevel->GetMenu()->HandleMenuActivateEvent(pVclSubMenu); - pSubMenu->mbInActivateCallback = false; - pVclSubMenu->UpdateNativeMenu(); -} - -void GtkSalMenu::Deactivate(const gchar* pCommand) -{ - MenuAndId aMenuAndId = decode_command(pCommand); - GtkSalMenu* pSalMenu = aMenuAndId.first; - GtkSalMenu* pTopLevel = pSalMenu->GetTopLevel(); - Menu* pVclMenu = pSalMenu->GetMenu(); - Menu* pVclSubMenu = pVclMenu->GetPopupMenu(aMenuAndId.second); - pTopLevel->GetMenu()->HandleMenuDeActivateEvent(pVclSubMenu); -} - -void GtkSalMenu::EnableUnity(bool bEnable) -{ - bUnityMode = bEnable; - - MenuBar* pMenuBar(static_cast<MenuBar*>(mpVCLMenu.get())); - bool bDisplayable(pMenuBar->IsDisplayable()); - - if (bEnable) - { - DestroyMenuBarWidget(); - UpdateFull(); - if (!bDisplayable) - ShowMenuBar(false); - } - else - { - Update(); - ShowMenuBar(bDisplayable); - } - - pMenuBar->LayoutChanged(); -} - -void GtkSalMenu::ShowMenuBar( bool bVisible ) -{ - // Unity tdf#106271: Can't hide global menu, so empty it instead when user wants to hide menubar, - if (bUnityMode) - { - if (bVisible) - Update(); - else if (mpMenuModel && g_menu_model_get_n_items(G_MENU_MODEL(mpMenuModel)) > 0) - g_lo_menu_remove(G_LO_MENU(mpMenuModel), 0); - } - else if (bVisible) - CreateMenuBarWidget(); - else - DestroyMenuBarWidget(); -} - -bool GtkSalMenu::IsItemVisible( unsigned nPos ) -{ - SolarMutexGuard aGuard; - bool bVisible = false; - - if ( nPos < maItems.size() ) - bVisible = maItems[ nPos ]->mbVisible; - - return bVisible; -} - -void GtkSalMenu::CheckItem( unsigned, bool ) -{ -} - -void GtkSalMenu::EnableItem( unsigned nPos, bool bEnable ) -{ - SolarMutexGuard aGuard; - if ( bUnityMode && !mbInActivateCallback && !mbNeedsUpdate && GetTopLevel()->mbMenuBar && ( nPos < maItems.size() ) ) - { - gchar* pCommand = GetCommandForItem( GetItemAtPos( nPos ) ); - NativeSetEnableItem( pCommand, bEnable ); - g_free( pCommand ); - } -} - -void GtkSalMenu::ShowItem( unsigned nPos, bool bShow ) -{ - SolarMutexGuard aGuard; - if ( nPos < maItems.size() ) - { - maItems[ nPos ]->mbVisible = bShow; - if ( bUnityMode && !mbInActivateCallback && !mbNeedsUpdate && GetTopLevel()->mbMenuBar ) - Update(); - } -} - -void GtkSalMenu::SetItemText( unsigned nPos, SalMenuItem* pSalMenuItem, const OUString& rText ) -{ - SolarMutexGuard aGuard; - if ( bUnityMode && !mbInActivateCallback && !mbNeedsUpdate && GetTopLevel()->mbMenuBar && ( nPos < maItems.size() ) ) - { - gchar* pCommand = GetCommandForItem( static_cast< GtkSalMenuItem* >( pSalMenuItem ) ); - - gint nSectionsCount = g_menu_model_get_n_items( mpMenuModel ); - for ( gint nSection = 0; nSection < nSectionsCount; ++nSection ) - { - gint nItemsCount = g_lo_menu_get_n_items_from_section( G_LO_MENU( mpMenuModel ), nSection ); - for ( gint nItem = 0; nItem < nItemsCount; ++nItem ) - { - gchar* pCommandFromModel = g_lo_menu_get_command_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItem ); - - if ( !g_strcmp0( pCommandFromModel, pCommand ) ) - { - NativeSetItemText( nSection, nItem, rText ); - g_free( pCommandFromModel ); - g_free( pCommand ); - return; - } - - g_free( pCommandFromModel ); - } - } - - g_free( pCommand ); - } -} - -void GtkSalMenu::SetItemImage( unsigned, SalMenuItem*, const Image& ) -{ -} - -void GtkSalMenu::SetAccelerator( unsigned, SalMenuItem*, const vcl::KeyCode&, const OUString& ) -{ -} - -void GtkSalMenu::GetSystemMenuData( SystemMenuData* ) -{ -} - -int GtkSalMenu::GetMenuBarHeight() const -{ -#if GTK_CHECK_VERSION(3,0,0) - return mpMenuBarWidget ? gtk_widget_get_allocated_height(mpMenuBarWidget) : 0; -#else - return 0; -#endif -} - -/* - * GtkSalMenuItem - */ - -GtkSalMenuItem::GtkSalMenuItem( const SalItemParams* pItemData ) : - mpParentMenu( nullptr ), - mpSubMenu( nullptr ), - mnType( pItemData->eType ), - mnId( pItemData->nId ), - mbVisible( true ) -{ -} - -GtkSalMenuItem::~GtkSalMenuItem() -{ -} - -#endif - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/gtksys.cxx b/vcl/unx/gtk/gtksys.cxx deleted file mode 100644 index 373459589429..000000000000 --- a/vcl/unx/gtk/gtksys.cxx +++ /dev/null @@ -1,296 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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.h> -#include <gmodule.h> -#include <gtk/gtk.h> -#include <unx/gtk/gtkinst.hxx> -#include <unx/gtk/gtksys.hxx> -#include <osl/module.h> - -GtkSalSystem *GtkSalSystem::GetSingleton() -{ - static GtkSalSystem *pSingleton = new GtkSalSystem(); - return pSingleton; -} - -SalSystem *GtkInstance::CreateSalSystem() -{ - return GtkSalSystem::GetSingleton(); -} - -GtkSalSystem::GtkSalSystem() : SalGenericSystem() -{ - mpDisplay = gdk_display_get_default(); - countScreenMonitors(); -#if GTK_CHECK_VERSION(3,0,0) - // rhbz#1285356, native look will be gtk2, which crashes - // when gtk3 is already loaded. Until there is a solution - // java-side force look and feel to something that doesn't - // crash when we are using gtk3 - setenv("STOC_FORCE_SYSTEM_LAF", "true", 1); -#endif -} - -GtkSalSystem::~GtkSalSystem() -{ -} - -int -GtkSalSystem::GetDisplayXScreenCount() -{ - return gdk_display_get_n_screens (mpDisplay); -} - -namespace -{ - -struct GdkRectangleCoincidentLess -{ - // fdo#78799 - detect and elide overlaying monitors of different sizes - bool operator()(GdkRectangle const& rLeft, GdkRectangle const& rRight) - { - return - rLeft.x < rRight.x - || rLeft.y < rRight.y - ; - } -}; -struct GdkRectangleCoincident -{ - // fdo#78799 - detect and elide overlaying monitors of different sizes - bool operator()(GdkRectangle const& rLeft, GdkRectangle const& rRight) - { - return - rLeft.x == rRight.x - && rLeft.y == rRight.y - ; - } -}; - -} - -/** - * GtkSalSystem::countScreenMonitors() - * - * This method builds the vector which allows us to map from VCL's - * idea of linear integer ScreenNumber to gtk+'s rather more - * complicated screen + monitor concept. - */ -void -GtkSalSystem::countScreenMonitors() -{ - maScreenMonitors.clear(); - for (gint i = 0; i < gdk_display_get_n_screens(mpDisplay); i++) - { - GdkScreen* const pScreen(gdk_display_get_screen(mpDisplay, i)); - gint nMonitors(pScreen ? gdk_screen_get_n_monitors(pScreen) : 0); - if (nMonitors > 1) - { - std::vector<GdkRectangle> aGeometries; - aGeometries.reserve(nMonitors); - for (gint j(0); j != nMonitors; ++j) - { - GdkRectangle aGeometry; - gdk_screen_get_monitor_geometry(pScreen, j, &aGeometry); - aGeometries.push_back(aGeometry); - } - std::sort(aGeometries.begin(), aGeometries.end(), - GdkRectangleCoincidentLess()); - const std::vector<GdkRectangle>::iterator aUniqueEnd( - std::unique(aGeometries.begin(), aGeometries.end(), - GdkRectangleCoincident())); - nMonitors = std::distance(aGeometries.begin(), aUniqueEnd); - } - maScreenMonitors.emplace_back(pScreen, nMonitors); - } -} - -// Including gdkx.h kills us with the Window / XWindow conflict -extern "C" { -#if GTK_CHECK_VERSION(3,0,0) - GType gdk_x11_display_get_type(); -#endif - int gdk_x11_screen_get_screen_number (GdkScreen *screen); -} - -SalX11Screen -GtkSalSystem::getXScreenFromDisplayScreen(unsigned int nScreen) -{ - gint nMonitor; - - GdkScreen *pScreen = getScreenMonitorFromIdx (nScreen, nMonitor); - if (!pScreen) - return SalX11Screen (0); -#if GTK_CHECK_VERSION(3,0,0) - if (!G_TYPE_CHECK_INSTANCE_TYPE (mpDisplay, gdk_x11_display_get_type ())) - return SalX11Screen (0); -#endif - return SalX11Screen (gdk_x11_screen_get_screen_number (pScreen)); -} - -GdkScreen * -GtkSalSystem::getScreenMonitorFromIdx (int nIdx, gint &nMonitor) -{ - GdkScreen *pScreen = nullptr; - for (auto const& screenMonitor : maScreenMonitors) - { - pScreen = screenMonitor.first; - if (!pScreen) - break; - if (nIdx >= screenMonitor.second) - nIdx -= screenMonitor.second; - else - break; - } - nMonitor = nIdx; - - // handle invalid monitor indexes as non-existent screens - if (nMonitor < 0 || (pScreen && nMonitor >= gdk_screen_get_n_monitors (pScreen))) - pScreen = nullptr; - - return pScreen; -} - -int -GtkSalSystem::getScreenIdxFromPtr (GdkScreen *pScreen) -{ - int nIdx = 0; - for (auto const& screenMonitor : maScreenMonitors) - { - if (screenMonitor.first == pScreen) - return nIdx; - nIdx += screenMonitor.second; - } - g_warning ("failed to find screen %p", pScreen); - return 0; -} - -int GtkSalSystem::getScreenMonitorIdx (GdkScreen *pScreen, - int nX, int nY) -{ - // TODO: this will fail horribly for exotic combinations like two - // monitors in mirror mode and one extra. Hopefully such - // abominations are not used (or, even better, not possible) in - // practice .-) - return getScreenIdxFromPtr (pScreen) + - gdk_screen_get_monitor_at_point (pScreen, nX, nY); -} - -unsigned int GtkSalSystem::GetDisplayScreenCount() -{ - gint nMonitor; - (void)getScreenMonitorFromIdx (G_MAXINT, nMonitor); - return G_MAXINT - nMonitor; -} - -bool GtkSalSystem::IsUnifiedDisplay() -{ - return gdk_display_get_n_screens (mpDisplay) == 1; -} - -namespace { -int _fallback_get_primary_monitor (GdkScreen *pScreen) -{ - // Use monitor name as primacy heuristic - int max = gdk_screen_get_n_monitors (pScreen); - for (int i = 0; i < max; ++i) - { - char *name = gdk_screen_get_monitor_plug_name (pScreen, i); - bool bLaptop = (name && !g_ascii_strncasecmp (name, "LVDS", 4)); - g_free (name); - if (bLaptop) - return i; - } - return 0; -} - -int _get_primary_monitor (GdkScreen *pScreen) -{ - static int (*get_fn) (GdkScreen *) = nullptr; -#if GTK_CHECK_VERSION(3,0,0) - get_fn = gdk_screen_get_primary_monitor; -#endif - // Perhaps we have a newer gtk+ with this symbol: - if (!get_fn) - { - get_fn = reinterpret_cast<int(*)(GdkScreen*)>(osl_getAsciiFunctionSymbol(nullptr, - "gdk_screen_get_primary_monitor")); - } - if (!get_fn) - get_fn = _fallback_get_primary_monitor; - if (get_fn) - return get_fn (pScreen); - else - return 0; -} -} // end anonymous namespace - -unsigned int GtkSalSystem::GetDisplayBuiltInScreen() -{ - GdkScreen *pDefault = gdk_display_get_default_screen (mpDisplay); - int idx = getScreenIdxFromPtr (pDefault); - return idx + _get_primary_monitor (pDefault); -} - -tools::Rectangle GtkSalSystem::GetDisplayScreenPosSizePixel (unsigned int nScreen) -{ - gint nMonitor; - GdkScreen *pScreen; - GdkRectangle aRect; - pScreen = getScreenMonitorFromIdx (nScreen, nMonitor); - if (!pScreen) - return tools::Rectangle(); - gdk_screen_get_monitor_geometry (pScreen, nMonitor, &aRect); - return tools::Rectangle (Point(aRect.x, aRect.y), Size(aRect.width, aRect.height)); -} - -// convert ~ to indicate mnemonic to '_' -static OString MapToGtkAccelerator(const OUString &rStr) -{ - return OUStringToOString(rStr.replaceFirst("~", "_"), RTL_TEXTENCODING_UTF8); -} - -int GtkSalSystem::ShowNativeDialog (const OUString& rTitle, const OUString& rMessage, - const std::vector< OUString >& rButtonNames) -{ - OString aTitle (OUStringToOString (rTitle, RTL_TEXTENCODING_UTF8)); - OString aMessage (OUStringToOString (rMessage, RTL_TEXTENCODING_UTF8)); - - GtkDialog *pDialog = GTK_DIALOG ( - g_object_new (GTK_TYPE_MESSAGE_DIALOG, - "title", aTitle.getStr(), - "message-type", int(GTK_MESSAGE_WARNING), - "text", aMessage.getStr(), - nullptr)); - int nButton = 0; - for (auto const& buttonName : rButtonNames) - gtk_dialog_add_button (pDialog, MapToGtkAccelerator(buttonName).getStr(), nButton++); - gtk_dialog_set_default_response (pDialog, 0/*nDefaultButton*/); - - nButton = gtk_dialog_run (pDialog); - if (nButton < 0) - nButton = -1; - - gtk_widget_destroy (GTK_WIDGET (pDialog)); - - return nButton; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/hudawareness.cxx b/vcl/unx/gtk/hudawareness.cxx deleted file mode 100644 index b7985fdb58ab..000000000000 --- a/vcl/unx/gtk/hudawareness.cxx +++ /dev/null @@ -1,112 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.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 <string.h> - -#include <unx/gtk/gtksalmenu.hxx> - -#ifdef ENABLE_GMENU_INTEGRATION - -#include <unx/gtk/hudawareness.h> - -struct HudAwarenessHandle -{ - GDBusConnection *connection; - HudAwarenessCallback callback; - gpointer user_data; - GDestroyNotify notify; -}; - -static void -hud_awareness_method_call (GDBusConnection * /* connection */, - const gchar * /* sender */, - const gchar * /* object_path */, - const gchar * /* interface_name */, - const gchar *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data) -{ - HudAwarenessHandle *handle = static_cast<HudAwarenessHandle*>(user_data); - - if (g_str_equal (method_name, "HudActiveChanged")) - { - gboolean active; - - g_variant_get (parameters, "(b)", &active); - - (* handle->callback) (active, handle->user_data); - } - - g_dbus_method_invocation_return_value (invocation, nullptr); -} - -guint -hud_awareness_register (GDBusConnection *connection, - const gchar *object_path, - HudAwarenessCallback callback, - gpointer user_data, - GDestroyNotify notify, - GError **error) -{ - static GDBusInterfaceInfo *iface; - static GDBusNodeInfo *info; - GDBusInterfaceVTable vtable; - HudAwarenessHandle *handle; - guint object_id; - - memset (static_cast<void *>(&vtable), 0, sizeof (vtable)); - vtable.method_call = hud_awareness_method_call; - - if G_UNLIKELY (iface == nullptr) - { - GError *local_error = nullptr; - - info = g_dbus_node_info_new_for_xml ("<node>" - "<interface name='com.canonical.hud.Awareness'>" - "<method name='CheckAwareness'/>" - "<method name='HudActiveChanged'>" - "<arg type='b'/>" - "</method>" - "</interface>" - "</node>", - &local_error); - g_assert_no_error (local_error); - iface = g_dbus_node_info_lookup_interface (info, "com.canonical.hud.Awareness"); - g_assert (iface != nullptr); - } - - handle = static_cast<HudAwarenessHandle*>(g_malloc (sizeof (HudAwarenessHandle))); - - object_id = g_dbus_connection_register_object (connection, object_path, iface, &vtable, handle, &g_free, error); - - if (object_id == 0) - { - g_free (handle); - return 0; - } - - handle->connection = static_cast<GDBusConnection*>(g_object_ref (connection)); - handle->callback = callback; - handle->user_data = user_data; - handle->notify = notify; - - return object_id; -} - -void -hud_awareness_unregister (GDBusConnection *connection, - guint subscription_id) -{ - g_dbus_connection_unregister_object (connection, subscription_id); -} - -#endif - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/salnativewidgets-gtk.cxx b/vcl/unx/gtk/salnativewidgets-gtk.cxx deleted file mode 100644 index eb2f59457575..000000000000 --- a/vcl/unx/gtk/salnativewidgets-gtk.cxx +++ /dev/null @@ -1,4480 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this 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_version.h> - -#include <vcl/svapp.hxx> - -#include <unx/gtk/gtkframe.hxx> -#include <unx/gtk/gtkdata.hxx> -#include <unx/gtk/gtkinst.hxx> -#include <unx/gtk/gtkgdi.hxx> - -#include <unx/pixmap.hxx> -#include <saldatabasic.hxx> -#include <unx/saldisp.hxx> - -#include <cstdio> -#include <cmath> -#include <memory> -#include <vector> -#include <algorithm> -#include <unordered_map> - -#include <boost/optional.hpp> - -#include <vcl/vclenum.hxx> -#include <vcl/settings.hxx> -#include <unx/fontmanager.hxx> -#include <vcl/decoview.hxx> - -#include <vcl/opengl/OpenGLHelper.hxx> -#include <ControlCacheKey.hxx> - -typedef struct _cairo_font_options cairo_font_options_t; -const char* const tabPrelitDataName="libreoffice-tab-is-prelit"; - -// initialize statics -bool GtkSalGraphics::bThemeChanged = true; -bool GtkSalGraphics::bNeedPixmapPaint = false; -bool GtkSalGraphics::bNeedTwoPasses = false; - -enum -{ - BG_NONE = 0, - BG_FILL, - BG_WHITE, - BG_BLACK -}; - -GtkSalGraphics::GtkSalGraphics( GtkSalFrame *pFrame, GtkWidget *pWindow, - SalX11Screen nXScreen ) - : X11SalGraphics(), - m_pWindow( pWindow ), - m_aClipRegion(true) -{ - Init( pFrame, GDK_WINDOW_XID( widget_get_window( pWindow ) ), nXScreen ); - - initWidgetDrawBackends(); -} - -GtkSalGraphics::~GtkSalGraphics() -{ -} - -/************************************* - * Cached native widget objects - *************************************/ -class NWPixmapCacheList; -class NWPixmapCache; -struct NWFWidgetData -{ - GtkWidget * gCacheWindow; - GtkWidget * gDumbContainer; - - GtkWidget * gBtnWidget; - GtkWidget * gRadioWidget; - GtkWidget * gRadioWidgetSibling; - GtkWidget * gCheckWidget; - GtkWidget * gScrollHorizWidget; - GtkWidget * gScrollVertWidget; - GtkWidget * gArrowWidget; - GtkWidget * gDropdownWidget; - GtkWidget * gEditBoxWidget; - GtkWidget * gSpinButtonWidget; - GtkWidget * gNotebookWidget; - GtkWidget * gOptionMenuWidget; - GtkWidget * gComboWidget; - GtkWidget * gScrolledWindowWidget; - GtkWidget * gToolbarWidget; - GtkWidget * gToolbarButtonWidget; - GtkWidget * gHandleBoxWidget; - GtkWidget * gMenubarWidget; - GtkWidget * gMenuItemMenubarWidget; - GtkWidget * gMenuWidget; - GtkWidget * gMenuItemMenuWidget; - GtkWidget * gMenuItemCheckMenuWidget; - GtkWidget * gMenuItemRadioMenuWidget; - GtkWidget * gMenuItemSeparatorMenuWidget; - GtkWidget * gImageMenuItem; - GtkWidget * gTooltipPopup; - GtkWidget * gProgressBar; - GtkWidget * gTreeView; - GtkWidget * gHScale; - GtkWidget * gVScale; - GtkWidget * gSeparator; - GtkWidget * gDialog; - GtkWidget * gFrame; - - NWPixmapCacheList* gNWPixmapCacheList; - NWPixmapCache* gCacheTabItems; - NWPixmapCache* gCacheTabPages; - - NWFWidgetData() : - gCacheWindow( nullptr ), - gDumbContainer( nullptr ), - gBtnWidget( nullptr ), - gRadioWidget( nullptr ), - gRadioWidgetSibling( nullptr ), - gCheckWidget( nullptr ), - gScrollHorizWidget( nullptr ), - gScrollVertWidget( nullptr ), - gArrowWidget( nullptr ), - gDropdownWidget( nullptr ), - gEditBoxWidget( nullptr ), - gSpinButtonWidget( nullptr ), - gNotebookWidget( nullptr ), - gOptionMenuWidget( nullptr ), - gComboWidget( nullptr ), - gScrolledWindowWidget( nullptr ), - gToolbarWidget( nullptr ), - gToolbarButtonWidget( nullptr ), - gHandleBoxWidget( nullptr ), - gMenubarWidget( nullptr ), - gMenuItemMenubarWidget( nullptr ), - gMenuWidget( nullptr ), - gMenuItemMenuWidget( nullptr ), - gMenuItemCheckMenuWidget( nullptr ), - gMenuItemRadioMenuWidget( nullptr ), - gMenuItemSeparatorMenuWidget( nullptr ), - gImageMenuItem( nullptr ), - gTooltipPopup( nullptr ), - gProgressBar( nullptr ), - gTreeView( nullptr ), - gHScale( nullptr ), - gVScale( nullptr ), - gSeparator( nullptr ), - gDialog( nullptr ), - gFrame( nullptr ), - gNWPixmapCacheList( nullptr ), - gCacheTabItems( nullptr ), - gCacheTabPages( nullptr ) - {} -}; - -// Keep a hash table of Widgets->default flags so that we can -// easily and quickly reset each to a default state before using -// them -static std::unordered_map<long, guint> gWidgetDefaultFlags; -class WidgetDataVector -{ -private: - std::vector<NWFWidgetData> mData; - -public: - explicit WidgetDataVector(size_t nElems = 0) : mData( nElems ) {} - size_t size() const { return mData.size(); } - NWFWidgetData &operator [](size_t i) { return mData.at(i); } - NWFWidgetData &operator [](const SalX11Screen &s) { return mData.at(s.getXScreen()); } -}; -static WidgetDataVector gWidgetData; - -static const GtkBorder aDefDefBorder = { 1, 1, 1, 1 }; - -// Some GTK defaults -#define MIN_ARROW_SIZE 11 -#define BTN_CHILD_SPACING 1 -#define MIN_SPIN_ARROW_WIDTH 6 - -static void NWEnsureGTKRadio ( SalX11Screen nScreen ); -static void NWEnsureGTKButton ( SalX11Screen nScreen ); -static void NWEnsureGTKCheck ( SalX11Screen nScreen ); -static void NWEnsureGTKScrollbars ( SalX11Screen nScreen ); -static void NWEnsureGTKArrow ( SalX11Screen nScreen ); -static void NWEnsureGTKEditBox ( SalX11Screen nScreen ); -static void NWEnsureGTKSpinButton ( SalX11Screen nScreen ); -static void NWEnsureGTKNotebook ( SalX11Screen nScreen ); -static void NWEnsureGTKOptionMenu ( SalX11Screen nScreen ); -static void NWEnsureGTKCombo ( SalX11Screen nScreen ); -static void NWEnsureGTKScrolledWindow ( SalX11Screen nScreen ); -static void NWEnsureGTKToolbar ( SalX11Screen nScreen ); -static void NWEnsureGTKMenubar ( SalX11Screen nScreen ); -static void NWEnsureGTKMenu ( SalX11Screen nScreen ); -static void NWEnsureGTKTooltip ( SalX11Screen nScreen ); -static void NWEnsureGTKDialog ( SalX11Screen nScreen ); -static void NWEnsureGTKFrame ( SalX11Screen nScreen ); -static void NWEnsureGTKProgressBar ( SalX11Screen nScreen ); -static void NWEnsureGTKTreeView ( SalX11Screen nScreen ); -static void NWEnsureGTKSlider ( SalX11Screen nScreen ); - -static void NWConvertVCLStateToGTKState( ControlState nVCLState, GtkStateType* nGTKState, GtkShadowType* nGTKShadow ); -static void NWAddWidgetToCacheWindow( GtkWidget* widget, SalX11Screen nScreen ); -static void NWSetWidgetState( GtkWidget* widget, ControlState nState, GtkStateType nGtkState ); - -static void NWCalcArrowRect( const tools::Rectangle& rButton, tools::Rectangle& rArrow ); - -/* - * Individual helper functions - * - */ - -static tools::Rectangle NWGetButtonArea( SalX11Screen nScreen, tools::Rectangle aAreaRect, ControlState nState); - -static tools::Rectangle NWGetTabItemRect( SalX11Screen nScreen, tools::Rectangle aAreaRect ); - -static tools::Rectangle NWGetEditBoxPixmapRect( SalX11Screen nScreen, tools::Rectangle aAreaRect ); - -static void NWPaintOneEditBox( SalX11Screen nScreen, GdkDrawable * gdkDrawable, GdkRectangle const *gdkRect, - ControlType nType, tools::Rectangle aEditBoxRect, - ControlState nState ); - -static tools::Rectangle NWGetSpinButtonRect( SalX11Screen nScreen, ControlPart nPart, tools::Rectangle aAreaRect ); - -static void NWPaintOneSpinButton( SalX11Screen nScreen, GdkPixmap * pixmap, ControlPart nPart, tools::Rectangle aAreaRect, - ControlState nState ); - -static tools::Rectangle NWGetComboBoxButtonRect( SalX11Screen nScreen, ControlPart nPart, tools::Rectangle aAreaRect ); - -static tools::Rectangle NWGetListBoxButtonRect( SalX11Screen nScreen, ControlPart nPart, tools::Rectangle aAreaRect); - -static tools::Rectangle NWGetListBoxIndicatorRect( SalX11Screen nScreen, tools::Rectangle aAreaRect); - -static tools::Rectangle NWGetToolbarRect( SalX11Screen nScreen, - ControlPart nPart, - tools::Rectangle aAreaRect ); - -static int getFrameWidth(GtkWidget const * widget); - -static tools::Rectangle NWGetScrollButtonRect( SalX11Screen nScreen, ControlPart nPart, tools::Rectangle aAreaRect ); - - -/************************************************************************ - * GDK implementation of X11Pixmap - ************************************************************************/ - -class GdkX11Pixmap : public X11Pixmap -{ -public: - GdkX11Pixmap( int nWidth, int nHeight, int nDepth ); - virtual ~GdkX11Pixmap() override; - - virtual int GetDepth() const override; - virtual SalX11Screen GetScreen() const override; - virtual Pixmap GetPixmap() const override; - GdkPixmap* GetGdkPixmap() const; - GdkDrawable* GetGdkDrawable() const; - -protected: - GdkPixmap* mpGdkPixmap; - int mnDepth; -}; - -GdkX11Pixmap::GdkX11Pixmap( int nWidth, int nHeight, int nDepth ) -: X11Pixmap( nWidth, nHeight ) -{ - mpGdkPixmap = gdk_pixmap_new( nullptr, nWidth, nHeight, nDepth ); - mnDepth = gdk_drawable_get_depth( GDK_DRAWABLE( mpGdkPixmap ) ); - - GdkScreen *pScreen = gdk_drawable_get_screen( GDK_DRAWABLE( mpGdkPixmap ) ); - gdk_drawable_set_colormap( GDK_DRAWABLE( mpGdkPixmap ), gdk_screen_get_default_colormap( pScreen ) ); -} - -GdkX11Pixmap::~GdkX11Pixmap() -{ - g_object_unref( mpGdkPixmap ); -} - -int GdkX11Pixmap::GetDepth() const -{ - return mnDepth; -} - -SalX11Screen GdkX11Pixmap::GetScreen() const -{ - return SalX11Screen( gdk_screen_get_number( gdk_drawable_get_screen( GDK_DRAWABLE(mpGdkPixmap) ) ) ); -} - -Pixmap GdkX11Pixmap::GetPixmap() const -{ - return GDK_PIXMAP_XID( mpGdkPixmap ); -} - -GdkPixmap* GdkX11Pixmap::GetGdkPixmap() const -{ - return mpGdkPixmap; -} - -GdkDrawable* GdkX11Pixmap::GetGdkDrawable() const -{ - return GDK_DRAWABLE( mpGdkPixmap ); -} - - -/********************************************************* - * PixmapCache - *********************************************************/ - -// as some native widget drawing operations are pretty slow -// with certain themes (eg tabpages) -// this cache can be used to cache the corresponding pixmap -// see NWPaintGTKTabItem - -class NWPixmapCacheData -{ -public: - ControlType m_nType; - ControlState m_nState; - tools::Rectangle m_pixmapRect; - std::unique_ptr<GdkX11Pixmap> m_pixmap; - std::unique_ptr<GdkX11Pixmap> m_mask; - - NWPixmapCacheData() : m_nType(ControlType::Generic), m_nState(ControlState::NONE) {} - void SetPixmap( std::unique_ptr<GdkX11Pixmap> pPixmap, std::unique_ptr<GdkX11Pixmap> pMask ); -}; - -class NWPixmapCache -{ - int m_size; - int m_idx; - int m_screen; - std::unique_ptr<NWPixmapCacheData[]> pData; -public: - explicit NWPixmapCache( SalX11Screen nScreen ); - ~NWPixmapCache(); - - void SetSize( int n) - { m_idx = 0; m_size = n; pData.reset(new NWPixmapCacheData[m_size]); } - int GetSize() const { return m_size; } - - bool Find( ControlType aType, ControlState aState, const tools::Rectangle& r_pixmapRect, GdkX11Pixmap** pPixmap, GdkX11Pixmap** pMask ); - void Fill( ControlType aType, ControlState aState, const tools::Rectangle& r_pixmapRect, std::unique_ptr<GdkX11Pixmap> pPixmap, std::unique_ptr<GdkX11Pixmap> pMask ); - - void ThemeChanged(); -}; - -class NWPixmapCacheList -{ -public: - ::std::vector< NWPixmapCache* > mCaches; - - void AddCache( NWPixmapCache *pCache ); - void RemoveCache( NWPixmapCache *pCache ); - void ThemeChanged(); -}; - -// --- implementation --- - -void NWPixmapCacheData::SetPixmap( std::unique_ptr<GdkX11Pixmap> pPixmap, std::unique_ptr<GdkX11Pixmap> pMask ) -{ - m_pixmap = std::move(pPixmap); - m_mask = std::move(pMask); -} - -NWPixmapCache::NWPixmapCache( SalX11Screen nScreen ) -{ - m_idx = 0; - m_size = 0; - m_screen = nScreen.getXScreen(); - pData = nullptr; - if( gWidgetData[m_screen].gNWPixmapCacheList ) - gWidgetData[m_screen].gNWPixmapCacheList->AddCache(this); -} -NWPixmapCache::~NWPixmapCache() -{ - if( gWidgetData[m_screen].gNWPixmapCacheList ) - gWidgetData[m_screen].gNWPixmapCacheList->RemoveCache(this); -} -void NWPixmapCache::ThemeChanged() -{ - // throw away cached pixmaps - for(int i=0; i<m_size; i++) - pData[i].SetPixmap( nullptr, nullptr ); -} - -bool NWPixmapCache::Find( ControlType aType, ControlState aState, const tools::Rectangle& r_pixmapRect, GdkX11Pixmap** pPixmap, GdkX11Pixmap** pMask ) -{ - aState &= ~ControlState::CACHING_ALLOWED; // mask clipping flag - int i; - for(i=0; i<m_size; i++) - { - if( pData[i].m_nType == aType && - pData[i].m_nState == aState && - pData[i].m_pixmapRect.GetWidth() == r_pixmapRect.GetWidth() && - pData[i].m_pixmapRect.GetHeight() == r_pixmapRect.GetHeight() && - pData[i].m_pixmap != nullptr ) - { - *pPixmap = pData[i].m_pixmap.get(); - *pMask = pData[i].m_mask.get(); - return true; - } - } - return false; -} - -void NWPixmapCache::Fill( ControlType aType, ControlState aState, const tools::Rectangle& r_pixmapRect, - std::unique_ptr<GdkX11Pixmap> pPixmap, - std::unique_ptr<GdkX11Pixmap> pMask ) -{ - if( !(aState & ControlState::CACHING_ALLOWED) ) - return; - - aState &= ~ControlState::CACHING_ALLOWED; // mask clipping flag - m_idx = (m_idx+1) % m_size; // just wrap - pData[m_idx].m_nType = aType; - pData[m_idx].m_nState = aState; - pData[m_idx].m_pixmapRect = r_pixmapRect; - pData[m_idx].SetPixmap( std::move(pPixmap), std::move(pMask) ); -} - -void NWPixmapCacheList::AddCache( NWPixmapCache* pCache ) -{ - mCaches.push_back( pCache ); -} -void NWPixmapCacheList::RemoveCache( NWPixmapCache* pCache ) -{ - auto p = ::std::find( mCaches.begin(), mCaches.end(), pCache ); - if( p != mCaches.end() ) - mCaches.erase( p ); -} -void NWPixmapCacheList::ThemeChanged( ) -{ - for (auto const& cache : mCaches) - cache->ThemeChanged(); -} - -/********************************************************* - * Make border manipulation easier - *********************************************************/ -static void NW_gtk_border_set_from_border( GtkBorder& aDst, const GtkBorder * pSrc ) -{ - aDst.left = pSrc->left; - aDst.top = pSrc->top; - aDst.right = pSrc->right; - aDst.bottom = pSrc->bottom; -} - -/********************************************************* - * Initialize GTK and local stuff - *********************************************************/ -void GtkSalData::initNWF() -{ - ImplSVData* pSVData = ImplGetSVData(); - - // draw no border for popup menus (NWF draws its own) - pSVData->maNWFData.mbFlatMenu = true; - - // draw separate buttons for toolbox dropdown items - pSVData->maNWFData.mbToolboxDropDownSeparate = true; - - // draw toolbars in separate lines - pSVData->maNWFData.mbDockingAreaSeparateTB = true; - - // open first menu on F10 - pSVData->maNWFData.mbOpenMenuOnF10 = true; - - // omit GetNativeControl while painting (see brdwin.cxx) - pSVData->maNWFData.mbCanDrawWidgetAnySize = true; - - pSVData->maNWFData.mbDDListBoxNoTextArea = true; - - // use offscreen rendering when using OpenGL backend - if( OpenGLHelper::isVCLOpenGLEnabled() ) - { - GtkSalGraphics::bNeedPixmapPaint = true; - GtkSalGraphics::bNeedTwoPasses = true; - } - - int nScreens = GetGtkSalData()->GetGtkDisplay()->GetXScreenCount(); - gWidgetData = WidgetDataVector( nScreens ); - for( int i = 0; i < nScreens; i++ ) - gWidgetData[i].gNWPixmapCacheList = new NWPixmapCacheList; - - // small extra border around menu items - NWEnsureGTKMenu( SalX11Screen( 0 ) ); - gint horizontal_padding = 1; - gint vertical_padding = 1; - gint separator_padding = 1; - gtk_widget_style_get( gWidgetData[0].gMenuWidget, - "horizontal-padding", &horizontal_padding, - nullptr); - gtk_widget_style_get( gWidgetData[0].gMenuWidget, - "vertical-padding", &vertical_padding, - nullptr); - gtk_widget_style_get( gWidgetData[0].gMenuItemSeparatorMenuWidget, - "horizontal-padding", &separator_padding, - nullptr); - gint xthickness = gWidgetData[0].gMenuWidget->style->xthickness; - gint ythickness = gWidgetData[0].gMenuWidget->style->ythickness; - pSVData->maNWFData.mnMenuFormatBorderX = xthickness + horizontal_padding; - pSVData->maNWFData.mnMenuFormatBorderY = ythickness + vertical_padding; - pSVData->maNWFData.mnMenuSeparatorBorderX = separator_padding; - - static const char* pEnv = getenv( "SAL_GTK_USE_PIXMAPPAINT" ); - if( pEnv && *pEnv ) - GtkSalGraphics::bNeedPixmapPaint = true; - - #if OSL_DEBUG_LEVEL > 1 - std::fprintf( stderr, "GtkPlugin: using %s NWF\n", - GtkSalGraphics::bNeedPixmapPaint ? "offscreen" : "direct" ); - #endif - - GtkSettings *gtks = gtk_settings_get_default (); - gint val; - g_object_get (gtks, "gtk-auto-mnemonics", &val, nullptr); - if (val) pSVData->maNWFData.mbAutoAccel = true; - else pSVData->maNWFData.mbAutoAccel = false; - g_object_get (gtks, "gtk-enable-mnemonics", &val, nullptr); - if (val) pSVData->maNWFData.mbEnableAccel = true; - else pSVData->maNWFData.mbEnableAccel = false; -} - -/********************************************************* - * Release GTK and local stuff - *********************************************************/ -void GtkSalData::deInitNWF() -{ - for( size_t i = 0; i < gWidgetData.size(); i++ ) - { - // free up global widgets - // gtk_widget_destroy will in turn destroy the child hierarchy - // so only destroy disjunct hierarchies - if( gWidgetData[i].gCacheWindow ) - gtk_widget_destroy( gWidgetData[i].gCacheWindow ); - if( gWidgetData[i].gMenuWidget ) - g_object_unref (gWidgetData[i].gMenuWidget); - if( gWidgetData[i].gTooltipPopup ) - gtk_widget_destroy( gWidgetData[i].gTooltipPopup ); - if( gWidgetData[i].gDialog ) - gtk_widget_destroy( gWidgetData[i].gDialog ); - delete gWidgetData[i].gCacheTabPages; - gWidgetData[i].gCacheTabPages = nullptr; - delete gWidgetData[i].gCacheTabItems; - gWidgetData[i].gCacheTabItems = nullptr; - delete gWidgetData[i].gNWPixmapCacheList; - gWidgetData[i].gNWPixmapCacheList = nullptr; - } -} - -/********************************************************** - * track clip region - **********************************************************/ -void GtkSalGraphics::ResetClipRegion() -{ - m_aClipRegion.SetNull(); - X11SalGraphics::ResetClipRegion(); -} - -bool GtkSalGraphics::setClipRegion( const vcl::Region& i_rClip ) -{ - m_aClipRegion = i_rClip; - bool bRet = X11SalGraphics::setClipRegion( m_aClipRegion ); - if( m_aClipRegion.IsEmpty() ) - m_aClipRegion.SetNull(); - return bRet; -} - -void GtkSalGraphics::copyBits( const SalTwoRect& rPosAry, - SalGraphics* pSrcGraphics ) -{ - GtkSalFrame* pFrame = GetGtkFrame(); - ::Window aWin = None; - if( pFrame && m_pWindow ) - { - /* #i64117# some themes set the background pixmap VERY frequently */ - GdkWindow* pWin = GTK_WIDGET(m_pWindow)->window; - if( pWin ) - { - aWin = GDK_WINDOW_XWINDOW(pWin); - if( aWin != None ) - XSetWindowBackgroundPixmap( GtkSalFrame::getDisplay()->GetDisplay(), - aWin, - None ); - } - } - X11SalGraphics::copyBits( rPosAry, pSrcGraphics ); -} - -bool GtkSalGraphics::isNativeControlSupported( ControlType nType, ControlPart nPart ) -{ - switch(nType) - { - case ControlType::Pushbutton: - case ControlType::Radiobutton: - case ControlType::Checkbox: - case ControlType::Tooltip: - case ControlType::Progress: - case ControlType::ListNode: - case ControlType::ListNet: - if(nPart==ControlPart::Entire) - return true; - break; - - case ControlType::Scrollbar: - if(nPart==ControlPart::DrawBackgroundHorz || nPart==ControlPart::DrawBackgroundVert || - nPart==ControlPart::Entire || nPart==ControlPart::HasThreeButtons) - return true; - break; - - case ControlType::Editbox: - case ControlType::MultilineEditbox: - case ControlType::Combobox: - if(nPart==ControlPart::Entire || nPart==ControlPart::HasBackgroundTexture) - return true; - break; - - case ControlType::Spinbox: - if(nPart==ControlPart::Entire || nPart==ControlPart::AllButtons || nPart==ControlPart::HasBackgroundTexture) - return true; - break; - - case ControlType::SpinButtons: - if(nPart==ControlPart::Entire || nPart==ControlPart::AllButtons) - return true; - break; - - case ControlType::Frame: - case ControlType::WindowBackground: - return true; - - case ControlType::TabItem: - case ControlType::TabPane: - case ControlType::TabBody: - if(nPart==ControlPart::Entire || nPart==ControlPart::TabsDrawRtl) - return true; - break; - - case ControlType::Listbox: - if(nPart==ControlPart::Entire || nPart==ControlPart::ListboxWindow || nPart==ControlPart::HasBackgroundTexture) - return true; - break; - - case ControlType::Toolbar: - if( nPart==ControlPart::Entire - || nPart==ControlPart::DrawBackgroundHorz - || nPart==ControlPart::DrawBackgroundVert - || nPart==ControlPart::ThumbHorz - || nPart==ControlPart::ThumbVert - || nPart==ControlPart::Button - || nPart==ControlPart::SeparatorHorz - || nPart==ControlPart::SeparatorVert - ) - return true; - break; - - case ControlType::Menubar: - if(nPart==ControlPart::Entire || nPart==ControlPart::MenuItem) - return true; - break; - - case ControlType::MenuPopup: - if (nPart==ControlPart::Entire - || nPart==ControlPart::MenuItem - || nPart==ControlPart::MenuItemCheckMark - || nPart==ControlPart::MenuItemRadioMark - || nPart==ControlPart::Separator - || nPart==ControlPart::SubmenuArrow - ) - return true; - break; - - case ControlType::Slider: - if(nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea) - return true; - break; - - case ControlType::Fixedline: - if(nPart == ControlPart::SeparatorVert || nPart == ControlPart::SeparatorHorz) - return true; - break; - - case ControlType::ListHeader: - if(nPart == ControlPart::Button || nPart == ControlPart::Arrow) - return true; - break; - default: break; - } - - return false; -} - -bool GtkSalGraphics::hitTestNativeControl( ControlType nType, - ControlPart nPart, - const tools::Rectangle& rControlRegion, - const Point& aPos, - bool& rIsInside ) -{ - if ( ( nType == ControlType::Scrollbar ) && - ( ( nPart == ControlPart::ButtonUp ) || - ( nPart == ControlPart::ButtonDown ) || - ( nPart == ControlPart::ButtonLeft ) || - ( nPart == ControlPart::ButtonRight ) ) ) - { - NWEnsureGTKScrollbars( m_nXScreen ); - - // Grab some button style attributes - gboolean has_forward; - gboolean has_forward2; - gboolean has_backward; - gboolean has_backward2; - - gtk_widget_style_get( gWidgetData[m_nXScreen].gScrollHorizWidget, - "has-forward-stepper", &has_forward, - "has-secondary-forward-stepper", &has_forward2, - "has-backward-stepper", &has_backward, - "has-secondary-backward-stepper", &has_backward2, - nullptr ); - tools::Rectangle aForward; - tools::Rectangle aBackward; - - rIsInside = false; - - ControlPart nCounterPart = ControlPart::NONE; - if ( nPart == ControlPart::ButtonUp ) - nCounterPart = ControlPart::ButtonDown; - else if ( nPart == ControlPart::ButtonDown ) - nCounterPart = ControlPart::ButtonUp; - else if ( nPart == ControlPart::ButtonLeft ) - nCounterPart = ControlPart::ButtonRight; - else if ( nPart == ControlPart::ButtonRight ) - nCounterPart = ControlPart::ButtonLeft; - - aBackward = NWGetScrollButtonRect( m_nXScreen, nPart, rControlRegion ); - aForward = NWGetScrollButtonRect( m_nXScreen, nCounterPart, rControlRegion ); - - if ( has_backward && has_forward2 ) - { - Size aSize( aBackward.GetSize() ); - if ( ( nPart == ControlPart::ButtonUp ) || ( nPart == ControlPart::ButtonDown ) ) - aSize.setHeight( aBackward.GetHeight() / 2 ); - else - aSize.setWidth( aBackward.GetWidth() / 2 ); - aBackward.SetSize( aSize ); - - if ( nPart == ControlPart::ButtonDown ) - aBackward.Move( 0, aBackward.GetHeight() / 2 ); - else if ( nPart == ControlPart::ButtonRight ) - aBackward.Move( aBackward.GetWidth() / 2, 0 ); - } - - if ( has_backward2 && has_forward ) - { - Size aSize( aForward.GetSize() ); - if ( ( nPart == ControlPart::ButtonUp ) || ( nPart == ControlPart::ButtonDown ) ) - aSize.setHeight( aForward.GetHeight() / 2 ); - else - aSize.setWidth( aForward.GetWidth() / 2 ); - aForward.SetSize( aSize ); - - if ( nPart == ControlPart::ButtonDown ) - aForward.Move( 0, aForward.GetHeight() / 2 ); - else if ( nPart == ControlPart::ButtonRight ) - aForward.Move( aForward.GetWidth() / 2, 0 ); - } - - if ( ( nPart == ControlPart::ButtonUp ) || ( nPart == ControlPart::ButtonLeft ) ) - { - if ( has_backward ) - rIsInside |= aBackward.IsInside( aPos ); - if ( has_backward2 ) - rIsInside |= aForward.IsInside( aPos ); - } - else - { - if ( has_forward ) - rIsInside |= aBackward.IsInside( aPos ); - if ( has_forward2 ) - rIsInside |= aForward.IsInside( aPos ); - } - return true; - } - - if( isNativeControlSupported(nType, nPart) ) - { - rIsInside = rControlRegion.IsInside( aPos ); - return true; - } - else - { - return false; - } -} - -bool GtkSalGraphics::drawNativeControl(ControlType nType, ControlPart nPart, - const tools::Rectangle& rControlRegion, ControlState nState, - const ImplControlValue& aValue, const OUString& /*rCaption*/) -{ - // get a GC with current clipping region set - GetFontGC(); - - // theme changed ? - if( GtkSalGraphics::bThemeChanged ) - { - // invalidate caches - for( size_t i = 0; i < gWidgetData.size(); i++ ) - if( gWidgetData[i].gNWPixmapCacheList ) - gWidgetData[i].gNWPixmapCacheList->ThemeChanged(); - GtkSalGraphics::bThemeChanged = false; - } - - tools::Rectangle aCtrlRect( rControlRegion ); - vcl::Region aClipRegion( m_aClipRegion ); - if( aClipRegion.IsNull() ) - aClipRegion = aCtrlRect; - - // make pixmap a little larger since some themes draw decoration - // outside the rectangle, see e.g. checkbox - tools::Rectangle aPixmapRect(Point( aCtrlRect.Left()-1, aCtrlRect.Top()-1 ), - Size( aCtrlRect.GetWidth()+2, aCtrlRect.GetHeight()+2) ); - - ControlCacheKey aControlCacheKey(nType, nPart, nState, aPixmapRect.GetSize()); - if (aControlCacheKey.canCacheControl() - && TryRenderCachedNativeControl(aControlCacheKey, aPixmapRect.Left(), aPixmapRect.Top())) - { - return true; - } - - std::vector< tools::Rectangle > aClip; - int nPasses = 0; - GdkDrawable* gdkDrawable[2]; - std::unique_ptr<GdkX11Pixmap> xPixmap; - std::unique_ptr<GdkX11Pixmap> xMask; - - if ((bNeedPixmapPaint || (nState & ControlState::DOUBLEBUFFERING)) - && nType != ControlType::Scrollbar - && nType != ControlType::Spinbox - && nType != ControlType::TabItem - && nType != ControlType::TabPane - && nType != ControlType::Progress - && ! (nType == ControlType::Toolbar && (nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert) ) - ) - { - if( bNeedTwoPasses ) - { - xPixmap = NWGetPixmapFromScreen( aPixmapRect, BG_WHITE ); - xMask = NWGetPixmapFromScreen( aPixmapRect, BG_BLACK ); - if( !xPixmap || !xMask ) - return false; - nPasses = 2; - gdkDrawable[0] = xPixmap->GetGdkDrawable(); - gdkDrawable[1] = xMask->GetGdkDrawable(); - } - else - { - xPixmap = NWGetPixmapFromScreen( aPixmapRect, BG_FILL ); - if( !xPixmap ) - return false; - nPasses = 1; - gdkDrawable[0] = xPixmap->GetGdkDrawable(); - } - - aCtrlRect = tools::Rectangle( Point(1,1), aCtrlRect.GetSize() ); - aClip.push_back( aCtrlRect ); - } - else - { - nPasses = 1; - gdkDrawable[0] = GDK_DRAWABLE( GetGdkWindow() ); - RectangleVector aRectangles; - aClipRegion.GetRegionRectangles(aRectangles); - - for (auto const& rectangle : aRectangles) - { - tools::Rectangle aPaintRect = aCtrlRect.GetIntersection(rectangle); - if( aPaintRect.IsEmpty() ) - continue; - aClip.push_back( aPaintRect ); - } - } - - bool returnVal = false; - - for( int i = 0; i < nPasses; ++i ) - { - assert(gdkDrawable[i] && "rhbz#1050162"); - if( gdkDrawable[i] == nullptr ) - return false; - - returnVal = DoDrawNativeControl(gdkDrawable[i], nType, nPart, aCtrlRect, aClip, - nState, aValue, aControlCacheKey); - if( !returnVal ) - break; - } - - if( xPixmap ) - returnVal = returnVal && RenderAndCacheNativeControl(xPixmap.get(), xMask.get(), - aPixmapRect.Left(), aPixmapRect.Top(), - aControlCacheKey); - - return returnVal; -} - - -bool GtkSalGraphics::DoDrawNativeControl( - GdkDrawable* pDrawable, - ControlType nType, - ControlPart nPart, - const tools::Rectangle& aCtrlRect, - const std::vector< tools::Rectangle >& aClip, - ControlState nState, - const ImplControlValue& aValue, - ControlCacheKey& rControlCacheKey) -{ - if ( (nType==ControlType::Pushbutton) && (nPart==ControlPart::Entire) ) - { - return NWPaintGTKButton( pDrawable, aCtrlRect, aClip, nState ); - } - else if ( (nType==ControlType::Radiobutton) && (nPart==ControlPart::Entire) ) - { - return NWPaintGTKRadio( pDrawable, aCtrlRect, aClip, nState, aValue ); - } - else if ( (nType==ControlType::Checkbox) && (nPart==ControlPart::Entire) ) - { - return NWPaintGTKCheck( pDrawable, aCtrlRect, aClip, nState, aValue ); - } - else if ( (nType==ControlType::Scrollbar) && ((nPart==ControlPart::DrawBackgroundHorz) || (nPart==ControlPart::DrawBackgroundVert)) ) - { - return NWPaintGTKScrollbar( nPart, aCtrlRect, nState, aValue ); - } - else if ( ((nType==ControlType::Editbox) && ((nPart==ControlPart::Entire) || (nPart==ControlPart::HasBackgroundTexture)) ) - || ((nType==ControlType::Spinbox) && (nPart==ControlPart::HasBackgroundTexture)) - || ((nType==ControlType::Combobox) && (nPart==ControlPart::HasBackgroundTexture)) - || ((nType==ControlType::Listbox) && (nPart==ControlPart::HasBackgroundTexture)) ) - { - return NWPaintGTKEditBox( pDrawable, nType, aCtrlRect, aClip, nState ); - } - else if ( (nType==ControlType::MultilineEditbox) && ((nPart==ControlPart::Entire) || (nPart==ControlPart::HasBackgroundTexture)) ) - { - return NWPaintGTKEditBox( pDrawable, nType, aCtrlRect, aClip, nState ); - } - else if ( ((nType==ControlType::Spinbox) || (nType==ControlType::SpinButtons)) - && ((nPart==ControlPart::Entire) || (nPart==ControlPart::AllButtons)) ) - { - return NWPaintGTKSpinBox(nType, nPart, aCtrlRect, nState, aValue, rControlCacheKey); - } - else if ( (nType == ControlType::Combobox) && - ( (nPart==ControlPart::Entire) - ||(nPart==ControlPart::ButtonDown) - ) ) - { - return NWPaintGTKComboBox( pDrawable, nType, nPart, aCtrlRect, aClip, nState ); - } - else if ( (nType==ControlType::TabItem) || (nType==ControlType::TabPane) || (nType==ControlType::TabBody) ) - { - if ( nType == ControlType::TabBody ) - return true; - else - return NWPaintGTKTabItem( nType, aCtrlRect, nState, aValue); - } - else if ( (nType==ControlType::Listbox) && ((nPart==ControlPart::Entire) || (nPart==ControlPart::ListboxWindow)) ) - { - return NWPaintGTKListBox( pDrawable, nPart, aCtrlRect, aClip, nState ); - } - else if ( nType== ControlType::Toolbar ) - { - return NWPaintGTKToolbar( pDrawable, nPart, aCtrlRect, aClip, nState, aValue ); - } - else if ( nType== ControlType::Menubar ) - { - return NWPaintGTKMenubar( pDrawable, nPart, aCtrlRect, aClip, nState ); - } - else if( (nType == ControlType::MenuPopup) - && ( (nPart == ControlPart::Entire) - || (nPart == ControlPart::MenuItem) - || (nPart == ControlPart::MenuItemCheckMark) - || (nPart == ControlPart::MenuItemRadioMark) - || (nPart == ControlPart::Separator) - || (nPart == ControlPart::SubmenuArrow) - ) - ) - { - return NWPaintGTKPopupMenu( pDrawable, nPart, aCtrlRect, aClip, nState ); - } - else if( (nType == ControlType::Tooltip) && (nPart == ControlPart::Entire) ) - { - return NWPaintGTKTooltip( pDrawable, aCtrlRect, aClip ); - } - else if( (nType == ControlType::Progress) && (nPart == ControlPart::Entire) ) - { - return NWPaintGTKProgress( aCtrlRect, aValue ); - } - else if( (nType == ControlType::ListNode) && (nPart == ControlPart::Entire) ) - { - return NWPaintGTKListNode( pDrawable, aCtrlRect, nState, aValue ); - } - else if( (nType == ControlType::ListNet) && (nPart == ControlPart::Entire) ) - { - // don't actually draw anything; gtk treeviews do not draw lines - return TRUE; - } - else if( nType == ControlType::Slider ) - { - return NWPaintGTKSlider(pDrawable, nPart, aCtrlRect, nState, aValue); - } - else if( nType == ControlType::WindowBackground ) - { - return NWPaintGTKWindowBackground( pDrawable, aCtrlRect, aClip ); - } - else if( nType == ControlType::Fixedline ) - { - return NWPaintGTKFixedLine( pDrawable, nPart, aCtrlRect ); - } - else if(nType==ControlType::Frame) - { - return NWPaintGTKFrame( pDrawable, aCtrlRect, aClip, aValue); - } - else if(nType==ControlType::ListHeader) - { - if(nPart == ControlPart::Button) - return NWPaintGTKListHeader( pDrawable, aCtrlRect, aClip, nState ); - else if(nPart == ControlPart::Arrow) - return NWPaintGTKArrow( pDrawable, aCtrlRect, aClip, nState, aValue ); - } - - return false; -} - -bool GtkSalGraphics::getNativeControlRegion( ControlType nType, - ControlPart nPart, - const tools::Rectangle& rControlRegion, - ControlState nState, - const ImplControlValue& aValue, - const OUString& /*rCaption*/, - tools::Rectangle &rNativeBoundingRegion, - tools::Rectangle &rNativeContentRegion ) -{ - bool returnVal = false; - - if ( (nType==ControlType::Pushbutton) && (nPart==ControlPart::Entire) - && (rControlRegion.GetWidth() > 16) - && (rControlRegion.GetHeight() > 16) ) - { - rNativeBoundingRegion = NWGetButtonArea( m_nXScreen, rControlRegion, nState ); - rNativeContentRegion = rControlRegion; - returnVal = true; - } - if (nType == ControlType::TabItem && nPart == ControlPart::Entire) - { - rNativeBoundingRegion = NWGetTabItemRect(m_nXScreen, rControlRegion); - rNativeContentRegion = rNativeBoundingRegion; - returnVal = true; - } - if ( (nType==ControlType::Combobox) && ((nPart==ControlPart::ButtonDown) || (nPart==ControlPart::SubEdit)) ) - { - rNativeBoundingRegion = NWGetComboBoxButtonRect( m_nXScreen, nPart, rControlRegion); - rNativeContentRegion = rNativeBoundingRegion; - - returnVal = true; - } - if ( (nType==ControlType::Spinbox) && ((nPart==ControlPart::ButtonUp) || (nPart==ControlPart::ButtonDown) || (nPart==ControlPart::SubEdit)) ) - { - - rNativeBoundingRegion = NWGetSpinButtonRect( m_nXScreen, nPart, rControlRegion ); - rNativeContentRegion = rNativeBoundingRegion; - - returnVal = true; - } - if ( (nType==ControlType::Listbox) && ((nPart==ControlPart::ButtonDown) || (nPart==ControlPart::SubEdit)) ) - { - rNativeBoundingRegion = NWGetListBoxButtonRect( m_nXScreen, nPart, rControlRegion); - rNativeContentRegion = rNativeBoundingRegion; - - returnVal = true; - } - if ( (nType==ControlType::Toolbar) && - ((nPart==ControlPart::DrawBackgroundHorz) || - (nPart==ControlPart::DrawBackgroundVert) || - (nPart==ControlPart::ThumbHorz) || - (nPart==ControlPart::ThumbVert) || - (nPart==ControlPart::Button) - )) - { - rNativeBoundingRegion = NWGetToolbarRect( m_nXScreen, nPart, rControlRegion ); - rNativeContentRegion = rNativeBoundingRegion; - returnVal = true; - } - if ( (nType==ControlType::Scrollbar) && ((nPart==ControlPart::ButtonLeft) || (nPart==ControlPart::ButtonRight) || - (nPart==ControlPart::ButtonUp) || (nPart==ControlPart::ButtonDown) ) ) - { - rNativeBoundingRegion = NWGetScrollButtonRect( m_nXScreen, nPart, rControlRegion ); - rNativeContentRegion = rNativeBoundingRegion; - - //See fdo#33523, possibly makes sense to do this test for all return values - if (!rNativeContentRegion.GetWidth()) - rNativeContentRegion.SetRight( rNativeContentRegion.Left() + 1 ); - if (!rNativeContentRegion.GetHeight()) - rNativeContentRegion.SetBottom( rNativeContentRegion.Top() + 1 ); - returnVal = true; - } - if( (nType == ControlType::Menubar) && (nPart == ControlPart::Entire) ) - { - NWEnsureGTKMenubar( m_nXScreen ); - GtkRequisition aReq; - gtk_widget_size_request( gWidgetData[m_nXScreen].gMenubarWidget, &aReq ); - rNativeBoundingRegion = tools::Rectangle( rControlRegion.TopLeft(), - Size( rControlRegion.GetWidth(), aReq.height+1 ) ); - rNativeContentRegion = rNativeBoundingRegion; - returnVal = true; - } - if( nType == ControlType::MenuPopup ) - { - if( (nPart == ControlPart::MenuItemCheckMark) || - (nPart == ControlPart::MenuItemRadioMark) ) - { - NWEnsureGTKMenu( m_nXScreen ); - - gint indicator_size = 0; - GtkWidget* pWidget = (nPart == ControlPart::MenuItemCheckMark) ? - gWidgetData[m_nXScreen].gMenuItemCheckMenuWidget : gWidgetData[m_nXScreen].gMenuItemRadioMenuWidget; - gtk_widget_style_get( pWidget, - "indicator_size", &indicator_size, - nullptr ); - rNativeBoundingRegion = rControlRegion; - tools::Rectangle aIndicatorRect( Point( 0, - (rControlRegion.GetHeight()-indicator_size)/2), - Size( indicator_size, indicator_size ) ); - rNativeContentRegion = aIndicatorRect; - returnVal = true; - } - else if( nPart == ControlPart::SubmenuArrow ) - { - GtkWidget* widget = gWidgetData[m_nXScreen].gMenuItemMenuWidget; - GtkWidget* child; - PangoContext *context; - PangoFontMetrics *metrics; - gint arrow_size; - gint arrow_extent; - guint horizontal_padding; - gfloat arrow_scaling = 0.4; // Default for early GTK versions - - gtk_widget_style_get( widget, - "horizontal-padding", &horizontal_padding, - nullptr ); - - // Use arrow-scaling property if available (2.15+), avoid warning otherwise - if ( gtk_widget_class_find_style_property( GTK_WIDGET_GET_CLASS( widget ), - "arrow-scaling" ) ) - { - gtk_widget_style_get( widget, - "arrow-scaling", &arrow_scaling, - nullptr ); - } - - child = GTK_BIN( widget )->child; - - context = gtk_widget_get_pango_context( child ); - metrics = pango_context_get_metrics( context, - child->style->font_desc, - pango_context_get_language( context ) ); - - arrow_size = PANGO_PIXELS( pango_font_metrics_get_ascent( metrics ) + - pango_font_metrics_get_descent( metrics ) ); - - pango_font_metrics_unref( metrics ); - - arrow_extent = static_cast<gint>(arrow_size * arrow_scaling); - - rNativeContentRegion = tools::Rectangle( Point( 0, 0 ), - Size( arrow_extent, arrow_extent )); - rNativeBoundingRegion = tools::Rectangle( Point( 0, 0 ), - Size( arrow_extent + horizontal_padding, arrow_extent )); - returnVal = true; - } - } - if( nType == ControlType::Radiobutton || nType == ControlType::Checkbox ) - { - NWEnsureGTKRadio( m_nXScreen ); - NWEnsureGTKCheck( m_nXScreen ); - GtkWidget* widget = (nType == ControlType::Radiobutton) ? gWidgetData[m_nXScreen].gRadioWidget : gWidgetData[m_nXScreen].gCheckWidget; - gint indicator_size, indicator_spacing, focusPad, focusWidth; - gtk_widget_style_get( widget, - "indicator_size", &indicator_size, - "indicator_spacing", &indicator_spacing, - "focus-line-width", &focusWidth, - "focus-padding", &focusPad, - nullptr); - indicator_size += 2*indicator_spacing + 2*(focusWidth + focusWidth); - rNativeBoundingRegion = rControlRegion; - tools::Rectangle aIndicatorRect( Point( 0, - (rControlRegion.GetHeight()-indicator_size)/2), - Size( indicator_size, indicator_size ) ); - rNativeContentRegion = aIndicatorRect; - returnVal = true; - } - if( (nType == ControlType::Editbox || nType == ControlType::Spinbox || nType == ControlType::Combobox) && nPart == ControlPart::Entire ) - { - NWEnsureGTKEditBox( m_nXScreen ); - GtkWidget* widget = gWidgetData[m_nXScreen].gEditBoxWidget; - GtkRequisition aReq; - gtk_widget_size_request( widget, &aReq ); - tools::Rectangle aEditRect = rControlRegion; - long nHeight = std::max<long>(aEditRect.GetHeight(), aReq.height); - aEditRect = tools::Rectangle( aEditRect.TopLeft(), - Size( aEditRect.GetWidth(), nHeight ) ); - rNativeBoundingRegion = aEditRect; - rNativeContentRegion = rNativeBoundingRegion; - returnVal = true; - } - if( (nType == ControlType::Slider) && (nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert) ) - { - NWEnsureGTKSlider( m_nXScreen ); - GtkWidget* widget = (nPart == ControlPart::ThumbHorz) ? gWidgetData[m_nXScreen].gHScale : gWidgetData[m_nXScreen].gVScale; - gint slider_length = 10; - gint slider_width = 10; - gtk_widget_style_get( widget, - "slider-width", &slider_width, - "slider-length", &slider_length, - nullptr); - tools::Rectangle aRect( rControlRegion ); - if( nPart == ControlPart::ThumbHorz ) - { - aRect.SetRight( aRect.Left() + slider_length - 1 ); - aRect.SetBottom( aRect.Top() + slider_width - 1 ); - } - else - { - aRect.SetBottom( aRect.Top() + slider_length - 1 ); - aRect.SetRight( aRect.Left() + slider_width - 1 ); - } - rNativeBoundingRegion = rNativeContentRegion = aRect; - returnVal = true; - } - if( nType == ControlType::Frame && nPart == ControlPart::Border ) - { - int frameWidth = getFrameWidth(gWidgetData[m_nXScreen].gFrame); - rNativeBoundingRegion = rControlRegion; - DrawFrameFlags nStyle = static_cast<DrawFrameFlags>(aValue.getNumericVal() & 0xfff0); - int x1=rControlRegion.Left(); - int y1=rControlRegion.Top(); - int x2=rControlRegion.Right(); - int y2=rControlRegion.Bottom(); - - if( nStyle & DrawFrameFlags::NoDraw ) - { - rNativeContentRegion = tools::Rectangle(x1+frameWidth, - y1+frameWidth, - x2-frameWidth, - y2-frameWidth); - } - else - rNativeContentRegion = rControlRegion; - returnVal=true; - } - - return returnVal; -} - -/************************************************************************ - * Individual control drawing functions - ************************************************************************/ - -// macros to call before and after the rendering code for a widget -// it takes care of creating the needed pixmaps -#define BEGIN_PIXMAP_RENDER(aRect, gdkPixmap) \ - std::unique_ptr<GdkX11Pixmap> _pixmap, _mask; \ - int _nPasses = 0; \ - if( bNeedTwoPasses ) \ - { \ - _nPasses = 2; \ - _pixmap = NWGetPixmapFromScreen( aRect, BG_WHITE ); \ - _mask = NWGetPixmapFromScreen( aRect, BG_BLACK ); \ - } \ - else \ - { \ - _nPasses = 1; \ - _pixmap = NWGetPixmapFromScreen( aRect, BG_FILL ); \ - } \ - if( !_pixmap || ( bNeedTwoPasses && !_mask ) ) \ - return false; \ - for( int i = 0; i < _nPasses; ++i ) \ - { \ - GdkPixmap* gdkPixmap = (i == 0) ? _pixmap->GetGdkPixmap() \ - : _mask->GetGdkPixmap(); - -#define END_PIXMAP_RENDER(aRect) \ - } \ - if( !NWRenderPixmapToScreen( _pixmap.get(), _mask.get(), aRect ) ) \ - return false; - -#define END_PIXMAP_RENDER_WITH_CONTROL_KEY(aRect, aControlKey) \ - } \ - if( !RenderAndCacheNativeControl( _pixmap.get(), _mask.get(), aRect.Left(), aRect.Top(), aControlKey ) ) \ - return false; - -// same as above but with pixmaps that should be kept for caching -#define BEGIN_CACHE_PIXMAP_RENDER(aRect, pixmap, mask, gdkPixmap) \ - int _nPasses = 0; \ - if( bNeedTwoPasses ) \ - { \ - _nPasses = 2; \ - pixmap = NWGetPixmapFromScreen( aRect, BG_WHITE ).release(); \ - mask = NWGetPixmapFromScreen( aRect, BG_BLACK ).release(); \ - } \ - else \ - { \ - _nPasses = 1; \ - pixmap = NWGetPixmapFromScreen( aRect, BG_FILL ).release(); \ - mask = nullptr; \ - } \ - if( !pixmap || ( bNeedTwoPasses && !mask ) ) \ - return false; \ - for( int i = 0; i < _nPasses; ++i ) \ - { \ - GdkPixmap* gdkPixmap = (i == 0) ? pixmap->GetGdkPixmap() \ - : mask->GetGdkPixmap(); - -#define END_CACHE_PIXMAP_RENDER(aRect, pixmap, mask) \ - } \ - if( !NWRenderPixmapToScreen( pixmap, mask, aRect ) ) \ - return false; - -bool GtkSalGraphics::NWPaintGTKArrow( - GdkDrawable* gdkDrawable, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState, const ImplControlValue& aValue) -{ - GtkArrowType arrowType(aValue.getNumericVal()&1?GTK_ARROW_DOWN:GTK_ARROW_UP); - GtkStateType stateType(nState&ControlState::PRESSED?GTK_STATE_ACTIVE:GTK_STATE_NORMAL); - - GdkRectangle clipRect; - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - gtk_paint_arrow(m_pWindow->style,gdkDrawable,stateType,GTK_SHADOW_NONE,&clipRect, - m_pWindow,"arrow",arrowType,true, - rControlRectangle.Left(), - rControlRectangle.Top(), - rControlRectangle.GetWidth(), - rControlRectangle.GetHeight()); - } - return true; -} - -bool GtkSalGraphics::NWPaintGTKListHeader( - GdkDrawable* gdkDrawable, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState ) -{ - GtkStateType stateType; - GtkShadowType shadowType; - NWEnsureGTKTreeView( m_nXScreen ); - GtkWidget* &treeview(gWidgetData[m_nXScreen].gTreeView); - GtkTreeViewColumn* column=gtk_tree_view_get_column(GTK_TREE_VIEW(treeview),0); - GtkWidget* button=gtk_tree_view_column_get_widget(column); - while(button && !GTK_IS_BUTTON(button)) - button=gtk_widget_get_parent(button); - if(!button) - // Shouldn't ever happen - return false; - gtk_widget_realize(button); - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - NWSetWidgetState( button, nState, stateType ); - - GdkRectangle clipRect; - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - gtk_paint_box(button->style,gdkDrawable,stateType,shadowType,&clipRect, - button,"button", - rControlRectangle.Left()-1, - rControlRectangle.Top(), - rControlRectangle.GetWidth()+1, - rControlRectangle.GetHeight()); - } - return true; -} - -bool GtkSalGraphics::NWPaintGTKFixedLine( - GdkDrawable* gdkDrawable, - ControlPart nPart, - const tools::Rectangle& rControlRectangle ) -{ - if(nPart == ControlPart::SeparatorHorz) - gtk_paint_hline(m_pWindow->style,gdkDrawable,GTK_STATE_NORMAL,nullptr,m_pWindow,"hseparator",rControlRectangle.Left(),rControlRectangle.Right(),rControlRectangle.Top()); - else - gtk_paint_vline(m_pWindow->style,gdkDrawable,GTK_STATE_NORMAL,nullptr,m_pWindow,"vseparator",rControlRectangle.Top(),rControlRectangle.Bottom(),rControlRectangle.Left()); - - return true; -} - -bool GtkSalGraphics::NWPaintGTKFrame( - GdkDrawable* gdkDrawable, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - const ImplControlValue& aValue ) -{ - GdkRectangle clipRect; - int frameWidth=getFrameWidth(gWidgetData[m_nXScreen].gFrame); - GtkShadowType shadowType=GTK_SHADOW_IN; - DrawFrameStyle nStyle = static_cast<DrawFrameStyle>(aValue.getNumericVal() & 0x0f); - if( nStyle == DrawFrameStyle::In ) - shadowType=GTK_SHADOW_OUT; - if( nStyle == DrawFrameStyle::Out ) - shadowType=GTK_SHADOW_IN; - - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - // Draw background first - - // Top - gtk_paint_flat_box(m_pWindow->style,gdkDrawable,GTK_STATE_NORMAL,GTK_SHADOW_OUT,&clipRect, - m_pWindow,"base", - rControlRectangle.Left(), - rControlRectangle.Top(), - rControlRectangle.GetWidth(), - frameWidth); - // Bottom - gtk_paint_flat_box(m_pWindow->style,gdkDrawable,GTK_STATE_NORMAL,GTK_SHADOW_OUT,&clipRect, - m_pWindow,"base", - rControlRectangle.Left(), - rControlRectangle.Top()+rControlRectangle.GetHeight()-frameWidth, - rControlRectangle.GetWidth(), - frameWidth); - // Left - gtk_paint_flat_box(m_pWindow->style,gdkDrawable,GTK_STATE_NORMAL,GTK_SHADOW_OUT,&clipRect, - m_pWindow,"base", - rControlRectangle.Left(), - rControlRectangle.Top(), - 2*frameWidth, - rControlRectangle.GetHeight()); - // Right - gtk_paint_flat_box(m_pWindow->style,gdkDrawable,GTK_STATE_NORMAL,GTK_SHADOW_OUT,&clipRect, - m_pWindow,"base", - rControlRectangle.Left()+rControlRectangle.GetWidth()-frameWidth, - rControlRectangle.Top(), - 2*frameWidth, - rControlRectangle.GetHeight()); - - // Now render the frame - gtk_paint_shadow(gWidgetData[m_nXScreen].gFrame->style,gdkDrawable,GTK_STATE_NORMAL,shadowType,&clipRect, - gWidgetData[m_nXScreen].gFrame,"base", - rControlRectangle.Left(), - rControlRectangle.Top(), - rControlRectangle.GetWidth(), - rControlRectangle.GetHeight()); - } - - return true; -} - -bool GtkSalGraphics::NWPaintGTKWindowBackground( - GdkDrawable* gdkDrawable, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList ) -{ - GdkRectangle clipRect; - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - gtk_paint_flat_box(m_pWindow->style,gdkDrawable,GTK_STATE_NORMAL,GTK_SHADOW_NONE,&clipRect, - m_pWindow,"base", - rControlRectangle.Left(), - rControlRectangle.Top(), - rControlRectangle.GetWidth(), - rControlRectangle.GetHeight()); - } - - return true; -} - -bool GtkSalGraphics::NWPaintGTKButtonReal( - GtkWidget* button, - GdkDrawable* gdkDrawable, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState ) -{ - GtkStateType stateType; - GtkShadowType shadowType; - gboolean interiorFocus; - gint focusWidth; - gint focusPad; - bool bDrawFocus = true; - gint x, y, w, h; - GtkBorder aDefBorder; - GtkBorder* pBorder; - GdkRectangle clipRect; - - NWEnsureGTKButton( m_nXScreen ); - NWEnsureGTKToolbar( m_nXScreen ); - - // Flat toolbutton has a bit bigger variety of states than normal buttons, so handle it differently - if(GTK_IS_TOGGLE_BUTTON(button)) - { - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) - shadowType=GTK_SHADOW_IN; - else - shadowType=GTK_SHADOW_OUT; - - if(nState & ControlState::ROLLOVER) - stateType=GTK_STATE_PRELIGHT; - else - stateType=GTK_STATE_NORMAL; - - if(nState & ControlState::PRESSED) - { - stateType=GTK_STATE_ACTIVE; - shadowType=GTK_SHADOW_IN; - } - } - else - { - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - NWSetWidgetState( gWidgetData[m_nXScreen].gBtnWidget, nState, stateType ); - } - - x = rControlRectangle.Left(); - y = rControlRectangle.Top(); - w = rControlRectangle.GetWidth(); - h = rControlRectangle.GetHeight(); - - gint internal_padding = 0; - if(GTK_IS_TOOL_ITEM(button)) - { - gtk_widget_style_get (GTK_WIDGET (gWidgetData[m_nXScreen].gToolbarWidget), - "internal-padding", &internal_padding, - nullptr); - x += internal_padding/2; - w -= internal_padding; - stateType = GTK_STATE_PRELIGHT; - } - - // Grab some button style attributes - gtk_widget_style_get( gWidgetData[m_nXScreen].gBtnWidget, "focus-line-width", &focusWidth, - "focus-padding", &focusPad, - "interior_focus", &interiorFocus, - nullptr ); - gtk_widget_style_get( gWidgetData[m_nXScreen].gBtnWidget, - "default_border", &pBorder, - nullptr ); - - // Make sure the border values exist, otherwise use some defaults - if ( pBorder ) - { - NW_gtk_border_set_from_border( aDefBorder, pBorder ); - gtk_border_free( pBorder ); - } - else NW_gtk_border_set_from_border( aDefBorder, &aDefDefBorder ); - - // If the button is too small, don't ever draw focus or grab more space - if ( (w < 16) || (h < 16) ) - bDrawFocus = false; - - gint xi = x, yi = y, wi = w, hi = h; - if ( (nState & ControlState::DEFAULT) && bDrawFocus ) - { - xi += aDefBorder.left; - yi += aDefBorder.top; - wi -= aDefBorder.left + aDefBorder.right; - hi -= aDefBorder.top + aDefBorder.bottom; - } - - if ( !interiorFocus && bDrawFocus ) - { - xi += focusWidth + focusPad; - yi += focusWidth + focusPad; - wi -= 2 * (focusWidth + focusPad); - hi -= 2 * (focusWidth + focusPad); - } - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - // Buttons must paint opaque since some themes have alpha-channel enabled buttons - if(button == gWidgetData[m_nXScreen].gToolbarButtonWidget) - { - gtk_paint_box( gWidgetData[m_nXScreen].gToolbarWidget->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE, - &clipRect, gWidgetData[m_nXScreen].gToolbarWidget, "toolbar", x, y, w, h ); - } - else - { - gtk_paint_box( m_pWindow->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE, - &clipRect, m_pWindow, "base", x, y, w, h ); - } - - if ( GTK_IS_BUTTON(button) ) - { - if ( nState & ControlState::DEFAULT ) - gtk_paint_box( button->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, - &clipRect, button, "buttondefault", x, y, w, h ); - - /* don't draw "button", because it can be a tool_button, and - * it causes some weird things, so, the default button is - * just fine */ - gtk_paint_box( button->style, gdkDrawable, stateType, shadowType, - &clipRect, button, "button", xi, yi, wi, hi ); - } - } - - return true; -} - -bool GtkSalGraphics::NWPaintGTKButton( - GdkDrawable* gdkDrawable, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState) -{ - return NWPaintGTKButtonReal( - gWidgetData[m_nXScreen].gBtnWidget, - gdkDrawable, - rControlRectangle, - rClipList, - nState ); -} - -static tools::Rectangle NWGetButtonArea( SalX11Screen nScreen, - tools::Rectangle aAreaRect, ControlState nState ) -{ - gboolean interiorFocus; - gint focusWidth; - gint focusPad; - GtkBorder aDefBorder; - GtkBorder * pBorder; - bool bDrawFocus = true; - tools::Rectangle aRect; - gint x, y, w, h; - - NWEnsureGTKButton( nScreen ); - gtk_widget_style_get( gWidgetData[nScreen].gBtnWidget, - "focus-line-width", &focusWidth, - "focus-padding", &focusPad, - "interior_focus", &interiorFocus, - "default_border", &pBorder, - nullptr ); - - // Make sure the border values exist, otherwise use some defaults - if ( pBorder ) - { - NW_gtk_border_set_from_border( aDefBorder, pBorder ); - gtk_border_free( pBorder ); - } - else NW_gtk_border_set_from_border( aDefBorder, &aDefDefBorder ); - - x = aAreaRect.Left(); - y = aAreaRect.Top(); - w = aAreaRect.GetWidth(); - h = aAreaRect.GetHeight(); - - // If the button is too small, don't ever draw focus or grab more space - if ( (w < 16) || (h < 16) ) - bDrawFocus = false; - - if ( (nState & ControlState::DEFAULT) && bDrawFocus ) - { - x -= aDefBorder.left; - y -= aDefBorder.top; - w += aDefBorder.left + aDefBorder.right; - h += aDefBorder.top + aDefBorder.bottom; - } - - aRect = tools::Rectangle( Point( x, y ), Size( w, h ) ); - - return aRect; -} - -static tools::Rectangle NWGetTabItemRect( SalX11Screen nScreen, tools::Rectangle aAreaRect ) -{ - NWEnsureGTKNotebook( nScreen ); - - gint x, y, w, h; - - x = aAreaRect.Left(); - y = aAreaRect.Top(); - w = aAreaRect.GetWidth(); - h = aAreaRect.GetHeight(); - - gint xthickness = gWidgetData[nScreen].gNotebookWidget->style->xthickness; - gint ythickness = gWidgetData[nScreen].gNotebookWidget->style->ythickness; - - x -= xthickness; - y -= ythickness; - w += xthickness*2; - h += ythickness*2; - - return tools::Rectangle( Point( x, y ), Size( w, h ) ); -} - -bool GtkSalGraphics::NWPaintGTKRadio( GdkDrawable* gdkDrawable, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState, - const ImplControlValue& aValue ) -{ - GtkStateType stateType; - GtkShadowType shadowType; - bool isChecked = (aValue.getTristateVal()==ButtonValue::On); - gint x, y; - GdkRectangle clipRect; - - NWEnsureGTKButton( m_nXScreen ); - NWEnsureGTKRadio( m_nXScreen ); - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - gint indicator_size; - gtk_widget_style_get( gWidgetData[m_nXScreen].gRadioWidget, "indicator_size", &indicator_size, nullptr); - - x = rControlRectangle.Left() + (rControlRectangle.GetWidth()-indicator_size)/2; - y = rControlRectangle.Top() + (rControlRectangle.GetHeight()-indicator_size)/2; - - // Set the shadow based on if checked or not so we get a freakin checkmark. - shadowType = isChecked ? GTK_SHADOW_IN : GTK_SHADOW_OUT; - NWSetWidgetState( gWidgetData[m_nXScreen].gRadioWidget, nState, stateType ); - NWSetWidgetState( gWidgetData[m_nXScreen].gRadioWidgetSibling, nState, stateType ); - - // GTK enforces radio groups, so that if we don't have 2 buttons in the group, - // the single button will always be active. So we have to have 2 buttons. - - // #i59666# set the members directly where we should use - // gtk_toggle_button_set_active. reason: there are animated themes - // which are in active state only after a while leading to painting - // intermediate states between active/inactive. Let's hope that - // GtkToggleButtone stays binary compatible. - if (!isChecked) - GTK_TOGGLE_BUTTON(gWidgetData[m_nXScreen].gRadioWidgetSibling)->active = true; - GTK_TOGGLE_BUTTON(gWidgetData[m_nXScreen].gRadioWidget)->active = isChecked; - - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - gtk_paint_option( gWidgetData[m_nXScreen].gRadioWidget->style, gdkDrawable, stateType, shadowType, - &clipRect, gWidgetData[m_nXScreen].gRadioWidget, "radiobutton", - x, y, indicator_size, indicator_size ); - } - - return true; -} - -bool GtkSalGraphics::NWPaintGTKCheck( GdkDrawable* gdkDrawable, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState, - const ImplControlValue& aValue ) -{ - GtkStateType stateType; - GtkShadowType shadowType; - bool isChecked = (aValue.getTristateVal() == ButtonValue::On); - bool isInconsistent = (aValue.getTristateVal() == ButtonValue::Mixed); - GdkRectangle clipRect; - gint x,y; - - NWEnsureGTKButton( m_nXScreen ); - NWEnsureGTKCheck( m_nXScreen ); - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - gint indicator_size; - gtk_widget_style_get( gWidgetData[m_nXScreen].gCheckWidget, "indicator_size", &indicator_size, nullptr); - - x = rControlRectangle.Left() + (rControlRectangle.GetWidth()-indicator_size)/2; - y = rControlRectangle.Top() + (rControlRectangle.GetHeight()-indicator_size)/2; - - // Set the shadow based on if checked or not so we get a checkmark. - shadowType = isChecked ? GTK_SHADOW_IN : isInconsistent ? GTK_SHADOW_ETCHED_IN : GTK_SHADOW_OUT; - NWSetWidgetState( gWidgetData[m_nXScreen].gCheckWidget, nState, stateType ); - GTK_TOGGLE_BUTTON(gWidgetData[m_nXScreen].gCheckWidget)->active = isChecked; - - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - gtk_paint_check( gWidgetData[m_nXScreen].gCheckWidget->style, gdkDrawable, stateType, shadowType, - &clipRect, gWidgetData[m_nXScreen].gCheckWidget, "checkbutton", - x, y, indicator_size, indicator_size ); - } - - return true; -} - -static void NWCalcArrowRect( const tools::Rectangle& rButton, tools::Rectangle& rArrow ) -{ - // Size the arrow appropriately - Size aSize( rButton.GetWidth()/2, rButton.GetHeight()/2 ); - rArrow.SetSize( aSize ); - - rArrow.SetPos( Point( - rButton.Left() + ( rButton.GetWidth() - rArrow.GetWidth() ) / 2, - rButton.Top() + ( rButton.GetHeight() - rArrow.GetHeight() ) / 2 - ) ); -} - -bool GtkSalGraphics::NWPaintGTKScrollbar( ControlPart nPart, - const tools::Rectangle& rControlRectangle, - ControlState nState, - const ImplControlValue& aValue ) -{ - assert(aValue.getType() == ControlType::Scrollbar); - const ScrollbarValue& rScrollbarVal = static_cast<const ScrollbarValue&>(aValue); - std::unique_ptr<GdkX11Pixmap> pixmap; - tools::Rectangle pixmapRect, scrollbarRect; - GtkStateType stateType; - GtkShadowType shadowType; - GtkScrollbar * scrollbarWidget; - GtkStyle * style; - GtkAdjustment* scrollbarValues = nullptr; - GtkOrientation scrollbarOrientation; - tools::Rectangle thumbRect = rScrollbarVal.maThumbRect; - tools::Rectangle button11BoundRect = rScrollbarVal.maButton1Rect; // backward - tools::Rectangle button22BoundRect = rScrollbarVal.maButton2Rect; // forward - tools::Rectangle button12BoundRect = rScrollbarVal.maButton1Rect; // secondary forward - tools::Rectangle button21BoundRect = rScrollbarVal.maButton2Rect; // secondary backward - GtkArrowType button1Type; // backward - GtkArrowType button2Type; // forward - gchar * scrollbarTagH = const_cast<gchar *>("hscrollbar"); - gchar * scrollbarTagV = const_cast<gchar *>("vscrollbar"); - gchar * scrollbarTag = nullptr; - tools::Rectangle arrowRect; - gint slider_width = 0; - gint stepper_size = 0; - gint stepper_spacing = 0; - gint trough_border = 0; - gint min_slider_length = 0; - gint vShim = 0; - gint hShim = 0; - gint x,y,w,h; - - // make controlvalue rectangles relative to area - thumbRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() ); - button11BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() ); - button22BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() ); - button12BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() ); - button21BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() ); - - NWEnsureGTKButton( m_nXScreen ); - NWEnsureGTKScrollbars( m_nXScreen ); - NWEnsureGTKArrow( m_nXScreen ); - - // Find the overall bounding rect of the control - pixmapRect = rControlRectangle; - scrollbarRect = pixmapRect; - - if ( (scrollbarRect.GetWidth() <= 1) || (scrollbarRect.GetHeight() <= 1) ) - return true; - - // Grab some button style attributes - gtk_widget_style_get( gWidgetData[m_nXScreen].gScrollHorizWidget, - "slider_width", &slider_width, - "stepper_size", &stepper_size, - "trough_border", &trough_border, - "stepper_spacing", &stepper_spacing, - "min_slider_length", &min_slider_length, nullptr ); - gboolean has_forward; - gboolean has_forward2; - gboolean has_backward; - gboolean has_backward2; - - gtk_widget_style_get( gWidgetData[m_nXScreen].gScrollHorizWidget, "has-forward-stepper", &has_forward, - "has-secondary-forward-stepper", &has_forward2, - "has-backward-stepper", &has_backward, - "has-secondary-backward-stepper", &has_backward2, nullptr ); - gint magic = trough_border ? 1 : 0; - - if ( nPart == ControlPart::DrawBackgroundHorz ) - { - unsigned int sliderHeight = slider_width + (trough_border * 2); - vShim = (pixmapRect.GetHeight() - sliderHeight) / 2; - bool bRTLSwap = button11BoundRect.Left() > button22BoundRect.Left(); - - scrollbarRect.Move( 0, vShim ); - scrollbarRect.SetSize( Size( scrollbarRect.GetWidth(), sliderHeight ) ); - - scrollbarWidget = GTK_SCROLLBAR( gWidgetData[m_nXScreen].gScrollHorizWidget ); - scrollbarOrientation = GTK_ORIENTATION_HORIZONTAL; - scrollbarTag = scrollbarTagH; - button1Type = bRTLSwap? GTK_ARROW_RIGHT: GTK_ARROW_LEFT; - button2Type = bRTLSwap? GTK_ARROW_LEFT: GTK_ARROW_RIGHT; - - if ( has_backward ) - { - button12BoundRect.Move( stepper_size - trough_border, - (scrollbarRect.GetHeight() - slider_width) / 2 ); - } - - button11BoundRect.Move( trough_border, (scrollbarRect.GetHeight() - slider_width) / 2 ); - button11BoundRect.SetSize( Size( stepper_size, slider_width ) ); - button12BoundRect.SetSize( Size( stepper_size, slider_width ) ); - - if ( has_backward2 ) - { - button22BoundRect.Move( stepper_size+(trough_border+1)/2, (scrollbarRect.GetHeight() - slider_width) / 2 ); - button21BoundRect.Move( (trough_border+1)/2, (scrollbarRect.GetHeight() - slider_width) / 2 ); - } - else - { - button22BoundRect.Move( (trough_border+1)/2, (scrollbarRect.GetHeight() - slider_width) / 2 ); - } - - button21BoundRect.SetSize( Size( stepper_size, slider_width ) ); - button22BoundRect.SetSize( Size( stepper_size, slider_width ) ); - - thumbRect.SetBottom( thumbRect.Top() + slider_width - 1 ); - // Make sure the thumb is at least the default width (so we don't get tiny thumbs), - // but if the VCL gives us a size smaller than the theme's default thumb size, - // honor the VCL size - thumbRect.AdjustRight(magic ); - // Center vertically in the track - thumbRect.Move( 0, (scrollbarRect.GetHeight() - slider_width) / 2 ); - } - else - { - unsigned int sliderWidth = slider_width + (trough_border * 2); - hShim = (pixmapRect.GetWidth() - sliderWidth) / 2; - - scrollbarRect.Move( hShim, 0 ); - scrollbarRect.SetSize( Size( sliderWidth, scrollbarRect.GetHeight() ) ); - - scrollbarWidget = GTK_SCROLLBAR( gWidgetData[m_nXScreen].gScrollVertWidget ); - scrollbarOrientation = GTK_ORIENTATION_VERTICAL; - scrollbarTag = scrollbarTagV; - button1Type = GTK_ARROW_UP; - button2Type = GTK_ARROW_DOWN; - - if ( has_backward ) - { - button12BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, - stepper_size + trough_border ); - } - button11BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, trough_border ); - button11BoundRect.SetSize( Size( slider_width, stepper_size ) ); - button12BoundRect.SetSize( Size( slider_width, stepper_size ) ); - - if ( has_backward2 ) - { - button22BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, stepper_size+(trough_border+1)/2 ); - button21BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, (trough_border+1)/2 ); - } - else - { - button22BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, (trough_border+1)/2 ); - } - - button21BoundRect.SetSize( Size( slider_width, stepper_size ) ); - button22BoundRect.SetSize( Size( slider_width, stepper_size ) ); - - thumbRect.SetRight( thumbRect.Left() + slider_width - 1 ); - - thumbRect.AdjustBottom(magic ); - // Center horizontally in the track - thumbRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, 0 ); - } - - bool has_slider = ( thumbRect.GetWidth() > 0 && thumbRect.GetHeight() > 0 ); - - scrollbarValues = gtk_range_get_adjustment( GTK_RANGE(scrollbarWidget) ); - if ( scrollbarValues == nullptr ) - scrollbarValues = GTK_ADJUSTMENT( gtk_adjustment_new(0, 0, 0, 0, 0, 0) ); - if ( nPart == ControlPart::DrawBackgroundHorz ) - { - scrollbarValues->lower = rScrollbarVal.mnMin; - scrollbarValues->upper = rScrollbarVal.mnMax; - scrollbarValues->value = rScrollbarVal.mnCur; - scrollbarValues->page_size = scrollbarRect.GetWidth() / 2; - } - else - { - scrollbarValues->lower = rScrollbarVal.mnMin; - scrollbarValues->upper = rScrollbarVal.mnMax; - scrollbarValues->value = rScrollbarVal.mnCur; - scrollbarValues->page_size = scrollbarRect.GetHeight() / 2; - } - gtk_adjustment_changed( scrollbarValues ); - - // as multiple paints are required for the scrollbar - // painting them directly to the window flickers - pixmap = NWGetPixmapFromScreen( pixmapRect ); - if( ! pixmap ) - return false; - x = y = 0; - - w = pixmapRect.GetWidth(); - h = pixmapRect.GetHeight(); - - GdkDrawable* const &gdkDrawable = pixmap->GetGdkDrawable(); - GdkRectangle* gdkRect = nullptr; - - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - NWSetWidgetState( GTK_WIDGET(scrollbarWidget), nState, stateType ); - NWSetWidgetState( gWidgetData[m_nXScreen].gBtnWidget, nState, stateType ); - style = GTK_WIDGET( scrollbarWidget )->style; - - gtk_style_apply_default_background( m_pWindow->style, gdkDrawable, TRUE, - GTK_STATE_NORMAL, gdkRect, - x, y, w, h ); - - // ----------------- TROUGH - // Pass coordinates of draw rect: window(0,0) -> widget(bottom-right) (coords relative to widget) - gtk_paint_flat_box(m_pWindow->style, gdkDrawable, - GTK_STATE_NORMAL, GTK_SHADOW_NONE, gdkRect, - m_pWindow, "base", x-pixmapRect.Left(),y-pixmapRect.Top(),x+pixmapRect.Right(),y+pixmapRect.Bottom()); - - gtk_paint_box( style, gdkDrawable, GTK_STATE_ACTIVE, GTK_SHADOW_IN, - gdkRect, GTK_WIDGET(scrollbarWidget), "trough", - x, y, - scrollbarRect.GetWidth(), scrollbarRect.GetHeight() ); - - if ( nState & ControlState::FOCUSED ) - { - gtk_paint_focus( style, gdkDrawable, GTK_STATE_ACTIVE, - gdkRect, GTK_WIDGET(scrollbarWidget), "trough", - x, y, - scrollbarRect.GetWidth(), scrollbarRect.GetHeight() ); - } - - // ----------------- THUMB - if ( has_slider ) - { - NWConvertVCLStateToGTKState( rScrollbarVal.mnThumbState, &stateType, &shadowType ); - gtk_paint_slider( style, gdkDrawable, stateType, GTK_SHADOW_OUT, - gdkRect, GTK_WIDGET(scrollbarWidget), "slider", - x+hShim+thumbRect.Left(), y+vShim+thumbRect.Top(), - thumbRect.GetWidth(), thumbRect.GetHeight(), scrollbarOrientation ); - } - - // Some engines require allocation, e.g. Clearlooks uses it to identify - // positions of the buttons, whereupon the first and the last button will - // have rounded corners. - GTK_WIDGET(scrollbarWidget)->allocation.x = x; - GTK_WIDGET(scrollbarWidget)->allocation.y = y; - GTK_WIDGET(scrollbarWidget)->allocation.width = w; - GTK_WIDGET(scrollbarWidget)->allocation.height = h; - - bool backwardButtonInsensitive = - rScrollbarVal.mnCur == rScrollbarVal.mnMin; - bool forwardButtonInsensitive = rScrollbarVal.mnMax == 0 || - rScrollbarVal.mnCur + rScrollbarVal.mnVisibleSize >= rScrollbarVal.mnMax; - - // ----------------- BUTTON 1 - if ( has_backward ) - { - NWConvertVCLStateToGTKState( rScrollbarVal.mnButton1State, &stateType, &shadowType ); - if ( backwardButtonInsensitive ) - stateType = GTK_STATE_INSENSITIVE; - gtk_paint_box( style, gdkDrawable, stateType, shadowType, - gdkRect, GTK_WIDGET(scrollbarWidget), scrollbarTag, - x+hShim+button11BoundRect.Left(), y+vShim+button11BoundRect.Top(), - button11BoundRect.GetWidth(), button11BoundRect.GetHeight() ); - // ----------------- ARROW 1 - NWCalcArrowRect( button11BoundRect, arrowRect ); - gtk_paint_arrow( style, gdkDrawable, stateType, shadowType, - gdkRect, GTK_WIDGET(scrollbarWidget), scrollbarTag, button1Type, true, - x+hShim+arrowRect.Left(), y+vShim+arrowRect.Top(), - arrowRect.GetWidth(), arrowRect.GetHeight() ); - } - if ( has_forward2 ) - { - NWConvertVCLStateToGTKState( rScrollbarVal.mnButton2State, &stateType, &shadowType ); - if ( forwardButtonInsensitive ) - stateType = GTK_STATE_INSENSITIVE; - gtk_paint_box( style, gdkDrawable, stateType, shadowType, - gdkRect, GTK_WIDGET(scrollbarWidget), scrollbarTag, - x+hShim+button12BoundRect.Left(), y+vShim+button12BoundRect.Top(), - button12BoundRect.GetWidth(), button12BoundRect.GetHeight() ); - // ----------------- ARROW 1 - NWCalcArrowRect( button12BoundRect, arrowRect ); - gtk_paint_arrow( style, gdkDrawable, stateType, shadowType, - gdkRect, GTK_WIDGET(scrollbarWidget), scrollbarTag, button2Type, true, - x+hShim+arrowRect.Left(), y+vShim+arrowRect.Top(), - arrowRect.GetWidth(), arrowRect.GetHeight() ); - } - // ----------------- BUTTON 2 - if ( has_backward2 ) - { - NWConvertVCLStateToGTKState( rScrollbarVal.mnButton1State, &stateType, &shadowType ); - if ( backwardButtonInsensitive ) - stateType = GTK_STATE_INSENSITIVE; - gtk_paint_box( style, gdkDrawable, stateType, shadowType, gdkRect, - GTK_WIDGET(scrollbarWidget), scrollbarTag, - x+hShim+button21BoundRect.Left(), y+vShim+button21BoundRect.Top(), - button21BoundRect.GetWidth(), button21BoundRect.GetHeight() ); - // ----------------- ARROW 2 - NWCalcArrowRect( button21BoundRect, arrowRect ); - gtk_paint_arrow( style, gdkDrawable, stateType, shadowType, - gdkRect, GTK_WIDGET(scrollbarWidget), scrollbarTag, button1Type, true, - x+hShim+arrowRect.Left(), y+vShim+arrowRect.Top(), - arrowRect.GetWidth(), arrowRect.GetHeight() ); - } - if ( has_forward ) - { - NWConvertVCLStateToGTKState( rScrollbarVal.mnButton2State, &stateType, &shadowType ); - if ( forwardButtonInsensitive ) - stateType = GTK_STATE_INSENSITIVE; - gtk_paint_box( style, gdkDrawable, stateType, shadowType, gdkRect, - GTK_WIDGET(scrollbarWidget), scrollbarTag, - x+hShim+button22BoundRect.Left(), y+vShim+button22BoundRect.Top(), - button22BoundRect.GetWidth(), button22BoundRect.GetHeight() ); - // ----------------- ARROW 2 - NWCalcArrowRect( button22BoundRect, arrowRect ); - gtk_paint_arrow( style, gdkDrawable, stateType, shadowType, - gdkRect, GTK_WIDGET(scrollbarWidget), scrollbarTag, button2Type, true, - x+hShim+arrowRect.Left(), y+vShim+arrowRect.Top(), - arrowRect.GetWidth(), arrowRect.GetHeight() ); - } - - bool bRet = NWRenderPixmapToScreen( pixmap.get(), nullptr, pixmapRect ); - - return bRet; -} - -static tools::Rectangle NWGetScrollButtonRect( SalX11Screen nScreen, ControlPart nPart, tools::Rectangle aAreaRect ) -{ - gint slider_width; - gint stepper_size; - gint stepper_spacing; - gint trough_border; - - NWEnsureGTKScrollbars( nScreen ); - - // Grab some button style attributes - gtk_widget_style_get( gWidgetData[nScreen].gScrollHorizWidget, - "slider-width", &slider_width, - "stepper-size", &stepper_size, - "trough-border", &trough_border, - "stepper-spacing", &stepper_spacing, nullptr ); - - gboolean has_forward; - gboolean has_forward2; - gboolean has_backward; - gboolean has_backward2; - - gtk_widget_style_get( gWidgetData[nScreen].gScrollHorizWidget, - "has-forward-stepper", &has_forward, - "has-secondary-forward-stepper", &has_forward2, - "has-backward-stepper", &has_backward, - "has-secondary-backward-stepper", &has_backward2, nullptr ); - gint buttonWidth; - gint buttonHeight; - tools::Rectangle buttonRect; - - gint nFirst = 0; - gint nSecond = 0; - - if ( has_forward ) nSecond += 1; - if ( has_forward2 ) nFirst += 1; - if ( has_backward ) nFirst += 1; - if ( has_backward2 ) nSecond += 1; - - if ( ( nPart == ControlPart::ButtonUp ) || ( nPart == ControlPart::ButtonDown ) ) - { - buttonWidth = slider_width + 2 * trough_border; - buttonHeight = stepper_size + trough_border + stepper_spacing; - } - else - { - buttonWidth = stepper_size + trough_border + stepper_spacing; - buttonHeight = slider_width + 2 * trough_border; - } - - if ( nPart == ControlPart::ButtonUp ) - { - buttonHeight *= nFirst; - buttonHeight -= 1; - buttonRect.setX( aAreaRect.Left() ); - buttonRect.setY( aAreaRect.Top() ); - } - else if ( nPart == ControlPart::ButtonLeft ) - { - buttonWidth *= nFirst; - buttonWidth -= 1; - buttonRect.setX( aAreaRect.Left() ); - buttonRect.setY( aAreaRect.Top() ); - } - else if ( nPart == ControlPart::ButtonDown ) - { - buttonHeight *= nSecond; - buttonRect.setX( aAreaRect.Left() ); - buttonRect.setY( aAreaRect.Top() + aAreaRect.GetHeight() - buttonHeight ); - } - else if ( nPart == ControlPart::ButtonRight ) - { - buttonWidth *= nSecond; - buttonRect.setX( aAreaRect.Left() + aAreaRect.GetWidth() - buttonWidth ); - buttonRect.setY( aAreaRect.Top() ); - } - - buttonRect.SetSize( Size( buttonWidth, buttonHeight ) ); - - return buttonRect; -} - -bool GtkSalGraphics::NWPaintGTKEditBox( GdkDrawable* gdkDrawable, - ControlType nType, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState ) -{ - tools::Rectangle pixmapRect; - GdkRectangle clipRect; - - // Find the overall bounding rect of the buttons's drawing area, - // plus its actual draw rect excluding adornment - pixmapRect = NWGetEditBoxPixmapRect( m_nXScreen, rControlRectangle ); - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - NWPaintOneEditBox( m_nXScreen, gdkDrawable, &clipRect, nType, pixmapRect, nState ); - } - - return true; -} - -/* Take interior/exterior focus into account and return - * the bounding rectangle of the edit box including - * any focus requirements. - */ -static tools::Rectangle NWGetEditBoxPixmapRect(SalX11Screen nScreen, - tools::Rectangle aAreaRect) -{ - tools::Rectangle pixmapRect = aAreaRect; - gboolean interiorFocus; - gint focusWidth; - - NWEnsureGTKEditBox( nScreen ); - - // Grab some entry style attributes - gtk_widget_style_get( gWidgetData[nScreen].gEditBoxWidget, - "focus-line-width", &focusWidth, - "interior-focus", &interiorFocus, nullptr ); - - if ( !interiorFocus ) - { - pixmapRect.Move( -focusWidth, -focusWidth ); - pixmapRect.SetSize( Size( pixmapRect.GetWidth() + (2*focusWidth), - pixmapRect.GetHeight() + (2*focusWidth) ) ); - } - - return pixmapRect; -} - -/* Paint a GTK Entry widget into the specified GdkPixmap. - * All coordinates should be local to the Pixmap, NOT - * screen/window coordinates. - */ -static void NWPaintOneEditBox( SalX11Screen nScreen, - GdkDrawable * gdkDrawable, - GdkRectangle const * gdkRect, - ControlType nType, - tools::Rectangle aEditBoxRect, - ControlState nState ) -{ - GtkStateType stateType; - GtkShadowType shadowType; - GtkWidget *widget; - - NWEnsureGTKButton( nScreen ); - NWEnsureGTKEditBox( nScreen ); - NWEnsureGTKSpinButton( nScreen ); - NWEnsureGTKCombo( nScreen ); - NWEnsureGTKScrolledWindow( nScreen ); - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - switch ( nType ) - { - case ControlType::Spinbox: - widget = gWidgetData[nScreen].gSpinButtonWidget; - break; - - case ControlType::MultilineEditbox: - widget = gWidgetData[nScreen].gScrolledWindowWidget; - break; - case ControlType::Combobox: - widget = GTK_COMBO(gWidgetData[nScreen].gComboWidget)->entry; - break; - - default: - widget = gWidgetData[nScreen].gEditBoxWidget; - break; - } - - if ( stateType == GTK_STATE_PRELIGHT ) - stateType = GTK_STATE_NORMAL; - - NWSetWidgetState( widget, nState, stateType ); - - gint xborder = widget->style->xthickness; - gint yborder = widget->style->ythickness; - gint bInteriorFocus, nFocusLineWidth; - gtk_widget_style_get( widget, - "interior-focus", &bInteriorFocus, - "focus-line-width", &nFocusLineWidth, - nullptr); - if ( !bInteriorFocus ) - { - xborder += nFocusLineWidth; - yborder += nFocusLineWidth; - } - - gtk_paint_flat_box( widget->style, gdkDrawable, stateType, GTK_SHADOW_NONE, - gdkRect, widget, "entry_bg", - aEditBoxRect.Left() + xborder, aEditBoxRect.Top() + yborder, - aEditBoxRect.GetWidth() - 2*xborder, aEditBoxRect.GetHeight() - 2*yborder ); - gtk_paint_shadow( widget->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, - gdkRect, widget, "entry", - aEditBoxRect.Left(), aEditBoxRect.Top(), - aEditBoxRect.GetWidth(), aEditBoxRect.GetHeight() ); - -} - -bool GtkSalGraphics::NWPaintGTKSpinBox(ControlType nType, ControlPart nPart, - const tools::Rectangle& rControlRectangle, - ControlState nState, - const ImplControlValue& aValue, - ControlCacheKey& rControlCacheKey) -{ - tools::Rectangle pixmapRect; - GtkStateType stateType; - GtkShadowType shadowType; - const SpinbuttonValue * pSpinVal = (aValue.getType() == ControlType::SpinButtons) ? static_cast<const SpinbuttonValue *>(&aValue) : nullptr; - tools::Rectangle upBtnRect; - ControlPart upBtnPart = ControlPart::ButtonUp; - ControlState upBtnState = ControlState::ENABLED; - tools::Rectangle downBtnRect; - ControlPart downBtnPart = ControlPart::ButtonDown; - ControlState downBtnState = ControlState::ENABLED; - - NWEnsureGTKButton( m_nXScreen ); - NWEnsureGTKSpinButton( m_nXScreen ); - NWEnsureGTKArrow( m_nXScreen ); - - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - if ( pSpinVal ) - { - upBtnPart = pSpinVal->mnUpperPart; - upBtnState = pSpinVal->mnUpperState; - - downBtnPart = pSpinVal->mnLowerPart; - downBtnState = pSpinVal->mnLowerState; - } - - pixmapRect = rControlRectangle; - - BEGIN_PIXMAP_RENDER( pixmapRect, gdkPixmap ) - { - // First render background - gtk_paint_flat_box(m_pWindow->style,gdkPixmap,GTK_STATE_NORMAL,GTK_SHADOW_NONE,nullptr,m_pWindow,"base", - -pixmapRect.Left(), - -pixmapRect.Top(), - pixmapRect.Right(), - pixmapRect.Bottom() ); - - upBtnRect = NWGetSpinButtonRect( m_nXScreen, upBtnPart, pixmapRect ); - downBtnRect = NWGetSpinButtonRect( m_nXScreen, downBtnPart, pixmapRect ); - - if ( (nType==ControlType::Spinbox) && (nPart!=ControlPart::AllButtons) ) - { - // Draw an edit field for SpinBoxes and ComboBoxes - tools::Rectangle aEditBoxRect( pixmapRect ); - aEditBoxRect.SetSize( Size( pixmapRect.GetWidth() - upBtnRect.GetWidth(), aEditBoxRect.GetHeight() ) ); - if( AllSettings::GetLayoutRTL() ) - aEditBoxRect.setX( upBtnRect.GetWidth() ); - else - aEditBoxRect.setX( 0 ); - aEditBoxRect.setY( 0 ); - - NWPaintOneEditBox( m_nXScreen, gdkPixmap, nullptr, nType, aEditBoxRect, nState ); - } - - NWSetWidgetState( gWidgetData[m_nXScreen].gSpinButtonWidget, nState, stateType ); - gtk_widget_style_get( gWidgetData[m_nXScreen].gSpinButtonWidget, "shadow_type", &shadowType, nullptr ); - - if ( shadowType != GTK_SHADOW_NONE ) - { - tools::Rectangle shadowRect( upBtnRect ); - - shadowRect.Union( downBtnRect ); - gtk_paint_box( gWidgetData[m_nXScreen].gSpinButtonWidget->style, gdkPixmap, GTK_STATE_NORMAL, shadowType, nullptr, - gWidgetData[m_nXScreen].gSpinButtonWidget, "spinbutton", - (shadowRect.Left() - pixmapRect.Left()), (shadowRect.Top() - pixmapRect.Top()), - shadowRect.GetWidth(), shadowRect.GetHeight() ); - } - - NWPaintOneSpinButton( m_nXScreen, gdkPixmap, upBtnPart, pixmapRect, upBtnState ); - NWPaintOneSpinButton( m_nXScreen, gdkPixmap, downBtnPart, pixmapRect, downBtnState ); - } - END_PIXMAP_RENDER_WITH_CONTROL_KEY(pixmapRect, rControlCacheKey); - - return true; -} - -static tools::Rectangle NWGetSpinButtonRect( SalX11Screen nScreen, - ControlPart nPart, - tools::Rectangle aAreaRect ) -{ - gint buttonSize; - tools::Rectangle buttonRect; - - NWEnsureGTKSpinButton( nScreen ); - - buttonSize = MAX( PANGO_PIXELS( pango_font_description_get_size(GTK_WIDGET(gWidgetData[nScreen].gSpinButtonWidget)->style->font_desc) ), - MIN_SPIN_ARROW_WIDTH ); - buttonSize -= buttonSize % 2 - 1; /* force odd */ - buttonRect.SetSize( Size( buttonSize + 2 * gWidgetData[nScreen].gSpinButtonWidget->style->xthickness, - buttonRect.GetHeight() ) ); - if( AllSettings::GetLayoutRTL() ) - buttonRect.setX( aAreaRect.Left() ); - else - buttonRect.setX( aAreaRect.Left() + (aAreaRect.GetWidth() - buttonRect.GetWidth()) ); - if ( nPart == ControlPart::ButtonUp ) - { - buttonRect.setY( aAreaRect.Top() ); - buttonRect.SetBottom( buttonRect.Top() + (aAreaRect.GetHeight() / 2) ); - } - else if( nPart == ControlPart::ButtonDown ) - { - buttonRect.setY( aAreaRect.Top() + (aAreaRect.GetHeight() / 2) ); - buttonRect.SetBottom( aAreaRect.Bottom() ); // cover area completely - } - else - { - if( AllSettings::GetLayoutRTL() ) { - buttonRect.SetLeft( buttonRect.Right()+1 ); - buttonRect.SetRight( aAreaRect.Right() ); - } else { - buttonRect.SetRight( buttonRect.Left()-1 ); - buttonRect.SetLeft( aAreaRect.Left() ); - } - buttonRect.SetTop( aAreaRect.Top() ); - buttonRect.SetBottom( aAreaRect.Bottom() ); - } - - return buttonRect; -} - -static void NWPaintOneSpinButton( SalX11Screen nScreen, - GdkPixmap* pixmap, - ControlPart nPart, - tools::Rectangle aAreaRect, - ControlState nState ) -{ - tools::Rectangle buttonRect; - GtkStateType stateType; - GtkShadowType shadowType; - tools::Rectangle arrowRect; - gint arrowSize; - - NWEnsureGTKSpinButton( nScreen ); - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - buttonRect = NWGetSpinButtonRect( nScreen, nPart, aAreaRect ); - - NWSetWidgetState( gWidgetData[nScreen].gSpinButtonWidget, nState, stateType ); - gtk_paint_box( gWidgetData[nScreen].gSpinButtonWidget->style, pixmap, stateType, shadowType, nullptr, gWidgetData[nScreen].gSpinButtonWidget, - (nPart == ControlPart::ButtonUp) ? "spinbutton_up" : "spinbutton_down", - (buttonRect.Left() - aAreaRect.Left()), (buttonRect.Top() - aAreaRect.Top()), - buttonRect.GetWidth(), buttonRect.GetHeight() ); - - arrowSize = (buttonRect.GetWidth() - (2 * gWidgetData[nScreen].gSpinButtonWidget->style->xthickness)) - 4; - arrowSize -= arrowSize % 2 - 1; /* force odd */ - arrowRect.SetSize( Size( arrowSize, arrowSize ) ); - arrowRect.setX( buttonRect.Left() + (buttonRect.GetWidth() - arrowRect.GetWidth()) / 2 ); - if ( nPart == ControlPart::ButtonUp ) - arrowRect.setY( buttonRect.Top() + (buttonRect.GetHeight() - arrowRect.GetHeight()) / 2 + 1); - else - arrowRect.setY( buttonRect.Top() + (buttonRect.GetHeight() - arrowRect.GetHeight()) / 2 - 1); - - gtk_paint_arrow( gWidgetData[nScreen].gSpinButtonWidget->style, pixmap, stateType, GTK_SHADOW_OUT, nullptr, gWidgetData[nScreen].gSpinButtonWidget, - "spinbutton", (nPart == ControlPart::ButtonUp) ? GTK_ARROW_UP : GTK_ARROW_DOWN, true, - (arrowRect.Left() - aAreaRect.Left()), (arrowRect.Top() - aAreaRect.Top()), - arrowRect.GetWidth(), arrowRect.GetHeight() ); -} - -bool GtkSalGraphics::NWPaintGTKComboBox( GdkDrawable* gdkDrawable, - ControlType nType, ControlPart nPart, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState ) -{ - tools::Rectangle pixmapRect; - tools::Rectangle buttonRect; - GtkStateType stateType; - GtkShadowType shadowType; - tools::Rectangle arrowRect; - gint x,y; - GdkRectangle clipRect; - - NWEnsureGTKButton( m_nXScreen ); - NWEnsureGTKArrow( m_nXScreen ); - NWEnsureGTKCombo( m_nXScreen ); - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - // Find the overall bounding rect of the buttons's drawing area, - // plus its actual draw rect excluding adornment - pixmapRect = rControlRectangle; - x = rControlRectangle.Left(); - y = rControlRectangle.Top(); - - NWSetWidgetState( gWidgetData[m_nXScreen].gBtnWidget, nState, stateType ); - NWSetWidgetState( gWidgetData[m_nXScreen].gComboWidget, nState, stateType ); - NWSetWidgetState( gWidgetData[m_nXScreen].gArrowWidget, nState, stateType ); - - buttonRect = NWGetComboBoxButtonRect( m_nXScreen, ControlPart::ButtonDown, pixmapRect ); - if( nPart == ControlPart::ButtonDown ) - buttonRect.AdjustLeft(1 ); - - tools::Rectangle aEditBoxRect( pixmapRect ); - aEditBoxRect.SetSize( Size( pixmapRect.GetWidth() - buttonRect.GetWidth(), aEditBoxRect.GetHeight() ) ); - if( AllSettings::GetLayoutRTL() ) - aEditBoxRect.SetPos( Point( x + buttonRect.GetWidth() , y ) ); - - #define ARROW_EXTENT 0.7 - arrowRect.SetSize( Size( gint(MIN_ARROW_SIZE * ARROW_EXTENT), - gint(MIN_ARROW_SIZE * ARROW_EXTENT) ) ); - arrowRect.SetPos( Point( buttonRect.Left() + static_cast<gint>((buttonRect.GetWidth() - arrowRect.GetWidth()) / 2), - buttonRect.Top() + static_cast<gint>((buttonRect.GetHeight() - arrowRect.GetHeight()) / 2) ) ); - - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - if( nPart == ControlPart::Entire ) - NWPaintOneEditBox( m_nXScreen, gdkDrawable, &clipRect, nType, aEditBoxRect, - nState ); - - // Buttons must paint opaque since some themes have alpha-channel enabled buttons - gtk_paint_flat_box( m_pWindow->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE, - &clipRect, m_pWindow, "base", - x+(buttonRect.Left() - pixmapRect.Left()), - y+(buttonRect.Top() - pixmapRect.Top()), - buttonRect.GetWidth(), buttonRect.GetHeight() ); - gtk_paint_box( GTK_COMBO(gWidgetData[m_nXScreen].gComboWidget)->button->style, gdkDrawable, stateType, shadowType, - &clipRect, GTK_COMBO(gWidgetData[m_nXScreen].gComboWidget)->button, "button", - x+(buttonRect.Left() - pixmapRect.Left()), - y+(buttonRect.Top() - pixmapRect.Top()), - buttonRect.GetWidth(), buttonRect.GetHeight() ); - - gtk_paint_arrow( gWidgetData[m_nXScreen].gArrowWidget->style, gdkDrawable, stateType, shadowType, - &clipRect, gWidgetData[m_nXScreen].gArrowWidget, "arrow", GTK_ARROW_DOWN, true, - x+(arrowRect.Left() - pixmapRect.Left()), y+(arrowRect.Top() - pixmapRect.Top()), - arrowRect.GetWidth(), arrowRect.GetHeight() ); - } - - return true; -} - -static tools::Rectangle NWGetComboBoxButtonRect( SalX11Screen nScreen, - ControlPart nPart, - tools::Rectangle aAreaRect ) -{ - tools::Rectangle aButtonRect; - gint nArrowWidth; - gint nButtonWidth; - gint nFocusWidth; - gint nFocusPad; - - NWEnsureGTKArrow( nScreen ); - - // Grab some button style attributes - gtk_widget_style_get( gWidgetData[nScreen].gDropdownWidget, - "focus-line-width", &nFocusWidth, - "focus-padding", &nFocusPad, nullptr ); - - nArrowWidth = MIN_ARROW_SIZE + (GTK_MISC(gWidgetData[nScreen].gArrowWidget)->xpad * 2); - nButtonWidth = nArrowWidth + - ((BTN_CHILD_SPACING + gWidgetData[nScreen].gDropdownWidget->style->xthickness) * 2) - + (2 * (nFocusWidth+nFocusPad)); - if( nPart == ControlPart::ButtonDown ) - { - aButtonRect.SetSize( Size( nButtonWidth, aAreaRect.GetHeight() ) ); - if( AllSettings::GetLayoutRTL() ) - aButtonRect.SetPos( Point( aAreaRect.Left(), aAreaRect.Top() ) ); - else - aButtonRect.SetPos( Point( aAreaRect.Left() + aAreaRect.GetWidth() - nButtonWidth, - aAreaRect.Top() ) ); - } - else if( nPart == ControlPart::SubEdit ) - { - NWEnsureGTKCombo( nScreen ); - - gint adjust_x = GTK_CONTAINER(gWidgetData[nScreen].gComboWidget)->border_width + - nFocusWidth + - nFocusPad; - gint adjust_y = adjust_x + gWidgetData[nScreen].gComboWidget->style->ythickness; - adjust_x += gWidgetData[nScreen].gComboWidget->style->xthickness; - aButtonRect.SetSize( Size( aAreaRect.GetWidth() - nButtonWidth - 2 * adjust_x, - aAreaRect.GetHeight() - 2 * adjust_y ) ); - Point aEditPos = aAreaRect.TopLeft(); - aEditPos.AdjustX(adjust_x ); - aEditPos.AdjustY(adjust_y ); - if( AllSettings::GetLayoutRTL() ) - aEditPos.AdjustX(nButtonWidth ); - aButtonRect.SetPos( aEditPos ); - } - - return aButtonRect; -} - -bool GtkSalGraphics::NWPaintGTKTabItem( ControlType nType, - const tools::Rectangle& rControlRectangle, - ControlState nState, - const ImplControlValue& aValue ) -{ - OSL_ASSERT( nType != ControlType::TabItem || aValue.getType() == ControlType::TabItem ); - GdkX11Pixmap * pixmap; - GdkX11Pixmap * mask; - tools::Rectangle pixmapRect; - tools::Rectangle tabRect; - GtkStateType stateType; - GtkShadowType shadowType; - if( ! gWidgetData[ m_nXScreen ].gCacheTabItems ) - { - gWidgetData[ m_nXScreen ].gCacheTabItems = new NWPixmapCache( m_nXScreen ); - gWidgetData[ m_nXScreen ].gCacheTabPages = new NWPixmapCache( m_nXScreen ); - } - NWPixmapCache& aCacheItems = *gWidgetData[ m_nXScreen ].gCacheTabItems; - NWPixmapCache& aCachePage = *gWidgetData[ m_nXScreen ].gCacheTabPages; - - if( !aCacheItems.GetSize() ) - aCacheItems.SetSize( 20 ); - if( !aCachePage.GetSize() ) - aCachePage.SetSize( 1 ); - - if ( (nType == ControlType::TabItem) && (aValue.getType() != ControlType::TabItem) ) - { - return false; - } - - NWEnsureGTKButton( m_nXScreen ); - NWEnsureGTKNotebook( m_nXScreen ); - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - // Find the overall bounding rect of the buttons's drawing area, - // plus its actual draw rect excluding adornment - pixmapRect = rControlRectangle; - if ( nType == ControlType::TabItem ) - { - const TabitemValue * pTabitemValue = static_cast<const TabitemValue *>(&aValue); - if ( !pTabitemValue->isFirst() ) - { - // GTK+ tabs overlap on the right edge (the top tab obscures the - // left edge of the tab right "below" it, so adjust the rectangle - // to draw tabs slightly large so the overlap happens - pixmapRect.Move( -2, 0 ); - pixmapRect.SetSize( Size( pixmapRect.GetWidth() + 2, pixmapRect.GetHeight() ) ); - } - if ( nState & ControlState::SELECTED ) - { - // In GTK+, the selected tab is 2px taller than all other tabs - pixmapRect.Move( 0, -2 ); - pixmapRect.AdjustBottom(2 ); - tabRect = pixmapRect; - // Only draw over 1 pixel of the tab pane that this tab is drawn on top of. - tabRect.AdjustBottom( -1 ); - } - else - tabRect = pixmapRect; - - // Allow the tab to draw a right border if needed - tabRect.AdjustRight( -1 ); - - // avoid degenerate cases which might lead to crashes - if( tabRect.GetWidth() <= 1 || tabRect.GetHeight() <= 1 ) - return false; - } - - if( nType == ControlType::TabItem ) - { - if( aCacheItems.Find( nType, nState, pixmapRect, &pixmap, &mask ) ) - return NWRenderPixmapToScreen( pixmap, mask, pixmapRect ); - } - else - { - if( aCachePage.Find( nType, nState, pixmapRect, &pixmap, &mask ) ) - return NWRenderPixmapToScreen( pixmap, mask, pixmapRect ); - } - - GdkRectangle paintRect; - paintRect.x = paintRect.y = 0; - paintRect.width = pixmapRect.GetWidth(); - paintRect.height = pixmapRect.GetHeight(); - - BEGIN_CACHE_PIXMAP_RENDER( pixmapRect, pixmap, mask, gdkPixmap ) - { - gtk_paint_flat_box( m_pWindow->style, gdkPixmap, GTK_STATE_NORMAL, - GTK_SHADOW_NONE, &paintRect, m_pWindow, "base", - -rControlRectangle.Left(), - -rControlRectangle.Top(), - pixmapRect.GetWidth()+rControlRectangle.Left(), - pixmapRect.GetHeight()+rControlRectangle.Top()); - - NWSetWidgetState( gWidgetData[m_nXScreen].gNotebookWidget, nState, stateType ); - - switch( nType ) - { - case ControlType::TabBody: - break; - - case ControlType::TabPane: - gtk_paint_box_gap( gWidgetData[m_nXScreen].gNotebookWidget->style, gdkPixmap, GTK_STATE_NORMAL, GTK_SHADOW_OUT, nullptr, gWidgetData[m_nXScreen].gNotebookWidget, - "notebook", 0, 0, pixmapRect.GetWidth(), pixmapRect.GetHeight(), GTK_POS_TOP, 0, 0 ); - break; - - case ControlType::TabItem: - { - stateType = ( nState & ControlState::SELECTED ) ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE; - - // First draw the background - gtk_paint_flat_box(gWidgetData[m_nXScreen].gNotebookWidget->style, gdkPixmap, - GTK_STATE_NORMAL, GTK_SHADOW_NONE, nullptr, m_pWindow, "base", - -rControlRectangle.Left(), - -rControlRectangle.Top(), - pixmapRect.GetWidth()+rControlRectangle.Left(), - pixmapRect.GetHeight()+rControlRectangle.Top()); - - // Now the tab itself - if( nState & ControlState::ROLLOVER ) - g_object_set_data(G_OBJECT(gdkPixmap),tabPrelitDataName,reinterpret_cast<gpointer>(TRUE)); - - gtk_paint_extension( gWidgetData[m_nXScreen].gNotebookWidget->style, gdkPixmap, stateType, GTK_SHADOW_OUT, nullptr, gWidgetData[m_nXScreen].gNotebookWidget, - "tab", (tabRect.Left() - pixmapRect.Left()), (tabRect.Top() - pixmapRect.Top()), - tabRect.GetWidth(), tabRect.GetHeight(), GTK_POS_BOTTOM ); - - g_object_steal_data(G_OBJECT(gdkPixmap),tabPrelitDataName); - - if ( nState & ControlState::SELECTED ) - { - gtk_paint_flat_box( m_pWindow->style, gdkPixmap, stateType, GTK_SHADOW_NONE, nullptr, m_pWindow, - "base", 0, (pixmapRect.GetHeight() - 1), pixmapRect.GetWidth(), 1 ); - } - break; - } - - default: - break; - } - } - END_CACHE_PIXMAP_RENDER( pixmapRect, pixmap, mask ) - - // cache data - if( nType == ControlType::TabItem ) - aCacheItems.Fill( nType, nState, pixmapRect, std::unique_ptr<GdkX11Pixmap>(pixmap), std::unique_ptr<GdkX11Pixmap>(mask) ); - else - aCachePage.Fill( nType, nState, pixmapRect, std::unique_ptr<GdkX11Pixmap>(pixmap), std::unique_ptr<GdkX11Pixmap>(mask) ); - - return true; -} - -bool GtkSalGraphics::NWPaintGTKListBox( GdkDrawable* gdkDrawable, - ControlPart nPart, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState ) -{ - tools::Rectangle aIndicatorRect; - GtkStateType stateType; - GtkShadowType shadowType; - gint bInteriorFocus; - gint nFocusLineWidth; - gint nFocusPadding; - gint x,y,w,h; - GdkRectangle clipRect; - - NWEnsureGTKButton( m_nXScreen ); - NWEnsureGTKOptionMenu( m_nXScreen ); - NWEnsureGTKScrolledWindow( m_nXScreen ); - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - // set up references to correct drawable and cliprect - NWSetWidgetState( gWidgetData[m_nXScreen].gBtnWidget, nState, stateType ); - NWSetWidgetState( gWidgetData[m_nXScreen].gOptionMenuWidget, nState, stateType ); - NWSetWidgetState( gWidgetData[m_nXScreen].gScrolledWindowWidget, nState, stateType ); - - x = rControlRectangle.Left(); - y = rControlRectangle.Top(); - w = rControlRectangle.GetWidth(); - h = rControlRectangle.GetHeight(); - - if ( nPart != ControlPart::ListboxWindow ) - { - gtk_widget_style_get( gWidgetData[m_nXScreen].gOptionMenuWidget, - "interior_focus", &bInteriorFocus, - "focus_line_width", &nFocusLineWidth, - "focus_padding", &nFocusPadding, - nullptr); - } - - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - if ( nPart != ControlPart::ListboxWindow ) - { - // Listboxes must paint opaque since some themes have alpha-channel enabled bodies - gtk_paint_flat_box( m_pWindow->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE, - &clipRect, m_pWindow, "base", x, y, w, h); - gtk_paint_box( gWidgetData[m_nXScreen].gOptionMenuWidget->style, gdkDrawable, stateType, shadowType, &clipRect, - gWidgetData[m_nXScreen].gOptionMenuWidget, "optionmenu", - x, y, w, h); - aIndicatorRect = NWGetListBoxIndicatorRect( m_nXScreen, rControlRectangle); - gtk_paint_tab( gWidgetData[m_nXScreen].gOptionMenuWidget->style, gdkDrawable, stateType, shadowType, &clipRect, - gWidgetData[m_nXScreen].gOptionMenuWidget, "optionmenutab", - aIndicatorRect.Left(), aIndicatorRect.Top(), - aIndicatorRect.GetWidth(), aIndicatorRect.GetHeight() ); - } - else - { - shadowType = GTK_SHADOW_IN; - - gtk_paint_shadow( gWidgetData[m_nXScreen].gScrolledWindowWidget->style, gdkDrawable, GTK_STATE_NORMAL, shadowType, - &clipRect, gWidgetData[m_nXScreen].gScrolledWindowWidget, "scrolled_window", - x, y, w, h ); - } - } - - return true; -} - -bool GtkSalGraphics::NWPaintGTKToolbar( - GdkDrawable* gdkDrawable, - ControlPart nPart, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState, const ImplControlValue& aValue) -{ - GtkStateType stateType; - GtkShadowType shadowType; - gint x, y, w, h; - gint g_x=0, g_y=0, g_w=10, g_h=10; - GtkWidget* pButtonWidget = gWidgetData[m_nXScreen].gToolbarButtonWidget; - GdkRectangle clipRect; - - NWEnsureGTKToolbar( m_nXScreen ); - if( nPart == ControlPart::Button ) // toolbar buttons cannot focus in gtk - nState &= ~ControlState::FOCUSED; - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - x = rControlRectangle.Left(); - y = rControlRectangle.Top(); - w = rControlRectangle.GetWidth(); - h = rControlRectangle.GetHeight(); - - // handle toolbar - if( nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert ) - { - NWSetWidgetState( gWidgetData[m_nXScreen].gToolbarWidget, nState, stateType ); - - GTK_WIDGET_UNSET_FLAGS( gWidgetData[m_nXScreen].gToolbarWidget, GTK_SENSITIVE ); - if ( nState & ControlState::ENABLED ) - GTK_WIDGET_SET_FLAGS( gWidgetData[m_nXScreen].gToolbarWidget, GTK_SENSITIVE ); - - if( nPart == ControlPart::DrawBackgroundHorz ) - gtk_toolbar_set_orientation( GTK_TOOLBAR(gWidgetData[m_nXScreen].gToolbarWidget), GTK_ORIENTATION_HORIZONTAL ); - else - gtk_toolbar_set_orientation( GTK_TOOLBAR(gWidgetData[m_nXScreen].gToolbarWidget), GTK_ORIENTATION_VERTICAL ); - } - // handle grip - else if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert ) - { - NWSetWidgetState( gWidgetData[m_nXScreen].gHandleBoxWidget, nState, stateType ); - - GTK_WIDGET_UNSET_FLAGS( gWidgetData[m_nXScreen].gHandleBoxWidget, GTK_SENSITIVE ); - if ( nState & ControlState::ENABLED ) - GTK_WIDGET_SET_FLAGS( gWidgetData[m_nXScreen].gHandleBoxWidget, GTK_SENSITIVE ); - - gtk_handle_box_set_shadow_type( GTK_HANDLE_BOX(gWidgetData[m_nXScreen].gHandleBoxWidget), shadowType ); - - // evaluate grip rect - if( aValue.getType() == ControlType::Toolbar ) - { - const ToolbarValue* pVal = static_cast<const ToolbarValue*>(&aValue); - g_x = pVal->maGripRect.Left(); - g_y = pVal->maGripRect.Top(); - g_w = pVal->maGripRect.GetWidth(); - g_h = pVal->maGripRect.GetHeight(); - } - } - // handle button - else if( nPart == ControlPart::Button ) - { - bool bPaintButton = (nState & ControlState::PRESSED) - || (nState & ControlState::ROLLOVER); - if( aValue.getTristateVal() == ButtonValue::On ) - { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pButtonWidget),TRUE); - bPaintButton = true; - } - else - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pButtonWidget),FALSE); - - NWSetWidgetState( pButtonWidget, nState, stateType ); - gtk_widget_ensure_style( pButtonWidget ); - if(bPaintButton) - NWPaintGTKButtonReal(pButtonWidget, gdkDrawable, rControlRectangle, rClipList, nState); - } - - if( nPart != ControlPart::Button ) - { - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - // draw toolbar - if( nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert ) - { - gtk_paint_flat_box( gWidgetData[m_nXScreen].gToolbarWidget->style, - gdkDrawable, - GTK_STATE_NORMAL, - GTK_SHADOW_NONE, - &clipRect, - gWidgetData[m_nXScreen].gToolbarWidget, - "base", - x, y, w, h ); - gtk_paint_box( gWidgetData[m_nXScreen].gToolbarWidget->style, - gdkDrawable, - stateType, - shadowType, - &clipRect, - gWidgetData[m_nXScreen].gToolbarWidget, - "toolbar", - x, y, w, h ); - } - // draw grip - else if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert ) - { - gtk_paint_handle( gWidgetData[m_nXScreen].gHandleBoxWidget->style, - gdkDrawable, - GTK_STATE_NORMAL, - GTK_SHADOW_OUT, - &clipRect, - gWidgetData[m_nXScreen].gHandleBoxWidget, - "handlebox", - g_x, g_y, g_w, g_h, - nPart == ControlPart::ThumbHorz ? - GTK_ORIENTATION_HORIZONTAL : - GTK_ORIENTATION_VERTICAL - ); - } - else if( nPart == ControlPart::SeparatorHorz || nPart == ControlPart::SeparatorVert ) - { - const double shim = 0.2; - - gint separator_height, separator_width, wide_separators = 0; - - gtk_widget_style_get (gWidgetData[m_nXScreen].gSeparator, - "wide-separators", &wide_separators, - "separator-width", &separator_width, - "separator-height", &separator_height, - nullptr); - - if (wide_separators) - { - if (nPart == ControlPart::SeparatorVert) - gtk_paint_box (gWidgetData[m_nXScreen].gSeparator->style, gdkDrawable, - GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT, - &clipRect, gWidgetData[m_nXScreen].gSeparator, "vseparator", - x + (w - separator_width) / 2, y + h * shim, - separator_width, h * (1 - 2*shim)); - else - gtk_paint_box (gWidgetData[m_nXScreen].gSeparator->style, gdkDrawable, - GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT, - &clipRect, gWidgetData[m_nXScreen].gSeparator, "hseparator", - x + w * shim, y + (h - separator_width) / 2, - w * (1 - 2*shim), separator_width); - } - else - { - if (nPart == ControlPart::SeparatorVert) - gtk_paint_vline (gWidgetData[m_nXScreen].gSeparator->style, gdkDrawable, - GTK_STATE_NORMAL, - &clipRect, gWidgetData[m_nXScreen].gSeparator, "vseparator", - y + h * shim, y + h * (1 - shim), x + w/2 - 1); - else - gtk_paint_hline (gWidgetData[m_nXScreen].gSeparator->style, gdkDrawable, - GTK_STATE_NORMAL, - &clipRect, gWidgetData[m_nXScreen].gSeparator, "hseparator", - x + w * shim, x + w * (1 - shim), y + h/2 - 1); - } - } - } - } - - return true; -} - -/// Converts a VCL Rectangle to a GdkRectangle. -static void lcl_rectangleToGdkRectangle(const tools::Rectangle& rRectangle, GdkRectangle& rGdkRectangle) -{ - rGdkRectangle.x = rRectangle.Left(); - rGdkRectangle.y = rRectangle.Top(); - rGdkRectangle.width = rRectangle.GetWidth(); - rGdkRectangle.height = rRectangle.GetHeight(); -} - -bool GtkSalGraphics::NWPaintGTKMenubar( - GdkDrawable* gdkDrawable, - ControlPart nPart, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState ) -{ - GtkStateType stateType; - GtkShadowType shadowType; - GtkShadowType selected_shadow_type = GTK_SHADOW_OUT; - gint x, y, w, h; - GdkRectangle clipRect; - - NWEnsureGTKMenubar( m_nXScreen ); - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - x = rControlRectangle.Left(); - y = rControlRectangle.Top(); - w = rControlRectangle.GetWidth(); - h = rControlRectangle.GetHeight(); - - if( nPart == ControlPart::MenuItem ) - { - if( nState & ControlState::SELECTED ) - { - gtk_widget_style_get( gWidgetData[m_nXScreen].gMenuItemMenubarWidget, - "selected_shadow_type", &selected_shadow_type, - nullptr); - } - } - - for (auto const& clip : rClipList) - { - lcl_rectangleToGdkRectangle(clip, clipRect); - - // handle Menubar - if( nPart == ControlPart::Entire ) - { - NWSetWidgetState( gWidgetData[m_nXScreen].gMenubarWidget, nState, stateType ); - - GTK_WIDGET_UNSET_FLAGS( gWidgetData[m_nXScreen].gMenubarWidget, GTK_SENSITIVE ); - if ( nState & ControlState::ENABLED ) - GTK_WIDGET_SET_FLAGS( gWidgetData[m_nXScreen].gMenubarWidget, GTK_SENSITIVE ); - - // for translucent menubar styles paint background first - gtk_paint_flat_box( gWidgetData[m_nXScreen].gMenubarWidget->style, - gdkDrawable, - GTK_STATE_NORMAL, - GTK_SHADOW_NONE, - &clipRect, - GTK_WIDGET(m_pWindow), - "base", - x, y, w, h ); - - // Do the conversion again, in case clipRect has been modified. - lcl_rectangleToGdkRectangle(clip, clipRect); - - gtk_paint_box( gWidgetData[m_nXScreen].gMenubarWidget->style, - gdkDrawable, - stateType, - shadowType, - &clipRect, - gWidgetData[m_nXScreen].gMenubarWidget, - "menubar", - x, y, w, h ); - } - - else if( nPart == ControlPart::MenuItem ) - { - if( nState & ControlState::SELECTED ) - { - gtk_paint_box( gWidgetData[m_nXScreen].gMenuItemMenubarWidget->style, - gdkDrawable, - GTK_STATE_PRELIGHT, - selected_shadow_type, - &clipRect, - gWidgetData[m_nXScreen].gMenuItemMenubarWidget, - "menuitem", - x, y, w, h); - } - } - } - - return true; -} - -bool GtkSalGraphics::NWPaintGTKPopupMenu( - GdkDrawable* gdkDrawable, - ControlPart nPart, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList, - ControlState nState ) -{ - // #i50745# gtk does not draw disabled menu entries (and crux theme - // even crashes) in very old (Fedora Core 4 vintage) gtk's - if (gtk_major_version <= 2 && gtk_minor_version <= 8 && - nPart == ControlPart::MenuItem && ! (nState & ControlState::ENABLED) ) - return true; - - GtkStateType stateType; - GtkShadowType shadowType; - GtkShadowType selected_shadow_type = GTK_SHADOW_OUT; - gint x, y, w, h; - GdkRectangle clipRect; - - NWEnsureGTKMenu( m_nXScreen ); - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - x = rControlRectangle.Left(); - y = rControlRectangle.Top(); - w = rControlRectangle.GetWidth(); - h = rControlRectangle.GetHeight(); - - if( nPart == ControlPart::MenuItem && - ( nState & (ControlState::SELECTED|ControlState::ROLLOVER) ) ) - { - gtk_widget_style_get( gWidgetData[m_nXScreen].gMenuItemMenuWidget, - "selected_shadow_type", &selected_shadow_type, - nullptr); - } - - NWSetWidgetState( gWidgetData[m_nXScreen].gMenuWidget, nState, stateType ); - - GTK_WIDGET_UNSET_FLAGS( gWidgetData[m_nXScreen].gMenuWidget, GTK_SENSITIVE ); - if ( nState & ControlState::ENABLED ) - GTK_WIDGET_SET_FLAGS( gWidgetData[m_nXScreen].gMenuWidget, GTK_SENSITIVE ); - - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - if( nPart == ControlPart::Entire ) - { - // for translucent menubar styles paint background first - gtk_paint_flat_box( gWidgetData[m_nXScreen].gMenuWidget->style, - gdkDrawable, - GTK_STATE_NORMAL, - GTK_SHADOW_NONE, - &clipRect, - GTK_WIDGET(m_pWindow), - "base", - x, y, w, h ); - gtk_paint_box( gWidgetData[m_nXScreen].gMenuWidget->style, - gdkDrawable, - GTK_STATE_NORMAL, - GTK_SHADOW_OUT, - &clipRect, - gWidgetData[m_nXScreen].gMenuWidget, - "menu", - x, y, w, h ); - } - else if( nPart == ControlPart::MenuItem ) - { - if( nState & (ControlState::SELECTED|ControlState::ROLLOVER) ) - { - if( nState & ControlState::ENABLED ) - gtk_paint_box( gWidgetData[m_nXScreen].gMenuItemMenuWidget->style, - gdkDrawable, - GTK_STATE_PRELIGHT, - selected_shadow_type, - &clipRect, - gWidgetData[m_nXScreen].gMenuItemMenuWidget, - "menuitem", - x, y, w, h); - } - } - else if( nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark ) - { - GtkWidget* pWidget = (nPart == ControlPart::MenuItemCheckMark) ? - gWidgetData[m_nXScreen].gMenuItemCheckMenuWidget : - gWidgetData[m_nXScreen].gMenuItemRadioMenuWidget; - - GtkStateType nStateType; - GtkShadowType nShadowType; - NWConvertVCLStateToGTKState( nState, &nStateType, &nShadowType ); - - if ( (nState & ControlState::SELECTED) && (nState & ControlState::ENABLED) ) - nStateType = GTK_STATE_PRELIGHT; - - NWSetWidgetState( pWidget, nState, nStateType ); - - if ( nPart == ControlPart::MenuItemCheckMark ) - { - gtk_paint_check( pWidget->style, - gdkDrawable, - nStateType, - nShadowType, - &clipRect, - gWidgetData[m_nXScreen].gMenuItemMenuWidget, - "check", - x, y, w, h ); - } - else - { - gtk_paint_option( pWidget->style, - gdkDrawable, - nStateType, - nShadowType, - &clipRect, - gWidgetData[m_nXScreen].gMenuItemMenuWidget, - "option", - x, y, w, h ); - } - } - else if( nPart == ControlPart::Separator ) - { - gtk_paint_hline( gWidgetData[m_nXScreen].gMenuItemSeparatorMenuWidget->style, - gdkDrawable, - GTK_STATE_NORMAL, - &clipRect, - gWidgetData[m_nXScreen].gMenuItemSeparatorMenuWidget, - "menuitem", - x, x + w, y + h / 2); - } - else if( nPart == ControlPart::SubmenuArrow ) - { - GtkStateType nStateType; - GtkShadowType nShadowType; - NWConvertVCLStateToGTKState( nState, &nStateType, &nShadowType ); - - if ( (nState & ControlState::SELECTED) && (nState & ControlState::ENABLED) ) - nStateType = GTK_STATE_PRELIGHT; - - NWSetWidgetState( gWidgetData[m_nXScreen].gMenuItemMenuWidget, - nState, nStateType ); - - GtkArrowType eArrow; - if( AllSettings::GetLayoutRTL() ) - eArrow = GTK_ARROW_LEFT; - else - eArrow = GTK_ARROW_RIGHT; - - gtk_paint_arrow( gWidgetData[m_nXScreen].gMenuItemMenuWidget->style, - gdkDrawable, - nStateType, - nShadowType, - &clipRect, - gWidgetData[m_nXScreen].gMenuItemMenuWidget, - "menuitem", - eArrow, TRUE, - x, y, w, h); - } - } - - return true; -} - -bool GtkSalGraphics::NWPaintGTKTooltip( - GdkDrawable* gdkDrawable, - const tools::Rectangle& rControlRectangle, - const std::vector< tools::Rectangle >& rClipList ) -{ - NWEnsureGTKTooltip( m_nXScreen ); - - gint x, y, w, h; - GdkRectangle clipRect; - - x = rControlRectangle.Left(); - y = rControlRectangle.Top(); - w = rControlRectangle.GetWidth(); - h = rControlRectangle.GetHeight(); - - for (auto const& clip : rClipList) - { - clipRect.x = clip.Left(); - clipRect.y = clip.Top(); - clipRect.width = clip.GetWidth(); - clipRect.height = clip.GetHeight(); - - gtk_paint_flat_box( gWidgetData[m_nXScreen].gTooltipPopup->style, - gdkDrawable, - GTK_STATE_NORMAL, - GTK_SHADOW_OUT, - &clipRect, - gWidgetData[m_nXScreen].gTooltipPopup, - "tooltip", - x, y, w, h ); - } - - return true; -} - -namespace -{ -void NWPaintGTKListNodeReal(SalX11Screen nXScreen, GdkDrawable* gdkDrawable, GtkStateType stateType, - gint w, int h, GtkExpanderStyle eStyle) -{ - gtk_paint_expander(gWidgetData[nXScreen].gTreeView->style, gdkDrawable, stateType, nullptr, - gWidgetData[nXScreen].gTreeView, "treeview", w / 2, h / 2, eStyle); -} -} - -bool GtkSalGraphics::NWPaintGTKListNode( - GdkDrawable* gdkDrawable, - const tools::Rectangle& rControlRectangle, - ControlState nState, const ImplControlValue& rValue ) -{ - NWEnsureGTKTreeView( m_nXScreen ); - - tools::Rectangle aRect( rControlRectangle ); - aRect.AdjustLeft( -2 ); - aRect.AdjustRight(2 ); - aRect.AdjustTop( -2 ); - aRect.AdjustBottom(2 ); - gint w, h; - w = aRect.GetWidth(); - h = aRect.GetHeight(); - - GtkStateType stateType; - GtkShadowType shadowType; - NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); - - ButtonValue aButtonValue = rValue.getTristateVal(); - GtkExpanderStyle eStyle = GTK_EXPANDER_EXPANDED; - - switch( aButtonValue ) - { - case ButtonValue::On: eStyle = GTK_EXPANDER_EXPANDED;break; - case ButtonValue::Off: eStyle = GTK_EXPANDER_COLLAPSED; break; - default: - break; - } - - if (GtkSalGraphics::bNeedPixmapPaint) - { - NWPaintGTKListNodeReal(m_nXScreen, gdkDrawable, stateType, w, h, eStyle); - return true; - } - - BEGIN_PIXMAP_RENDER( aRect, pixDrawable ) - { - NWPaintGTKListNodeReal(m_nXScreen, pixDrawable, stateType, w, h, eStyle); - } - END_PIXMAP_RENDER( aRect ) - - return true; -} - -bool GtkSalGraphics::NWPaintGTKProgress( - const tools::Rectangle& rControlRectangle, - const ImplControlValue& rValue ) -{ - NWEnsureGTKProgressBar( m_nXScreen ); - - gint w, h; - w = rControlRectangle.GetWidth(); - h = rControlRectangle.GetHeight(); - tools::Rectangle aRect( Point( 0, 0 ), Size( w, h ) ); - - long nProgressWidth = rValue.getNumericVal(); - - BEGIN_PIXMAP_RENDER( aRect, pixDrawable ) - { - // paint background - gtk_paint_flat_box(gWidgetData[m_nXScreen].gProgressBar->style, pixDrawable, - GTK_STATE_NORMAL, GTK_SHADOW_NONE, nullptr, m_pWindow, "base", - -rControlRectangle.Left(),-rControlRectangle.Top(), - rControlRectangle.Left()+w,rControlRectangle.Top()+h); - - gtk_paint_flat_box( gWidgetData[m_nXScreen].gProgressBar->style, - pixDrawable, - GTK_STATE_NORMAL, - GTK_SHADOW_NONE, - nullptr, - gWidgetData[m_nXScreen].gProgressBar, - "trough", - 0, 0, w, h ); - if( nProgressWidth > 0 ) - { - // paint progress - if( AllSettings::GetLayoutRTL() ) - { - gtk_paint_box( gWidgetData[m_nXScreen].gProgressBar->style, - pixDrawable, - GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, - nullptr, - gWidgetData[m_nXScreen].gProgressBar, - "bar", - w-nProgressWidth, 0, nProgressWidth, h - ); - } - else - { - gtk_paint_box( gWidgetData[m_nXScreen].gProgressBar->style, - pixDrawable, - GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, - nullptr, - gWidgetData[m_nXScreen].gProgressBar, - "bar", - 0, 0, nProgressWidth, h - ); - } - } - } - END_PIXMAP_RENDER( rControlRectangle ) - - return true; -} - -namespace -{ -void NWPaintGTKSliderReal(SalX11Screen nXScreen, GdkDrawable* gdkDrawable, ControlPart nPart, - const tools::Rectangle& rControlRectangle, ControlState nState, - const ImplControlValue& rValue) -{ - gint w, h; - w = rControlRectangle.GetWidth(); - h = rControlRectangle.GetHeight(); - - const SliderValue* pVal = static_cast<const SliderValue*>(&rValue); - - GtkWidget* pWidget = (nPart == ControlPart::TrackHorzArea) - ? GTK_WIDGET(gWidgetData[nXScreen].gHScale) - : GTK_WIDGET(gWidgetData[nXScreen].gVScale); - const gchar* pDetail = (nPart == ControlPart::TrackHorzArea) ? "hscale" : "vscale"; - GtkOrientation eOri = (nPart == ControlPart::TrackHorzArea) ? GTK_ORIENTATION_HORIZONTAL - : GTK_ORIENTATION_VERTICAL; - gint slider_width = 10; - gint slider_length = 10; - gint trough_border = 0; - gtk_widget_style_get(pWidget, "slider-width", &slider_width, "slider-length", &slider_length, - "trough-border", &trough_border, nullptr); - - GtkStateType eState - = (nState & ControlState::ENABLED) ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE; - if (nPart == ControlPart::TrackHorzArea) - { - gtk_paint_box(pWidget->style, gdkDrawable, eState, GTK_SHADOW_IN, nullptr, pWidget, - "trough", 0, (h - slider_width - 2 * trough_border) / 2, w, - slider_width + 2 * trough_border); - gint x - = (w - slider_length + 1) * (pVal->mnCur - pVal->mnMin) / (pVal->mnMax - pVal->mnMin); - gtk_paint_slider(pWidget->style, gdkDrawable, eState, GTK_SHADOW_OUT, nullptr, pWidget, - pDetail, x, (h - slider_width) / 2, slider_length, slider_width, eOri); - } - else - { - gtk_paint_box(pWidget->style, gdkDrawable, eState, GTK_SHADOW_IN, nullptr, pWidget, - "trough", (w - slider_width - 2 * trough_border) / 2, 0, - slider_width + 2 * trough_border, h); - gint y - = (h - slider_length + 1) * (pVal->mnCur - pVal->mnMin) / (pVal->mnMax - pVal->mnMin); - gtk_paint_slider(pWidget->style, gdkDrawable, eState, GTK_SHADOW_OUT, nullptr, pWidget, - pDetail, (w - slider_width) / 2, y, slider_width, slider_length, eOri); - } -} -} - -bool GtkSalGraphics::NWPaintGTKSlider( - GdkDrawable* gdkDrawable, - ControlPart nPart, - const tools::Rectangle& rControlRectangle, - ControlState nState, const ImplControlValue& rValue ) -{ - OSL_ASSERT( rValue.getType() == ControlType::Slider ); - NWEnsureGTKSlider( m_nXScreen ); - - if (GtkSalGraphics::bNeedPixmapPaint) - { - NWPaintGTKSliderReal(m_nXScreen, gdkDrawable, nPart, rControlRectangle, nState, rValue); - return true; - } - - BEGIN_PIXMAP_RENDER( rControlRectangle, pixDrawable ) - { - NWPaintGTKSliderReal(m_nXScreen, pixDrawable, nPart, rControlRectangle, nState, rValue); - } - END_PIXMAP_RENDER( rControlRectangle ) - - return true; -} - -static int getFrameWidth(GtkWidget const * widget) -{ - return widget->style->xthickness; -} - -static tools::Rectangle NWGetListBoxButtonRect( SalX11Screen nScreen, - ControlPart nPart, - tools::Rectangle aAreaRect ) -{ - tools::Rectangle aPartRect; - GtkRequisition *pIndicatorSize = nullptr; - GtkBorder *pIndicatorSpacing = nullptr; - gint width = 13; // GTK+ default - gint right = 5; // GTK+ default - gint nButtonAreaWidth = 0; - gint xthickness = 0; - - NWEnsureGTKOptionMenu( nScreen ); - - gtk_widget_style_get( gWidgetData[nScreen].gOptionMenuWidget, - "indicator_size", &pIndicatorSize, - "indicator_spacing",&pIndicatorSpacing, nullptr); - - if ( pIndicatorSize ) - width = pIndicatorSize->width; - - if ( pIndicatorSpacing ) - right = pIndicatorSpacing->right; - - Size aPartSize( 0, aAreaRect.GetHeight() ); - Point aPartPos ( 0, aAreaRect.Top() ); - - xthickness = gWidgetData[nScreen].gOptionMenuWidget->style->xthickness; - nButtonAreaWidth = width + right + (xthickness * 2); - switch( nPart ) - { - case ControlPart::ButtonDown: - aPartSize.setWidth( nButtonAreaWidth ); - aPartPos.setX( aAreaRect.Left() + aAreaRect.GetWidth() - aPartSize.Width() ); - break; - - case ControlPart::SubEdit: - aPartSize.setWidth( aAreaRect.GetWidth() - nButtonAreaWidth - xthickness ); - if( AllSettings::GetLayoutRTL() ) - aPartPos.setX( aAreaRect.Left() + nButtonAreaWidth ); - else - aPartPos.setX( aAreaRect.Left() + xthickness ); - break; - - default: - aPartSize.setWidth( aAreaRect.GetWidth() ); - aPartPos.setX( aAreaRect.Left() ); - break; - } - aPartRect = tools::Rectangle( aPartPos, aPartSize ); - - if ( pIndicatorSize ) - gtk_requisition_free( pIndicatorSize ); - if ( pIndicatorSpacing ) - gtk_border_free( pIndicatorSpacing ); - - return aPartRect; -} - -static tools::Rectangle NWGetListBoxIndicatorRect( SalX11Screen nScreen, - tools::Rectangle aAreaRect ) -{ - tools::Rectangle aIndicatorRect; - GtkRequisition *pIndicatorSize = nullptr; - GtkBorder *pIndicatorSpacing = nullptr; - gint width = 13; // GTK+ default - gint height = 13; // GTK+ default - gint right = 5; // GTK+ default - gint x; - - NWEnsureGTKOptionMenu( nScreen ); - - gtk_widget_style_get( gWidgetData[nScreen].gOptionMenuWidget, - "indicator_size", &pIndicatorSize, - "indicator_spacing",&pIndicatorSpacing, nullptr); - - if ( pIndicatorSize ) - { - width = pIndicatorSize->width; - height = pIndicatorSize->height; - } - - if ( pIndicatorSpacing ) - right = pIndicatorSpacing->right; - - aIndicatorRect.SetSize( Size( width, height ) ); - if( AllSettings::GetLayoutRTL() ) - x = aAreaRect.Left() + right; - else - x = aAreaRect.Left() + aAreaRect.GetWidth() - width - right - gWidgetData[nScreen].gOptionMenuWidget->style->xthickness; - aIndicatorRect.SetPos( Point( x, aAreaRect.Top() + ((aAreaRect.GetHeight() - height) / 2) ) ); - - // If height is odd, move the indicator down 1 pixel - if ( aIndicatorRect.GetHeight() % 2 ) - aIndicatorRect.Move( 0, 1 ); - - if ( pIndicatorSize ) - gtk_requisition_free( pIndicatorSize ); - if ( pIndicatorSpacing ) - gtk_border_free( pIndicatorSpacing ); - - return aIndicatorRect; -} - -static tools::Rectangle NWGetToolbarRect( SalX11Screen nScreen, - ControlPart nPart, - tools::Rectangle aAreaRect ) -{ - tools::Rectangle aRet; - - if( nPart == ControlPart::DrawBackgroundHorz || - nPart == ControlPart::DrawBackgroundVert ) - aRet = aAreaRect; - else if( nPart == ControlPart::ThumbHorz ) - aRet = tools::Rectangle( Point( 0, 0 ), Size( aAreaRect.GetWidth(), 10 ) ); - else if( nPart == ControlPart::ThumbVert ) - aRet = tools::Rectangle( Point( 0, 0 ), Size( 10, aAreaRect.GetHeight() ) ); - else if( nPart == ControlPart::Button ) - { - aRet = aAreaRect; - - NWEnsureGTKToolbar( nScreen ); - - gint nMinWidth = - 2*gWidgetData[nScreen].gToolbarButtonWidget->style->xthickness - + 1 // CHILD_SPACING constant, found in gtk_button.c - + 3*gWidgetData[nScreen].gToolbarButtonWidget->style->xthickness; // Murphy factor - gint nMinHeight = - 2*gWidgetData[nScreen].gToolbarButtonWidget->style->ythickness - + 1 // CHILD_SPACING constant, found in gtk_button.c - + 3*gWidgetData[nScreen].gToolbarButtonWidget->style->ythickness; // Murphy factor - - gtk_widget_ensure_style( gWidgetData[nScreen].gToolbarButtonWidget ); - if( aAreaRect.GetWidth() < nMinWidth ) - aRet.SetRight( aRet.Left() + nMinWidth ); - if( aAreaRect.GetHeight() < nMinHeight ) - aRet.SetBottom( aRet.Top() + nMinHeight ); - } - - return aRet; -} - -/************************************************************************ - * helper for GtkSalFrame - ************************************************************************/ -static Color getColor( const GdkColor& rCol ) -{ - return Color( rCol.red >> 8, rCol.green >> 8, rCol.blue >> 8 ); -} - -#if OSL_DEBUG_LEVEL > 1 - -void printColor( const char* name, const GdkColor& rCol ) -{ - std::fprintf( stderr, " %s = 0x%2x 0x%2x 0x%2x\n", - name, - rCol.red >> 8, rCol.green >> 8, rCol.blue >> 8 ); -} - -void printStyleColors( GtkStyle* pStyle ) -{ - static const char* pStates[] = { "NORMAL", "ACTIVE", "PRELIGHT", "SELECTED", "INSENSITIVE" }; - - for( int i = 0; i < 5; i++ ) - { - std::fprintf( stderr, "state %s colors:\n", pStates[i] ); - printColor( "bg ", pStyle->bg[i] ); - printColor( "fg ", pStyle->fg[i] ); - printColor( "light ", pStyle->light[i] ); - printColor( "dark ", pStyle->dark[i] ); - printColor( "mid ", pStyle->mid[i] ); - printColor( "text ", pStyle->text[i] ); - printColor( "base ", pStyle->base[i] ); - printColor( "text_aa", pStyle->text_aa[i] ); - } -} -#endif - -void GtkSalGraphics::signalSettingsNotify( GObject *pSettings, GParamSpec *pSpec, gpointer ) -{ - g_return_if_fail( pSpec != nullptr ); - - if( !strcmp( pSpec->name, "gtk-fontconfig-timestamp" ) ) - GtkSalGraphics::refreshFontconfig( GTK_SETTINGS( pSettings ) ); -} - -void GtkSalGraphics::refreshFontconfig( GtkSettings *pSettings ) -{ - guint latest_fontconfig_timestamp = 0; - static guint our_fontconfig_timestamp = 0; - g_object_get( pSettings, "gtk-fontconfig-timestamp", &latest_fontconfig_timestamp, nullptr ); - if (latest_fontconfig_timestamp != our_fontconfig_timestamp) - { - bool bFirstTime = our_fontconfig_timestamp == 0; - our_fontconfig_timestamp = latest_fontconfig_timestamp; - if (!bFirstTime) - { - psp::PrintFontManager::get().initialize(); - } - } -} - -bool GtkSalGraphics::updateSettings( AllSettings& rSettings ) -{ - gtk_widget_ensure_style( m_pWindow ); - GtkStyle* pStyle = gtk_widget_get_style( m_pWindow ); - GtkSettings* pSettings = gtk_widget_get_settings( m_pWindow ); - StyleSettings aStyleSet = rSettings.GetStyleSettings(); - - // Listen for font changes - if( !g_object_get_data( G_OBJECT( pSettings ), "libo:listening" ) ) - { - g_object_set_data( G_OBJECT( pSettings ), "libo:listening", - GUINT_TO_POINTER( 1 ) ); - g_signal_connect_data( G_OBJECT( pSettings ), "notify", - G_CALLBACK( signalSettingsNotify ), - nullptr, nullptr, G_CONNECT_AFTER ); - } - - refreshFontconfig( pSettings ); - - // get the widgets in place - NWEnsureGTKMenu( m_nXScreen ); - NWEnsureGTKMenubar( m_nXScreen ); - NWEnsureGTKToolbar( m_nXScreen ); - NWEnsureGTKScrollbars( m_nXScreen ); - NWEnsureGTKEditBox( m_nXScreen ); - NWEnsureGTKTooltip( m_nXScreen ); - NWEnsureGTKDialog( m_nXScreen ); - NWEnsureGTKFrame( m_nXScreen ); - -#if OSL_DEBUG_LEVEL > 2 - printStyleColors( pStyle ); -#endif - - // text colors - Color aTextColor = getColor( pStyle->text[GTK_STATE_NORMAL] ); - aStyleSet.SetDialogTextColor( aTextColor ); - aStyleSet.SetWindowTextColor( aTextColor ); - aStyleSet.SetFieldTextColor( aTextColor ); - aTextColor = getColor( pStyle->fg[GTK_STATE_NORMAL] ); - aStyleSet.SetButtonTextColor( aTextColor ); - aStyleSet.SetRadioCheckTextColor( aTextColor ); - aStyleSet.SetGroupTextColor( aTextColor ); - aStyleSet.SetLabelTextColor( aTextColor ); - aStyleSet.SetTabTextColor( aTextColor ); - aStyleSet.SetTabRolloverTextColor( aTextColor ); - aStyleSet.SetTabHighlightTextColor( aTextColor ); - - // Tooltip colors - GtkStyle* pTooltipStyle = gtk_widget_get_style( gWidgetData[m_nXScreen].gTooltipPopup ); - aTextColor = getColor( pTooltipStyle->fg[ GTK_STATE_NORMAL ] ); - aStyleSet.SetHelpTextColor( aTextColor ); - - DialogStyle aDialogStyle(aStyleSet.GetDialogStyle()); - gtk_widget_style_get (gWidgetData[m_nXScreen].gDialog, - "content-area-border", &aDialogStyle.content_area_border, - "content-area-spacing", &aDialogStyle.content_area_spacing, - "button-spacing", &aDialogStyle.button_spacing, - "action-area-border", &aDialogStyle.action_area_border, - nullptr); - aStyleSet.SetDialogStyle(aDialogStyle); - - FrameStyle aFrameStyle(aStyleSet.GetFrameStyle()); - aFrameStyle.left = aFrameStyle.right = - gWidgetData[m_nXScreen].gFrame->style->xthickness; - aFrameStyle.top = aFrameStyle.bottom = - gWidgetData[m_nXScreen].gFrame->style->ythickness; - aStyleSet.SetFrameStyle(aFrameStyle); - - // mouse over text colors - aTextColor = getColor( pStyle->fg[ GTK_STATE_PRELIGHT ] ); - aStyleSet.SetButtonRolloverTextColor( aTextColor ); - aStyleSet.SetButtonPressedRolloverTextColor( aTextColor ); - aStyleSet.SetFieldRolloverTextColor( aTextColor ); - - // background colors - Color aBackColor = getColor( pStyle->bg[GTK_STATE_NORMAL] ); - Color aBackFieldColor = getColor( pStyle->base[GTK_STATE_NORMAL] ); - aStyleSet.BatchSetBackgrounds( aBackColor ); - - aStyleSet.SetFieldColor( aBackFieldColor ); - aStyleSet.SetWindowColor( aBackFieldColor ); - - // Dark shadow color - Color aDarkShadowColor = getColor( pStyle->fg[GTK_STATE_INSENSITIVE] ); - aStyleSet.SetDarkShadowColor( aDarkShadowColor ); - - ::Color aShadowColor(aBackColor); - if (aDarkShadowColor.GetLuminance() > aBackColor.GetLuminance()) - aShadowColor.IncreaseLuminance(64); - else - aShadowColor.DecreaseLuminance(64); - aStyleSet.SetShadowColor(aShadowColor); - - // highlighting colors - Color aHighlightColor = getColor( pStyle->base[GTK_STATE_SELECTED] ); - Color aHighlightTextColor = getColor( pStyle->text[GTK_STATE_SELECTED] ); - aStyleSet.SetHighlightColor( aHighlightColor ); - aStyleSet.SetHighlightTextColor( aHighlightTextColor ); - - // hyperlink colors - GdkColor *link_color = nullptr; - gtk_widget_style_get (m_pWindow, "link-color", &link_color, nullptr); - if (link_color) - { - aStyleSet.SetLinkColor(getColor(*link_color)); - gdk_color_free (link_color); - link_color = nullptr; - } - gtk_widget_style_get (m_pWindow, "visited-link-color", &link_color, nullptr); - if (link_color) - { - aStyleSet.SetVisitedLinkColor(getColor(*link_color)); - gdk_color_free (link_color); - } - - // Tab colors - aStyleSet.SetActiveTabColor( aBackFieldColor ); // same as the window color. - Color aSelectedBackColor = getColor( pStyle->bg[GTK_STATE_ACTIVE] ); - aStyleSet.SetInactiveTabColor( aSelectedBackColor ); - - // menu disabled entries handling - aStyleSet.SetSkipDisabledInMenus( true ); - aStyleSet.SetPreferredContextMenuShortcuts( false ); - // menu colors - GtkStyle* pMenuStyle = gtk_widget_get_style( gWidgetData[m_nXScreen].gMenuWidget ); - GtkStyle* pMenuItemStyle = gtk_rc_get_style( gWidgetData[m_nXScreen].gMenuItemMenuWidget ); - GtkStyle* pMenubarStyle = gtk_rc_get_style( gWidgetData[m_nXScreen].gMenubarWidget ); - GtkStyle* pMenuTextStyle = gtk_rc_get_style( gtk_bin_get_child( GTK_BIN(gWidgetData[m_nXScreen].gMenuItemMenuWidget) ) ); - aBackColor = getColor( pMenubarStyle->bg[GTK_STATE_NORMAL] ); - aStyleSet.SetMenuBarColor( aBackColor ); - aStyleSet.SetMenuBarRolloverColor( aBackColor ); - aBackColor = getColor( pMenuStyle->bg[GTK_STATE_NORMAL] ); - aTextColor = getColor( pMenuTextStyle->fg[GTK_STATE_NORMAL] ); - aStyleSet.SetMenuColor( aBackColor ); - aStyleSet.SetMenuTextColor( aTextColor ); - - aTextColor = aStyleSet.GetPersonaMenuBarTextColor().get_value_or( getColor( pMenubarStyle->fg[GTK_STATE_NORMAL] ) ); - aStyleSet.SetMenuBarTextColor( aTextColor ); - aStyleSet.SetMenuBarRolloverTextColor(getColor(pMenubarStyle->fg[GTK_STATE_PRELIGHT])); - aStyleSet.SetMenuBarHighlightTextColor(getColor(pMenubarStyle->fg[GTK_STATE_SELECTED])); - - // toolbar colors - GtkStyle* pToolbarButtonStyle = gtk_rc_get_style( gWidgetData[m_nXScreen].gToolbarButtonWidget ); - aStyleSet.SetToolTextColor(getColor(pToolbarButtonStyle->fg[GTK_STATE_NORMAL])); - -#if OSL_DEBUG_LEVEL > 1 - std::fprintf( stderr, "==\n" ); - std::fprintf( stderr, "MenuColor = %x (%d)\n", (int)aStyleSet.GetMenuColor(), aStyleSet.GetMenuColor().GetLuminance() ); - std::fprintf( stderr, "MenuTextColor = %x (%d)\n", (int)aStyleSet.GetMenuTextColor(), aStyleSet.GetMenuTextColor().GetLuminance() ); - std::fprintf( stderr, "MenuBarColor = %x (%d)\n", (int)aStyleSet.GetMenuBarColor(), aStyleSet.GetMenuBarColor().GetLuminance() ); - std::fprintf( stderr, "MenuBarRolloverColor = %x (%d)\n", (int)aStyleSet.GetMenuBarRolloverColor(), aStyleSet.GetMenuBarRolloverColor().GetLuminance() ); - std::fprintf( stderr, "MenuBarTextColor = %x (%d)\n", (int)aStyleSet.GetMenuBarTextColor(), aStyleSet.GetMenuBarTextColor().GetLuminance() ); - std::fprintf( stderr, "MenuBarRolloverTextColor = %x (%d)\n", (int)aStyleSet.GetMenuBarRolloverTextColor(), aStyleSet.GetMenuBarRolloverTextColor().GetLuminance() ); - std::fprintf( stderr, "LightColor = %x (%d)\n", (int)aStyleSet.GetLightColor(), aStyleSet.GetLightColor().GetLuminance() ); - std::fprintf( stderr, "ShadowColor = %x (%d)\n", (int)aStyleSet.GetShadowColor(), aStyleSet.GetShadowColor().GetLuminance() ); - std::fprintf( stderr, "DarkShadowColor = %x (%d)\n", (int)aStyleSet.GetDarkShadowColor(), aStyleSet.GetDarkShadowColor().GetLuminance() ); -#endif - - aHighlightColor = getColor( pMenuItemStyle->bg[ GTK_STATE_SELECTED ] ); - aHighlightTextColor = getColor( pMenuItemStyle->fg[ GTK_STATE_SELECTED ] ); - aStyleSet.SetMenuHighlightColor( aHighlightColor ); - aStyleSet.SetMenuHighlightTextColor( aHighlightTextColor ); - - // UI font - OString aFamily = pango_font_description_get_family( pStyle->font_desc ); - int nPangoHeight = pango_font_description_get_size( pStyle->font_desc ); - PangoStyle eStyle = pango_font_description_get_style( pStyle->font_desc ); - PangoWeight eWeight = pango_font_description_get_weight( pStyle->font_desc ); - PangoStretch eStretch = pango_font_description_get_stretch( pStyle->font_desc ); - - psp::FastPrintFontInfo aInfo; - // set family name - aInfo.m_aFamilyName = OStringToOUString( aFamily, RTL_TEXTENCODING_UTF8 ); - // set italic - switch( eStyle ) - { - case PANGO_STYLE_NORMAL: aInfo.m_eItalic = ITALIC_NONE;break; - case PANGO_STYLE_ITALIC: aInfo.m_eItalic = ITALIC_NORMAL;break; - case PANGO_STYLE_OBLIQUE: aInfo.m_eItalic = ITALIC_OBLIQUE;break; - } - // set weight - if( eWeight <= PANGO_WEIGHT_ULTRALIGHT ) - aInfo.m_eWeight = WEIGHT_ULTRALIGHT; - else if( eWeight <= PANGO_WEIGHT_LIGHT ) - aInfo.m_eWeight = WEIGHT_LIGHT; - else if( eWeight <= PANGO_WEIGHT_NORMAL ) - aInfo.m_eWeight = WEIGHT_NORMAL; - else if( eWeight <= PANGO_WEIGHT_BOLD ) - aInfo.m_eWeight = WEIGHT_BOLD; - else - aInfo.m_eWeight = WEIGHT_ULTRABOLD; - // set width - switch( eStretch ) - { - case PANGO_STRETCH_ULTRA_CONDENSED: aInfo.m_eWidth = WIDTH_ULTRA_CONDENSED;break; - case PANGO_STRETCH_EXTRA_CONDENSED: aInfo.m_eWidth = WIDTH_EXTRA_CONDENSED;break; - case PANGO_STRETCH_CONDENSED: aInfo.m_eWidth = WIDTH_CONDENSED;break; - case PANGO_STRETCH_SEMI_CONDENSED: aInfo.m_eWidth = WIDTH_SEMI_CONDENSED;break; - case PANGO_STRETCH_NORMAL: aInfo.m_eWidth = WIDTH_NORMAL;break; - case PANGO_STRETCH_SEMI_EXPANDED: aInfo.m_eWidth = WIDTH_SEMI_EXPANDED;break; - case PANGO_STRETCH_EXPANDED: aInfo.m_eWidth = WIDTH_EXPANDED;break; - case PANGO_STRETCH_EXTRA_EXPANDED: aInfo.m_eWidth = WIDTH_EXTRA_EXPANDED;break; - case PANGO_STRETCH_ULTRA_EXPANDED: aInfo.m_eWidth = WIDTH_ULTRA_EXPANDED;break; - } - -#if OSL_DEBUG_LEVEL > 1 - std::fprintf( stderr, "font name BEFORE system match: \"%s\"\n", aFamily.getStr() ); -#endif - - // match font to e.g. resolve "Sans" - psp::PrintFontManager::get().matchFont( aInfo, rSettings.GetUILanguageTag().getLocale() ); - -#if OSL_DEBUG_LEVEL > 1 - std::fprintf( stderr, "font match %s, name AFTER: \"%s\"\n", - aInfo.m_nID != 0 ? "succeeded" : "failed", - OUStringToOString( aInfo.m_aFamilyName, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); -#endif - - sal_Int32 nDispDPIY = GetDisplay()->GetResolution().B(); - int nPointHeight; - if (pango_font_description_get_size_is_absolute(pStyle->font_desc)) - nPointHeight = (nPangoHeight * 72 + nDispDPIY*PANGO_SCALE/2) / (nDispDPIY * PANGO_SCALE); - else - nPointHeight = nPangoHeight/PANGO_SCALE; - - vcl::Font aFont( aInfo.m_aFamilyName, Size( 0, nPointHeight ) ); - if( aInfo.m_eWeight != WEIGHT_DONTKNOW ) - aFont.SetWeight( aInfo.m_eWeight ); - if( aInfo.m_eWidth != WIDTH_DONTKNOW ) - aFont.SetWidthType( aInfo.m_eWidth ); - if( aInfo.m_eItalic != ITALIC_DONTKNOW ) - aFont.SetItalic( aInfo.m_eItalic ); - if( aInfo.m_ePitch != PITCH_DONTKNOW ) - aFont.SetPitch( aInfo.m_ePitch ); - - aStyleSet.BatchSetFonts( aFont, aFont ); - - aFont.SetWeight( WEIGHT_BOLD ); - aStyleSet.SetTitleFont( aFont ); - aStyleSet.SetFloatTitleFont( aFont ); - - // Cursor width - gfloat caretAspectRatio = 0.04f; - gtk_widget_style_get( gWidgetData[m_nXScreen].gEditBoxWidget, "cursor-aspect-ratio", &caretAspectRatio, nullptr ); - // Assume 20px tall for the ratio computation, which should give reasonable results - aStyleSet.SetCursorSize( 20 * caretAspectRatio + 1 ); - - // get cursor blink time - gboolean blink = false; - - g_object_get( pSettings, "gtk-cursor-blink", &blink, nullptr ); - if( blink ) - { - gint blink_time = static_cast<gint>(STYLE_CURSOR_NOBLINKTIME); - g_object_get( pSettings, "gtk-cursor-blink-time", &blink_time, nullptr ); - // set the blink_time if there is a setting and it is reasonable - // else leave the default value - if( blink_time > 100 ) - aStyleSet.SetCursorBlinkTime( blink_time/2 ); - } - else - aStyleSet.SetCursorBlinkTime( STYLE_CURSOR_NOBLINKTIME ); - - MouseSettings aMouseSettings = rSettings.GetMouseSettings(); - int iDoubleClickTime, iDoubleClickDistance, iDragThreshold, iMenuPopupDelay; - g_object_get( pSettings, - "gtk-double-click-time", &iDoubleClickTime, - "gtk-double-click-distance", &iDoubleClickDistance, - "gtk-dnd-drag-threshold", &iDragThreshold, - "gtk-menu-popup-delay", &iMenuPopupDelay, - nullptr ); - aMouseSettings.SetDoubleClickTime( iDoubleClickTime ); - aMouseSettings.SetDoubleClickWidth( iDoubleClickDistance ); - aMouseSettings.SetDoubleClickHeight( iDoubleClickDistance ); - aMouseSettings.SetStartDragWidth( iDragThreshold ); - aMouseSettings.SetStartDragHeight( iDragThreshold ); - aMouseSettings.SetMenuDelay( iMenuPopupDelay ); - rSettings.SetMouseSettings( aMouseSettings ); - - gboolean showmenuicons = true, primarybuttonwarps = false; - g_object_get( pSettings, - "gtk-menu-images", &showmenuicons, - nullptr ); - if( g_object_class_find_property( - G_OBJECT_GET_CLASS(pSettings), "gtk-primary-button-warps-slider") ) - { - g_object_get( pSettings, - "gtk-primary-button-warps-slider", &primarybuttonwarps, - nullptr ); - } - aStyleSet.SetPreferredUseImagesInMenus(showmenuicons); - aStyleSet.SetPrimaryButtonWarpsSlider(primarybuttonwarps); - - // set scrollbar settings - gint slider_width = 14; - gint trough_border = 1; - gint min_slider_length = 21; - - // Grab some button style attributes - gtk_widget_style_get( gWidgetData[m_nXScreen].gScrollHorizWidget, - "slider-width", &slider_width, - "trough-border", &trough_border, - "min-slider-length", &min_slider_length, - nullptr ); - gint magic = trough_border ? 1 : 0; - aStyleSet.SetScrollBarSize( slider_width + 2*trough_border ); - aStyleSet.SetMinThumbSize( min_slider_length - magic ); - - // preferred icon style - gchar* pIconThemeName = nullptr; - g_object_get( pSettings, "gtk-icon-theme-name", &pIconThemeName, nullptr ); - aStyleSet.SetPreferredIconTheme( OUString::createFromAscii( pIconThemeName ) ); - g_free( pIconThemeName ); - - aStyleSet.SetToolbarIconSize( ToolbarIconSize::Large ); - - // finally update the collected settings - rSettings.SetStyleSettings( aStyleSet ); - - return true; -} - -/************************************************************************ - * Create a GdkPixmap filled with the contents of an area of an Xlib window - ************************************************************************/ - -std::unique_ptr<GdkX11Pixmap> GtkSalGraphics::NWGetPixmapFromScreen( tools::Rectangle srcRect, int nBgColor ) -{ - int nDepth = vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetVisual( m_nXScreen ).GetDepth(); - - std::unique_ptr<GdkX11Pixmap> pPixmap(new GdkX11Pixmap( srcRect.GetWidth(), srcRect.GetHeight(), nDepth )); - - if( nBgColor == BG_FILL ) - { - FillPixmapFromScreen( pPixmap.get(), srcRect.Left(), srcRect.Top() ); - } - else if( nBgColor != BG_NONE ) - { - cairo_t *cr = gdk_cairo_create( pPixmap->GetGdkDrawable() ); - if( nBgColor == BG_BLACK) - cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0); - else - cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - cairo_destroy(cr); - } - - return pPixmap; -} - -/************************************************************************ - * Copy an alpha pixmap to screen using a gc with clipping - ************************************************************************/ - -bool GtkSalGraphics::NWRenderPixmapToScreen( GdkX11Pixmap* pPixmap, GdkX11Pixmap* pMask, tools::Rectangle dstRect ) -{ - return RenderPixmapToScreen( pPixmap, pMask, dstRect.Left(), dstRect.Top() ); -} - -/************************************************************************ - * State conversion - ************************************************************************/ -static void NWConvertVCLStateToGTKState( ControlState nVCLState, - GtkStateType* nGTKState, GtkShadowType* nGTKShadow ) -{ - *nGTKShadow = GTK_SHADOW_OUT; - *nGTKState = GTK_STATE_INSENSITIVE; - - if ( nVCLState & ControlState::ENABLED ) - { - if ( nVCLState & ControlState::PRESSED ) - { - *nGTKState = GTK_STATE_ACTIVE; - *nGTKShadow = GTK_SHADOW_IN; - } - else if ( nVCLState & ControlState::ROLLOVER ) - { - *nGTKState = GTK_STATE_PRELIGHT; - *nGTKShadow = GTK_SHADOW_OUT; - } - else - { - *nGTKState = GTK_STATE_NORMAL; - *nGTKShadow = GTK_SHADOW_OUT; - } - } -} - -/************************************************************************ - * Set widget flags - ************************************************************************/ -static void NWSetWidgetState( GtkWidget* widget, ControlState nState, GtkStateType nGtkState ) -{ - // Set to default state, then build up from there - GTK_WIDGET_UNSET_FLAGS( widget, GTK_HAS_DEFAULT ); - GTK_WIDGET_UNSET_FLAGS( widget, GTK_HAS_FOCUS ); - GTK_WIDGET_UNSET_FLAGS( widget, GTK_SENSITIVE ); - GTK_WIDGET_SET_FLAGS( widget, gWidgetDefaultFlags[reinterpret_cast<long>(widget)] ); - - if ( nState & ControlState::DEFAULT ) - GTK_WIDGET_SET_FLAGS( widget, GTK_HAS_DEFAULT ); - if ( !GTK_IS_TOGGLE_BUTTON(widget) && (nState & ControlState::FOCUSED) ) - GTK_WIDGET_SET_FLAGS( widget, GTK_HAS_FOCUS ); - if ( nState & ControlState::ENABLED ) - GTK_WIDGET_SET_FLAGS( widget, GTK_SENSITIVE ); - gtk_widget_set_state( widget, nGtkState ); -} - -/************************************************************************ - * Widget ensure functions - make sure cached objects are valid - ************************************************************************/ - -static void NWAddWidgetToCacheWindow( GtkWidget* widget, SalX11Screen nScreen ) -{ - NWFWidgetData& rData = gWidgetData[nScreen]; - if ( !rData.gCacheWindow || !rData.gDumbContainer ) - { - if ( !rData.gCacheWindow ) - { - rData.gCacheWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL ); - g_object_set_data( G_OBJECT( rData.gCacheWindow ), "libo-version", - const_cast<char *>(LIBO_VERSION_DOTTED) ); - - GdkScreen* pScreen = gdk_display_get_screen( gdk_display_get_default(), - nScreen.getXScreen() ); - if( pScreen ) - gtk_window_set_screen( GTK_WINDOW(rData.gCacheWindow), pScreen ); - } - if ( !rData.gDumbContainer ) - rData.gDumbContainer = gtk_fixed_new(); - gtk_container_add( GTK_CONTAINER(rData.gCacheWindow), rData.gDumbContainer ); - gtk_widget_realize( rData.gDumbContainer ); - gtk_widget_realize( rData.gCacheWindow ); - } - - gtk_container_add( GTK_CONTAINER(rData.gDumbContainer), widget ); - gtk_widget_realize( widget ); - gtk_widget_ensure_style( widget ); - - // Store widget's default flags - gWidgetDefaultFlags[ reinterpret_cast<long>(widget) ] = GTK_WIDGET_FLAGS( widget ); -} - -static void NWEnsureGTKButton( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gBtnWidget ) - { - gWidgetData[nScreen].gBtnWidget = gtk_button_new_with_label( "" ); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gBtnWidget, nScreen ); - } -} - -static void NWEnsureGTKRadio( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gRadioWidget || !gWidgetData[nScreen].gRadioWidgetSibling ) - { - gWidgetData[nScreen].gRadioWidget = gtk_radio_button_new( nullptr ); - gWidgetData[nScreen].gRadioWidgetSibling = gtk_radio_button_new_from_widget( GTK_RADIO_BUTTON(gWidgetData[nScreen].gRadioWidget) ); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gRadioWidget, nScreen ); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gRadioWidgetSibling, nScreen ); - } -} - -static void NWEnsureGTKCheck( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gCheckWidget ) - { - gWidgetData[nScreen].gCheckWidget = gtk_check_button_new(); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gCheckWidget, nScreen ); - } -} - -static void NWEnsureGTKScrollbars( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gScrollHorizWidget ) - { - gWidgetData[nScreen].gScrollHorizWidget = gtk_hscrollbar_new( nullptr ); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gScrollHorizWidget, nScreen ); - } - - if ( !gWidgetData[nScreen].gScrollVertWidget ) - { - gWidgetData[nScreen].gScrollVertWidget = gtk_vscrollbar_new( nullptr ); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gScrollVertWidget, nScreen ); - } -} - -static void NWEnsureGTKArrow( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gArrowWidget || !gWidgetData[nScreen].gDropdownWidget ) - { - gWidgetData[nScreen].gDropdownWidget = gtk_toggle_button_new(); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gDropdownWidget, nScreen ); - gWidgetData[nScreen].gArrowWidget = gtk_arrow_new( GTK_ARROW_DOWN, GTK_SHADOW_OUT ); - gtk_container_add( GTK_CONTAINER(gWidgetData[nScreen].gDropdownWidget), gWidgetData[nScreen].gArrowWidget ); - gtk_widget_set_rc_style( gWidgetData[nScreen].gArrowWidget ); - gtk_widget_realize( gWidgetData[nScreen].gArrowWidget ); - } -} - -static void NWEnsureGTKEditBox( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gEditBoxWidget ) - { - gWidgetData[nScreen].gEditBoxWidget = gtk_entry_new(); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gEditBoxWidget, nScreen ); - } -} - -static void NWEnsureGTKSpinButton( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gSpinButtonWidget ) - { - GtkAdjustment *adj = GTK_ADJUSTMENT( gtk_adjustment_new(0, 0, 1, 1, 1, 0) ); - gWidgetData[nScreen].gSpinButtonWidget = gtk_spin_button_new( adj, 1, 2 ); - - //Setting non-editable means it doesn't blink, so there's no timeouts - //running around to nobble us - gtk_editable_set_editable(GTK_EDITABLE(gWidgetData[nScreen].gSpinButtonWidget), false); - - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gSpinButtonWidget, nScreen ); - } -} - -static void NWEnsureGTKNotebook( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gNotebookWidget ) - { - gWidgetData[nScreen].gNotebookWidget = gtk_notebook_new(); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gNotebookWidget, nScreen ); - } -} - -static void NWEnsureGTKOptionMenu( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gOptionMenuWidget ) - { - gWidgetData[nScreen].gOptionMenuWidget = gtk_option_menu_new(); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gOptionMenuWidget, nScreen ); - } -} - -static void NWEnsureGTKCombo( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gComboWidget ) - { - gWidgetData[nScreen].gComboWidget = gtk_combo_new(); - - // #i59129# Setting non-editable means it doesn't blink, so - // there are no timeouts running around to nobble us - gtk_editable_set_editable(GTK_EDITABLE(GTK_COMBO(gWidgetData[nScreen].gComboWidget)->entry), false); - - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gComboWidget, nScreen ); - // Must realize the ComboBox's children, since GTK - // does not do this for us in GtkCombo::gtk_widget_realize() - gtk_widget_realize( GTK_COMBO(gWidgetData[nScreen].gComboWidget)->button ); - gtk_widget_realize( GTK_COMBO(gWidgetData[nScreen].gComboWidget)->entry ); - } -} - -static void NWEnsureGTKScrolledWindow( SalX11Screen nScreen ) -{ - if ( !gWidgetData[nScreen].gScrolledWindowWidget ) - { - GtkAdjustment *hadj = GTK_ADJUSTMENT( gtk_adjustment_new(0, 0, 0, 0, 0, 0) ); - GtkAdjustment *vadj = GTK_ADJUSTMENT( gtk_adjustment_new(0, 0, 0, 0, 0, 0) ); - - gWidgetData[nScreen].gScrolledWindowWidget = gtk_scrolled_window_new( hadj, vadj ); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gScrolledWindowWidget, nScreen ); - } -} - -static void NWEnsureGTKToolbar( SalX11Screen nScreen ) -{ - if( !gWidgetData[nScreen].gToolbarWidget ) - { - gWidgetData[nScreen].gToolbarWidget = gtk_toolbar_new(); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gToolbarWidget, nScreen ); - gWidgetData[nScreen].gToolbarButtonWidget = GTK_WIDGET(gtk_toggle_button_new()); - gWidgetData[nScreen].gSeparator = GTK_WIDGET(gtk_separator_tool_item_new()); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gSeparator, nScreen ); - - GtkReliefStyle aRelief = GTK_RELIEF_NORMAL; - gtk_widget_ensure_style( gWidgetData[nScreen].gToolbarWidget ); - gtk_widget_style_get( gWidgetData[nScreen].gToolbarWidget, - "button_relief", &aRelief, - nullptr); - - gtk_button_set_relief( GTK_BUTTON(gWidgetData[nScreen].gToolbarButtonWidget), aRelief ); - GTK_WIDGET_UNSET_FLAGS( gWidgetData[nScreen].gToolbarButtonWidget, GTK_CAN_FOCUS ); - GTK_WIDGET_UNSET_FLAGS( gWidgetData[nScreen].gToolbarButtonWidget, GTK_CAN_DEFAULT ); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gToolbarButtonWidget, nScreen ); - - } - if( ! gWidgetData[nScreen].gHandleBoxWidget ) - { - gWidgetData[nScreen].gHandleBoxWidget = gtk_handle_box_new(); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gHandleBoxWidget, nScreen ); - } -} - -static void NWEnsureGTKMenubar( SalX11Screen nScreen ) -{ - if( !gWidgetData[nScreen].gMenubarWidget ) - { - gWidgetData[nScreen].gMenubarWidget = gtk_menu_bar_new(); - gWidgetData[nScreen].gMenuItemMenubarWidget = gtk_menu_item_new_with_label( "b" ); - gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenubarWidget ), gWidgetData[nScreen].gMenuItemMenubarWidget ); - gtk_widget_show( gWidgetData[nScreen].gMenuItemMenubarWidget ); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gMenubarWidget, nScreen ); - gtk_widget_show( gWidgetData[nScreen].gMenubarWidget ); - - // do what NWAddWidgetToCacheWindow does except adding to def container - gtk_widget_realize( gWidgetData[nScreen].gMenuItemMenubarWidget ); - gtk_widget_ensure_style( gWidgetData[nScreen].gMenuItemMenubarWidget ); - - gWidgetDefaultFlags[ reinterpret_cast<long>(gWidgetData[nScreen].gMenuItemMenubarWidget) ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuItemMenubarWidget ); - } -} - -static void NWEnsureGTKMenu( SalX11Screen nScreen ) -{ - if( gWidgetData[nScreen].gMenuWidget ) - return; - - gWidgetData[nScreen].gMenuWidget = gtk_menu_new(); - gWidgetData[nScreen].gMenuItemMenuWidget = gtk_menu_item_new_with_label( "b" ); - gWidgetData[nScreen].gMenuItemCheckMenuWidget = gtk_check_menu_item_new_with_label( "b" ); - gWidgetData[nScreen].gMenuItemRadioMenuWidget = gtk_radio_menu_item_new_with_label( nullptr, "b" ); - gWidgetData[nScreen].gMenuItemSeparatorMenuWidget = gtk_separator_menu_item_new(); - gWidgetData[nScreen].gImageMenuItem = gtk_image_menu_item_new(); - - g_object_ref_sink (gWidgetData[nScreen].gMenuWidget); - - gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenuWidget ), gWidgetData[nScreen].gMenuItemMenuWidget ); - gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenuWidget ), gWidgetData[nScreen].gMenuItemCheckMenuWidget ); - gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenuWidget ), gWidgetData[nScreen].gMenuItemRadioMenuWidget ); - gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenuWidget ), gWidgetData[nScreen].gMenuItemSeparatorMenuWidget ); - gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenuWidget ), gWidgetData[nScreen].gImageMenuItem ); - - // do what NWAddWidgetToCacheWindow does except adding to def container - gtk_widget_realize( gWidgetData[nScreen].gMenuWidget ); - gtk_widget_ensure_style( gWidgetData[nScreen].gMenuWidget ); - - gtk_widget_realize( gWidgetData[nScreen].gMenuItemMenuWidget ); - gtk_widget_ensure_style( gWidgetData[nScreen].gMenuItemMenuWidget ); - - gtk_widget_realize( gWidgetData[nScreen].gMenuItemCheckMenuWidget ); - gtk_widget_ensure_style( gWidgetData[nScreen].gMenuItemCheckMenuWidget ); - - gtk_widget_realize( gWidgetData[nScreen].gMenuItemRadioMenuWidget ); - gtk_widget_ensure_style( gWidgetData[nScreen].gMenuItemRadioMenuWidget ); - - gtk_widget_realize( gWidgetData[nScreen].gMenuItemSeparatorMenuWidget ); - gtk_widget_ensure_style( gWidgetData[nScreen].gMenuItemSeparatorMenuWidget ); - - gtk_widget_realize( gWidgetData[nScreen].gImageMenuItem ); - gtk_widget_ensure_style( gWidgetData[nScreen].gImageMenuItem ); - - gWidgetDefaultFlags[ reinterpret_cast<long>(gWidgetData[nScreen].gMenuWidget) ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuWidget ); - gWidgetDefaultFlags[ reinterpret_cast<long>(gWidgetData[nScreen].gMenuItemMenuWidget) ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuItemMenuWidget ); - gWidgetDefaultFlags[ reinterpret_cast<long>(gWidgetData[nScreen].gMenuItemCheckMenuWidget) ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuItemCheckMenuWidget ); - gWidgetDefaultFlags[ reinterpret_cast<long>(gWidgetData[nScreen].gMenuItemRadioMenuWidget) ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuItemRadioMenuWidget ); - gWidgetDefaultFlags[ reinterpret_cast<long>(gWidgetData[nScreen].gMenuItemSeparatorMenuWidget) ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuItemSeparatorMenuWidget ); - gWidgetDefaultFlags[ reinterpret_cast<long>(gWidgetData[nScreen].gImageMenuItem) ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gImageMenuItem ); - -} - -static void NWEnsureGTKTooltip( SalX11Screen nScreen ) -{ - if( !gWidgetData[nScreen].gTooltipPopup ) - { - gWidgetData[nScreen].gTooltipPopup = gtk_window_new (GTK_WINDOW_POPUP); - GdkScreen* pScreen = gdk_display_get_screen( gdk_display_get_default(), - nScreen.getXScreen() ); - if( pScreen ) - gtk_window_set_screen( GTK_WINDOW(gWidgetData[nScreen].gTooltipPopup), pScreen ); - gtk_widget_set_name( gWidgetData[nScreen].gTooltipPopup, "gtk-tooltips"); - gtk_widget_realize( gWidgetData[nScreen].gTooltipPopup ); - gtk_widget_ensure_style( gWidgetData[nScreen].gTooltipPopup ); - } -} - -static void NWEnsureGTKDialog( SalX11Screen nScreen ) -{ - if( !gWidgetData[nScreen].gDialog ) - { - gWidgetData[nScreen].gDialog = gtk_dialog_new(); - GdkScreen* pScreen = gdk_display_get_screen( gdk_display_get_default(), - nScreen.getXScreen() ); - if( pScreen ) - gtk_window_set_screen( GTK_WINDOW(gWidgetData[nScreen].gDialog), pScreen ); - gtk_widget_realize(gWidgetData[nScreen].gDialog); - gtk_widget_ensure_style(gWidgetData[nScreen].gDialog); - } -} - -static void NWEnsureGTKFrame( SalX11Screen nScreen ) -{ - if( !gWidgetData[nScreen].gFrame ) - { - gWidgetData[nScreen].gFrame = gtk_frame_new(nullptr); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gFrame, nScreen ); - } -} - -static void NWEnsureGTKProgressBar( SalX11Screen nScreen ) -{ - if( !gWidgetData[nScreen].gProgressBar ) - { - gWidgetData[nScreen].gProgressBar = gtk_progress_bar_new (); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gProgressBar, nScreen ); - } -} - -static void NWEnsureGTKTreeView( SalX11Screen nScreen ) -{ - if( !gWidgetData[nScreen].gTreeView ) - { - gWidgetData[nScreen].gTreeView = gtk_tree_view_new (); - - // Columns will be used for tree header rendering - GtkCellRenderer* renderer=gtk_cell_renderer_text_new(); - GtkTreeViewColumn* column=gtk_tree_view_column_new_with_attributes("",renderer,"text",0,nullptr); - gtk_tree_view_column_set_widget(column,gtk_label_new("")); - gtk_tree_view_append_column(GTK_TREE_VIEW(gWidgetData[nScreen].gTreeView), column); - - // Add one more column so that some engines like clearlooks did render separators between columns - column=gtk_tree_view_column_new_with_attributes("",renderer,"text",0,nullptr); - gtk_tree_view_append_column(GTK_TREE_VIEW(gWidgetData[nScreen].gTreeView), column); - - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gTreeView, nScreen ); - } -} - -static void NWEnsureGTKSlider( SalX11Screen nScreen ) -{ - if( !gWidgetData[nScreen].gHScale ) - { - gWidgetData[nScreen].gHScale = gtk_hscale_new_with_range(0, 10, 1); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gHScale, nScreen ); - } - if( !gWidgetData[nScreen].gVScale ) - { - gWidgetData[nScreen].gVScale = gtk_vscale_new_with_range(0, 10, 1); - NWAddWidgetToCacheWindow( gWidgetData[nScreen].gVScale, nScreen ); - } -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/salprn-gtk.cxx b/vcl/unx/gtk/salprn-gtk.cxx deleted file mode 100644 index e5e17a5ec9e1..000000000000 --- a/vcl/unx/gtk/salprn-gtk.cxx +++ /dev/null @@ -1,970 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.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 <unx/gtk/gtkprintwrapper.hxx> - -#include <unx/gtk/gtkdata.hxx> -#include <unx/gtk/gtkframe.hxx> -#include <unx/gtk/gtkinst.hxx> -#include <unx/gtk/gtkprn.hxx> - -#include <vcl/configsettings.hxx> -#include <vcl/help.hxx> -#include <vcl/print.hxx> -#include <vcl/svapp.hxx> -#include <vcl/window.hxx> - -#include <gtk/gtk.h> - -#include <com/sun/star/beans/PropertyValue.hpp> -#include <com/sun/star/container/XNamed.hpp> -#include <com/sun/star/document/XExporter.hpp> -#include <com/sun/star/document/XFilter.hpp> -#include <com/sun/star/frame/XFrame.hpp> -#include <com/sun/star/io/XOutputStream.hpp> -#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> -#include <com/sun/star/sheet/XSpreadsheet.hpp> -#include <com/sun/star/sheet/XSpreadsheetView.hpp> -#include <com/sun/star/view/PrintableState.hpp> -#include <com/sun/star/view/XSelectionSupplier.hpp> - -#include <officecfg/Office/Common.hxx> - -#include <rtl/ustring.hxx> -#include <sal/log.hxx> - -#include <unotools/streamwrap.hxx> - -#include <cstring> -#include <map> - -namespace beans = com::sun::star::beans; -namespace uno = com::sun::star::uno; -namespace view = com::sun::star::view; - -using vcl::unx::GtkPrintWrapper; - -using uno::UNO_QUERY; - -class GtkPrintDialog -{ -public: - explicit GtkPrintDialog(vcl::PrinterController& io_rController); - bool run(); - GtkPrinter* getPrinter() const - { - return m_xWrapper->print_unix_dialog_get_selected_printer(GTK_PRINT_UNIX_DIALOG(m_pDialog)); - } - GtkPrintSettings* getSettings() const - { - return m_xWrapper->print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(m_pDialog)); - } - void updateControllerPrintRange(); - - ~GtkPrintDialog(); - - static void UIOption_CheckHdl(GtkWidget* i_pWidget, GtkPrintDialog* io_pThis) - { - io_pThis->impl_UIOption_CheckHdl(i_pWidget); - } - static void UIOption_RadioHdl(GtkWidget* i_pWidget, GtkPrintDialog* io_pThis) - { - io_pThis->impl_UIOption_RadioHdl(i_pWidget); - } - static void UIOption_SelectHdl(GtkWidget* i_pWidget, GtkPrintDialog* io_pThis) - { - io_pThis->impl_UIOption_SelectHdl(i_pWidget); - } - -private: - beans::PropertyValue* impl_queryPropertyValue(GtkWidget* i_pWidget) const; - void impl_checkOptionalControlDependencies(); - - void impl_UIOption_CheckHdl(GtkWidget* i_pWidget); - void impl_UIOption_RadioHdl(GtkWidget* i_pWidget); - void impl_UIOption_SelectHdl(GtkWidget* i_pWidget); - - void impl_initDialog(); - void impl_initCustomTab(); - void impl_initPrintContent(uno::Sequence<sal_Bool> const& i_rDisabled); - - void impl_readFromSettings(); - void impl_storeToSettings() const; - -private: - GtkWidget* m_pDialog; - vcl::PrinterController& m_rController; - std::map<GtkWidget*, OUString> m_aControlToPropertyMap; - std::map<GtkWidget*, sal_Int32> m_aControlToNumValMap; - std::shared_ptr<GtkPrintWrapper> m_xWrapper; -}; - -struct GtkSalPrinter_Impl -{ - OString m_sSpoolFile; - OUString m_sJobName; - GtkPrinter* m_pPrinter; - GtkPrintSettings* m_pSettings; - - GtkSalPrinter_Impl(); - ~GtkSalPrinter_Impl(); -}; - -GtkSalPrinter_Impl::GtkSalPrinter_Impl() - : m_pPrinter(nullptr) - , m_pSettings(nullptr) -{ -} - -GtkSalPrinter_Impl::~GtkSalPrinter_Impl() -{ - if (m_pPrinter) - { - g_object_unref(G_OBJECT(m_pPrinter)); - m_pPrinter = nullptr; - } - if (m_pSettings) - { - g_object_unref(G_OBJECT(m_pSettings)); - m_pSettings = nullptr; - } -} - -namespace -{ - -GtkInstance const& -lcl_getGtkSalInstance() -{ - // we _know_ this is GtkInstance - return *static_cast<GtkInstance*>(GetGtkSalData()->m_pInstance); -} - -bool -lcl_useSystemPrintDialog() -{ - return officecfg::Office::Common::Misc::UseSystemPrintDialog::get() - && officecfg::Office::Common::Misc::ExperimentalMode::get() - && lcl_getGtkSalInstance().getPrintWrapper()->supportsPrinting(); -} - -} - -GtkSalPrinter::GtkSalPrinter(SalInfoPrinter* const i_pInfoPrinter) - : PspSalPrinter(i_pInfoPrinter) -{ -} - -GtkSalPrinter::~GtkSalPrinter() = default; - -bool -GtkSalPrinter::impl_doJob( - const OUString* const i_pFileName, - const OUString& i_rJobName, - const OUString& i_rAppName, - ImplJobSetup* const io_pSetupData, - const bool i_bCollate, - vcl::PrinterController& io_rController) -{ - io_rController.setJobState(view::PrintableState_JOB_STARTED); - io_rController.jobStarted(); - const bool bJobStarted( - PspSalPrinter::StartJob(i_pFileName, i_rJobName, i_rAppName, - 1/*i_nCopies*/, i_bCollate, true, io_pSetupData)) - ; - - if (bJobStarted) - { - io_rController.createProgressDialog(); - const int nPages(io_rController.getFilteredPageCount()); - for (int nPage(0); nPage != nPages; ++nPage) - { - if (nPage == nPages - 1) - io_rController.setLastPage(true); - io_rController.printFilteredPage(nPage); - } - io_rController.setJobState(view::PrintableState_JOB_COMPLETED); - } - - return bJobStarted; -} - -bool -GtkSalPrinter::StartJob( - const OUString* const i_pFileName, - const OUString& i_rJobName, - const OUString& i_rAppName, - ImplJobSetup* io_pSetupData, - vcl::PrinterController& io_rController) -{ - if (!lcl_useSystemPrintDialog()) - return PspSalPrinter::StartJob(i_pFileName, i_rJobName, i_rAppName, io_pSetupData, io_rController); - - assert(!m_xImpl); - - m_xImpl.reset(new GtkSalPrinter_Impl()); - m_xImpl->m_sJobName = i_rJobName; - - OString sFileName; - if (i_pFileName) - sFileName = OUStringToOString(*i_pFileName, osl_getThreadTextEncoding()); - - GtkPrintDialog aDialog(io_rController); - if (!aDialog.run()) - { - io_rController.abortJob(); - return false; - } - aDialog.updateControllerPrintRange(); - m_xImpl->m_pPrinter = aDialog.getPrinter(); - m_xImpl->m_pSettings = aDialog.getSettings(); - - //To-Do proper name, watch for encodings - sFileName = OString("/tmp/hacking.ps"); - m_xImpl->m_sSpoolFile = sFileName; - - OUString aFileName = OStringToOUString(sFileName, osl_getThreadTextEncoding()); - - //To-Do, swap ps/pdf for gtk_printer_accepts_ps()/gtk_printer_accepts_pdf() ? - - return impl_doJob(&aFileName, i_rJobName, i_rAppName, io_pSetupData, /*bCollate*/false, io_rController); -} - -bool -GtkSalPrinter::EndJob() -{ - bool bRet = PspSalPrinter::EndJob(); - - if (!lcl_useSystemPrintDialog()) - return bRet; - - assert(m_xImpl); - - if (!bRet || m_xImpl->m_sSpoolFile.isEmpty()) - return bRet; - - std::shared_ptr<GtkPrintWrapper> const xWrapper(lcl_getGtkSalInstance().getPrintWrapper()); - - GtkPageSetup* pPageSetup = xWrapper->page_setup_new(); - - GtkPrintJob* const pJob = xWrapper->print_job_new( - OUStringToOString(m_xImpl->m_sJobName, RTL_TEXTENCODING_UTF8).getStr(), - m_xImpl->m_pPrinter, m_xImpl->m_pSettings, pPageSetup); - - GError* error = nullptr; - bRet = xWrapper->print_job_set_source_file(pJob, m_xImpl->m_sSpoolFile.getStr(), &error); - if (bRet) - xWrapper->print_job_send(pJob, nullptr, nullptr, nullptr); - else - { - //To-Do, do something with this - fprintf(stderr, "error was %s\n", error->message); - g_error_free(error); - } - - g_object_unref(pPageSetup); - m_xImpl.reset(); - - //To-Do, remove temp spool file - - return bRet; -} - -namespace -{ - -void -lcl_setHelpText( - GtkWidget* const io_pWidget, - const uno::Sequence<OUString>& i_rHelpTexts, - const sal_Int32 i_nIndex) -{ - if (i_nIndex >= 0 && i_nIndex < i_rHelpTexts.getLength()) - gtk_widget_set_tooltip_text(io_pWidget, - OUStringToOString(i_rHelpTexts.getConstArray()[i_nIndex], RTL_TEXTENCODING_UTF8).getStr()); -} - -GtkWidget* -lcl_makeFrame( - GtkWidget* const i_pChild, - const OUString &i_rText, - const uno::Sequence<OUString> &i_rHelpTexts, - sal_Int32* const io_pCurHelpText) -{ - GtkWidget* const pLabel = gtk_label_new(nullptr); - lcl_setHelpText(pLabel, i_rHelpTexts, !io_pCurHelpText ? 0 : (*io_pCurHelpText)++); - gtk_misc_set_alignment(GTK_MISC(pLabel), 0.0, 0.5); - - { - gchar* const pText = g_markup_printf_escaped("<b>%s</b>", - OUStringToOString(i_rText, RTL_TEXTENCODING_UTF8).getStr()); - gtk_label_set_markup_with_mnemonic(GTK_LABEL(pLabel), pText); - g_free(pText); - } - - GtkWidget* const pFrame = gtk_vbox_new(FALSE, 6); - gtk_box_pack_start(GTK_BOX(pFrame), pLabel, FALSE, FALSE, 0); - - GtkWidget* const pAlignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); - gtk_alignment_set_padding(GTK_ALIGNMENT(pAlignment), 0, 0, 12, 0); - gtk_box_pack_start(GTK_BOX(pFrame), pAlignment, FALSE, FALSE, 0); - - gtk_container_add(GTK_CONTAINER(pAlignment), i_pChild); - return pFrame; -} - -void -lcl_extractHelpTextsOrIds( - const beans::PropertyValue& rEntry, - uno::Sequence<OUString>& rHelpStrings) -{ - if (!(rEntry.Value >>= rHelpStrings)) - { - OUString aHelpString; - if (rEntry.Value >>= aHelpString) - { - rHelpStrings.realloc(1); - *rHelpStrings.getArray() = aHelpString; - } - } -} - -GtkWidget* -lcl_combo_box_text_new() -{ -#if GTK_CHECK_VERSION(3,0,0) - return gtk_combo_box_text_new(); -#else - return gtk_combo_box_new_text(); -#endif -} - -void -lcl_combo_box_text_append(GtkWidget* const pWidget, gchar const* const pText) -{ -#if GTK_CHECK_VERSION(3,0,0) - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(pWidget), pText); -#else - gtk_combo_box_append_text(GTK_COMBO_BOX(pWidget), pText); -#endif -} - -} - -GtkPrintDialog::GtkPrintDialog(vcl::PrinterController& io_rController) - : m_rController(io_rController) - , m_xWrapper(lcl_getGtkSalInstance().getPrintWrapper()) -{ - assert(m_xWrapper->supportsPrinting()); - impl_initDialog(); - impl_initCustomTab(); - impl_readFromSettings(); -} - -void -GtkPrintDialog::impl_initDialog() -{ - //To-Do, like fpicker, set UI language - m_pDialog = m_xWrapper->print_unix_dialog_new(); - - vcl::Window* const pTopWindow(Application::GetActiveTopWindow()); - if (pTopWindow) - { - GtkSalFrame* const pFrame(dynamic_cast<GtkSalFrame*>(pTopWindow->ImplGetFrame())); - if (pFrame) - { - GtkWindow* const pParent(GTK_WINDOW(pFrame->getWindow())); - if (pParent) - gtk_window_set_transient_for(GTK_WINDOW(m_pDialog), pParent); - } - } - - m_xWrapper->print_unix_dialog_set_manual_capabilities(GTK_PRINT_UNIX_DIALOG(m_pDialog), - GtkPrintCapabilities(GTK_PRINT_CAPABILITY_COPIES - | GTK_PRINT_CAPABILITY_COLLATE - | GTK_PRINT_CAPABILITY_REVERSE - | GTK_PRINT_CAPABILITY_GENERATE_PS - | GTK_PRINT_CAPABILITY_NUMBER_UP - | GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT - )); -} - -void -GtkPrintDialog::impl_initCustomTab() -{ - typedef std::vector<std::pair<GtkWidget*, OUString> > CustomTabs_t; - - const uno::Sequence<beans::PropertyValue>& rOptions(m_rController.getUIOptions()); - std::map<OUString, GtkWidget*> aPropertyToDependencyRowMap; - CustomTabs_t aCustomTabs; - GtkWidget* pCurParent = nullptr; - GtkWidget* pCurTabPage = nullptr; - GtkWidget* pCurSubGroup = nullptr; - bool bIgnoreSubgroup = false; - for (const auto& rOption : rOptions) - { - uno::Sequence<beans::PropertyValue> aOptProp; - rOption.Value >>= aOptProp; - - OUString aCtrlType; - OUString aText; - OUString aPropertyName; - uno::Sequence<OUString> aChoices; - uno::Sequence<sal_Bool> aChoicesDisabled; - uno::Sequence<OUString> aHelpTexts; - sal_Int64 nMinValue = 0, nMaxValue = 0; - sal_Int32 nCurHelpText = 0; - OUString aDependsOnName; - sal_Int32 nDependsOnValue = 0; - bool bUseDependencyRow = false; - bool bIgnore = false; - GtkWidget* pGroup = nullptr; - bool bGtkInternal = false; - - //Fix fdo#69381 - //Next options if this one is empty - if (!aOptProp.hasElements()) - continue; - - for (const beans::PropertyValue& rEntry : std::as_const(aOptProp)) - { - if ( rEntry.Name == "Text" ) - { - OUString aValue; - rEntry.Value >>= aValue; - aText = aValue.replace('~', '_'); - } - else if ( rEntry.Name == "ControlType" ) - rEntry.Value >>= aCtrlType; - else if ( rEntry.Name == "Choices" ) - rEntry.Value >>= aChoices; - else if ( rEntry.Name == "ChoicesDisabled" ) - rEntry.Value >>= aChoicesDisabled; - else if ( rEntry.Name == "Property" ) - { - beans::PropertyValue aVal; - rEntry.Value >>= aVal; - aPropertyName = aVal.Name; - } - else if ( rEntry.Name == "DependsOnName" ) - rEntry.Value >>= aDependsOnName; - else if ( rEntry.Name == "DependsOnEntry" ) - rEntry.Value >>= nDependsOnValue; - else if ( rEntry.Name == "AttachToDependency" ) - rEntry.Value >>= bUseDependencyRow; - else if ( rEntry.Name == "MinValue" ) - rEntry.Value >>= nMinValue; - else if ( rEntry.Name == "MaxValue" ) - rEntry.Value >>= nMaxValue; - else if ( rEntry.Name == "HelpId" ) - { - uno::Sequence<OUString> aHelpIds; - lcl_extractHelpTextsOrIds(rEntry, aHelpIds); - Help* const pHelp = Application::GetHelp(); - if (pHelp) - { - const int nLen = aHelpIds.getLength(); - aHelpTexts.realloc(nLen); - std::transform(aHelpIds.begin(), aHelpIds.end(), aHelpTexts.begin(), - [&pHelp](const OUString& rHelpId) { return pHelp->GetHelpText(rHelpId, static_cast<weld::Widget*>(nullptr)); }); - } - else // fallback - aHelpTexts = aHelpIds; - } - else if ( rEntry.Name == "HelpText" ) - lcl_extractHelpTextsOrIds(rEntry, aHelpTexts); - else if ( rEntry.Name == "InternalUIOnly" ) - rEntry.Value >>= bIgnore; - else if ( rEntry.Name == "Enabled" ) - { - // Ignore this. We use UIControlOptions::isUIOptionEnabled - // to check whether a control should be enabled. - } - else if ( rEntry.Name == "GroupingHint" ) - { - // Ignore this. We cannot add/modify controls to/on existing - // tabs of the Gtk print dialog. - } - else - { - SAL_INFO("vcl.gtk", "unhandled UI option entry: " << rEntry.Name); - } - } - - if ( aPropertyName == "PrintContent" ) - bGtkInternal = true; - - if (aCtrlType == "Group" || !pCurParent) - { - pCurTabPage = gtk_vbox_new(FALSE, 12); - gtk_container_set_border_width(GTK_CONTAINER(pCurTabPage), 6); - lcl_setHelpText(pCurTabPage, aHelpTexts, 0); - - pCurParent = pCurTabPage; - aCustomTabs.emplace_back(pCurTabPage, aText); - } - else if (aCtrlType == "Subgroup") - { - bIgnoreSubgroup = bIgnore; - if (bIgnore) - continue; - pCurParent = gtk_vbox_new(FALSE, 12); - gtk_container_set_border_width(GTK_CONTAINER(pCurParent), 0); - - pCurSubGroup = lcl_makeFrame(pCurParent, aText, aHelpTexts, nullptr); - gtk_box_pack_start(GTK_BOX(pCurTabPage), pCurSubGroup, FALSE, FALSE, 0); - } - // special case: we need to map these to controls of the gtk print dialog - else if (bGtkInternal) - { - if ( aPropertyName == "PrintContent" ) - { - // What to print? And, more importantly, is there a selection? - impl_initPrintContent(aChoicesDisabled); - } - } - else if (bIgnoreSubgroup || bIgnore) - continue; - else - { - // change handlers for all the controls set up in this block - // should be set _after_ the control has been made (in)active, - // because: - // 1. value of the property is _known_--we are using it to - // _set_ the control, right?--no need to change it back .-) - // 2. it may cause warning because the widget may not - // have been placed in m_aControlToPropertyMap yet - - GtkWidget* pWidget = nullptr; - beans::PropertyValue* pVal = nullptr; - if (aCtrlType == "Bool" && pCurParent) - { - pWidget = gtk_check_button_new_with_mnemonic( - OUStringToOString(aText, RTL_TEXTENCODING_UTF8).getStr()); - lcl_setHelpText(pWidget, aHelpTexts, 0); - m_aControlToPropertyMap[pWidget] = aPropertyName; - - bool bVal = false; - pVal = m_rController.getValue(aPropertyName); - if (pVal) - pVal->Value >>= bVal; - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pWidget), bVal); - gtk_widget_set_sensitive(pWidget, - m_rController.isUIOptionEnabled(aPropertyName) && pVal != nullptr); - g_signal_connect(pWidget, "toggled", G_CALLBACK(GtkPrintDialog::UIOption_CheckHdl), this); - } - else if (aCtrlType == "Radio" && pCurParent) - { - GtkWidget* const pVbox = gtk_vbox_new(FALSE, 12); - gtk_container_set_border_width(GTK_CONTAINER(pVbox), 0); - - if (!aText.isEmpty()) - pGroup = lcl_makeFrame(pVbox, aText, aHelpTexts, &nCurHelpText); - - sal_Int32 nSelectVal = 0; - pVal = m_rController.getValue(aPropertyName); - if (pVal && pVal->Value.hasValue()) - pVal->Value >>= nSelectVal; - - for (sal_Int32 m = 0; m != aChoices.getLength(); m++) - { - pWidget = gtk_radio_button_new_with_mnemonic_from_widget( - GTK_RADIO_BUTTON(m == 0 ? nullptr : pWidget), - OUStringToOString(aChoices[m].replace('~', '_'), RTL_TEXTENCODING_UTF8).getStr()); - lcl_setHelpText(pWidget, aHelpTexts, nCurHelpText++); - m_aControlToPropertyMap[pWidget] = aPropertyName; - m_aControlToNumValMap[pWidget] = m; - GtkWidget* const pRow = gtk_hbox_new(FALSE, 12); - gtk_box_pack_start(GTK_BOX(pVbox), pRow, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(pRow), pWidget, FALSE, FALSE, 0); - aPropertyToDependencyRowMap[aPropertyName + OUString::number(m)] = pRow; - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pWidget), m == nSelectVal); - gtk_widget_set_sensitive(pWidget, - m_rController.isUIOptionEnabled(aPropertyName) && pVal != nullptr); - g_signal_connect(pWidget, "toggled", - G_CALLBACK(GtkPrintDialog::UIOption_RadioHdl), this); - } - - if (pGroup) - pWidget = pGroup; - else - pWidget = pVbox; - } - else if ((aCtrlType == "List" || - aCtrlType == "Range" || - aCtrlType == "Edit" - ) && pCurParent) - { - GtkWidget* const pHbox = gtk_hbox_new(FALSE, 12); - gtk_container_set_border_width(GTK_CONTAINER(pHbox), 0); - - if ( aCtrlType == "List" ) - { - pWidget = lcl_combo_box_text_new(); - - for (const auto& rChoice : std::as_const(aChoices)) - { - lcl_combo_box_text_append(pWidget, - OUStringToOString(rChoice, RTL_TEXTENCODING_UTF8).getStr()); - } - - sal_Int32 nSelectVal = 0; - pVal = m_rController.getValue(aPropertyName); - if (pVal && pVal->Value.hasValue()) - pVal->Value >>= nSelectVal; - gtk_combo_box_set_active(GTK_COMBO_BOX(pWidget), nSelectVal); - g_signal_connect(pWidget, "changed", G_CALLBACK(GtkPrintDialog::UIOption_SelectHdl), this); - } - else if (aCtrlType == "Edit" && pCurParent) - { - pWidget = gtk_entry_new(); - - OUString aCurVal; - pVal = m_rController.getValue(aPropertyName); - if (pVal && pVal->Value.hasValue()) - pVal->Value >>= aCurVal; - gtk_entry_set_text(GTK_ENTRY(pWidget), - OUStringToOString(aCurVal, RTL_TEXTENCODING_UTF8).getStr()); - } - else if (aCtrlType == "Range" && pCurParent) - { - pWidget = gtk_spin_button_new_with_range(nMinValue, nMaxValue, 1.0); - - sal_Int64 nCurVal = 0; - pVal = m_rController.getValue(aPropertyName); - if (pVal && pVal->Value.hasValue()) - pVal->Value >>= nCurVal; - gtk_spin_button_set_value(GTK_SPIN_BUTTON(pWidget), nCurVal); - } - - lcl_setHelpText(pWidget, aHelpTexts, 0); - m_aControlToPropertyMap[pWidget] = aPropertyName; - - gtk_widget_set_sensitive(pWidget, - m_rController.isUIOptionEnabled(aPropertyName) && pVal != nullptr); - - if (!aText.isEmpty()) - { - GtkWidget* const pLabel = gtk_label_new_with_mnemonic( - OUStringToOString(aText, RTL_TEXTENCODING_UTF8).getStr()); - gtk_label_set_mnemonic_widget(GTK_LABEL(pLabel), pWidget); - gtk_box_pack_start(GTK_BOX(pHbox), pLabel, FALSE, FALSE, 0); - } - - gtk_box_pack_start(GTK_BOX(pHbox), pWidget, FALSE, FALSE, 0); - - pWidget = pHbox; - - } - else - SAL_INFO("vcl.gtk", "unhandled option type: " << aCtrlType); - - GtkWidget* pRow = nullptr; - if (pWidget) - { - if (bUseDependencyRow && !aDependsOnName.isEmpty()) - { - pRow = aPropertyToDependencyRowMap[aDependsOnName + OUString::number(nDependsOnValue)]; - if (!pRow) - { - gtk_widget_destroy(pWidget); - pWidget = nullptr; - } - } - } - if (pWidget) - { - if (!pRow) - { - pRow = gtk_hbox_new(FALSE, 12); - gtk_box_pack_start(GTK_BOX(pCurParent), pRow, FALSE, FALSE, 0); - } - if (!pGroup) - aPropertyToDependencyRowMap[aPropertyName + OUString::number(0)] = pRow; - gtk_box_pack_start(GTK_BOX(pRow), pWidget, FALSE, FALSE, 0); - } - } - } - - CustomTabs_t::const_reverse_iterator aEnd = aCustomTabs.rend(); - for (CustomTabs_t::const_reverse_iterator aI = aCustomTabs.rbegin(); aI != aEnd; ++aI) - { - gtk_widget_show_all(aI->first); - m_xWrapper->print_unix_dialog_add_custom_tab(GTK_PRINT_UNIX_DIALOG(m_pDialog), aI->first, - gtk_label_new(OUStringToOString(aI->second, RTL_TEXTENCODING_UTF8).getStr())); - } -} - -void -GtkPrintDialog::impl_initPrintContent(uno::Sequence<sal_Bool> const& i_rDisabled) -{ - SAL_WARN_IF(i_rDisabled.getLength() != 3, "vcl.gtk", "there is more choices than we expected"); - if (i_rDisabled.getLength() != 3) - return; - - GtkPrintUnixDialog* const pDialog(GTK_PRINT_UNIX_DIALOG(m_pDialog)); - - // XXX: This is a hack that depends on the number and the ordering of - // the controls in the rDisabled sequence (cf. the initialization of - // the "PrintContent" UI option in SwPrintUIOptions::SwPrintUIOptions, - // sw/source/core/view/printdata.cxx) - if (m_xWrapper->supportsPrintSelection() && !i_rDisabled[2]) - { - m_xWrapper->print_unix_dialog_set_support_selection(pDialog, TRUE); - m_xWrapper->print_unix_dialog_set_has_selection(pDialog, TRUE); - } - - beans::PropertyValue* const pPrintContent( - m_rController.getValue(OUString("PrintContent"))); - - if (pPrintContent) - { - sal_Int32 nSelectionType(0); - pPrintContent->Value >>= nSelectionType; - GtkPrintSettings* const pSettings(getSettings()); - GtkPrintPages ePrintPages(GTK_PRINT_PAGES_ALL); - switch (nSelectionType) - { - case 0: - ePrintPages = GTK_PRINT_PAGES_ALL; - break; - case 1: - ePrintPages = GTK_PRINT_PAGES_RANGES; - break; - case 2: - if (m_xWrapper->supportsPrintSelection()) - ePrintPages = GTK_PRINT_PAGES_SELECTION; - else - SAL_INFO("vcl.gtk", "the application wants to print a selection, but the present gtk version does not support it"); - break; - default: - SAL_WARN("vcl.gtk", "unexpected selection type: " << nSelectionType); - } - m_xWrapper->print_settings_set_print_pages(pSettings, ePrintPages); - m_xWrapper->print_unix_dialog_set_settings(pDialog, pSettings); - g_object_unref(G_OBJECT(pSettings)); - } -} - -void -GtkPrintDialog::impl_checkOptionalControlDependencies() -{ - for (auto& rEntry : m_aControlToPropertyMap) - { - gtk_widget_set_sensitive(rEntry.first, m_rController.isUIOptionEnabled(rEntry.second)); - } -} - -beans::PropertyValue* -GtkPrintDialog::impl_queryPropertyValue(GtkWidget* const i_pWidget) const -{ - beans::PropertyValue* pVal(nullptr); - std::map<GtkWidget*, OUString>::const_iterator aIt(m_aControlToPropertyMap.find(i_pWidget)); - if (aIt != m_aControlToPropertyMap.end()) - { - pVal = m_rController.getValue(aIt->second); - SAL_WARN_IF(!pVal, "vcl.gtk", "property value not found"); - } - else - { - SAL_WARN("vcl.gtk", "changed control not in property map"); - } - return pVal; -} - -void -GtkPrintDialog::impl_UIOption_CheckHdl(GtkWidget* const i_pWidget) -{ - beans::PropertyValue* const pVal = impl_queryPropertyValue(i_pWidget); - if (pVal) - { - const bool bVal = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(i_pWidget)); - pVal->Value <<= bVal; - - impl_checkOptionalControlDependencies(); - } -} - -void -GtkPrintDialog::impl_UIOption_RadioHdl(GtkWidget* const i_pWidget) -{ - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(i_pWidget))) - { - beans::PropertyValue* const pVal = impl_queryPropertyValue(i_pWidget); - std::map<GtkWidget*, sal_Int32>::const_iterator it = m_aControlToNumValMap.find(i_pWidget); - if (pVal && it != m_aControlToNumValMap.end()) - { - - const sal_Int32 nVal = it->second; - pVal->Value <<= nVal; - - impl_checkOptionalControlDependencies(); - } - } -} - -void -GtkPrintDialog::impl_UIOption_SelectHdl(GtkWidget* const i_pWidget) -{ - beans::PropertyValue* const pVal = impl_queryPropertyValue(i_pWidget); - if (pVal) - { - const sal_Int32 nVal(gtk_combo_box_get_active(GTK_COMBO_BOX(i_pWidget))); - pVal->Value <<= nVal; - - impl_checkOptionalControlDependencies(); - } -} - -bool -GtkPrintDialog::run() -{ - bool bDoJob = false; - bool bContinue = true; - while (bContinue) - { - bContinue = false; - const gint nStatus = gtk_dialog_run(GTK_DIALOG(m_pDialog)); - switch (nStatus) - { - case GTK_RESPONSE_HELP: - fprintf(stderr, "To-Do: Help ?\n"); - bContinue = true; - break; - case GTK_RESPONSE_OK: - bDoJob = true; - break; - default: - break; - } - } - gtk_widget_hide(m_pDialog); - impl_storeToSettings(); - return bDoJob; -} - -void -GtkPrintDialog::updateControllerPrintRange() -{ - GtkPrintSettings* const pSettings(getSettings()); - // TODO: use get_print_pages - if (const gchar* const pStr = m_xWrapper->print_settings_get(pSettings, GTK_PRINT_SETTINGS_PRINT_PAGES)) - { - beans::PropertyValue* pVal = m_rController.getValue(OUString("PrintRange")); - if (!pVal) - pVal = m_rController.getValue(OUString("PrintContent")); - SAL_WARN_IF(!pVal, "vcl.gtk", "Nothing to map standard print options to!"); - if (pVal) - { - sal_Int32 nVal = 0; - if (!strcmp(pStr, "all")) - nVal = 0; - else if (!strcmp(pStr, "ranges")) - nVal = 1; - else if (!strcmp(pStr, "selection")) - nVal = 2; - pVal->Value <<= nVal; - - if (nVal == 1) - { - pVal = m_rController.getValue(OUString("PageRange")); - SAL_WARN_IF(!pVal, "vcl.gtk", "PageRange doesn't exist!"); - if (pVal) - { - OUStringBuffer sBuf; - gint num_ranges; - const GtkPageRange* const pRanges = m_xWrapper->print_settings_get_page_ranges(pSettings, &num_ranges); - for (gint i = 0; i != num_ranges && pRanges; ++i) - { - sBuf.append(sal_Int32(pRanges[i].start+1)); - if (pRanges[i].start != pRanges[i].end) - { - sBuf.append('-'); - sBuf.append(sal_Int32(pRanges[i].end+1)); - } - - if (i != num_ranges-1) - sBuf.append(','); - } - pVal->Value <<= sBuf.makeStringAndClear(); - } - } - } - } - g_object_unref(G_OBJECT(pSettings)); -} - -GtkPrintDialog::~GtkPrintDialog() -{ - gtk_widget_destroy(m_pDialog); -} - -void -GtkPrintDialog::impl_readFromSettings() -{ - vcl::SettingsConfigItem* const pItem(vcl::SettingsConfigItem::get()); - GtkPrintSettings* const pSettings(getSettings()); - - const OUString aPrintDialogStr("PrintDialog"); - const OUString aCopyCount(pItem->getValue(aPrintDialogStr, - "CopyCount")); - const OUString aCollate(pItem->getValue(aPrintDialogStr, - "Collate")); - - const gint nOldCopyCount(m_xWrapper->print_settings_get_n_copies(pSettings)); - const sal_Int32 nCopyCount(aCopyCount.toInt32()); - if (nCopyCount > 0 && nOldCopyCount != nCopyCount) - { - m_xWrapper->print_settings_set_n_copies(pSettings, sal::static_int_cast<gint>(nCopyCount)); - } - - const bool bOldCollate(m_xWrapper->print_settings_get_collate(pSettings)); - const bool bCollate(aCollate.equalsIgnoreAsciiCase("true")); - if (bOldCollate != bCollate) - { - m_xWrapper->print_settings_set_collate(pSettings, bCollate); - } - - m_xWrapper->print_unix_dialog_set_settings(GTK_PRINT_UNIX_DIALOG(m_pDialog), pSettings); - g_object_unref(G_OBJECT(pSettings)); -} - -void -GtkPrintDialog::impl_storeToSettings() -const -{ - vcl::SettingsConfigItem* const pItem(vcl::SettingsConfigItem::get()); - GtkPrintSettings* const pSettings(getSettings()); - - const OUString aPrintDialogStr("PrintDialog"); - pItem->setValue(aPrintDialogStr, - "CopyCount", - OUString::number(m_xWrapper->print_settings_get_n_copies(pSettings))); - pItem->setValue(aPrintDialogStr, - "Collate", - m_xWrapper->print_settings_get_collate(pSettings) - ? OUString("true") - : OUString("false")) - ; - // pItem->setValue(aPrintDialog, OUString("ToFile"), ); - g_object_unref(G_OBJECT(pSettings)); - pItem->Commit(); -} - -sal_uInt32 -GtkSalInfoPrinter::GetCapabilities( - const ImplJobSetup* const i_pSetupData, - const PrinterCapType i_nType) -{ - if (i_nType == PrinterCapType::ExternalDialog && lcl_useSystemPrintDialog()) - return 1; - return PspSalInfoPrinter::GetCapabilities(i_pSetupData, i_nType); -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/xid_fullscreen_on_all_monitors.c b/vcl/unx/gtk/xid_fullscreen_on_all_monitors.c deleted file mode 100644 index 558ede597a48..000000000000 --- a/vcl/unx/gtk/xid_fullscreen_on_all_monitors.c +++ /dev/null @@ -1,101 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.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 <dlfcn.h> -#include <stdlib.h> - -typedef int Window; -typedef union _GdkEvent GdkEvent; -typedef struct _GdkWindow GdkWindow; -typedef struct _GdkDisplay GdkDisplay; -typedef struct _GdkScreen GdkScreen; - -typedef enum -{ - GDK_FULLSCREEN_ON_CURRENT_MONITOR, - GDK_FULLSCREEN_ON_ALL_MONITORS -} GdkFullscreenMode; - -int main(int argc, char *argv[]) -{ - void *handle; - void (*gtk_init)(int*, char***); - GdkWindow* (*gdk_x11_window_foreign_new_for_display)(GdkDisplay*, Window); - GdkDisplay* (*gdk_display_get_default)(void); - GdkEvent* (*gdk_event_get)(void); - void (*gtk_main_do_event)(GdkEvent*); - void (*gdk_event_free)(GdkEvent*); - void (*gdk_window_fullscreen)(GdkWindow *); - void (*gdk_window_set_fullscreen_mode)(GdkWindow *, GdkFullscreenMode); - - GdkEvent *event; - GdkWindow *window; - int windowid; - int spanmonitors; - - handle = dlopen("libgtk-3.so.0", RTLD_LAZY); - if( NULL == handle ) - return -1; - - gtk_init = (void (*) (int*, char***)) - dlsym(handle, "gtk_init"); - gdk_x11_window_foreign_new_for_display = (GdkWindow* (*)(GdkDisplay*, Window)) - dlsym(handle, "gdk_x11_window_foreign_new_for_display"); - gdk_display_get_default = (GdkDisplay* (*)(void)) - dlsym(handle, "gdk_display_get_default"); - gdk_event_get = (GdkEvent* (*)(void)) - dlsym(handle, "gdk_event_get"); - gtk_main_do_event = (void (*)(GdkEvent*)) - dlsym(handle, "gtk_main_do_event"); - gdk_event_free = (void (*)(GdkEvent*)) - dlsym(handle, "gdk_event_free"); - gdk_window_fullscreen = (void (*)(GdkWindow *)) - dlsym(handle, "gdk_window_fullscreen"); - gdk_window_set_fullscreen_mode = (void (*)(GdkWindow *, GdkFullscreenMode)) - dlsym(handle, "gdk_window_set_fullscreen_mode"); - - if (!gtk_init || - !gdk_x11_window_foreign_new_for_display || - !gdk_display_get_default || - !gdk_event_get || - !gtk_main_do_event || - !gdk_event_free || - !gdk_window_fullscreen || - !gdk_window_set_fullscreen_mode) - { - dlclose(handle); - return -1; - } - - gtk_init(&argc, &argv); - - windowid = atoi(argv[1]); - spanmonitors = atoi(argv[2]); - - window = gdk_x11_window_foreign_new_for_display(gdk_display_get_default(), windowid); - if (!window) - { - dlclose(handle); - return -1; - } - - gdk_window_set_fullscreen_mode(window, spanmonitors ? GDK_FULLSCREEN_ON_ALL_MONITORS : GDK_FULLSCREEN_ON_CURRENT_MONITOR); - gdk_window_fullscreen(window); - - while ((event = gdk_event_get()) != NULL) - { - gtk_main_do_event(event); - gdk_event_free(event); - } - - dlclose(handle); - return 0; -} -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ - diff --git a/vcl/unx/gtk/a11y/TODO b/vcl/unx/gtk3/a11y/TODO index 1048bd96ef75..1048bd96ef75 100644 --- a/vcl/unx/gtk/a11y/TODO +++ b/vcl/unx/gtk3/a11y/TODO diff --git a/vcl/unx/gtk/a11y/atkfactory.hxx b/vcl/unx/gtk3/a11y/atkfactory.hxx index ac72b5897813..ac72b5897813 100644 --- a/vcl/unx/gtk/a11y/atkfactory.hxx +++ b/vcl/unx/gtk3/a11y/atkfactory.hxx diff --git a/vcl/unx/gtk/a11y/atklistener.hxx b/vcl/unx/gtk3/a11y/atklistener.hxx index 58798d4439fb..58798d4439fb 100644 --- a/vcl/unx/gtk/a11y/atklistener.hxx +++ b/vcl/unx/gtk3/a11y/atklistener.hxx diff --git a/vcl/unx/gtk/a11y/atkregistry.hxx b/vcl/unx/gtk3/a11y/atkregistry.hxx index b692290847d8..b692290847d8 100644 --- a/vcl/unx/gtk/a11y/atkregistry.hxx +++ b/vcl/unx/gtk3/a11y/atkregistry.hxx diff --git a/vcl/unx/gtk/a11y/atktextattributes.hxx b/vcl/unx/gtk3/a11y/atktextattributes.hxx index f86c82f4fe09..f86c82f4fe09 100644 --- a/vcl/unx/gtk/a11y/atktextattributes.hxx +++ b/vcl/unx/gtk3/a11y/atktextattributes.hxx diff --git a/vcl/unx/gtk/a11y/atkutil.hxx b/vcl/unx/gtk3/a11y/atkutil.hxx index cb4a005157a0..cb4a005157a0 100644 --- a/vcl/unx/gtk/a11y/atkutil.hxx +++ b/vcl/unx/gtk3/a11y/atkutil.hxx diff --git a/vcl/unx/gtk/a11y/atkwindow.hxx b/vcl/unx/gtk3/a11y/atkwindow.hxx index 35a4bcedf486..35a4bcedf486 100644 --- a/vcl/unx/gtk/a11y/atkwindow.hxx +++ b/vcl/unx/gtk3/a11y/atkwindow.hxx diff --git a/vcl/unx/gtk/a11y/atkwrapper.hxx b/vcl/unx/gtk3/a11y/atkwrapper.hxx index 8725e54ccf5b..7ae0379de9c3 100644 --- a/vcl/unx/gtk/a11y/atkwrapper.hxx +++ b/vcl/unx/gtk3/a11y/atkwrapper.hxx @@ -22,9 +22,7 @@ #include <atk/atk.h> #include <gtk/gtk.h> -#if GTK_CHECK_VERSION(3,0,0) #include <gtk/gtk-a11y.h> -#endif #include <com/sun/star/accessibility/XAccessible.hpp> extern "C" { @@ -74,11 +72,7 @@ struct AtkObjectWrapper struct AtkObjectWrapperClass { -#if GTK_CHECK_VERSION(3,0,0) GtkWidgetAccessibleClass aParentClass; -#else - AtkObjectClass const aParentClass; -#endif }; GType atk_object_wrapper_get_type() G_GNUC_CONST; diff --git a/vcl/unx/gtk3/a11y/gtk3atkaction.cxx b/vcl/unx/gtk3/a11y/gtk3atkaction.cxx index a3fa632d08a1..81e743238d84 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkaction.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkaction.cxx @@ -5,8 +5,271 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkaction.cxx" +#include "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleAction.hpp> +#include <com/sun/star/accessibility/XAccessibleKeyBinding.hpp> + +#include <com/sun/star/awt/Key.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> + +#include <rtl/strbuf.hxx> +#include <algorithm> +#include <map> + +using namespace ::com::sun::star; + +// FIXME +static G_CONST_RETURN gchar * +getAsConst( const OString& rString ) +{ + static const int nMax = 10; + static OString aUgly[nMax]; + static int nIdx = 0; + nIdx = (nIdx + 1) % nMax; + aUgly[nIdx] = rString; + return aUgly[ nIdx ].getStr(); +} + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleAction> + getAction( AtkAction *action ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( action ); + + if( pWrap ) + { + if( !pWrap->mpAction.is() ) + { + pWrap->mpAction.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpAction; + } + + return css::uno::Reference<css::accessibility::XAccessibleAction>(); +} + +extern "C" { + +static gboolean +action_wrapper_do_action (AtkAction *action, + gint i) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleAction> pAction + = getAction( action ); + if( pAction.is() ) + return pAction->doAccessibleAction( i ); + } + catch(const uno::Exception&) { + g_warning( "Exception in doAccessibleAction()" ); + } + + return FALSE; +} + +static gint +action_wrapper_get_n_actions (AtkAction *action) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleAction> pAction + = getAction( action ); + if( pAction.is() ) + return pAction->getAccessibleActionCount(); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleActionCount()" ); + } + + return 0; +} + +static G_CONST_RETURN gchar * +action_wrapper_get_description (AtkAction *, gint) +{ + // GAIL implement this only for cells + g_warning( "Not implemented: get_description()" ); + return ""; +} + +static G_CONST_RETURN gchar * +action_wrapper_get_localized_name (AtkAction *, gint) +{ + // GAIL doesn't implement this as well + g_warning( "Not implemented: get_localized_name()" ); + return ""; +} + +#define ACTION_NAME_PAIR( OOoName, AtkName ) \ + std::pair< const OUString, const gchar * > ( OUString( OOoName ), AtkName ) + +static G_CONST_RETURN gchar * +action_wrapper_get_name (AtkAction *action, + gint i) +{ + static std::map< OUString, const gchar * > aNameMap; + + if( aNameMap.empty() ) + { + aNameMap.insert( ACTION_NAME_PAIR( "click", "click" ) ); + aNameMap.insert( ACTION_NAME_PAIR( "select", "click" ) ); + aNameMap.insert( ACTION_NAME_PAIR( "togglePopup", "push" ) ); + } + + try { + css::uno::Reference<css::accessibility::XAccessibleAction> pAction + = getAction( action ); + if( pAction.is() ) + { + std::map< OUString, const gchar * >::iterator iter; + + OUString aDesc( pAction->getAccessibleActionDescription( i ) ); + + iter = aNameMap.find( aDesc ); + if( iter != aNameMap.end() ) + return iter->second; + + std::pair< const OUString, const gchar * > aNewVal( aDesc, + g_strdup( OUStringToConstGChar(aDesc) ) ); + + if( aNameMap.insert( aNewVal ).second ) + return aNewVal.second; + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleActionDescription()" ); + } + + return ""; +} + +/* +* GNOME Expects a string in the format: +* +* <mnemonic>;<full-path>;<accelerator> +* +* The keybindings in <full-path> should be separated by ":" +*/ + +static void +appendKeyStrokes(OStringBuffer& rBuffer, const uno::Sequence< awt::KeyStroke >& rKeyStrokes) +{ + for( const auto& rKeyStroke : rKeyStrokes ) + { + if( rKeyStroke.Modifiers & awt::KeyModifier::SHIFT ) + rBuffer.append("<Shift>"); + if( rKeyStroke.Modifiers & awt::KeyModifier::MOD1 ) + rBuffer.append("<Control>"); + if( rKeyStroke.Modifiers & awt::KeyModifier::MOD2 ) + rBuffer.append("<Alt>"); + + if( ( rKeyStroke.KeyCode >= awt::Key::A ) && ( rKeyStroke.KeyCode <= awt::Key::Z ) ) + rBuffer.append( static_cast<sal_Char>( 'a' + ( rKeyStroke.KeyCode - awt::Key::A ) ) ); + else + { + sal_Char c = '\0'; + + switch( rKeyStroke.KeyCode ) + { + case awt::Key::TAB: c = '\t'; break; + case awt::Key::SPACE: c = ' '; break; + case awt::Key::ADD: c = '+'; break; + case awt::Key::SUBTRACT: c = '-'; break; + case awt::Key::MULTIPLY: c = '*'; break; + case awt::Key::DIVIDE: c = '/'; break; + case awt::Key::POINT: c = '.'; break; + case awt::Key::COMMA: c = ','; break; + case awt::Key::LESS: c = '<'; break; + case awt::Key::GREATER: c = '>'; break; + case awt::Key::EQUAL: c = '='; break; + case 0: + break; + default: + g_warning( "Unmapped KeyCode: %d", rKeyStroke.KeyCode ); + break; + } + + if( c != '\0' ) + rBuffer.append( c ); + else + { + // The KeyCode approach did not work, probably a non ascii character + // let's hope that there is a character given in KeyChar. + rBuffer.append( OUStringToGChar( OUString( rKeyStroke.KeyChar ) ) ); + } + } + } +} + +static G_CONST_RETURN gchar * +action_wrapper_get_keybinding (AtkAction *action, + gint i) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleAction> pAction + = getAction( action ); + if( pAction.is() ) + { + uno::Reference< accessibility::XAccessibleKeyBinding > xBinding( pAction->getAccessibleActionKeyBinding( i )); + + if( xBinding.is() ) + { + OStringBuffer aRet; + + sal_Int32 nmax = std::min( xBinding->getAccessibleKeyBindingCount(), sal_Int32(3) ); + for( sal_Int32 n = 0; n < nmax; n++ ) + { + appendKeyStrokes( aRet, xBinding->getAccessibleKeyBinding( n ) ); + + if( n < 2 ) + aRet.append( ';' ); + } + + // !! FIXME !! remember keystroke in wrapper object ? + return getAsConst( aRet.makeStringAndClear() ); + } + } + } + catch(const uno::Exception&) { + g_warning( "Exception in get_keybinding()" ); + } + + return ""; +} + +static gboolean +action_wrapper_set_description (AtkAction *, gint, const gchar *) +{ + return FALSE; +} + +} // extern "C" + +void +actionIfaceInit (AtkActionIface *iface) +{ + g_return_if_fail (iface != nullptr); + + iface->do_action = action_wrapper_do_action; + iface->get_n_actions = action_wrapper_get_n_actions; + iface->get_description = action_wrapper_get_description; + iface->get_keybinding = action_wrapper_get_keybinding; + iface->get_name = action_wrapper_get_name; + iface->get_localized_name = action_wrapper_get_localized_name; + iface->set_description = action_wrapper_set_description; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkbridge.cxx b/vcl/unx/gtk3/a11y/gtk3atkbridge.cxx index d8e087956067..0e2781cf8fd5 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkbridge.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkbridge.cxx @@ -5,8 +5,33 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkbridge.cxx" +#include <unx/gtk/atkbridge.hxx> +#include <unx/gtk/gtkframe.hxx> + +#include "atkfactory.hxx" +#include "atkutil.hxx" +#include "atkwindow.hxx" + +bool InitAtkBridge() +{ + ooo_atk_util_ensure_event_listener(); + return true; +} + +void DeInitAtkBridge() +{ +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkcomponent.cxx b/vcl/unx/gtk3/a11y/gtk3atkcomponent.cxx index e4eabec20fb8..e904b12ac610 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkcomponent.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkcomponent.cxx @@ -5,8 +5,379 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkcomponent.cxx" +#include "atkwrapper.hxx" +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <gtk/gtk.h> + +using namespace ::com::sun::star; + +static AtkObjectWrapper* getObjectWrapper(AtkComponent *pComponent) +{ + AtkObjectWrapper *pWrap = nullptr; + if (ATK_IS_OBJECT_WRAPPER(pComponent)) + pWrap = ATK_OBJECT_WRAPPER(pComponent); + else if (GTK_IS_DRAWING_AREA(pComponent)) //when using a GtkDrawingArea as a custom widget in welded gtk3 + { + GtkWidget* pDrawingArea = GTK_WIDGET(pComponent); + AtkObject* pAtkObject = gtk_widget_get_accessible(pDrawingArea); + pWrap = ATK_IS_OBJECT_WRAPPER(pAtkObject) ? ATK_OBJECT_WRAPPER(pAtkObject) : nullptr; + } + return pWrap; +} + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleComponent> + getComponent(AtkObjectWrapper *pWrap) +{ + if (pWrap) + { + if (!pWrap->mpComponent.is()) + pWrap->mpComponent.set(pWrap->mpContext, css::uno::UNO_QUERY); + return pWrap->mpComponent; + } + + return css::uno::Reference<css::accessibility::XAccessibleComponent>(); +} + +/*****************************************************************************/ + +static awt::Point +translatePoint( css::uno::Reference<accessibility::XAccessibleComponent> const & pComponent, + gint x, gint y, AtkCoordType t) +{ + awt::Point aOrigin( 0, 0 ); + if( t == ATK_XY_SCREEN ) + aOrigin = pComponent->getLocationOnScreen(); + return awt::Point( x - aOrigin.X, y - aOrigin.Y ); +} + +/*****************************************************************************/ + +extern "C" { + +static gboolean +component_wrapper_grab_focus (AtkComponent *component) +{ + AtkObjectWrapper* obj = getObjectWrapper(component); + //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y + if (obj && obj->mpOrig) + return atk_component_grab_focus(ATK_COMPONENT(obj->mpOrig)); + + try + { + css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent + = getComponent(obj); + if( pComponent.is() ) + { + pComponent->grabFocus(); + return TRUE; + } + } + catch( const uno::Exception & ) + { + g_warning( "Exception in grabFocus()" ); + } + + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +component_wrapper_contains (AtkComponent *component, + gint x, + gint y, + AtkCoordType coord_type) +{ + AtkObjectWrapper* obj = getObjectWrapper(component); + //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y + if (obj && obj->mpOrig) + return atk_component_contains(ATK_COMPONENT(obj->mpOrig), x, y, coord_type); + + try + { + css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent + = getComponent(obj); + if( pComponent.is() ) + return pComponent->containsPoint( translatePoint( pComponent, x, y, coord_type ) ); + } + catch( const uno::Exception & ) + { + g_warning( "Exception in containsPoint()" ); + } + + return FALSE; +} + +/*****************************************************************************/ + +static AtkObject * +component_wrapper_ref_accessible_at_point (AtkComponent *component, + gint x, + gint y, + AtkCoordType coord_type) +{ + AtkObjectWrapper* obj = getObjectWrapper(component); + //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y + if (obj && obj->mpOrig) + return atk_component_ref_accessible_at_point(ATK_COMPONENT(obj->mpOrig), x, y, coord_type); + + try + { + css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent + = getComponent(obj); + + if( pComponent.is() ) + { + uno::Reference< accessibility::XAccessible > xAccessible = pComponent->getAccessibleAtPoint( + translatePoint( pComponent, x, y, coord_type ) ); + return atk_object_wrapper_ref( xAccessible ); + } + } + catch( const uno::Exception & ) + { + g_warning( "Exception in getAccessibleAtPoint()" ); + } + + return nullptr; +} + +/*****************************************************************************/ + +static void +component_wrapper_get_position (AtkComponent *component, + gint *x, + gint *y, + AtkCoordType coord_type) +{ + AtkObjectWrapper* obj = getObjectWrapper(component); + //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y + if (obj && obj->mpOrig) + { + atk_component_get_extents(ATK_COMPONENT(obj->mpOrig), x, y, nullptr, nullptr, coord_type); + return; + } + + try + { + css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent + = getComponent(obj); + if( pComponent.is() ) + { + awt::Point aPos; + + if( coord_type == ATK_XY_SCREEN ) + aPos = pComponent->getLocationOnScreen(); + else + aPos = pComponent->getLocation(); + + *x = aPos.X; + *y = aPos.Y; + } + } + catch( const uno::Exception & ) + { + g_warning( "Exception in getLocation[OnScreen]()" ); + } +} + +/*****************************************************************************/ + +static void +component_wrapper_get_size (AtkComponent *component, + gint *width, + gint *height) +{ + AtkObjectWrapper* obj = getObjectWrapper(component); + //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y + if (obj && obj->mpOrig) + { + atk_component_get_extents(ATK_COMPONENT(obj->mpOrig), nullptr, nullptr, width, height, ATK_XY_WINDOW); + return; + } + + try + { + css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent + = getComponent(obj); + if( pComponent.is() ) + { + awt::Size aSize = pComponent->getSize(); + *width = aSize.Width; + *height = aSize.Height; + } + } + catch( const uno::Exception & ) + { + g_warning( "Exception in getSize()" ); + } +} + +/*****************************************************************************/ + +static void +component_wrapper_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + component_wrapper_get_position( component, x, y, coord_type ); + component_wrapper_get_size( component, width, height ); +} + +/*****************************************************************************/ + +static gboolean +component_wrapper_set_extents (AtkComponent *, gint, gint, gint, gint, AtkCoordType) +{ + g_warning( "AtkComponent::set_extents unimplementable" ); + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +component_wrapper_set_position (AtkComponent *, gint, gint, AtkCoordType) +{ + g_warning( "AtkComponent::set_position unimplementable" ); + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +component_wrapper_set_size (AtkComponent *, gint, gint) +{ + g_warning( "AtkComponent::set_size unimplementable" ); + return FALSE; +} + +/*****************************************************************************/ + +static AtkLayer +component_wrapper_get_layer (AtkComponent *component) +{ + AtkRole role = atk_object_get_role( ATK_OBJECT( component ) ); + AtkLayer layer = ATK_LAYER_WIDGET; + + switch (role) + { + case ATK_ROLE_POPUP_MENU: + case ATK_ROLE_MENU_ITEM: + case ATK_ROLE_CHECK_MENU_ITEM: + case ATK_ROLE_SEPARATOR: + case ATK_ROLE_LIST_ITEM: + layer = ATK_LAYER_POPUP; + break; + case ATK_ROLE_MENU: + { + AtkObject * parent = atk_object_get_parent( ATK_OBJECT( component ) ); + if( atk_object_get_role( parent ) != ATK_ROLE_MENU_BAR ) + layer = ATK_LAYER_POPUP; + } + break; + + case ATK_ROLE_LIST: + { + AtkObject * parent = atk_object_get_parent( ATK_OBJECT( component ) ); + if( atk_object_get_role( parent ) == ATK_ROLE_COMBO_BOX ) + layer = ATK_LAYER_POPUP; + } + break; + + default: + ; + } + + return layer; +} + +/*****************************************************************************/ + +static gint +component_wrapper_get_mdi_zorder (AtkComponent *) +{ + // only needed for ATK_LAYER_MDI (not used) or ATK_LAYER_WINDOW (inherited from GAIL) + return G_MININT; +} + +/*****************************************************************************/ + +// This code is mostly stolen from libgail .. + +static guint +component_wrapper_add_focus_handler (AtkComponent *component, + AtkFocusHandler handler) +{ + GSignalMatchType match_type; + gulong ret; + guint signal_id; + + match_type = GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC); + signal_id = g_signal_lookup( "focus-event", ATK_TYPE_OBJECT ); + + ret = g_signal_handler_find( component, match_type, signal_id, 0, nullptr, + static_cast<gpointer>(&handler), nullptr); + if (!ret) + { + return g_signal_connect_closure_by_id (component, + signal_id, 0, + g_cclosure_new ( + G_CALLBACK (handler), nullptr, + nullptr), + FALSE); + } + else + { + return 0; + } +} + +/*****************************************************************************/ + +static void +component_wrapper_remove_focus_handler (AtkComponent *component, + guint handler_id) +{ + g_signal_handler_disconnect (component, handler_id); +} + +/*****************************************************************************/ + +} // extern "C" + +void +componentIfaceInit (AtkComponentIface *iface) +{ + g_return_if_fail (iface != nullptr); + + iface->add_focus_handler = component_wrapper_add_focus_handler; + iface->contains = component_wrapper_contains; + iface->get_extents = component_wrapper_get_extents; + iface->get_layer = component_wrapper_get_layer; + iface->get_mdi_zorder = component_wrapper_get_mdi_zorder; + iface->get_position = component_wrapper_get_position; + iface->get_size = component_wrapper_get_size; + iface->grab_focus = component_wrapper_grab_focus; + iface->ref_accessible_at_point = component_wrapper_ref_accessible_at_point; + iface->remove_focus_handler = component_wrapper_remove_focus_handler; + iface->set_extents = component_wrapper_set_extents; + iface->set_position = component_wrapper_set_position; + iface->set_size = component_wrapper_set_size; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkeditabletext.cxx b/vcl/unx/gtk3/a11y/gtk3atkeditabletext.cxx index ea3f0895fb8e..49d3eb9ccfc9 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkeditabletext.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkeditabletext.cxx @@ -5,8 +5,190 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkeditabletext.cxx" +#include "atkwrapper.hxx" +#include "atktextattributes.hxx" + +#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> +#include <com/sun/star/accessibility/TextSegment.hpp> + +#include <string.h> + +using namespace ::com::sun::star; + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleEditableText> + getEditableText( AtkEditableText *pEditableText ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pEditableText ); + if( pWrap ) + { + if( !pWrap->mpEditableText.is() ) + { + pWrap->mpEditableText.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpEditableText; + } + + return css::uno::Reference<css::accessibility::XAccessibleEditableText>(); +} + +/*****************************************************************************/ + +extern "C" { + +static gboolean +editable_text_wrapper_set_run_attributes( AtkEditableText *text, + AtkAttributeSet *attribute_set, + gint nStartOffset, + gint nEndOffset) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleEditableText> + pEditableText = getEditableText( text ); + if( pEditableText.is() ) + { + uno::Sequence< beans::PropertyValue > aAttributeList; + + if( attribute_set_map_to_property_values( attribute_set, aAttributeList ) ) + return pEditableText->setAttributes(nStartOffset, nEndOffset, aAttributeList); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in setAttributes()" ); + } + + return FALSE; +} + +static void +editable_text_wrapper_set_text_contents( AtkEditableText *text, + const gchar *string ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleEditableText> + pEditableText = getEditableText( text ); + if( pEditableText.is() ) + { + OUString aString ( string, strlen(string), RTL_TEXTENCODING_UTF8 ); + pEditableText->setText( aString ); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in setText()" ); + } +} + +static void +editable_text_wrapper_insert_text( AtkEditableText *text, + const gchar *string, + gint length, + gint *pos ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleEditableText> + pEditableText = getEditableText( text ); + if( pEditableText.is() ) + { + OUString aString ( string, length, RTL_TEXTENCODING_UTF8 ); + if( pEditableText->insertText( aString, *pos ) ) + *pos += length; + } + } + catch(const uno::Exception&) { + g_warning( "Exception in insertText()" ); + } +} + +static void +editable_text_wrapper_cut_text( AtkEditableText *text, + gint start, + gint end ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleEditableText> + pEditableText = getEditableText( text ); + if( pEditableText.is() ) + pEditableText->cutText( start, end ); + } + catch(const uno::Exception&) { + g_warning( "Exception in cutText()" ); + } +} + +static void +editable_text_wrapper_delete_text( AtkEditableText *text, + gint start, + gint end ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleEditableText> + pEditableText = getEditableText( text ); + if( pEditableText.is() ) + pEditableText->deleteText( start, end ); + } + catch(const uno::Exception&) { + g_warning( "Exception in deleteText()" ); + } +} + +static void +editable_text_wrapper_paste_text( AtkEditableText *text, + gint pos ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleEditableText> + pEditableText = getEditableText( text ); + if( pEditableText.is() ) + pEditableText->pasteText( pos ); + } + catch(const uno::Exception&) { + g_warning( "Exception in pasteText()" ); + } +} + +static void +editable_text_wrapper_copy_text( AtkEditableText *text, + gint start, + gint end ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleEditableText> + pEditableText = getEditableText( text ); + if( pEditableText.is() ) + pEditableText->copyText( start, end ); + } + catch(const uno::Exception&) { + g_warning( "Exception in copyText()" ); + } +} + +} // extern "C" + +void +editableTextIfaceInit (AtkEditableTextIface *iface) +{ + g_return_if_fail (iface != nullptr); + + iface->set_text_contents = editable_text_wrapper_set_text_contents; + iface->insert_text = editable_text_wrapper_insert_text; + iface->copy_text = editable_text_wrapper_copy_text; + iface->cut_text = editable_text_wrapper_cut_text; + iface->delete_text = editable_text_wrapper_delete_text; + iface->paste_text = editable_text_wrapper_paste_text; + iface->set_run_attributes = editable_text_wrapper_set_run_attributes; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkfactory.cxx b/vcl/unx/gtk3/a11y/gtk3atkfactory.cxx index c60db2fcc074..d7c8bf9f6289 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkfactory.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkfactory.cxx @@ -5,8 +5,182 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <unx/gtk/gtkframe.hxx> +#include <vcl/window.hxx> +#include "atkwrapper.hxx" +#include "atkfactory.hxx" +#include "atkregistry.hxx" + +using namespace ::com::sun::star; + +extern "C" { + +/* + * Instances of this dummy object class are returned whenever we have to + * create an AtkObject, but can't touch the OOo object anymore since it + * is already disposed. + */ + +static AtkStateSet * +noop_wrapper_ref_state_set( AtkObject * ) +{ + AtkStateSet *state_set = atk_state_set_new(); + atk_state_set_add_state( state_set, ATK_STATE_DEFUNCT ); + return state_set; +} + +static void +atk_noop_object_wrapper_class_init(AtkNoOpObjectClass *klass) +{ + AtkObjectClass *atk_class = ATK_OBJECT_CLASS( klass ); + atk_class->ref_state_set = noop_wrapper_ref_state_set; +} + +static GType +atk_noop_object_wrapper_get_type() +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo typeInfo = + { + sizeof (AtkNoOpObjectClass), + nullptr, + nullptr, + reinterpret_cast<GClassInitFunc>(atk_noop_object_wrapper_class_init), + nullptr, + nullptr, + sizeof (AtkObjectWrapper), + 0, + nullptr, + nullptr + } ; + + type = g_type_register_static (ATK_TYPE_OBJECT, "OOoAtkNoOpObj", &typeInfo, GTypeFlags(0)) ; + } + return type; +} + +static AtkObject* +atk_noop_object_wrapper_new() +{ + AtkObject *accessible; + + accessible = static_cast<AtkObject *>(g_object_new (atk_noop_object_wrapper_get_type(), nullptr)); + g_return_val_if_fail (accessible != nullptr, nullptr); + + accessible->role = ATK_ROLE_INVALID; + accessible->layer = ATK_LAYER_INVALID; + + return accessible; +} + +/* + * The wrapper factory */ -#include "../../gtk/a11y/atkfactory.cxx" +static GType +wrapper_factory_get_accessible_type() +{ + return atk_object_wrapper_get_type(); +} + +static AtkObject* +wrapper_factory_create_accessible( GObject *obj ) +{ + GtkWidget* pEventBox = gtk_widget_get_parent(GTK_WIDGET(obj)); + + // gail_container_real_remove_gtk tries to re-instantiate an accessible + // for a widget that is about to vanish .. + if (!pEventBox) + return atk_noop_object_wrapper_new(); + + GtkWidget* pTopLevelGrid = gtk_widget_get_parent(pEventBox); + if (!pTopLevelGrid) + return atk_noop_object_wrapper_new(); + + GtkWidget* pTopLevel = gtk_widget_get_parent(pTopLevelGrid); + if (!pTopLevel) + return atk_noop_object_wrapper_new(); + + GtkSalFrame* pFrame = GtkSalFrame::getFromWindow(GTK_WINDOW(pTopLevel)); + g_return_val_if_fail( pFrame != nullptr, nullptr ); + + vcl::Window* pFrameWindow = pFrame->GetWindow(); + if( pFrameWindow ) + { + vcl::Window* pWindow = pFrameWindow; + + // skip accessible objects already exposed by the frame objects + if( WindowType::BORDERWINDOW == pWindow->GetType() ) + pWindow = pFrameWindow->GetAccessibleChildWindow(0); + + if( pWindow ) + { + uno::Reference< accessibility::XAccessible > xAccessible = pWindow->GetAccessible(); + if( xAccessible.is() ) + { + AtkObject *accessible = ooo_wrapper_registry_get( xAccessible ); + + if( accessible ) + g_object_ref( G_OBJECT(accessible) ); + else + accessible = atk_object_wrapper_new( xAccessible, gtk_widget_get_accessible(pTopLevel) ); + + return accessible; + } + } + } + + return nullptr; +} + +AtkObject* ooo_fixed_get_accessible(GtkWidget *obj) +{ + return wrapper_factory_create_accessible(G_OBJECT(obj)); +} + +static void +wrapper_factory_class_init( AtkObjectFactoryClass *klass ) +{ + klass->create_accessible = wrapper_factory_create_accessible; + klass->get_accessible_type = wrapper_factory_get_accessible_type; +} + +GType +wrapper_factory_get_type() +{ + static GType t = 0; + + if (!t) { + static const GTypeInfo tinfo = + { + sizeof (AtkObjectFactoryClass), + nullptr, nullptr, reinterpret_cast<GClassInitFunc>(wrapper_factory_class_init), + nullptr, nullptr, sizeof (AtkObjectFactory), 0, nullptr, nullptr + }; + + t = g_type_register_static ( + ATK_TYPE_OBJECT_FACTORY, "OOoAtkObjectWrapperFactory", + &tinfo, GTypeFlags(0)); + } + + return t; +} + +} // extern C /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkhypertext.cxx b/vcl/unx/gtk3/a11y/gtk3atkhypertext.cxx index bb9749c36af3..ab94126dfc51 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkhypertext.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkhypertext.cxx @@ -5,8 +5,269 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkhypertext.cxx" +#include "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleHypertext.hpp> + +using namespace ::com::sun::star; + +// ---------------------- AtkHyperlink ---------------------- + +struct HyperLink { + AtkHyperlink const atk_hyper_link; + + uno::Reference< accessibility::XAccessibleHyperlink > xLink; +}; + +static uno::Reference< accessibility::XAccessibleHyperlink > const & + getHyperlink( AtkHyperlink *pHyperlink ) +{ + HyperLink *pLink = reinterpret_cast<HyperLink *>(pHyperlink); + return pLink->xLink; +} + +static GObjectClass *hyper_parent_class = nullptr; + +extern "C" { + +static void +hyper_link_finalize (GObject *obj) +{ + HyperLink *hl = reinterpret_cast<HyperLink *>(obj); + hl->xLink.clear(); + hyper_parent_class->finalize (obj); +} + +static gchar * +hyper_link_get_uri( AtkHyperlink *pLink, + gint i ) +{ + try { + uno::Any aAny = getHyperlink( pLink )->getAccessibleActionObject( i ); + OUString aUri = aAny.get< OUString > (); + return OUStringToGChar(aUri); + } + catch(const uno::Exception&) { + g_warning( "Exception in hyper_link_get_uri" ); + } + return nullptr; +} + +static AtkObject * +hyper_link_get_object( AtkHyperlink *, + gint ) +{ + g_warning( "FIXME: hyper_link_get_object unimplemented" ); + return nullptr; +} +static gint +hyper_link_get_end_index( AtkHyperlink *pLink ) +{ + try { + return getHyperlink( pLink )->getEndIndex(); + } + catch(const uno::Exception&) { + } + return -1; +} +static gint +hyper_link_get_start_index( AtkHyperlink *pLink ) +{ + try { + return getHyperlink( pLink )->getStartIndex(); + } + catch(const uno::Exception&) { + } + return -1; +} +static gboolean +hyper_link_is_valid( AtkHyperlink *pLink ) +{ + try { + return getHyperlink( pLink )->isValid(); + } + catch(const uno::Exception&) { + } + return FALSE; +} +static gint +hyper_link_get_n_anchors( AtkHyperlink *pLink ) +{ + try { + return getHyperlink( pLink )->getAccessibleActionCount(); + } + catch(const uno::Exception&) { + } + return 0; +} + +static guint +hyper_link_link_state( AtkHyperlink * ) +{ + g_warning( "FIXME: hyper_link_link_state unimplemented" ); + return 0; +} +static gboolean +hyper_link_is_selected_link( AtkHyperlink * ) +{ + g_warning( "FIXME: hyper_link_is_selected_link unimplemented" ); + return FALSE; +} + +static void +hyper_link_class_init (AtkHyperlinkClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = hyper_link_finalize; + + hyper_parent_class = static_cast<GObjectClass *>(g_type_class_peek_parent (klass)); + + klass->get_uri = hyper_link_get_uri; + klass->get_object = hyper_link_get_object; + klass->get_end_index = hyper_link_get_end_index; + klass->get_start_index = hyper_link_get_start_index; + klass->is_valid = hyper_link_is_valid; + klass->get_n_anchors = hyper_link_get_n_anchors; + klass->link_state = hyper_link_link_state; + klass->is_selected_link = hyper_link_is_selected_link; +} + +static GType +hyper_link_get_type() +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo tinfo = { + sizeof (AtkHyperlinkClass), + nullptr, /* base init */ + nullptr, /* base finalize */ + reinterpret_cast<GClassInitFunc>(hyper_link_class_init), + nullptr, /* class finalize */ + nullptr, /* class data */ + sizeof (HyperLink), /* instance size */ + 0, /* nb preallocs */ + nullptr, /* instance init */ + nullptr /* value table */ + }; + + static const GInterfaceInfo atk_action_info = { + reinterpret_cast<GInterfaceInitFunc>(actionIfaceInit), + nullptr, + nullptr + }; + + type = g_type_register_static (ATK_TYPE_HYPERLINK, + "OOoAtkObjHyperLink", &tinfo, + GTypeFlags(0)); + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); + } + + return type; +} + +// ---------------------- AtkHyperText ---------------------- + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleHypertext> + getHypertext( AtkHypertext *pHypertext ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pHypertext ); + if( pWrap ) + { + if( !pWrap->mpHypertext.is() ) + { + pWrap->mpHypertext.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpHypertext; + } + + return css::uno::Reference<css::accessibility::XAccessibleHypertext>(); +} + +static AtkHyperlink * +hypertext_get_link( AtkHypertext *hypertext, + gint link_index) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleHypertext> pHypertext + = getHypertext( hypertext ); + if( pHypertext.is() ) + { + HyperLink *pLink = static_cast<HyperLink *>(g_object_new( hyper_link_get_type(), nullptr )); + pLink->xLink = pHypertext->getHyperLink( link_index ); + if( !pLink->xLink.is() ) { + g_object_unref( G_OBJECT( pLink ) ); + pLink = nullptr; + } + return ATK_HYPERLINK( pLink ); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getHyperLink()" ); + } + + return nullptr; +} + +static gint +hypertext_get_n_links( AtkHypertext *hypertext ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleHypertext> pHypertext + = getHypertext( hypertext ); + if( pHypertext.is() ) + return pHypertext->getHyperLinkCount(); + } + catch(const uno::Exception&) { + g_warning( "Exception in getHyperLinkCount()" ); + } + + return 0; +} + +static gint +hypertext_get_link_index( AtkHypertext *hypertext, + gint index) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleHypertext> pHypertext + = getHypertext( hypertext ); + if( pHypertext.is() ) + return pHypertext->getHyperLinkIndex( index ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getHyperLinkIndex()" ); + } + + return 0; +} + +} // extern "C" + +void +hypertextIfaceInit (AtkHypertextIface *iface) +{ + g_return_if_fail (iface != nullptr); + + iface->get_link = hypertext_get_link; + iface->get_n_links = hypertext_get_n_links; + iface->get_link_index = hypertext_get_link_index; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkimage.cxx b/vcl/unx/gtk3/a11y/gtk3atkimage.cxx index 4e2c77e9f2b1..1c9bc2c640f9 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkimage.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkimage.cxx @@ -5,8 +5,128 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkimage.cxx" +#include "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleImage.hpp> + +using namespace ::com::sun::star; + +// FIXME +static G_CONST_RETURN gchar * +getAsConst( const OUString& rString ) +{ + static const int nMax = 10; + static OString aUgly[nMax]; + static int nIdx = 0; + nIdx = (nIdx + 1) % nMax; + aUgly[nIdx] = OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ); + return aUgly[ nIdx ].getStr(); +} + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleImage> + getImage( AtkImage *pImage ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pImage ); + if( pWrap ) + { + if( !pWrap->mpImage.is() ) + { + pWrap->mpImage.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpImage; + } + + return css::uno::Reference<css::accessibility::XAccessibleImage>(); +} + +extern "C" { + +static G_CONST_RETURN gchar * +image_get_image_description( AtkImage *image ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleImage> pImage + = getImage( image ); + if( pImage.is() ) + return getAsConst( pImage->getAccessibleImageDescription() ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleImageDescription()" ); + } + + return nullptr; +} + +static void +image_get_image_position( AtkImage *image, + gint *x, + gint *y, + AtkCoordType coord_type ) +{ + *x = *y = 0; + if( ATK_IS_COMPONENT( image ) ) + { + SAL_WNODEPRECATED_DECLARATIONS_PUSH + atk_component_get_position( ATK_COMPONENT( image ), x, y, coord_type ); + SAL_WNODEPRECATED_DECLARATIONS_POP + } + else + g_warning( "FIXME: no image position information" ); +} + +static void +image_get_image_size( AtkImage *image, + gint *width, + gint *height ) +{ + *width = 0; + *height = 0; + try { + css::uno::Reference<css::accessibility::XAccessibleImage> pImage + = getImage( image ); + if( pImage.is() ) + { + *width = pImage->getAccessibleImageWidth(); + *height = pImage->getAccessibleImageHeight(); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleImageHeight() or Width" ); + } +} + +static gboolean +image_set_image_description( AtkImage *, const gchar * ) +{ + g_warning ("FIXME: no set image description"); + return FALSE; +} + +} // extern "C" + +void +imageIfaceInit (AtkImageIface *iface) +{ + g_return_if_fail (iface != nullptr); + + iface->set_image_description = image_set_image_description; + iface->get_image_description = image_get_image_description; + iface->get_image_position = image_get_image_position; + iface->get_image_size = image_get_image_size; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atklistener.cxx b/vcl/unx/gtk3/a11y/gtk3atklistener.cxx index eca1cd7ec1d0..c30bc638bced 100644 --- a/vcl/unx/gtk3/a11y/gtk3atklistener.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atklistener.cxx @@ -5,8 +5,735 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atklistener.cxx" +#ifdef AIX +#define _LINUX_SOURCE_COMPAT +#include <sys/timer.h> +#undef _LINUX_SOURCE_COMPAT +#endif + +#include <com/sun/star/accessibility/TextSegment.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +#include "atklistener.hxx" +#include "atkwrapper.hxx" +#include <vcl/svapp.hxx> + +#include <rtl/ref.hxx> +#include <sal/log.hxx> + +#define DEBUG_ATK_LISTENER 0 + +#if DEBUG_ATK_LISTENER +#include <iostream> +#include <sstream> +#endif + +using namespace com::sun::star; + +AtkListener::AtkListener( AtkObjectWrapper* pWrapper ) : mpWrapper( pWrapper ) +{ + if( mpWrapper ) + { + g_object_ref( mpWrapper ); + updateChildList( mpWrapper->mpContext ); + } +} + +AtkListener::~AtkListener() +{ + if( mpWrapper ) + g_object_unref( mpWrapper ); +} + +/*****************************************************************************/ + +static AtkStateType mapState( const uno::Any &rAny ) +{ + sal_Int16 nState = accessibility::AccessibleStateType::INVALID; + rAny >>= nState; + return mapAtkState( nState ); +} + +/*****************************************************************************/ + +extern "C" { + // rhbz#1001768 - down to horrific problems releasing the solar mutex + // while destroying a Window - which occurs inside these notifications. + static gboolean + idle_defunc_state_change( AtkObject *atk_obj ) + { + SolarMutexGuard aGuard; + + // This is an equivalent to a state change to DEFUNC(T). + atk_object_notify_state_change( atk_obj, ATK_STATE_DEFUNCT, TRUE ); + if( atk_get_focus_object() == atk_obj ) + { + SAL_WNODEPRECATED_DECLARATIONS_PUSH + atk_focus_tracker_notify( nullptr ); + SAL_WNODEPRECATED_DECLARATIONS_POP + } + g_object_unref( G_OBJECT( atk_obj ) ); + return false; + } +} + +// XEventListener implementation +void AtkListener::disposing( const lang::EventObject& ) +{ + if( mpWrapper ) + { + AtkObject *atk_obj = ATK_OBJECT( mpWrapper ); + + // Release all interface references to avoid shutdown problems with + // global mutex + atk_object_wrapper_dispose( mpWrapper ); + + g_idle_add( reinterpret_cast<GSourceFunc>(idle_defunc_state_change), + g_object_ref( G_OBJECT( atk_obj ) ) ); + + // Release the wrapper object so that it can vanish .. + g_object_unref( mpWrapper ); + mpWrapper = nullptr; + } +} + +/*****************************************************************************/ + +static AtkObject *getObjFromAny( const uno::Any &rAny ) +{ + uno::Reference< accessibility::XAccessible > xAccessible; + rAny >>= xAccessible; + return xAccessible.is() ? atk_object_wrapper_ref( xAccessible ) : nullptr; +} + +/*****************************************************************************/ + +// Updates the child list held to provide the old IndexInParent on children_changed::remove +void AtkListener::updateChildList( + css::uno::Reference<css::accessibility::XAccessibleContext> const & + pContext) +{ + m_aChildList.clear(); + + uno::Reference< accessibility::XAccessibleStateSet > xStateSet = pContext->getAccessibleStateSet(); + if( xStateSet.is() + && !xStateSet->contains(accessibility::AccessibleStateType::DEFUNC) + && !xStateSet->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS) ) + { + sal_Int32 nChildren = pContext->getAccessibleChildCount(); + m_aChildList.resize(nChildren); + for(sal_Int32 n = 0; n < nChildren; n++) + { + try + { + m_aChildList[n] = pContext->getAccessibleChild(n); + } + catch (lang::IndexOutOfBoundsException const&) + { + sal_Int32 nChildren2 = pContext->getAccessibleChildCount(); + assert(nChildren2 <= n && "consistency?"); + m_aChildList.resize(std::min(nChildren2, n)); + break; + } + } + } +} + +/*****************************************************************************/ + +void AtkListener::handleChildAdded( + const uno::Reference< accessibility::XAccessibleContext >& rxParent, + const uno::Reference< accessibility::XAccessible>& rxAccessible) +{ + AtkObject * pChild = rxAccessible.is() ? atk_object_wrapper_ref( rxAccessible ) : nullptr; + + if( pChild ) + { + updateChildList(rxParent); + + atk_object_wrapper_add_child( mpWrapper, pChild, + atk_object_get_index_in_parent( pChild )); + + g_object_unref( pChild ); + } +} + +/*****************************************************************************/ + +void AtkListener::handleChildRemoved( + const uno::Reference< accessibility::XAccessibleContext >& rxParent, + const uno::Reference< accessibility::XAccessible>& rxChild) +{ + sal_Int32 nIndex = -1; + + // Locate the child in the children list + size_t n, nmax = m_aChildList.size(); + for( n = 0; n < nmax; ++n ) + { + if( rxChild == m_aChildList[n] ) + { + nIndex = n; + break; + } + } + + // FIXME: two problems here: + // a) we get child-removed events for objects that are no real children + // in the accessibility hierarchy or have been removed before due to + // some child removing batch. + // b) spi_atk_bridge_signal_listener ignores the given parameters + // for children_changed events and always asks the parent for the + // 0. child, which breaks somehow on vanishing list boxes. + // Ignoring "remove" events for objects not in the m_aChildList + // for now. + if( nIndex >= 0 ) + { + uno::Reference<accessibility::XAccessibleEventBroadcaster> xBroadcaster( + rxChild->getAccessibleContext(), uno::UNO_QUERY); + + if (xBroadcaster.is()) + { + uno::Reference<accessibility::XAccessibleEventListener> xListener(this); + xBroadcaster->removeAccessibleEventListener(xListener); + } + + updateChildList(rxParent); + + AtkObject * pChild = atk_object_wrapper_ref( rxChild, false ); + if( pChild ) + { + atk_object_wrapper_remove_child( mpWrapper, pChild, nIndex ); + g_object_unref( pChild ); + } + } +} + +/*****************************************************************************/ + +void AtkListener::handleInvalidateChildren( + const uno::Reference< accessibility::XAccessibleContext >& rxParent) +{ + // Send notifications for all previous children + size_t n = m_aChildList.size(); + while( n-- > 0 ) + { + if( m_aChildList[n].is() ) + { + AtkObject * pChild = atk_object_wrapper_ref( m_aChildList[n], false ); + if( pChild ) + { + atk_object_wrapper_remove_child( mpWrapper, pChild, n ); + g_object_unref( pChild ); + } + } + } + + updateChildList(rxParent); + + // Send notifications for all new children + size_t nmax = m_aChildList.size(); + for( n = 0; n < nmax; ++n ) + { + if( m_aChildList[n].is() ) + { + AtkObject * pChild = atk_object_wrapper_ref( m_aChildList[n] ); + + if( pChild ) + { + atk_object_wrapper_add_child( mpWrapper, pChild, n ); + g_object_unref( pChild ); + } + } + } +} + +/*****************************************************************************/ + +static uno::Reference< accessibility::XAccessibleContext > +getAccessibleContextFromSource( const uno::Reference< uno::XInterface >& rxSource ) +{ + uno::Reference< accessibility::XAccessibleContext > xContext(rxSource, uno::UNO_QUERY); + if( ! xContext.is() ) + { + g_warning( "ERROR: Event source does not implement XAccessibleContext" ); + + // Second try - query for XAccessible, which should give us access to + // XAccessibleContext. + uno::Reference< accessibility::XAccessible > xAccessible(rxSource, uno::UNO_QUERY); + if( xAccessible.is() ) + xContext = xAccessible->getAccessibleContext(); + } + + return xContext; +} + +#if DEBUG_ATK_LISTENER + +namespace { + +void printNotifyEvent( const accessibility::AccessibleEventObject& rEvent ) +{ + static std::vector<const char*> aLabels = { + 0, + "NAME_CHANGED", // 01 + "DESCRIPTION_CHANGED", // 02 + "ACTION_CHANGED", // 03 + "STATE_CHANGED", // 04 + "ACTIVE_DESCENDANT_CHANGED", // 05 + "BOUNDRECT_CHANGED", // 06 + "CHILD", // 07 + "INVALIDATE_ALL_CHILDREN", // 08 + "SELECTION_CHANGED", // 09 + "VISIBLE_DATA_CHANGED", // 10 + "VALUE_CHANGED", // 11 + "CONTENT_FLOWS_FROM_RELATION_CHANGED", // 12 + "CONTENT_FLOWS_TO_RELATION_CHANGED", // 13 + "CONTROLLED_BY_RELATION_CHANGED", // 14 + "CONTROLLER_FOR_RELATION_CHANGED", // 15 + "LABEL_FOR_RELATION_CHANGED", // 16 + "LABELED_BY_RELATION_CHANGED", // 17 + "MEMBER_OF_RELATION_CHANGED", // 18 + "SUB_WINDOW_OF_RELATION_CHANGED", // 19 + "CARET_CHANGED", // 20 + "TEXT_SELECTION_CHANGED", // 21 + "TEXT_CHANGED", // 22 + "TEXT_ATTRIBUTE_CHANGED", // 23 + "HYPERTEXT_CHANGED", // 24 + "TABLE_CAPTION_CHANGED", // 25 + "TABLE_COLUMN_DESCRIPTION_CHANGED", // 26 + "TABLE_COLUMN_HEADER_CHANGED", // 27 + "TABLE_MODEL_CHANGED", // 28 + "TABLE_ROW_DESCRIPTION_CHANGED", // 29 + "TABLE_ROW_HEADER_CHANGED", // 30 + "TABLE_SUMMARY_CHANGED", // 31 + "LISTBOX_ENTRY_EXPANDED", // 32 + "LISTBOX_ENTRY_COLLAPSED", // 33 + "ACTIVE_DESCENDANT_CHANGED_NOFOCUS", // 34 + "SELECTION_CHANGED_ADD", // 35 + "SELECTION_CHANGED_REMOVE", // 36 + "SELECTION_CHANGED_WITHIN", // 37 + "PAGE_CHANGED", // 38 + "SECTION_CHANGED", // 39 + "COLUMN_CHANGED", // 40 + "ROLE_CHANGED", // 41 + }; + + static std::vector<const char*> aStates = { + "INVALID", // 00 + "ACTIVE", // 01 + "ARMED", // 02 + "BUSY", // 03 + "CHECKED", // 04 + "DEFUNC", // 05 + "EDITABLE", // 06 + "ENABLED", // 07 + "EXPANDABLE", // 08 + "EXPANDED", // 09 + "FOCUSABLE", // 10 + "FOCUSED", // 11 + "HORIZONTAL", // 12 + "ICONIFIED", // 13 + "INDETERMINATE", // 14 + "MANAGES_DESCENDANTS", // 15 + "MODAL", // 16 + "MULTI_LINE", // 17 + "MULTI_SELECTABLE", // 18 + "OPAQUE", // 19 + "PRESSED", // 20 + "RESIZABLE", // 21 + "SELECTABLE", // 22 + "SELECTED", // 23 + "SENSITIVE", // 24 + "SHOWING", // 25 + "SINGLE_LINE", // 26 + "STALE", // 27 + "TRANSIENT", // 28 + "VERTICAL", // 29 + "VISIBLE", // 30 + "MOVEABLE", // 31 + "DEFAULT", // 32 + "OFFSCREEN", // 33 + "COLLAPSE", // 34 + }; + + auto getOrUnknown = [](const std::vector<const char*>& rCont, size_t nIndex) -> std::string + { + return (nIndex < rCont.size()) ? rCont[nIndex] : "<unknown>"; + }; + + std::ostringstream os; + os << "--" << std::endl; + os << "* event = " << getOrUnknown(aLabels, rEvent.EventId) << std::endl; + + switch (rEvent.EventId) + { + case accessibility::AccessibleEventId::STATE_CHANGED: + { + sal_Int16 nState; + if (rEvent.OldValue >>= nState) + os << " * old state = " << getOrUnknown(aStates, nState); + if (rEvent.NewValue >>= nState) + os << " * new state = " << getOrUnknown(aStates, nState); + + os << std::endl; + break; + } + default: + ; + } + + std::cout << os.str(); +} + +} + +#endif + +void AtkListener::notifyEvent( const accessibility::AccessibleEventObject& aEvent ) +{ + if( !mpWrapper ) + return; + + AtkObject *atk_obj = ATK_OBJECT( mpWrapper ); + + switch( aEvent.EventId ) + { + // AtkObject signals: + // Hierarchy signals + case accessibility::AccessibleEventId::CHILD: + { + uno::Reference< accessibility::XAccessibleContext > xParent; + uno::Reference< accessibility::XAccessible > xChild; + + xParent = getAccessibleContextFromSource(aEvent.Source); + g_return_if_fail( xParent.is() ); + + if( aEvent.OldValue >>= xChild ) + handleChildRemoved(xParent, xChild); + + if( aEvent.NewValue >>= xChild ) + handleChildAdded(xParent, xChild); + break; + } + + case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN: + { + uno::Reference< accessibility::XAccessibleContext > xParent = getAccessibleContextFromSource(aEvent.Source); + g_return_if_fail( xParent.is() ); + + handleInvalidateChildren(xParent); + break; + } + + case accessibility::AccessibleEventId::NAME_CHANGED: + { + OUString aName; + if( aEvent.NewValue >>= aName ) + { + atk_object_set_name(atk_obj, + OUStringToOString(aName, RTL_TEXTENCODING_UTF8).getStr()); + } + break; + } + + case accessibility::AccessibleEventId::DESCRIPTION_CHANGED: + { + OUString aDescription; + if( aEvent.NewValue >>= aDescription ) + { + atk_object_set_description(atk_obj, + OUStringToOString(aDescription, RTL_TEXTENCODING_UTF8).getStr()); + } + break; + } + + case accessibility::AccessibleEventId::STATE_CHANGED: + { + AtkStateType eOldState = mapState( aEvent.OldValue ); + AtkStateType eNewState = mapState( aEvent.NewValue ); + + gboolean bState = eNewState != ATK_STATE_INVALID; + AtkStateType eRealState = bState ? eNewState : eOldState; + + atk_object_notify_state_change( atk_obj, eRealState, bState ); + break; + } + + case accessibility::AccessibleEventId::BOUNDRECT_CHANGED: + +#ifdef HAS_ATKRECTANGLE + if( ATK_IS_COMPONENT( atk_obj ) ) + { + AtkRectangle rect; + + atk_component_get_extents( ATK_COMPONENT( atk_obj ), + &rect.x, + &rect.y, + &rect.width, + &rect.height, + ATK_XY_SCREEN ); + + g_signal_emit_by_name( atk_obj, "bounds_changed", &rect ); + } + else + g_warning( "bounds_changed event for object not implementing AtkComponent\n"); +#endif + + break; + + case accessibility::AccessibleEventId::VISIBLE_DATA_CHANGED: + g_signal_emit_by_name( atk_obj, "visible-data-changed" ); + break; + + case accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED: + { + AtkObject *pChild = getObjFromAny( aEvent.NewValue ); + if( pChild ) + { + g_signal_emit_by_name( atk_obj, "active-descendant-changed", pChild ); + g_object_unref( pChild ); + } + break; + } + + //ACTIVE_DESCENDANT_CHANGED_NOFOCUS (sic) appears to have been added + //as a workaround or an aid for the ia2 winaccessibility implementation + //so ignore it silently without warning here + case accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS: + break; + + // #i92103# + case accessibility::AccessibleEventId::LISTBOX_ENTRY_EXPANDED: + { + AtkObject *pChild = getObjFromAny( aEvent.NewValue ); + if( pChild ) + { + atk_object_notify_state_change( pChild, ATK_STATE_EXPANDED, true ); + g_object_unref( pChild ); + } + break; + } + + case accessibility::AccessibleEventId::LISTBOX_ENTRY_COLLAPSED: + { + AtkObject *pChild = getObjFromAny( aEvent.NewValue ); + if( pChild ) + { + atk_object_notify_state_change( pChild, ATK_STATE_EXPANDED, false ); + g_object_unref( pChild ); + } + break; + } + + // AtkAction signals ... + case accessibility::AccessibleEventId::ACTION_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-actions"); + break; + + // AtkText + case accessibility::AccessibleEventId::CARET_CHANGED: + { + sal_Int32 nPos=0; + aEvent.NewValue >>= nPos; + g_signal_emit_by_name( atk_obj, "text_caret_moved", nPos ); + break; + } + case accessibility::AccessibleEventId::TEXT_CHANGED: + { + // TESTME: and remove this comment: + // cf. comphelper/source/misc/accessibletexthelper.cxx (implInitTextChangedEvent) + accessibility::TextSegment aDeletedText; + accessibility::TextSegment aInsertedText; + + // TODO: when GNOME starts to send "update" kind of events, change + // we need to re-think this implementation as well + if( aEvent.OldValue >>= aDeletedText ) + { + /* Remember the text segment here to be able to return removed text in get_text(). + * This is clearly a hack to be used until appropriate API exists in atk to pass + * the string value directly or we find a compelling reason to start caching the + * UTF-8 converted strings in the atk wrapper object. + */ + + g_object_set_data( G_OBJECT(atk_obj), "ooo::text_changed::delete", &aDeletedText); + + g_signal_emit_by_name( atk_obj, "text_changed::delete", + static_cast<gint>(aDeletedText.SegmentStart), + static_cast<gint>( aDeletedText.SegmentEnd - aDeletedText.SegmentStart ) ); + + g_object_steal_data( G_OBJECT(atk_obj), "ooo::text_changed::delete" ); + } + + if( aEvent.NewValue >>= aInsertedText ) + g_signal_emit_by_name( atk_obj, "text_changed::insert", + static_cast<gint>(aInsertedText.SegmentStart), + static_cast<gint>( aInsertedText.SegmentEnd - aInsertedText.SegmentStart ) ); + break; + } + + case accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED: + { + g_signal_emit_by_name( atk_obj, "text-selection-changed" ); + break; + } + + case accessibility::AccessibleEventId::TEXT_ATTRIBUTE_CHANGED: + g_signal_emit_by_name( atk_obj, "text-attributes-changed" ); + break; + + // AtkValue + case accessibility::AccessibleEventId::VALUE_CHANGED: + g_object_notify( G_OBJECT( atk_obj ), "accessible-value" ); + break; + + case accessibility::AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED: + case accessibility::AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED: + case accessibility::AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED: + case accessibility::AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED: + case accessibility::AccessibleEventId::LABEL_FOR_RELATION_CHANGED: + case accessibility::AccessibleEventId::LABELED_BY_RELATION_CHANGED: + case accessibility::AccessibleEventId::MEMBER_OF_RELATION_CHANGED: + case accessibility::AccessibleEventId::SUB_WINDOW_OF_RELATION_CHANGED: + // FIXME: ask Bill how Atk copes with this little lot ... + break; + + // AtkTable + case accessibility::AccessibleEventId::TABLE_MODEL_CHANGED: + { + accessibility::AccessibleTableModelChange aChange; + aEvent.NewValue >>= aChange; + + sal_Int32 nRowsChanged = aChange.LastRow - aChange.FirstRow + 1; + sal_Int32 nColumnsChanged = aChange.LastColumn - aChange.FirstColumn + 1; + + static const struct { + const char *row; + const char *col; + } aSignalNames[] = + { + { nullptr, nullptr }, // dummy + { "row_inserted", "column_inserted" }, // INSERT = 1 + { "row_deleted", "column_deleted" } // DELETE = 2 + }; + switch( aChange.Type ) + { + case accessibility::AccessibleTableModelChangeType::INSERT: + case accessibility::AccessibleTableModelChangeType::DELETE: + if( nRowsChanged > 0 ) + g_signal_emit_by_name( G_OBJECT( atk_obj ), + aSignalNames[aChange.Type].row, + aChange.FirstRow, nRowsChanged ); + if( nColumnsChanged > 0 ) + g_signal_emit_by_name( G_OBJECT( atk_obj ), + aSignalNames[aChange.Type].col, + aChange.FirstColumn, nColumnsChanged ); + break; + + case accessibility::AccessibleTableModelChangeType::UPDATE: + // This is not really a model change, is it ? + break; + default: + g_warning( "TESTME: unusual table model change %d\n", aChange.Type ); + break; + } + g_signal_emit_by_name( G_OBJECT( atk_obj ), "model-changed" ); + break; + } + + case accessibility::AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED: + { + accessibility::AccessibleTableModelChange aChange; + aEvent.NewValue >>= aChange; + + AtkPropertyValues values; + memset(&values, 0, sizeof(AtkPropertyValues)); + g_value_init (&values.new_value, G_TYPE_INT); + values.property_name = "accessible-table-column-header"; + + for (sal_Int32 nChangedColumn = aChange.FirstColumn; nChangedColumn <= aChange.LastColumn; ++nChangedColumn) + { + g_value_set_int (&values.new_value, nChangedColumn); + g_signal_emit_by_name(G_OBJECT(atk_obj), "property_change::accessible-table-column-header", &values, nullptr); + } + break; + } + + case accessibility::AccessibleEventId::TABLE_CAPTION_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-caption"); + break; + + case accessibility::AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-column-description"); + break; + + case accessibility::AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-row-description"); + break; + + case accessibility::AccessibleEventId::TABLE_ROW_HEADER_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-row-header"); + break; + + case accessibility::AccessibleEventId::TABLE_SUMMARY_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-summary"); + break; + + case accessibility::AccessibleEventId::SELECTION_CHANGED: + case accessibility::AccessibleEventId::SELECTION_CHANGED_ADD: + case accessibility::AccessibleEventId::SELECTION_CHANGED_REMOVE: + case accessibility::AccessibleEventId::SELECTION_CHANGED_WITHIN: + if (ATK_IS_SELECTION(atk_obj)) + g_signal_emit_by_name(G_OBJECT(atk_obj), "selection_changed"); + else + { + // e.g. tdf#122353, when such dialogs become native the problem will go away anyway + SAL_INFO("vcl.gtk", "selection change from obj which doesn't support XAccessibleSelection"); + } + break; + + case accessibility::AccessibleEventId::HYPERTEXT_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-hypertext-offset"); + break; + + case accessibility::AccessibleEventId::ROLE_CHANGED: + { + uno::Reference< accessibility::XAccessibleContext > xContext = getAccessibleContextFromSource( aEvent.Source ); + atk_object_wrapper_set_role( mpWrapper, xContext->getAccessibleRole() ); + break; + } + + case accessibility::AccessibleEventId::PAGE_CHANGED: + { + /* // If we implemented AtkDocument then I imagine this is what this + // handler should look like + sal_Int32 nPos=0; + aEvent.NewValue >>= nPos; + g_signal_emit_by_name( G_OBJECT( atk_obj ), "page_changed", nPos ); + */ + break; + } + + default: + SAL_WARN("vcl.gtk", "Unknown event notification: " << aEvent.EventId); + break; + } +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkregistry.cxx b/vcl/unx/gtk3/a11y/gtk3atkregistry.cxx index 126e97a808ae..ff96378c41dc 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkregistry.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkregistry.cxx @@ -5,8 +5,62 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkregistry.cxx" +#include "atkregistry.hxx" + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + +static GHashTable *uno_to_gobject = nullptr; + +/*****************************************************************************/ + +AtkObject * +ooo_wrapper_registry_get(const Reference< XAccessible >& rxAccessible) +{ + if( uno_to_gobject ) + { + gpointer cached = + g_hash_table_lookup(uno_to_gobject, static_cast<gpointer>(rxAccessible.get())); + + if( cached ) + return ATK_OBJECT( cached ); + } + + return nullptr; +} + +/*****************************************************************************/ + +void +ooo_wrapper_registry_add(const Reference< XAccessible >& rxAccessible, AtkObject *obj) +{ + if( !uno_to_gobject ) + uno_to_gobject = g_hash_table_new (nullptr, nullptr); + + g_hash_table_insert( uno_to_gobject, static_cast<gpointer>(rxAccessible.get()), obj ); +} + +/*****************************************************************************/ + +void +ooo_wrapper_registry_remove( + css::uno::Reference<css::accessibility::XAccessible> const & pAccessible) +{ + if( uno_to_gobject ) + g_hash_table_remove( + uno_to_gobject, static_cast<gpointer>(pAccessible.get()) ); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkselection.cxx b/vcl/unx/gtk3/a11y/gtk3atkselection.cxx index f67b665304d7..1d9772bc1587 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkselection.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkselection.cxx @@ -5,8 +5,186 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkselection.cxx" +#include "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> + +using namespace ::com::sun::star; + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleSelection> + getSelection( AtkSelection *pSelection ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pSelection ); + if( pWrap ) + { + if( !pWrap->mpSelection.is() ) + { + pWrap->mpSelection.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpSelection; + } + + return css::uno::Reference<css::accessibility::XAccessibleSelection>(); +} + +extern "C" { + +static gboolean +selection_add_selection( AtkSelection *selection, + gint i ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection + = getSelection( selection ); + if( pSelection.is() ) + { + pSelection->selectAccessibleChild( i ); + return TRUE; + } + } + catch(const uno::Exception&) { + g_warning( "Exception in selectAccessibleChild()" ); + } + + return FALSE; +} + +static gboolean +selection_clear_selection( AtkSelection *selection ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection + = getSelection( selection ); + if( pSelection.is() ) + { + pSelection->clearAccessibleSelection(); + return TRUE; + } + } + catch(const uno::Exception&) { + g_warning( "Exception in selectAccessibleChild()" ); + } + + return FALSE; +} + +static AtkObject* +selection_ref_selection( AtkSelection *selection, + gint i ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection + = getSelection( selection ); + if( pSelection.is() ) + return atk_object_wrapper_ref( pSelection->getSelectedAccessibleChild( i ) ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getSelectedAccessibleChild()" ); + } + + return nullptr; +} + +static gint +selection_get_selection_count( AtkSelection *selection) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection + = getSelection( selection ); + if( pSelection.is() ) + return pSelection->getSelectedAccessibleChildCount(); + } + catch(const uno::Exception&) { + g_warning( "Exception in getSelectedAccessibleChildCount()" ); + } + + return -1; +} + +static gboolean +selection_is_child_selected( AtkSelection *selection, + gint i) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection + = getSelection( selection ); + if( pSelection.is() ) + return pSelection->isAccessibleChildSelected( i ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getSelectedAccessibleChildCount()" ); + } + + return FALSE; +} + +static gboolean +selection_remove_selection( AtkSelection *selection, + gint i ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection + = getSelection( selection ); + if( pSelection.is() ) + { + pSelection->deselectAccessibleChild( i ); + return TRUE; + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getSelectedAccessibleChildCount()" ); + } + + return FALSE; +} + +static gboolean +selection_select_all_selection( AtkSelection *selection) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleSelection> pSelection + = getSelection( selection ); + if( pSelection.is() ) + { + pSelection->selectAllAccessibleChildren(); + return TRUE; + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getSelectedAccessibleChildCount()" ); + } + + return FALSE; +} + +} // extern "C" + +void +selectionIfaceInit( AtkSelectionIface *iface) +{ + g_return_if_fail (iface != nullptr); + + iface->add_selection = selection_add_selection; + iface->clear_selection = selection_clear_selection; + iface->ref_selection = selection_ref_selection; + iface->get_selection_count = selection_get_selection_count; + iface->is_child_selected = selection_is_child_selected; + iface->remove_selection = selection_remove_selection; + iface->select_all_selection = selection_select_all_selection; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atktable.cxx b/vcl/unx/gtk3/a11y/gtk3atktable.cxx index d886ac07296a..29ffa48d5b9d 100644 --- a/vcl/unx/gtk3/a11y/gtk3atktable.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atktable.cxx @@ -5,8 +5,576 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atktable.cxx" +#include "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleTable.hpp> +#include <comphelper/sequence.hxx> + +using namespace ::com::sun::star; + +static AtkObject * +atk_object_wrapper_conditional_ref( const uno::Reference< accessibility::XAccessible >& rxAccessible ) +{ + if( rxAccessible.is() ) + return atk_object_wrapper_ref( rxAccessible ); + + return nullptr; +} + +/*****************************************************************************/ + +// FIXME +static G_CONST_RETURN gchar * +getAsConst( const OUString& rString ) +{ + static const int nMax = 10; + static OString aUgly[nMax]; + static int nIdx = 0; + nIdx = (nIdx + 1) % nMax; + aUgly[nIdx] = OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ); + return aUgly[ nIdx ].getStr(); +} + +/*****************************************************************************/ + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleTable> + getTable( AtkTable *pTable ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pTable ); + if( pWrap ) + { + if( !pWrap->mpTable.is() ) + { + pWrap->mpTable.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpTable; + } + + return css::uno::Reference<css::accessibility::XAccessibleTable>(); +} + +/*****************************************************************************/ + +extern "C" { + +static AtkObject* +table_wrapper_ref_at (AtkTable *table, + gint row, + gint column) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable = getTable( table ); + if( pTable.is() ) + return atk_object_wrapper_conditional_ref( pTable->getAccessibleCellAt( row, column ) ); + } + + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleCellAt()" ); + } + + return nullptr; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_index_at (AtkTable *table, + gint row, + gint column) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return pTable->getAccessibleIndex( row, column ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleIndex()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_column_at_index (AtkTable *table, + gint nIndex) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return pTable->getAccessibleColumn( nIndex ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleColumn()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_row_at_index( AtkTable *table, + gint nIndex ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return pTable->getAccessibleRow( nIndex ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleRow()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_n_columns( AtkTable *table ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return pTable->getAccessibleColumnCount(); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleColumnCount()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_n_rows( AtkTable *table ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return pTable->getAccessibleRowCount(); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleRowCount()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_column_extent_at( AtkTable *table, + gint row, + gint column ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return pTable->getAccessibleColumnExtentAt( row, column ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleColumnExtentAt()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_row_extent_at( AtkTable *table, + gint row, + gint column ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return pTable->getAccessibleRowExtentAt( row, column ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleRowExtentAt()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static AtkObject * +table_wrapper_get_caption( AtkTable *table ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return atk_object_wrapper_conditional_ref( pTable->getAccessibleCaption() ); + } + + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleCaption()" ); + } + + return nullptr; +} + +/*****************************************************************************/ + +static G_CONST_RETURN gchar * +table_wrapper_get_row_description( AtkTable *table, + gint row ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return getAsConst( pTable->getAccessibleRowDescription( row ) ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleRowDescription()" ); + } + + return nullptr; +} + +/*****************************************************************************/ + +static G_CONST_RETURN gchar * +table_wrapper_get_column_description( AtkTable *table, + gint column ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return getAsConst( pTable->getAccessibleColumnDescription( column ) ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleColumnDescription()" ); + } + + return nullptr; +} + +/*****************************************************************************/ + +static AtkObject * +table_wrapper_get_row_header( AtkTable *table, + gint row ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + { + uno::Reference< accessibility::XAccessibleTable > xRowHeaders( pTable->getAccessibleRowHeaders() ); + if( xRowHeaders.is() ) + return atk_object_wrapper_conditional_ref( xRowHeaders->getAccessibleCellAt( row, 0 ) ); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleRowHeaders()" ); + } + + return nullptr; +} + +/*****************************************************************************/ + +static AtkObject * +table_wrapper_get_column_header( AtkTable *table, + gint column ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + { + uno::Reference< accessibility::XAccessibleTable > xColumnHeaders( pTable->getAccessibleColumnHeaders() ); + if( xColumnHeaders.is() ) + return atk_object_wrapper_conditional_ref( xColumnHeaders->getAccessibleCellAt( 0, column ) ); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleColumnHeaders()" ); + } + + return nullptr; +} + +/*****************************************************************************/ + +static AtkObject * +table_wrapper_get_summary( AtkTable *table ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + { + return atk_object_wrapper_conditional_ref( pTable->getAccessibleSummary() ); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleSummary()" ); + } + + return nullptr; +} + +/*****************************************************************************/ + +static gint +convertToGIntArray( const uno::Sequence< ::sal_Int32 >& aSequence, gint **pSelected ) +{ + if( aSequence.hasElements() ) + { + *pSelected = g_new( gint, aSequence.getLength() ); + + *pSelected = comphelper::sequenceToArray(*pSelected, aSequence); + } + + return aSequence.getLength(); +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_selected_columns( AtkTable *table, + gint **pSelected ) +{ + *pSelected = nullptr; + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return convertToGIntArray( pTable->getSelectedAccessibleColumns(), pSelected ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getSelectedAccessibleColumns()" ); + } + + return 0; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_selected_rows( AtkTable *table, + gint **pSelected ) +{ + *pSelected = nullptr; + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return convertToGIntArray( pTable->getSelectedAccessibleRows(), pSelected ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getSelectedAccessibleRows()" ); + } + + return 0; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_is_column_selected( AtkTable *table, + gint column ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return pTable->isAccessibleColumnSelected( column ); + } + catch(const uno::Exception&) { + g_warning( "Exception in isAccessibleColumnSelected()" ); + } + + return 0; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_is_row_selected( AtkTable *table, + gint row ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return pTable->isAccessibleRowSelected( row ); + } + catch(const uno::Exception&) { + g_warning( "Exception in isAccessibleRowSelected()" ); + } + + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_is_selected( AtkTable *table, + gint row, + gint column ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleTable> pTable + = getTable( table ); + if( pTable.is() ) + return pTable->isAccessibleSelected( row, column ); + } + catch(const uno::Exception&) { + g_warning( "Exception in isAccessibleSelected()" ); + } + + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_add_row_selection( AtkTable *, gint ) +{ + g_warning( "FIXME: no simple analogue for add_row_selection" ); + return 0; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_remove_row_selection( AtkTable *, gint ) +{ + g_warning( "FIXME: no simple analogue for remove_row_selection" ); + return 0; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_add_column_selection( AtkTable *, gint ) +{ + g_warning( "FIXME: no simple analogue for add_column_selection" ); + return 0; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_remove_column_selection( AtkTable *, gint ) +{ + g_warning( "FIXME: no simple analogue for remove_column_selection" ); + return 0; +} + +/*****************************************************************************/ + +static void +table_wrapper_set_caption( AtkTable *, AtkObject * ) +{ // meaningless helper +} + +/*****************************************************************************/ + +static void +table_wrapper_set_column_description( AtkTable *, gint, const gchar * ) +{ // meaningless helper +} + +/*****************************************************************************/ + +static void +table_wrapper_set_column_header( AtkTable *, gint, AtkObject * ) +{ // meaningless helper +} + +/*****************************************************************************/ + +static void +table_wrapper_set_row_description( AtkTable *, gint, const gchar * ) +{ // meaningless helper +} + +/*****************************************************************************/ + +static void +table_wrapper_set_row_header( AtkTable *, gint, AtkObject * ) +{ // meaningless helper +} + +/*****************************************************************************/ + +static void +table_wrapper_set_summary( AtkTable *, AtkObject * ) +{ // meaningless helper +} + +/*****************************************************************************/ + +} // extern "C" + +void +tableIfaceInit (AtkTableIface *iface) +{ + g_return_if_fail (iface != nullptr); + + iface->ref_at = table_wrapper_ref_at; + iface->get_n_rows = table_wrapper_get_n_rows; + iface->get_n_columns = table_wrapper_get_n_columns; + iface->get_index_at = table_wrapper_get_index_at; + iface->get_column_at_index = table_wrapper_get_column_at_index; + iface->get_row_at_index = table_wrapper_get_row_at_index; + iface->is_row_selected = table_wrapper_is_row_selected; + iface->is_selected = table_wrapper_is_selected; + iface->get_selected_rows = table_wrapper_get_selected_rows; + iface->add_row_selection = table_wrapper_add_row_selection; + iface->remove_row_selection = table_wrapper_remove_row_selection; + iface->add_column_selection = table_wrapper_add_column_selection; + iface->remove_column_selection = table_wrapper_remove_column_selection; + iface->get_selected_columns = table_wrapper_get_selected_columns; + iface->is_column_selected = table_wrapper_is_column_selected; + iface->get_column_extent_at = table_wrapper_get_column_extent_at; + iface->get_row_extent_at = table_wrapper_get_row_extent_at; + iface->get_row_header = table_wrapper_get_row_header; + iface->set_row_header = table_wrapper_set_row_header; + iface->get_column_header = table_wrapper_get_column_header; + iface->set_column_header = table_wrapper_set_column_header; + iface->get_caption = table_wrapper_get_caption; + iface->set_caption = table_wrapper_set_caption; + iface->get_summary = table_wrapper_get_summary; + iface->set_summary = table_wrapper_set_summary; + iface->get_row_description = table_wrapper_get_row_description; + iface->set_row_description = table_wrapper_set_row_description; + iface->get_column_description = table_wrapper_get_column_description; + iface->set_column_description = table_wrapper_set_column_description; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atktext.cxx b/vcl/unx/gtk3/a11y/gtk3atktext.cxx index e4bbd5a38980..532b55013ddd 100644 --- a/vcl/unx/gtk3/a11y/gtk3atktext.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atktext.cxx @@ -5,8 +5,833 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atktext.cxx" +#include "atkwrapper.hxx" +#include "atktextattributes.hxx" +#include <algorithm> + +#include <osl/diagnose.h> + +#include <com/sun/star/accessibility/AccessibleTextType.hpp> +#include <com/sun/star/accessibility/TextSegment.hpp> +#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp> +#include <com/sun/star/accessibility/XAccessibleText.hpp> +#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp> +#include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp> +#include <com/sun/star/text/TextMarkupType.hpp> + +using namespace ::com::sun::star; + +static sal_Int16 +text_type_from_boundary(AtkTextBoundary boundary_type) +{ + switch(boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: + return accessibility::AccessibleTextType::CHARACTER; + case ATK_TEXT_BOUNDARY_WORD_START: + case ATK_TEXT_BOUNDARY_WORD_END: + return accessibility::AccessibleTextType::WORD; + case ATK_TEXT_BOUNDARY_SENTENCE_START: + case ATK_TEXT_BOUNDARY_SENTENCE_END: + return accessibility::AccessibleTextType::SENTENCE; + case ATK_TEXT_BOUNDARY_LINE_START: + case ATK_TEXT_BOUNDARY_LINE_END: + return accessibility::AccessibleTextType::LINE; + default: + return -1; + } +} + +/*****************************************************************************/ + +static gchar * +adjust_boundaries( css::uno::Reference<css::accessibility::XAccessibleText> const & pText, + accessibility::TextSegment const & rTextSegment, + AtkTextBoundary boundary_type, + gint * start_offset, gint * end_offset ) +{ + accessibility::TextSegment aTextSegment; + OUString aString; + gint start = 0, end = 0; + + if( !rTextSegment.SegmentText.isEmpty() ) + { + switch(boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: + case ATK_TEXT_BOUNDARY_LINE_START: + case ATK_TEXT_BOUNDARY_LINE_END: + case ATK_TEXT_BOUNDARY_SENTENCE_START: + start = rTextSegment.SegmentStart; + end = rTextSegment.SegmentEnd; + aString = rTextSegment.SegmentText; + break; + + // the OOo break iterator behaves as SENTENCE_START + case ATK_TEXT_BOUNDARY_SENTENCE_END: + start = rTextSegment.SegmentStart; + end = rTextSegment.SegmentEnd; + + if( start > 0 ) + --start; + if( end > 0 && end < pText->getCharacterCount() - 1 ) + --end; + + aString = pText->getTextRange(start, end); + break; + + case ATK_TEXT_BOUNDARY_WORD_START: + start = rTextSegment.SegmentStart; + + // Determine the start index of the next segment + aTextSegment = pText->getTextBehindIndex(rTextSegment.SegmentEnd, + text_type_from_boundary(boundary_type)); + if( !aTextSegment.SegmentText.isEmpty() ) + end = aTextSegment.SegmentStart; + else + end = pText->getCharacterCount(); + + aString = pText->getTextRange(start, end); + break; + + case ATK_TEXT_BOUNDARY_WORD_END: + end = rTextSegment.SegmentEnd; + + // Determine the end index of the previous segment + aTextSegment = pText->getTextBeforeIndex(rTextSegment.SegmentStart, + text_type_from_boundary(boundary_type)); + if( !aTextSegment.SegmentText.isEmpty() ) + start = aTextSegment.SegmentEnd; + else + start = 0; + + aString = pText->getTextRange(start, end); + break; + + default: + return nullptr; + } + } + + *start_offset = start; + *end_offset = end; + + return OUStringToGChar(aString); +} + +/*****************************************************************************/ + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleText> + getText( AtkText *pText ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); + if( pWrap ) + { + if( !pWrap->mpText.is() ) + { + pWrap->mpText.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpText; + } + + return css::uno::Reference<css::accessibility::XAccessibleText>(); +} + +/*****************************************************************************/ + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleTextMarkup> + getTextMarkup( AtkText *pText ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); + if( pWrap ) + { + if( !pWrap->mpTextMarkup.is() ) + { + pWrap->mpTextMarkup.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpTextMarkup; + } + + return css::uno::Reference<css::accessibility::XAccessibleTextMarkup>(); +} + +/*****************************************************************************/ + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleTextAttributes> + getTextAttributes( AtkText *pText ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); + if( pWrap ) + { + if( !pWrap->mpTextAttributes.is() ) + { + pWrap->mpTextAttributes.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpTextAttributes; + } + + return css::uno::Reference<css::accessibility::XAccessibleTextAttributes>(); +} + +/*****************************************************************************/ + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleMultiLineText> + getMultiLineText( AtkText *pText ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); + if( pWrap ) + { + if( !pWrap->mpMultiLineText.is() ) + { + pWrap->mpMultiLineText.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpMultiLineText; + } + + return css::uno::Reference<css::accessibility::XAccessibleMultiLineText>(); +} + +/*****************************************************************************/ + +extern "C" { + +static gchar * +text_wrapper_get_text (AtkText *text, + gint start_offset, + gint end_offset) +{ + gchar * ret = nullptr; + + g_return_val_if_fail( (end_offset == -1) || (end_offset >= start_offset), nullptr ); + + /* at-spi expects the delete event to be send before the deletion happened + * so we save the deleted string object in the UNO event notification and + * fool libatk-bridge.so here .. + */ + void * pData = g_object_get_data( G_OBJECT(text), "ooo::text_changed::delete" ); + if( pData != nullptr ) + { + accessibility::TextSegment * pTextSegment = + static_cast <accessibility::TextSegment *> (pData); + + if( pTextSegment->SegmentStart == start_offset && + pTextSegment->SegmentEnd == end_offset ) + { + OString aUtf8 = OUStringToOString( pTextSegment->SegmentText, RTL_TEXTENCODING_UTF8 ); + return g_strdup( aUtf8.getStr() ); + } + } + + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + { + OUString aText; + sal_Int32 n = pText->getCharacterCount(); + + if( -1 == end_offset ) + aText = pText->getText(); + else if( start_offset < n ) + aText = pText->getTextRange(start_offset, end_offset); + + ret = g_strdup( OUStringToOString(aText, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getText()" ); + } + + return ret; +} + +static gchar * +text_wrapper_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + { + accessibility::TextSegment aTextSegment = pText->getTextBehindIndex(offset, text_type_from_boundary(boundary_type)); + return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in get_text_after_offset()" ); + } + + return nullptr; +} + +static gchar * +text_wrapper_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + { + /* If the user presses the 'End' key, the caret will be placed behind the last character, + * which is the same index as the first character of the next line. In atk the magic offset + * '-2' is used to cover this special case. + */ + if ( + -2 == offset && + (ATK_TEXT_BOUNDARY_LINE_START == boundary_type || + ATK_TEXT_BOUNDARY_LINE_END == boundary_type) + ) + { + css::uno::Reference< + css::accessibility::XAccessibleMultiLineText> pMultiLineText + = getMultiLineText( text ); + if( pMultiLineText.is() ) + { + accessibility::TextSegment aTextSegment = pMultiLineText->getTextAtLineWithCaret(); + return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); + } + } + + accessibility::TextSegment aTextSegment = pText->getTextAtIndex(offset, text_type_from_boundary(boundary_type)); + return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in get_text_at_offset()" ); + } + + return nullptr; +} + +static gunichar +text_wrapper_get_character_at_offset (AtkText *text, + gint offset) +{ + gint start, end; + gunichar uc = 0; + + gchar * char_as_string = + text_wrapper_get_text_at_offset(text, offset, ATK_TEXT_BOUNDARY_CHAR, + &start, &end); + if( char_as_string ) + { + uc = g_utf8_get_char( char_as_string ); + g_free( char_as_string ); + } + + return uc; +} + +static gchar * +text_wrapper_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + { + accessibility::TextSegment aTextSegment = pText->getTextBeforeIndex(offset, text_type_from_boundary(boundary_type)); + return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in text_before_offset()" ); + } + + return nullptr; +} + +static gint +text_wrapper_get_caret_offset (AtkText *text) +{ + gint offset = -1; + + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + offset = pText->getCaretPosition(); + } + catch(const uno::Exception&) { + g_warning( "Exception in getCaretPosition()" ); + } + + return offset; +} + +static gboolean +text_wrapper_set_caret_offset (AtkText *text, + gint offset) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + return pText->setCaretPosition( offset ); + } + catch(const uno::Exception&) { + g_warning( "Exception in setCaretPosition()" ); + } + + return FALSE; +} + +// #i92232# +static AtkAttributeSet* +handle_text_markup_as_run_attribute( css::uno::Reference<css::accessibility::XAccessibleTextMarkup> const & pTextMarkup, + const gint nTextMarkupType, + const gint offset, + AtkAttributeSet* pSet, + gint *start_offset, + gint *end_offset ) +{ + const gint nTextMarkupCount( pTextMarkup->getTextMarkupCount( nTextMarkupType ) ); + if ( nTextMarkupCount > 0 ) + { + for ( gint nTextMarkupIndex = 0; + nTextMarkupIndex < nTextMarkupCount; + ++nTextMarkupIndex ) + { + accessibility::TextSegment aTextSegment = + pTextMarkup->getTextMarkup( nTextMarkupIndex, nTextMarkupType ); + const gint nStartOffsetTextMarkup = aTextSegment.SegmentStart; + const gint nEndOffsetTextMarkup = aTextSegment.SegmentEnd; + if ( nStartOffsetTextMarkup <= offset ) + { + if ( offset < nEndOffsetTextMarkup ) + { + // text markup at <offset> + *start_offset = ::std::max( *start_offset, + nStartOffsetTextMarkup ); + *end_offset = ::std::min( *end_offset, + nEndOffsetTextMarkup ); + switch ( nTextMarkupType ) + { + case css::text::TextMarkupType::SPELLCHECK: + { + pSet = attribute_set_prepend_misspelled( pSet ); + } + break; + case css::text::TextMarkupType::TRACK_CHANGE_INSERTION: + { + pSet = attribute_set_prepend_tracked_change_insertion( pSet ); + } + break; + case css::text::TextMarkupType::TRACK_CHANGE_DELETION: + { + pSet = attribute_set_prepend_tracked_change_deletion( pSet ); + } + break; + case css::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE: + { + pSet = attribute_set_prepend_tracked_change_formatchange( pSet ); + } + break; + default: + { + OSL_ASSERT( false ); + } + } + break; // no further iteration needed. + } + else + { + *start_offset = ::std::max( *start_offset, + nEndOffsetTextMarkup ); + // continue iteration. + } + } + else + { + *end_offset = ::std::min( *end_offset, + nStartOffsetTextMarkup ); + break; // no further iteration. + } + } // eof iteration over text markups + } + + return pSet; +} + +static AtkAttributeSet * +text_wrapper_get_run_attributes( AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + AtkAttributeSet *pSet = nullptr; + + try { + bool bOffsetsAreValid = false; + + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is()) + { + uno::Sequence< beans::PropertyValue > aAttributeList; + + css::uno::Reference<css::accessibility::XAccessibleTextAttributes> + pTextAttributes = getTextAttributes( text ); + if(pTextAttributes.is()) // Text attributes are available for paragraphs only + { + aAttributeList = pTextAttributes->getRunAttributes( offset, uno::Sequence< OUString > () ); + } + else // For other text objects use character attributes + { + aAttributeList = pText->getCharacterAttributes( offset, uno::Sequence< OUString > () ); + } + + pSet = attribute_set_new_from_property_values( aAttributeList, true, text ); + // #i100938# + // - always provide start_offset and end_offset + { + accessibility::TextSegment aTextSegment = + pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN); + + *start_offset = aTextSegment.SegmentStart; + // #i100938# + // Do _not_ increment the end_offset provide by <accessibility::TextSegment> instance + *end_offset = aTextSegment.SegmentEnd; + bOffsetsAreValid = true; + } + } + + // Special handling for misspelled text + // #i92232# + // - add special handling for tracked changes and refactor the + // corresponding code for handling misspelled text. + css::uno::Reference<css::accessibility::XAccessibleTextMarkup> + pTextMarkup = getTextMarkup( text ); + if( pTextMarkup.is() ) + { + // Get attribute run here if it hasn't been done before + if (!bOffsetsAreValid && pText.is()) + { + accessibility::TextSegment aAttributeTextSegment = + pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN); + *start_offset = aAttributeTextSegment.SegmentStart; + *end_offset = aAttributeTextSegment.SegmentEnd; + } + // handle misspelled text + pSet = handle_text_markup_as_run_attribute( + pTextMarkup, + css::text::TextMarkupType::SPELLCHECK, + offset, pSet, start_offset, end_offset ); + // handle tracked changes + pSet = handle_text_markup_as_run_attribute( + pTextMarkup, + css::text::TextMarkupType::TRACK_CHANGE_INSERTION, + offset, pSet, start_offset, end_offset ); + pSet = handle_text_markup_as_run_attribute( + pTextMarkup, + css::text::TextMarkupType::TRACK_CHANGE_DELETION, + offset, pSet, start_offset, end_offset ); + pSet = handle_text_markup_as_run_attribute( + pTextMarkup, + css::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE, + offset, pSet, start_offset, end_offset ); + } + } + catch(const uno::Exception&){ + + g_warning( "Exception in get_run_attributes()" ); + + if( pSet ) + { + atk_attribute_set_free( pSet ); + pSet = nullptr; + } + } + + return pSet; +} + +/*****************************************************************************/ + +static AtkAttributeSet * +text_wrapper_get_default_attributes( AtkText *text ) +{ + AtkAttributeSet *pSet = nullptr; + + try { + css::uno::Reference<css::accessibility::XAccessibleTextAttributes> + pTextAttributes = getTextAttributes( text ); + if( pTextAttributes.is() ) + { + uno::Sequence< beans::PropertyValue > aAttributeList = + pTextAttributes->getDefaultAttributes( uno::Sequence< OUString > () ); + + pSet = attribute_set_new_from_property_values( aAttributeList, false, text ); + } + } + catch(const uno::Exception&) { + + g_warning( "Exception in get_default_attributes()" ); + + if( pSet ) + { + atk_attribute_set_free( pSet ); + pSet = nullptr; + } + } + + return pSet; +} + +/*****************************************************************************/ + +static void +text_wrapper_get_character_extents( AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + { + *x = *y = *width = *height = 0; + awt::Rectangle aRect = pText->getCharacterBounds( offset ); + + gint origin_x = 0; + gint origin_y = 0; + + if( coords == ATK_XY_SCREEN ) + { + g_return_if_fail( ATK_IS_COMPONENT( text ) ); + SAL_WNODEPRECATED_DECLARATIONS_PUSH + atk_component_get_position( ATK_COMPONENT( text ), &origin_x, &origin_y, coords); + SAL_WNODEPRECATED_DECLARATIONS_POP + } + + *x = aRect.X + origin_x; + *y = aRect.Y + origin_y; + *width = aRect.Width; + *height = aRect.Height; + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getCharacterBounds" ); + } +} + +static gint +text_wrapper_get_character_count (AtkText *text) +{ + gint rv = 0; + + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + rv = pText->getCharacterCount(); + } + catch(const uno::Exception&) { + g_warning( "Exception in getCharacterCount" ); + } + + return rv; +} + +static gint +text_wrapper_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + { + gint origin_x = 0; + gint origin_y = 0; + + if( coords == ATK_XY_SCREEN ) + { + g_return_val_if_fail( ATK_IS_COMPONENT( text ), -1 ); + SAL_WNODEPRECATED_DECLARATIONS_PUSH + atk_component_get_position( ATK_COMPONENT( text ), &origin_x, &origin_y, coords); + SAL_WNODEPRECATED_DECLARATIONS_POP + } + + return pText->getIndexAtPoint( awt::Point(x - origin_x, y - origin_y) ); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getIndexAtPoint" ); + } + + return -1; +} + +// FIXME: the whole series of selections API is problematic ... + +static gint +text_wrapper_get_n_selections (AtkText *text) +{ + gint rv = 0; + + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + rv = ( pText->getSelectionEnd() > pText->getSelectionStart() ) ? 1 : 0; + } + catch(const uno::Exception&) { + g_warning( "Exception in getSelectionEnd() or getSelectionStart()" ); + } + + return rv; +} + +static gchar * +text_wrapper_get_selection (AtkText *text, + gint selection_num, + gint *start_offset, + gint *end_offset) +{ + g_return_val_if_fail( selection_num == 0, FALSE ); + + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + { + *start_offset = pText->getSelectionStart(); + *end_offset = pText->getSelectionEnd(); + + return OUStringToGChar( pText->getSelectedText() ); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getSelectionEnd(), getSelectionStart() or getSelectedText()" ); + } + + return nullptr; +} + +static gboolean +text_wrapper_add_selection (AtkText *text, + gint start_offset, + gint end_offset) +{ + // FIXME: can we try to be more compatible by expanding an + // existing adjacent selection ? + + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + return pText->setSelection( start_offset, end_offset ); // ? + } + catch(const uno::Exception&) { + g_warning( "Exception in setSelection()" ); + } + + return FALSE; +} + +static gboolean +text_wrapper_remove_selection (AtkText *text, + gint selection_num) +{ + g_return_val_if_fail( selection_num == 0, FALSE ); + + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + return pText->setSelection( 0, 0 ); // ? + } + catch(const uno::Exception&) { + g_warning( "Exception in setSelection()" ); + } + + return FALSE; +} + +static gboolean +text_wrapper_set_selection (AtkText *text, + gint selection_num, + gint start_offset, + gint end_offset) +{ + g_return_val_if_fail( selection_num == 0, FALSE ); + + try { + css::uno::Reference<css::accessibility::XAccessibleText> pText + = getText( text ); + if( pText.is() ) + return pText->setSelection( start_offset, end_offset ); + } + catch(const uno::Exception&) { + g_warning( "Exception in setSelection()" ); + } + + return FALSE; +} + +} // extern "C" + +void +textIfaceInit (AtkTextIface *iface) +{ + g_return_if_fail (iface != nullptr); + + iface->get_text = text_wrapper_get_text; + iface->get_character_at_offset = text_wrapper_get_character_at_offset; + iface->get_text_before_offset = text_wrapper_get_text_before_offset; + iface->get_text_at_offset = text_wrapper_get_text_at_offset; + iface->get_text_after_offset = text_wrapper_get_text_after_offset; + iface->get_caret_offset = text_wrapper_get_caret_offset; + iface->set_caret_offset = text_wrapper_set_caret_offset; + iface->get_character_count = text_wrapper_get_character_count; + iface->get_n_selections = text_wrapper_get_n_selections; + iface->get_selection = text_wrapper_get_selection; + iface->add_selection = text_wrapper_add_selection; + iface->remove_selection = text_wrapper_remove_selection; + iface->set_selection = text_wrapper_set_selection; + iface->get_run_attributes = text_wrapper_get_run_attributes; + iface->get_default_attributes = text_wrapper_get_default_attributes; + iface->get_character_extents = text_wrapper_get_character_extents; + iface->get_offset_at_point = text_wrapper_get_offset_at_point; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atktextattributes.cxx b/vcl/unx/gtk3/a11y/gtk3atktextattributes.cxx index b0edad06a65c..0ba7fd561862 100644 --- a/vcl/unx/gtk3/a11y/gtk3atktextattributes.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atktextattributes.cxx @@ -5,8 +5,1379 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atktextattributes.cxx" +#include "atktextattributes.hxx" + +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/FontStrikeout.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> + +#include <com/sun/star/style/CaseMap.hpp> +#include <com/sun/star/style/LineSpacing.hpp> +#include <com/sun/star/style/LineSpacingMode.hpp> +#include <com/sun/star/style/ParagraphAdjust.hpp> +#include <com/sun/star/style/TabAlign.hpp> +#include <com/sun/star/style/TabStop.hpp> + +#include <com/sun/star/text/WritingMode2.hpp> + +#include "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> + +#include <i18nlangtag/languagetag.hxx> +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> + +#include <stdio.h> +#include <string.h> + +using namespace ::com::sun::star; + +typedef gchar* (* AtkTextAttrFunc) ( const uno::Any& rAny ); +typedef bool (* TextPropertyValueFunc) ( uno::Any& rAny, const gchar * value ); + +#define STRNCMP_PARAM( s ) s,sizeof( s )-1 + +/*****************************************************************************/ + +static AtkTextAttribute atk_text_attribute_paragraph_style = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_font_effect = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_decoration = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_line_height = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_rotation = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_shadow = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_tab_interval = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_tab_stops = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_writing_mode = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_vertical_align = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_misspelled = ATK_TEXT_ATTR_INVALID; +// #i92232# +static AtkTextAttribute atk_text_attribute_tracked_change = ATK_TEXT_ATTR_INVALID; +// #i92233# +static AtkTextAttribute atk_text_attribute_mm_to_pixel_ratio = ATK_TEXT_ATTR_INVALID; + +/*****************************************************************************/ + +/** + * !! IMPORTANT NOTE !! : when adding items to this list, KEEP THE LIST SORTED + * and re-arrange the enum values accordingly. + */ + +enum ExportedAttribute +{ + TEXT_ATTRIBUTE_BACKGROUND_COLOR = 0, + TEXT_ATTRIBUTE_CASEMAP, + TEXT_ATTRIBUTE_FOREGROUND_COLOR, + TEXT_ATTRIBUTE_CONTOURED, + TEXT_ATTRIBUTE_CHAR_ESCAPEMENT, + TEXT_ATTRIBUTE_BLINKING, + TEXT_ATTRIBUTE_FONT_NAME, + TEXT_ATTRIBUTE_HEIGHT, + TEXT_ATTRIBUTE_HIDDEN, + TEXT_ATTRIBUTE_KERNING, + TEXT_ATTRIBUTE_LOCALE, + TEXT_ATTRIBUTE_POSTURE, + TEXT_ATTRIBUTE_RELIEF, + TEXT_ATTRIBUTE_ROTATION, + TEXT_ATTRIBUTE_SCALE, + TEXT_ATTRIBUTE_SHADOWED, + TEXT_ATTRIBUTE_STRIKETHROUGH, + TEXT_ATTRIBUTE_UNDERLINE, + TEXT_ATTRIBUTE_WEIGHT, + // #i92233# + TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO, + TEXT_ATTRIBUTE_JUSTIFICATION, + TEXT_ATTRIBUTE_BOTTOM_MARGIN, + TEXT_ATTRIBUTE_FIRST_LINE_INDENT, + TEXT_ATTRIBUTE_LEFT_MARGIN, + TEXT_ATTRIBUTE_LINE_SPACING, + TEXT_ATTRIBUTE_RIGHT_MARGIN, + TEXT_ATTRIBUTE_STYLE_NAME, + TEXT_ATTRIBUTE_TAB_STOPS, + TEXT_ATTRIBUTE_TOP_MARGIN, + TEXT_ATTRIBUTE_WRITING_MODE, + TEXT_ATTRIBUTE_LAST +}; + +static const char * ExportedTextAttributes[TEXT_ATTRIBUTE_LAST] = +{ + "CharBackColor", // TEXT_ATTRIBUTE_BACKGROUND_COLOR + "CharCaseMap", // TEXT_ATTRIBUTE_CASEMAP + "CharColor", // TEXT_ATTRIBUTE_FOREGROUND_COLOR + "CharContoured", // TEXT_ATTRIBUTE_CONTOURED + "CharEscapement", // TEXT_ATTRIBUTE_CHAR_ESCAPEMENT + "CharFlash", // TEXT_ATTRIBUTE_BLINKING + "CharFontName", // TEXT_ATTRIBUTE_FONT_NAME + "CharHeight", // TEXT_ATTRIBUTE_HEIGHT + "CharHidden", // TEXT_ATTRIBUTE_HIDDEN + "CharKerning", // TEXT_ATTRIBUTE_KERNING + "CharLocale", // TEXT_ATTRIBUTE_LOCALE + "CharPosture", // TEXT_ATTRIBUTE_POSTURE + "CharRelief", // TEXT_ATTRIBUTE_RELIEF + "CharRotation", // TEXT_ATTRIBUTE_ROTATION + "CharScaleWidth", // TEXT_ATTRIBUTE_SCALE + "CharShadowed", // TEXT_ATTRIBUTE_SHADOWED + "CharStrikeout", // TEXT_ATTRIBUTE_STRIKETHROUGH + "CharUnderline", // TEXT_ATTRIBUTE_UNDERLINE + "CharWeight", // TEXT_ATTRIBUTE_WEIGHT + // #i92233# + "MMToPixelRatio", // TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO + "ParaAdjust", // TEXT_ATTRIBUTE_JUSTIFICATION + "ParaBottomMargin", // TEXT_ATTRIBUTE_BOTTOM_MARGIN + "ParaFirstLineIndent", // TEXT_ATTRIBUTE_FIRST_LINE_INDENT + "ParaLeftMargin", // TEXT_ATTRIBUTE_LEFT_MARGIN + "ParaLineSpacing", // TEXT_ATTRIBUTE_LINE_SPACING + "ParaRightMargin", // TEXT_ATTRIBUTE_RIGHT_MARGIN + "ParaStyleName", // TEXT_ATTRIBUTE_STYLE_NAME + "ParaTabStops", // TEXT_ATTRIBUTE_TAB_STOPS + "ParaTopMargin", // TEXT_ATTRIBUTE_TOP_MARGIN + "WritingMode" // TEXT_ATTRIBUTE_WRITING_MODE +}; + +/*****************************************************************************/ + +static gchar* +get_value( const uno::Sequence< beans::PropertyValue >& rAttributeList, + sal_Int32 nIndex, AtkTextAttrFunc func ) +{ + if( nIndex != -1 ) + return func(rAttributeList[nIndex].Value); + + return nullptr; +} + +#define get_bool_value( list, index ) get_value( list, index, Bool2String ) +#define get_height_value( list, index ) get_value( list, index, Float2String ) +#define get_justification_value( list, index ) get_value( list, index, Adjust2Justification ) +#define get_cmm_value( list, index ) get_value( list, index, CMM2UnitString ) +#define get_scale_width( list, index ) get_value( list, index, Scale2String ) +#define get_strikethrough_value( list, index ) get_value( list, index, Strikeout2String ) +#define get_string_value( list, index ) get_value( list, index, GetString ) +#define get_style_value( list, index ) get_value( list, index, FontSlant2Style ) +#define get_underline_value( list, index ) get_value( list, index, Underline2String ) +#define get_variant_value( list, index ) get_value( list, index, CaseMap2String ) +#define get_weight_value( list, index ) get_value( list, index, Weight2String ) +#define get_language_string( list, index ) get_value( list, index, Locale2String ) + +static double toPoint(sal_Int16 n) +{ + // 100th mm -> pt + return static_cast<double>(n * 72) / 2540; +} + +/*****************************************************************************/ + +static bool +InvalidValue( uno::Any&, const gchar * ) +{ + return false; +} + +/*****************************************************************************/ + +static gchar* +Float2String(const uno::Any& rAny) +{ + return g_strdup_printf( "%g", rAny.get<float>() ); +} + +static bool +String2Float( uno::Any& rAny, const gchar * value ) +{ + float fval; + + if( 1 != sscanf( value, "%g", &fval ) ) + return false; + + rAny <<= fval; + return true; +} + +/*****************************************************************************/ + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleComponent> + getComponent( AtkText *pText ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); + if( pWrap ) + { + if( !pWrap->mpComponent.is() ) + { + pWrap->mpComponent.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpComponent; + } + + return css::uno::Reference<css::accessibility::XAccessibleComponent>(); +} + +static gchar* +get_color_value(const uno::Sequence< beans::PropertyValue >& rAttributeList, + const sal_Int32 * pIndexArray, + ExportedAttribute attr, + AtkText * text) +{ + sal_Int32 nColor = -1; // AUTOMATIC + sal_Int32 nIndex = pIndexArray[attr]; + + if( nIndex != -1 ) + nColor = rAttributeList[nIndex].Value.get<sal_Int32>(); + + /* + * Check for color value for 100% alpha white, which means + * "automatic". Grab the RGB value from XAccessibleComponent + * in this case. + */ + + if( (nColor == -1) && text ) + { + try + { + css::uno::Reference<css::accessibility::XAccessibleComponent> + pComponent = getComponent( text ); + if( pComponent.is() ) + { + switch( attr ) + { + case TEXT_ATTRIBUTE_BACKGROUND_COLOR: + nColor = pComponent->getBackground(); + break; + case TEXT_ATTRIBUTE_FOREGROUND_COLOR: + nColor = pComponent->getForeground(); + break; + default: + break; + } + } + } + + catch(const uno::Exception&) { + g_warning( "Exception in get[Fore|Back]groundColor()" ); + } + } + + if( nColor != -1 ) + { + sal_uInt8 blue = nColor & 0xFF; + sal_uInt8 green = (nColor >> 8) & 0xFF; + sal_uInt8 red = (nColor >> 16) & 0xFF; + + return g_strdup_printf( "%u,%u,%u", red, green, blue ); + } + + return nullptr; +} + +static bool +String2Color( uno::Any& rAny, const gchar * value ) +{ + int red, green, blue; + + if( 3 != sscanf( value, "%d,%d,%d", &red, &green, &blue ) ) + return false; + + sal_Int32 nColor = static_cast<sal_Int32>(blue) | ( static_cast<sal_Int32>(green) << 8 ) | ( static_cast<sal_Int32>(red) << 16 ); + rAny <<= nColor; + return true; +} + +/*****************************************************************************/ + +static gchar* +FontSlant2Style(const uno::Any& rAny) +{ + const gchar * value = nullptr; + + awt::FontSlant aFontSlant; + if(!(rAny >>= aFontSlant)) + return nullptr; + + switch( aFontSlant ) + { + case awt::FontSlant_NONE: + value = "normal"; + break; + + case awt::FontSlant_OBLIQUE: + value = "oblique"; + break; + + case awt::FontSlant_ITALIC: + value = "italic"; + break; + + case awt::FontSlant_REVERSE_OBLIQUE: + value = "reverse oblique"; + break; + + case awt::FontSlant_REVERSE_ITALIC: + value = "reverse italic"; + break; + + default: + break; + } + + if( value ) + return g_strdup( value ); + + return nullptr; +} + +static bool +Style2FontSlant( uno::Any& rAny, const gchar * value ) +{ + awt::FontSlant aFontSlant; + + if( strncmp( value, STRNCMP_PARAM( "normal" ) ) == 0 ) + aFontSlant = awt::FontSlant_NONE; + else if( strncmp( value, STRNCMP_PARAM( "oblique" ) ) == 0 ) + aFontSlant = awt::FontSlant_OBLIQUE; + else if( strncmp( value, STRNCMP_PARAM( "italic" ) ) == 0 ) + aFontSlant = awt::FontSlant_ITALIC; + else if( strncmp( value, STRNCMP_PARAM( "reverse oblique" ) ) == 0 ) + aFontSlant = awt::FontSlant_REVERSE_OBLIQUE; + else if( strncmp( value, STRNCMP_PARAM( "reverse italic" ) ) == 0 ) + aFontSlant = awt::FontSlant_REVERSE_ITALIC; + else + return false; + + rAny <<= aFontSlant; + return true; +} + +/*****************************************************************************/ + +static gchar* +Weight2String(const uno::Any& rAny) +{ + return g_strdup_printf( "%g", rAny.get<float>() * 4 ); +} + +static bool +String2Weight( uno::Any& rAny, const gchar * value ) +{ + float weight; + + if( 1 != sscanf( value, "%g", &weight ) ) + return false; + + rAny <<= weight / 4; + return true; +} + +/*****************************************************************************/ + +static gchar* +Adjust2Justification(const uno::Any& rAny) +{ + const gchar * value = nullptr; + + switch( static_cast<style::ParagraphAdjust>(rAny.get<short>()) ) + { + case style::ParagraphAdjust_LEFT: + value = "left"; + break; + + case style::ParagraphAdjust_RIGHT: + value = "right"; + break; + + case style::ParagraphAdjust_BLOCK: + case style::ParagraphAdjust_STRETCH: + value = "fill"; + break; + + case style::ParagraphAdjust_CENTER: + value = "center"; + break; + + default: + break; + } + + if( value ) + return g_strdup( value ); + + return nullptr; +} + +static bool +Justification2Adjust( uno::Any& rAny, const gchar * value ) +{ + style::ParagraphAdjust nParagraphAdjust; + + if( strncmp( value, STRNCMP_PARAM( "left" ) ) == 0 ) + nParagraphAdjust = style::ParagraphAdjust_LEFT; + else if( strncmp( value, STRNCMP_PARAM( "right" ) ) == 0 ) + nParagraphAdjust = style::ParagraphAdjust_RIGHT; + else if( strncmp( value, STRNCMP_PARAM( "fill" ) ) == 0 ) + nParagraphAdjust = style::ParagraphAdjust_BLOCK; + else if( strncmp( value, STRNCMP_PARAM( "center" ) ) == 0 ) + nParagraphAdjust = style::ParagraphAdjust_CENTER; + else + return false; + + rAny <<= static_cast<short>(nParagraphAdjust); + return true; +} + +/*****************************************************************************/ + +const gchar * const font_strikethrough[] = { + "none", // FontStrikeout::NONE + "single", // FontStrikeout::SINGLE + "double", // FontStrikeout::DOUBLE + nullptr, // FontStrikeout::DONTKNOW + "bold", // FontStrikeout::BOLD + "with /", // FontStrikeout::SLASH + "with X" // FontStrikeout::X +}; + +static gchar* +Strikeout2String(const uno::Any& rAny) +{ + sal_Int16 n = rAny.get<sal_Int16>(); + + if( n >= 0 && n < sal_Int16(SAL_N_ELEMENTS(font_strikethrough)) ) + return g_strdup( font_strikethrough[n] ); + + return nullptr; +} + +static bool +String2Strikeout( uno::Any& rAny, const gchar * value ) +{ + for( sal_Int16 n=0; n < sal_Int16(SAL_N_ELEMENTS(font_strikethrough)); ++n ) + { + if( ( nullptr != font_strikethrough[n] ) && + 0 == strncmp( value, font_strikethrough[n], strlen( font_strikethrough[n] ) ) ) + { + rAny <<= n; + return true; + } + } + + return false; +} + +/*****************************************************************************/ + +static gchar* +Underline2String(const uno::Any& rAny) +{ + const gchar * value = nullptr; + + switch( rAny.get<sal_Int16>() ) + { + case awt::FontUnderline::NONE: + value = "none"; + break; + + case awt::FontUnderline::SINGLE: + value = "single"; + break; + + case awt::FontUnderline::DOUBLE: + value = "double"; + break; + + default: + break; + } + + if( value ) + return g_strdup( value ); + + return nullptr; +} + +static bool +String2Underline( uno::Any& rAny, const gchar * value ) +{ + short nUnderline; + + if( strncmp( value, STRNCMP_PARAM( "none" ) ) == 0 ) + nUnderline = awt::FontUnderline::NONE; + else if( strncmp( value, STRNCMP_PARAM( "single" ) ) == 0 ) + nUnderline = awt::FontUnderline::SINGLE; + else if( strncmp( value, STRNCMP_PARAM( "double" ) ) == 0 ) + nUnderline = awt::FontUnderline::DOUBLE; + else + return false; + + rAny <<= nUnderline; + return true; +} + +/*****************************************************************************/ + +static gchar* +GetString(const uno::Any& rAny) +{ + OString aFontName = OUStringToOString( rAny.get< OUString > (), RTL_TEXTENCODING_UTF8 ); + + if( !aFontName.isEmpty() ) + return g_strdup( aFontName.getStr() ); + + return nullptr; +} + +static bool +SetString( uno::Any& rAny, const gchar * value ) +{ + OString aFontName( value ); + + if( !aFontName.isEmpty() ) + { + rAny <<= OStringToOUString( aFontName, RTL_TEXTENCODING_UTF8 ); + return true; + } + + return false; +} + +/*****************************************************************************/ + +// @see http://developer.gnome.org/doc/API/2.0/atk/AtkText.html#AtkTextAttribute + +// CMM = 100th of mm +static gchar* +CMM2UnitString(const uno::Any& rAny) +{ + double fValue = rAny.get<sal_Int32>(); + fValue = fValue * 0.01; + + return g_strdup_printf( "%gmm", fValue ); +} + +static bool +UnitString2CMM( uno::Any& rAny, const gchar * value ) +{ + float fValue = 0.0; // pb: don't use double here because of warning on linux + + if( 1 != sscanf( value, "%gmm", &fValue ) ) + return false; + + fValue = fValue * 100; + + rAny <<= static_cast<sal_Int32>(fValue); + return true; +} + +/*****************************************************************************/ + +static const gchar * bool_values[] = { "true", "false" }; + +static gchar * +Bool2String( const uno::Any& rAny ) +{ + int n = 1; + + if( rAny.get<bool>() ) + n = 0; + + return g_strdup( bool_values[n] ); +} + +static bool +String2Bool( uno::Any& rAny, const gchar * value ) +{ + bool bValue; + + if( strncmp( value, STRNCMP_PARAM( "true" ) ) == 0 ) + bValue = true; + else if( strncmp( value, STRNCMP_PARAM( "false" ) ) == 0 ) + bValue = false; + else + return false; + + rAny <<= bValue; + return true; +} + +/*****************************************************************************/ + +static gchar* +Scale2String( const uno::Any& rAny ) +{ + return g_strdup_printf( "%g", static_cast<double>(rAny.get< sal_Int16 > ()) / 100 ); +} + +static bool +String2Scale( uno::Any& rAny, const gchar * value ) +{ + double dval; + + if( 1 != sscanf( value, "%lg", &dval ) ) + return false; + + rAny <<= static_cast<sal_Int16>(dval * 100); + return true; +} + +/*****************************************************************************/ + +static gchar * +CaseMap2String( const uno::Any& rAny ) +{ + const gchar * value; + + switch( rAny.get<short>() ) + { + case style::CaseMap::SMALLCAPS: + value = "small_caps"; + break; + + default: + value = "normal"; + break; + } + + return g_strdup(value); +} + +static bool +String2CaseMap( uno::Any& rAny, const gchar * value ) +{ + short nCaseMap; + + if( strncmp( value, STRNCMP_PARAM( "normal" ) ) == 0 ) + nCaseMap = style::CaseMap::NONE; + else if( strncmp( value, STRNCMP_PARAM( "small_caps" ) ) == 0 ) + nCaseMap = style::CaseMap::SMALLCAPS; + else + return false; + + rAny <<= nCaseMap; + return true; +} + +/*****************************************************************************/ + +const gchar * const font_stretch[] = { + "ultra_condensed", + "extra_condensed", + "condensed", + "semi_condensed", + "normal", + "semi_expanded", + "expanded", + "extra_expanded", + "ultra_expanded" +}; + +static gchar* +Kerning2Stretch(const uno::Any& rAny) +{ + sal_Int16 n = rAny.get<sal_Int16>(); + int i = 4; + + // No good idea for a mapping - just return the basic info + if( n < 0 ) + i=2; + else if( n > 0 ) + i=6; + + return g_strdup(font_stretch[i]); +} + +/*****************************************************************************/ + +static gchar* +Locale2String(const uno::Any& rAny) +{ + /* FIXME-BCP47: support language tags? And why is country lowercase? */ + lang::Locale aLocale = rAny.get<lang::Locale> (); + LanguageTag aLanguageTag( aLocale); + return g_strdup_printf( "%s-%s", + OUStringToOString( aLanguageTag.getLanguage(), RTL_TEXTENCODING_ASCII_US).getStr(), + OUStringToOString( aLanguageTag.getCountry(), RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase().getStr() ); +} + +static bool +String2Locale( uno::Any& rAny, const gchar * value ) +{ + /* FIXME-BCP47: support language tags? */ + bool ret = false; + + gchar ** str_array = g_strsplit_set( value, "-.@", -1 ); + if( str_array[0] != nullptr ) + { + ret = true; + + lang::Locale aLocale; + + aLocale.Language = OUString::createFromAscii(str_array[0]); + if( str_array[1] != nullptr ) + { + gchar * country = g_ascii_strup(str_array[1], -1); + aLocale.Country = OUString::createFromAscii(country); + g_free(country); + } + + rAny <<= aLocale; + } + + g_strfreev(str_array); + return ret; +} + +/*****************************************************************************/ + +// @see http://www.w3.org/TR/2002/WD-css3-fonts-20020802/#font-effect-prop +static const gchar * relief[] = { "none", "emboss", "engrave" }; +static const gchar * const outline = "outline"; + +static gchar * +get_font_effect(const uno::Sequence< beans::PropertyValue >& rAttributeList, + sal_Int32 nContourIndex, sal_Int32 nReliefIndex) +{ + if( nContourIndex != -1 ) + { + if( rAttributeList[nContourIndex].Value.get<bool>() ) + return g_strdup(outline); + } + + if( nReliefIndex != -1 ) + { + sal_Int16 n = rAttributeList[nReliefIndex].Value.get<sal_Int16>(); + if( n < 3) + return g_strdup(relief[n]); + } + + return nullptr; +} + +/*****************************************************************************/ + +// @see http://www.w3.org/TR/REC-CSS2/text.html#lining-striking-props + +enum +{ + DECORATION_NONE = 0, + DECORATION_BLINK, + DECORATION_UNDERLINE, + DECORATION_LINE_THROUGH +}; + +static const gchar * decorations[] = { "none", "blink", "underline", "line-through" }; + +static gchar * +get_text_decoration(const uno::Sequence< beans::PropertyValue >& rAttributeList, + sal_Int32 nBlinkIndex, sal_Int32 nUnderlineIndex, + sal_Int16 nStrikeoutIndex) +{ + gchar * value_list[4] = { nullptr, nullptr, nullptr, nullptr }; + gint count = 0; + + // no property value found + if( ( nBlinkIndex == -1 ) && (nUnderlineIndex == -1 ) && (nStrikeoutIndex == -1)) + return nullptr; + + if( nBlinkIndex != -1 ) + { + if( rAttributeList[nBlinkIndex].Value.get<bool>() ) + value_list[count++] = const_cast <gchar *> (decorations[DECORATION_BLINK]); + } + if( nUnderlineIndex != -1 ) + { + sal_Int16 n = rAttributeList[nUnderlineIndex].Value.get<sal_Int16> (); + if( n != awt::FontUnderline::NONE ) + value_list[count++] = const_cast <gchar *> (decorations[DECORATION_UNDERLINE]); + } + if( nStrikeoutIndex != -1 ) + { + sal_Int16 n = rAttributeList[nStrikeoutIndex].Value.get<sal_Int16> (); + if( n != awt::FontStrikeout::NONE && n != awt::FontStrikeout::DONTKNOW ) + value_list[count++] = const_cast <gchar *> (decorations[DECORATION_LINE_THROUGH]); + } + + if( count == 0 ) + value_list[count++] = const_cast <gchar *> (decorations[DECORATION_NONE]); + + return g_strjoinv(" ", value_list); +} + +/*****************************************************************************/ + +// @see http://www.w3.org/TR/REC-CSS2/text.html#propdef-text-shadow + +static const gchar * shadow_values[] = { "none", "black" }; + +static gchar * +Bool2Shadow( const uno::Any& rAny ) +{ + int n = 0; + + if( rAny.get<bool>() ) + n = 1; + + return g_strdup( shadow_values[n] ); +} + +/*****************************************************************************/ + +static gchar * +Short2Degree( const uno::Any& rAny ) +{ + float f = rAny.get<sal_Int16>() / 10.0; + return g_strdup_printf( "%g", f ); +} + +/*****************************************************************************/ + +const gchar * const directions[] = { "ltr", "rtl", "rtl", "ltr", "none" }; + +static gchar * +WritingMode2Direction( const uno::Any& rAny ) +{ + sal_Int16 n = rAny.get<sal_Int16>(); + + if( 0 <= n && n <= text::WritingMode2::PAGE ) + return g_strdup(directions[n]); + + return nullptr; +} + +// @see http://www.w3.org/TR/2001/WD-css3-text-20010517/#PrimaryTextAdvanceDirection + +const gchar * const writing_modes[] = { "lr-tb", "rl-tb", "tb-rl", "tb-lr", "none" }; +static gchar * +WritingMode2String( const uno::Any& rAny ) +{ + sal_Int16 n = rAny.get<sal_Int16>(); + + if( 0 <= n && n <= text::WritingMode2::PAGE ) + return g_strdup(writing_modes[n]); + + return nullptr; +} + +/*****************************************************************************/ + +const char * const baseline_values[] = { "baseline", "sub", "super" }; + +// @see http://www.w3.org/TR/REC-CSS2/visudet.html#propdef-vertical-align +static gchar * +Escapement2VerticalAlign( const uno::Any& rAny ) +{ + sal_Int16 n = rAny.get<sal_Int16>(); + gchar * ret = nullptr; + + // Values are in %, 101% means "automatic" + if( n == 0 ) + ret = g_strdup(baseline_values[0]); + else if( n == 101 ) + ret = g_strdup(baseline_values[2]); + else if( n == -101 ) + ret = g_strdup(baseline_values[1]); + else + ret = g_strdup_printf( "%d%%", n ); + + return ret; +} + +/*****************************************************************************/ + +// @see http://www.w3.org/TR/REC-CSS2/visudet.html#propdef-line-height +static gchar * +LineSpacing2LineHeight( const uno::Any& rAny ) +{ + style::LineSpacing ls; + gchar * ret = nullptr; + + if( rAny >>= ls ) + { + if( ls.Mode == style::LineSpacingMode::PROP ) + ret = g_strdup_printf( "%d%%", ls.Height ); + else if( ls.Mode == style::LineSpacingMode::FIX ) + ret = g_strdup_printf( "%.3gpt", toPoint(ls.Height) ); + } + + return ret; +} + +/*****************************************************************************/ + +// @see http://www.w3.org/People/howcome/t/970224HTMLERB-CSS/WD-tabs-970117.html +static gchar * +TabStopList2String( const uno::Any& rAny, bool default_tabs ) +{ + uno::Sequence< style::TabStop > theTabStops; + gchar * ret = nullptr; + + if( rAny >>= theTabStops) + { + sal_Unicode lastFillChar = ' '; + + for( const auto& rTabStop : std::as_const(theTabStops) ) + { + bool is_default_tab = (style::TabAlign_DEFAULT == rTabStop.Alignment); + + if( is_default_tab != default_tabs ) + continue; + + double fValue = rTabStop.Position; + fValue = fValue * 0.01; + + const gchar * tab_align = ""; + switch( rTabStop.Alignment ) + { + case style::TabAlign_LEFT : + tab_align = "left "; + break; + case style::TabAlign_CENTER : + tab_align = "center "; + break; + case style::TabAlign_RIGHT : + tab_align = "right "; + break; + case style::TabAlign_DECIMAL : + tab_align = "decimal "; + break; + default: + break; + } + + const gchar * lead_char = ""; + + if( rTabStop.FillChar != lastFillChar ) + { + lastFillChar = rTabStop.FillChar; + switch (lastFillChar) + { + case ' ': + lead_char = "blank "; + break; + + case '.': + lead_char = "dotted "; + break; + + case '-': + lead_char = "dashed "; + break; + + case '_': + lead_char = "lined "; + break; + + default: + lead_char = "custom "; + break; + } + } + + gchar * tab_str = g_strdup_printf( "%s%s%gmm", lead_char, tab_align, fValue ); + + if( ret ) + { + gchar * old_tab_str = ret; + ret = g_strconcat(old_tab_str, " ", tab_str, nullptr); + g_free( old_tab_str ); + } + else + ret = tab_str; + } + } + + return ret; +} + +static gchar * +TabStops2String( const uno::Any& rAny ) +{ + return TabStopList2String(rAny, false); +} + +static gchar * +DefaultTabStops2String( const uno::Any& rAny ) +{ + return TabStopList2String(rAny, true); +} + +/*****************************************************************************/ + +extern "C" { + +static int +attr_compare(const void *p1,const void *p2) +{ + const rtl_uString * pustr = static_cast<const rtl_uString *>(p1); + const char * pc = *static_cast<const char * const *>(p2); + + return rtl_ustr_ascii_compare_WithLength(pustr->buffer, pustr->length, pc); +} + +} + +static void +find_exported_attributes( sal_Int32 *pArray, + const css::uno::Sequence< css::beans::PropertyValue >& rAttributeList ) +{ + for( sal_Int32 i = 0; i < rAttributeList.getLength(); i++ ) + { + const char ** pAttr = static_cast<const char **>(bsearch(rAttributeList[i].Name.pData, + ExportedTextAttributes, TEXT_ATTRIBUTE_LAST, sizeof(const char *), + attr_compare)); + + if( pAttr ) + { + sal_Int32 nIndex = pAttr - ExportedTextAttributes; + pArray[nIndex] = i; + } + } +} + +/*****************************************************************************/ + +static AtkAttributeSet* +attribute_set_prepend( AtkAttributeSet* attribute_set, + AtkTextAttribute attribute, + gchar * value ) +{ + if( value ) + { + AtkAttribute *at = static_cast<AtkAttribute *>(g_malloc( sizeof (AtkAttribute) )); + at->name = g_strdup( atk_text_attribute_get_name( attribute ) ); + at->value = value; + + return g_slist_prepend(attribute_set, at); + } + + return attribute_set; +} + +/*****************************************************************************/ + +AtkAttributeSet* +attribute_set_new_from_property_values( + const uno::Sequence< beans::PropertyValue >& rAttributeList, + bool run_attributes_only, + AtkText *text) +{ + AtkAttributeSet* attribute_set = nullptr; + + sal_Int32 aIndexList[TEXT_ATTRIBUTE_LAST] = { -1 }; + + // Initialize index array with -1 + for(sal_Int32 & rn : aIndexList) + rn = -1; + + find_exported_attributes(aIndexList, rAttributeList); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_BG_COLOR, + get_color_value(rAttributeList, aIndexList, TEXT_ATTRIBUTE_BACKGROUND_COLOR, run_attributes_only ? nullptr : text ) ); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_FG_COLOR, + get_color_value(rAttributeList, aIndexList, TEXT_ATTRIBUTE_FOREGROUND_COLOR, run_attributes_only ? nullptr : text) ); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_INVISIBLE, + get_bool_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_HIDDEN])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_UNDERLINE, + get_underline_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_UNDERLINE])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STRIKETHROUGH, + get_strikethrough_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_STRIKETHROUGH])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_SIZE, + get_height_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_HEIGHT])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_WEIGHT, + get_weight_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WEIGHT])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_FAMILY_NAME, + get_string_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_FONT_NAME])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_VARIANT, + get_variant_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CASEMAP])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STYLE, + get_style_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_POSTURE])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_SCALE, + get_scale_width(rAttributeList, aIndexList[TEXT_ATTRIBUTE_SCALE])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_LANGUAGE, + get_language_string(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LOCALE])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_DIRECTION, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WRITING_MODE], WritingMode2Direction)); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STRETCH, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_KERNING], Kerning2Stretch)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_font_effect ) + atk_text_attribute_font_effect = atk_text_attribute_register("font-effect"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_font_effect, + get_font_effect(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CONTOURED], aIndexList[TEXT_ATTRIBUTE_RELIEF])); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_decoration ) + atk_text_attribute_decoration = atk_text_attribute_register("text-decoration"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_decoration, + get_text_decoration(rAttributeList, aIndexList[TEXT_ATTRIBUTE_BLINKING], + aIndexList[TEXT_ATTRIBUTE_UNDERLINE], aIndexList[TEXT_ATTRIBUTE_STRIKETHROUGH])); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_rotation ) + atk_text_attribute_rotation = atk_text_attribute_register("text-rotation"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_rotation, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_ROTATION], Short2Degree)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_shadow ) + atk_text_attribute_shadow = atk_text_attribute_register("text-shadow"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_shadow, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_SHADOWED], Bool2Shadow)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_writing_mode ) + atk_text_attribute_writing_mode = atk_text_attribute_register("writing-mode"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_writing_mode, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WRITING_MODE], WritingMode2String)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_vertical_align ) + atk_text_attribute_vertical_align = atk_text_attribute_register("vertical-align"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_vertical_align, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CHAR_ESCAPEMENT], Escapement2VerticalAlign)); + + if( run_attributes_only ) + return attribute_set; + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_LEFT_MARGIN, + get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LEFT_MARGIN])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_RIGHT_MARGIN, + get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_RIGHT_MARGIN])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_INDENT, + get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_FIRST_LINE_INDENT])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, + get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TOP_MARGIN])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, + get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_BOTTOM_MARGIN])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_JUSTIFICATION, + get_justification_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_JUSTIFICATION])); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_paragraph_style ) + atk_text_attribute_paragraph_style = atk_text_attribute_register("paragraph-style"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_paragraph_style, + get_string_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_STYLE_NAME])); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_line_height ) + atk_text_attribute_line_height = atk_text_attribute_register("line-height"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_line_height, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LINE_SPACING], LineSpacing2LineHeight)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tab_interval ) + atk_text_attribute_tab_interval = atk_text_attribute_register("tab-interval"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_tab_interval, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TAB_STOPS], DefaultTabStops2String)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tab_stops ) + atk_text_attribute_tab_stops = atk_text_attribute_register("tab-stops"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_tab_stops, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TAB_STOPS], TabStops2String)); + + // #i92233# + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_mm_to_pixel_ratio ) + atk_text_attribute_mm_to_pixel_ratio = atk_text_attribute_register("mm-to-pixel-ratio"); + + attribute_set = attribute_set_prepend( attribute_set, atk_text_attribute_mm_to_pixel_ratio, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO], Float2String)); + + return attribute_set; +} + +AtkAttributeSet* +attribute_set_new_from_extended_attributes( + const css::uno::Reference< css::accessibility::XAccessibleExtendedAttributes >& rExtendedAttributes ) +{ + AtkAttributeSet *pSet = nullptr; + + // extended attributes is a string of colon-separated pairs of property and value, + // with pairs separated by semicolons. Example: "heading-level:2;weight:bold;" + uno::Any anyVal = rExtendedAttributes->getExtendedAttributes(); + OUString sExtendedAttrs; + anyVal >>= sExtendedAttrs; + sal_Int32 nIndex = 0; + do + { + OUString sProperty = sExtendedAttrs.getToken( 0, ';', nIndex ); + + sal_Int32 nColonPos = 0; + OString sPropertyName = OUStringToOString( sProperty.getToken( 0, ':', nColonPos ), + RTL_TEXTENCODING_UTF8 ); + OString sPropertyValue = OUStringToOString( sProperty.getToken( 0, ':', nColonPos ), + RTL_TEXTENCODING_UTF8 ); + + pSet = attribute_set_prepend( pSet, + atk_text_attribute_register( sPropertyName.getStr() ), + g_strdup_printf( "%s", sPropertyValue.getStr() ) ); + } + while ( nIndex >= 0 && nIndex < sExtendedAttrs.getLength() ); + + return pSet; +} + +AtkAttributeSet* attribute_set_prepend_misspelled( AtkAttributeSet* attribute_set ) +{ + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_misspelled ) + atk_text_attribute_misspelled = atk_text_attribute_register( "text-spelling" ); + + attribute_set = attribute_set_prepend( attribute_set, atk_text_attribute_misspelled, + g_strdup_printf( "misspelled" ) ); + + return attribute_set; +} + +// #i92232# +AtkAttributeSet* attribute_set_prepend_tracked_change_insertion( AtkAttributeSet* attribute_set ) +{ + if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change ) + { + atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" ); + } + + attribute_set = attribute_set_prepend( attribute_set, + atk_text_attribute_tracked_change, + g_strdup_printf( "insertion" ) ); + + return attribute_set; +} + +AtkAttributeSet* attribute_set_prepend_tracked_change_deletion( AtkAttributeSet* attribute_set ) +{ + if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change ) + { + atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" ); + } + + attribute_set = attribute_set_prepend( attribute_set, + atk_text_attribute_tracked_change, + g_strdup_printf( "deletion" ) ); + + return attribute_set; +} + +AtkAttributeSet* attribute_set_prepend_tracked_change_formatchange( AtkAttributeSet* attribute_set ) +{ + if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change ) + { + atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" ); + } + + attribute_set = attribute_set_prepend( attribute_set, + atk_text_attribute_tracked_change, + g_strdup_printf( "attribute-change" ) ); + + return attribute_set; +} + +/*****************************************************************************/ + +struct AtkTextAttrMapping +{ + const char * name; + TextPropertyValueFunc const toPropertyValue; +}; + +const AtkTextAttrMapping g_TextAttrMap[] = +{ + { "", InvalidValue }, // ATK_TEXT_ATTR_INVALID = 0 + { "ParaLeftMargin", UnitString2CMM }, // ATK_TEXT_ATTR_LEFT_MARGIN + { "ParaRightMargin", UnitString2CMM }, // ATK_TEXT_ATTR_RIGHT_MARGIN + { "ParaFirstLineIndent", UnitString2CMM }, // ATK_TEXT_ATTR_INDENT + { "CharHidden", String2Bool }, // ATK_TEXT_ATTR_INVISIBLE + { "", InvalidValue }, // ATK_TEXT_ATTR_EDITABLE + { "ParaTopMargin", UnitString2CMM }, // ATK_TEXT_ATTR_PIXELS_ABOVE_LINES + { "ParaBottomMargin", UnitString2CMM }, // ATK_TEXT_ATTR_PIXELS_BELOW_LINES + { "", InvalidValue }, // ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP + { "", InvalidValue }, // ATK_TEXT_ATTR_BG_FULL_HEIGHT + { "", InvalidValue }, // ATK_TEXT_ATTR_RISE + { "CharUnderline", String2Underline }, // ATK_TEXT_ATTR_UNDERLINE + { "CharStrikeout", String2Strikeout }, // ATK_TEXT_ATTR_STRIKETHROUGH + { "CharHeight", String2Float }, // ATK_TEXT_ATTR_SIZE + { "CharScaleWidth", String2Scale }, // ATK_TEXT_ATTR_SCALE + { "CharWeight", String2Weight }, // ATK_TEXT_ATTR_WEIGHT + { "CharLocale", String2Locale }, // ATK_TEXT_ATTR_LANGUAGE + { "CharFontName", SetString }, // ATK_TEXT_ATTR_FAMILY_NAME + { "CharBackColor", String2Color }, // ATK_TEXT_ATTR_BG_COLOR + { "CharColor", String2Color }, // ATK_TEXT_ATTR_FG_COLOR + { "", InvalidValue }, // ATK_TEXT_ATTR_BG_STIPPLE + { "", InvalidValue }, // ATK_TEXT_ATTR_FG_STIPPLE + { "", InvalidValue }, // ATK_TEXT_ATTR_WRAP_MODE + { "", InvalidValue }, // ATK_TEXT_ATTR_DIRECTION + { "ParaAdjust", Justification2Adjust }, // ATK_TEXT_ATTR_JUSTIFICATION + { "", InvalidValue }, // ATK_TEXT_ATTR_STRETCH + { "CharCaseMap", String2CaseMap }, // ATK_TEXT_ATTR_VARIANT + { "CharPosture", Style2FontSlant } // ATK_TEXT_ATTR_STYLE +}; + +/*****************************************************************************/ + +bool +attribute_set_map_to_property_values( + AtkAttributeSet* attribute_set, + uno::Sequence< beans::PropertyValue >& rValueList ) +{ + // Ensure enough space .. + uno::Sequence< beans::PropertyValue > aAttributeList (SAL_N_ELEMENTS(g_TextAttrMap)); + + sal_Int32 nIndex = 0; + for( GSList * item = attribute_set; item != nullptr; item = g_slist_next( item ) ) + { + AtkAttribute* attribute = reinterpret_cast<AtkAttribute *>(item); + + AtkTextAttribute text_attr = atk_text_attribute_for_name( attribute->name ); + if( text_attr < SAL_N_ELEMENTS(g_TextAttrMap) ) + { + if( g_TextAttrMap[text_attr].name[0] != '\0' ) + { + if( ! g_TextAttrMap[text_attr].toPropertyValue( aAttributeList[nIndex].Value, attribute->value) ) + return false; + + aAttributeList[nIndex].Name = OUString::createFromAscii( g_TextAttrMap[text_attr].name ); + aAttributeList[nIndex].State = beans::PropertyState_DIRECT_VALUE; + ++nIndex; + } + } + else + { + // Unsupported text attribute + return false; + } + } + + aAttributeList.realloc( nIndex ); + rValueList = aAttributeList; + return true; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkutil.cxx b/vcl/unx/gtk3/a11y/gtk3atkutil.cxx index 8c1eeaf9882b..cac3ac6e4805 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkutil.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkutil.cxx @@ -5,8 +5,785 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkutil.cxx" +#ifdef AIX +#define _LINUX_SOURCE_COMPAT +#include <sys/timer.h> +#undef _LINUX_SOURCE_COMPAT +#endif + +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessibleText.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weakref.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <vcl/menu.hxx> +#include <vcl/toolbox.hxx> + +#include <unx/gtk/gtkdata.hxx> +#include "atkwrapper.hxx" +#include "atkutil.hxx" + +#include <gtk/gtk.h> +#include <config_version.h> + +#include <cassert> +#include <set> + +using namespace ::com::sun::star; + +namespace +{ + struct theNextFocusObject : + public rtl::Static< uno::WeakReference< accessibility::XAccessible >, theNextFocusObject> + { + }; +} + +static guint focus_notify_handler = 0; + +/*****************************************************************************/ + +extern "C" { + +static gboolean +atk_wrapper_focus_idle_handler (gpointer data) +{ + SolarMutexGuard aGuard; + + focus_notify_handler = 0; + + uno::Reference< accessibility::XAccessible > xAccessible = theNextFocusObject::get(); + if( xAccessible.get() == static_cast < accessibility::XAccessible * > (data) ) + { + AtkObject *atk_obj = xAccessible.is() ? atk_object_wrapper_ref( xAccessible ) : nullptr; + // Gail does not notify focus changes to NULL, so do we .. + if( atk_obj ) + { + SAL_WNODEPRECATED_DECLARATIONS_PUSH + atk_focus_tracker_notify(atk_obj); + SAL_WNODEPRECATED_DECLARATIONS_POP + // #i93269# + // emit text_caret_moved event for <XAccessibleText> object, + // if cursor is inside the <XAccessibleText> object. + // also emit state-changed:focused event under the same condition. + { + AtkObjectWrapper* wrapper_obj = ATK_OBJECT_WRAPPER (atk_obj); + if( wrapper_obj && !wrapper_obj->mpText.is() ) + { + wrapper_obj->mpText.set(wrapper_obj->mpContext, css::uno::UNO_QUERY); + if ( wrapper_obj->mpText.is() ) + { + gint caretPos = -1; + + try { + caretPos = wrapper_obj->mpText->getCaretPosition(); + } + catch(const uno::Exception&) { + g_warning( "Exception in getCaretPosition()" ); + } + + if ( caretPos != -1 ) + { + atk_object_notify_state_change( atk_obj, ATK_STATE_FOCUSED, TRUE ); + g_signal_emit_by_name( atk_obj, "text_caret_moved", caretPos ); + } + } + } + } + g_object_unref(atk_obj); + } + } + + return false; +} + +} // extern "C" + +/*****************************************************************************/ + +static void +atk_wrapper_focus_tracker_notify_when_idle( const uno::Reference< accessibility::XAccessible > &xAccessible ) +{ + if( focus_notify_handler ) + g_source_remove(focus_notify_handler); + + theNextFocusObject::get() = xAccessible; + + focus_notify_handler = g_idle_add (atk_wrapper_focus_idle_handler, xAccessible.get()); +} + +/*****************************************************************************/ + +class DocumentFocusListener : + public ::cppu::WeakImplHelper< accessibility::XAccessibleEventListener > +{ + + std::set< uno::Reference< uno::XInterface > > m_aRefList; + +public: + /// @throws lang::IndexOutOfBoundsException + /// @throws uno::RuntimeException + void attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible + ); + + /// @throws lang::IndexOutOfBoundsException + /// @throws uno::RuntimeException + void attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext + ); + + /// @throws lang::IndexOutOfBoundsException + /// @throws uno::RuntimeException + void attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet + ); + + /// @throws lang::IndexOutOfBoundsException + /// @throws uno::RuntimeException + void detachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible + ); + + /// @throws lang::IndexOutOfBoundsException + /// @throws uno::RuntimeException + void detachRecursive( + const uno::Reference< accessibility::XAccessibleContext >& xContext + ); + + /// @throws lang::IndexOutOfBoundsException + /// @throws uno::RuntimeException + void detachRecursive( + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet + ); + + /// @throws lang::IndexOutOfBoundsException + /// @throws uno::RuntimeException + static uno::Reference< accessibility::XAccessible > getAccessible(const lang::EventObject& aEvent ); + + // XEventListener + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + + // XAccessibleEventListener + virtual void SAL_CALL notifyEvent( const accessibility::AccessibleEventObject& aEvent ) override; +}; + +/*****************************************************************************/ + +void DocumentFocusListener::disposing( const lang::EventObject& aEvent ) +{ + + // Unref the object here, but do not remove as listener since the object + // might no longer be in a state that safely allows this. + if( aEvent.Source.is() ) + m_aRefList.erase(aEvent.Source); + +} + +/*****************************************************************************/ + +void DocumentFocusListener::notifyEvent( const accessibility::AccessibleEventObject& aEvent ) +{ + try { + switch( aEvent.EventId ) + { + case accessibility::AccessibleEventId::STATE_CHANGED: + { + sal_Int16 nState = accessibility::AccessibleStateType::INVALID; + aEvent.NewValue >>= nState; + + if( accessibility::AccessibleStateType::FOCUSED == nState ) + atk_wrapper_focus_tracker_notify_when_idle( getAccessible(aEvent) ); + + break; + } + + case accessibility::AccessibleEventId::CHILD: + { + uno::Reference< accessibility::XAccessible > xChild; + if( (aEvent.OldValue >>= xChild) && xChild.is() ) + detachRecursive(xChild); + + if( (aEvent.NewValue >>= xChild) && xChild.is() ) + attachRecursive(xChild); + + break; + } + + case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN: + SAL_INFO("vcl.a11y", "Invalidate all children called"); + break; + + default: + break; + } + } + catch( const lang::IndexOutOfBoundsException& e ) + { + g_warning("Focused object has invalid index in parent"); + } +} + +/*****************************************************************************/ + +uno::Reference< accessibility::XAccessible > DocumentFocusListener::getAccessible(const lang::EventObject& aEvent ) +{ + uno::Reference< accessibility::XAccessible > xAccessible(aEvent.Source, uno::UNO_QUERY); + + if( xAccessible.is() ) + return xAccessible; + + uno::Reference< accessibility::XAccessibleContext > xContext(aEvent.Source, uno::UNO_QUERY); + + if( xContext.is() ) + { + uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() ); + if( xParent.is() ) + { + uno::Reference< accessibility::XAccessibleContext > xParentContext( xParent->getAccessibleContext() ); + if( xParentContext.is() ) + { + return xParentContext->getAccessibleChild( xContext->getAccessibleIndexInParent() ); + } + } + } + + return uno::Reference< accessibility::XAccessible >(); +} + +/*****************************************************************************/ + +void DocumentFocusListener::attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible +) +{ + uno::Reference< accessibility::XAccessibleContext > xContext = + xAccessible->getAccessibleContext(); + + if( xContext.is() ) + attachRecursive(xAccessible, xContext); +} + +/*****************************************************************************/ + +void DocumentFocusListener::attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext +) +{ + uno::Reference< accessibility::XAccessibleStateSet > xStateSet = + xContext->getAccessibleStateSet(); + + if( xStateSet.is() ) + attachRecursive(xAccessible, xContext, xStateSet); +} + +/*****************************************************************************/ + +void DocumentFocusListener::attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet +) +{ + if( xStateSet->contains(accessibility::AccessibleStateType::FOCUSED ) ) + atk_wrapper_focus_tracker_notify_when_idle( xAccessible ); + + uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY); + + if (!xBroadcaster.is()) + return; + + // If not already done, add the broadcaster to the list and attach as listener. + const uno::Reference< uno::XInterface >& xInterface = xBroadcaster; + if( m_aRefList.insert(xInterface).second ) + { + xBroadcaster->addAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener *>(this)); + + if( ! xStateSet->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS ) ) + { + sal_Int32 n, nmax = xContext->getAccessibleChildCount(); + for( n = 0; n < nmax; n++ ) + { + uno::Reference< accessibility::XAccessible > xChild( xContext->getAccessibleChild( n ) ); + + if( xChild.is() ) + attachRecursive(xChild); + } + } + } +} + +/*****************************************************************************/ + +void DocumentFocusListener::detachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible +) +{ + uno::Reference< accessibility::XAccessibleContext > xContext = + xAccessible->getAccessibleContext(); + + if( xContext.is() ) + detachRecursive(xContext); +} + +/*****************************************************************************/ + +void DocumentFocusListener::detachRecursive( + const uno::Reference< accessibility::XAccessibleContext >& xContext +) +{ + uno::Reference< accessibility::XAccessibleStateSet > xStateSet = + xContext->getAccessibleStateSet(); + + if( xStateSet.is() ) + detachRecursive(xContext, xStateSet); +} + +/*****************************************************************************/ + +void DocumentFocusListener::detachRecursive( + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet +) +{ + uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY); + + if( xBroadcaster.is() && 0 < m_aRefList.erase(xBroadcaster) ) + { + xBroadcaster->removeAccessibleEventListener(static_cast< accessibility::XAccessibleEventListener *>(this)); + + if( ! xStateSet->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS ) ) + { + sal_Int32 n, nmax = xContext->getAccessibleChildCount(); + for( n = 0; n < nmax; n++ ) + { + uno::Reference< accessibility::XAccessible > xChild( xContext->getAccessibleChild( n ) ); + + if( xChild.is() ) + detachRecursive(xChild); + } + } + } +} + +/*****************************************************************************/ + +/* + * page tabs in gtk are widgets, so we need to simulate focus events for those + */ + +static void handle_tabpage_activated(vcl::Window *pWindow) +{ + uno::Reference< accessibility::XAccessible > xAccessible = + pWindow->GetAccessible(); + + if( ! xAccessible.is() ) + return; + + uno::Reference< accessibility::XAccessibleSelection > xSelection( + xAccessible->getAccessibleContext(), uno::UNO_QUERY); + + if( xSelection.is() ) + atk_wrapper_focus_tracker_notify_when_idle( xSelection->getSelectedAccessibleChild(0) ); +} + +/*****************************************************************************/ + +/* + * toolbar items in gtk are widgets, so we need to simulate focus events for those + */ + +static void notify_toolbox_item_focus(ToolBox *pToolBox) +{ + uno::Reference< accessibility::XAccessible > xAccessible = + pToolBox->GetAccessible(); + + if( ! xAccessible.is() ) + return; + + uno::Reference< accessibility::XAccessibleContext > xContext = + xAccessible->getAccessibleContext(); + + if( ! xContext.is() ) + return; + + ToolBox::ImplToolItems::size_type nPos = pToolBox->GetItemPos( pToolBox->GetHighlightItemId() ); + if( nPos != ToolBox::ITEM_NOTFOUND ) + atk_wrapper_focus_tracker_notify_when_idle( xContext->getAccessibleChild( nPos ) ); + //TODO: ToolBox::ImplToolItems::size_type -> sal_Int32 +} + +static void handle_toolbox_highlight(vcl::Window *pWindow) +{ + ToolBox *pToolBox = static_cast <ToolBox *> (pWindow); + + // Make sure either the toolbox or its parent toolbox has the focus + if ( ! pToolBox->HasFocus() ) + { + ToolBox* pToolBoxParent = dynamic_cast< ToolBox* >( pToolBox->GetParent() ); + if ( ! pToolBoxParent || ! pToolBoxParent->HasFocus() ) + return; + } + + notify_toolbox_item_focus(pToolBox); +} + +static void handle_toolbox_highlightoff(vcl::Window const *pWindow) +{ + ToolBox* pToolBoxParent = dynamic_cast< ToolBox* >( pWindow->GetParent() ); + + // Notify when leaving sub toolboxes + if( pToolBoxParent && pToolBoxParent->HasFocus() ) + notify_toolbox_item_focus( pToolBoxParent ); +} + +/*****************************************************************************/ + +static void create_wrapper_for_child( + const uno::Reference< accessibility::XAccessibleContext >& xContext, + sal_Int32 index) +{ + if( xContext.is() ) + { + uno::Reference< accessibility::XAccessible > xChild(xContext->getAccessibleChild(index)); + if( xChild.is() ) + { + // create the wrapper object - it will survive the unref unless it is a transient object + g_object_unref( atk_object_wrapper_ref( xChild ) ); + } + } +} + +/*****************************************************************************/ + +static void handle_toolbox_buttonchange(VclWindowEvent const *pEvent) +{ + vcl::Window* pWindow = pEvent->GetWindow(); + sal_Int32 index = static_cast<sal_Int32>(reinterpret_cast<sal_IntPtr>(pEvent->GetData())); + + if( pWindow && pWindow->IsReallyVisible() ) + { + uno::Reference< accessibility::XAccessible > xAccessible(pWindow->GetAccessible()); + if( xAccessible.is() ) + { + create_wrapper_for_child(xAccessible->getAccessibleContext(), index); + } + } +} + +/*****************************************************************************/ + +namespace { + +struct WindowList { + ~WindowList() { assert(list.empty()); }; + // needs to be empty already on DeInitVCL, but at least check it's empty + // on exit + + std::set< VclPtr<vcl::Window> > list; +}; + +WindowList g_aWindowList; + +} + +DocumentFocusListener & GtkSalData::GetDocumentFocusListener() +{ + if (!m_pDocumentFocusListener) + { + m_pDocumentFocusListener = new DocumentFocusListener; + m_xDocumentFocusListener.set(m_pDocumentFocusListener); + } + return *m_pDocumentFocusListener; +} + +static void handle_get_focus(::VclWindowEvent const * pEvent) +{ + GtkSalData *const pSalData(GetGtkSalData()); + assert(pSalData); + + DocumentFocusListener & rDocumentFocusListener(pSalData->GetDocumentFocusListener()); + + vcl::Window *pWindow = pEvent->GetWindow(); + + // The menu bar is handled through VclEventId::MenuHighlightED + if( ! pWindow || !pWindow->IsReallyVisible() || pWindow->GetType() == WindowType::MENUBARWINDOW ) + return; + + // ToolBoxes are handled through VclEventId::ToolboxHighlight + if( pWindow->GetType() == WindowType::TOOLBOX ) + return; + + if( pWindow->GetType() == WindowType::TABCONTROL ) + { + handle_tabpage_activated( pWindow ); + return; + } + + uno::Reference< accessibility::XAccessible > xAccessible = + pWindow->GetAccessible(); + + if( ! xAccessible.is() ) + return; + + uno::Reference< accessibility::XAccessibleContext > xContext = + xAccessible->getAccessibleContext(); + + if( ! xContext.is() ) + return; + + uno::Reference< accessibility::XAccessibleStateSet > xStateSet = + xContext->getAccessibleStateSet(); + + if( ! xStateSet.is() ) + return; + +/* the UNO ToolBox wrapper does not (yet?) support XAccessibleSelection, so we + * need to add listeners to the children instead of re-using the tabpage stuff + */ + if( xStateSet->contains(accessibility::AccessibleStateType::FOCUSED) && + ( pWindow->GetType() != WindowType::TREELISTBOX ) ) + { + atk_wrapper_focus_tracker_notify_when_idle( xAccessible ); + } + else + { + if( g_aWindowList.list.insert(pWindow).second ) + { + try + { + rDocumentFocusListener.attachRecursive(xAccessible, xContext, xStateSet); + } + catch (const uno::Exception&) + { + g_warning( "Exception caught processing focus events" ); + } + } + } +} + +/*****************************************************************************/ + +static void handle_menu_highlighted(::VclMenuEvent const * pEvent) +{ + try + { + Menu* pMenu = pEvent->GetMenu(); + sal_uInt16 nPos = pEvent->GetItemPos(); + + if( pMenu && nPos != 0xFFFF) + { + uno::Reference< accessibility::XAccessible > xAccessible ( pMenu->GetAccessible() ); + + if( xAccessible.is() ) + { + uno::Reference< accessibility::XAccessibleContext > xContext ( xAccessible->getAccessibleContext() ); + + if( xContext.is() ) + atk_wrapper_focus_tracker_notify_when_idle( xContext->getAccessibleChild( nPos ) ); + } + } + } + catch (const uno::Exception&) + { + g_warning( "Exception caught processing menu highlight events" ); + } +} + +/*****************************************************************************/ + +static void WindowEventHandler(void *, VclSimpleEvent& rEvent) +{ + try + { + switch (rEvent.GetId()) + { + case VclEventId::WindowShow: + break; + case VclEventId::WindowHide: + break; + case VclEventId::WindowClose: + break; + case VclEventId::WindowGetFocus: + handle_get_focus(static_cast< ::VclWindowEvent const * >(&rEvent)); + break; + case VclEventId::WindowLoseFocus: + break; + case VclEventId::WindowMinimize: + break; + case VclEventId::WindowNormalize: + break; + case VclEventId::WindowKeyInput: + case VclEventId::WindowKeyUp: + case VclEventId::WindowCommand: + case VclEventId::WindowMouseMove: + break; + + case VclEventId::MenuHighlight: + if (const VclMenuEvent* pMenuEvent = dynamic_cast<const VclMenuEvent*>(&rEvent)) + { + handle_menu_highlighted(pMenuEvent); + } + else if (const VclAccessibleEvent* pAccEvent = dynamic_cast<const VclAccessibleEvent*>(&rEvent)) + { + const uno::Reference< accessibility::XAccessible >& xAccessible = pAccEvent->GetAccessible(); + if (xAccessible.is()) + atk_wrapper_focus_tracker_notify_when_idle(xAccessible); + } + break; + + case VclEventId::ToolboxHighlight: + handle_toolbox_highlight(static_cast< ::VclWindowEvent const * >(&rEvent)->GetWindow()); + break; + + case VclEventId::ToolboxButtonStateChanged: + handle_toolbox_buttonchange(static_cast< ::VclWindowEvent const * >(&rEvent)); + break; + + case VclEventId::ObjectDying: + g_aWindowList.list.erase( static_cast< ::VclWindowEvent const * >(&rEvent)->GetWindow() ); + [[fallthrough]]; + case VclEventId::ToolboxHighlightOff: + handle_toolbox_highlightoff(static_cast< ::VclWindowEvent const * >(&rEvent)->GetWindow()); + break; + + case VclEventId::TabpageActivate: + handle_tabpage_activated(static_cast< ::VclWindowEvent const * >(&rEvent)->GetWindow()); + break; + + case VclEventId::ComboboxSetText: + // This looks quite strange to me. Stumbled over this when fixing #i104290#. + // This kicked in when leaving the combobox in the toolbar, after that the events worked. + // I guess this was a try to work around missing combobox events, which didn't do the full job, and shouldn't be necessary anymore. + // Fix for #i104290# was done in toolkit/source/awt/vclxaccessiblecomponent, FOCUSED state for compound controls in general. + // create_wrapper_for_children(static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + + default: + break; + } + } + catch (const lang::IndexOutOfBoundsException&) + { + g_warning("Focused object has invalid index in parent"); + } +} + +static Link<VclSimpleEvent&,void> g_aEventListenerLink( nullptr, WindowEventHandler ); + +/*****************************************************************************/ + +extern "C" { + +static G_CONST_RETURN gchar * +ooo_atk_util_get_toolkit_name() +{ + return "VCL"; +} + +/*****************************************************************************/ + +static G_CONST_RETURN gchar * +ooo_atk_util_get_toolkit_version() +{ + return LIBO_VERSION_DOTTED; +} + +/*****************************************************************************/ + +/* + * GObject inheritance + */ + +static void +ooo_atk_util_class_init (AtkUtilClass *) +{ + AtkUtilClass *atk_class; + gpointer data; + + data = g_type_class_peek (ATK_TYPE_UTIL); + atk_class = ATK_UTIL_CLASS (data); + + atk_class->get_toolkit_name = ooo_atk_util_get_toolkit_name; + atk_class->get_toolkit_version = ooo_atk_util_get_toolkit_version; + + ooo_atk_util_ensure_event_listener(); +} + +} // extern "C" + +void ooo_atk_util_ensure_event_listener() +{ + static bool bInited; + if (!bInited) + { + Application::AddEventListener( g_aEventListenerLink ); + bInited = true; + } +} + +GType +ooo_atk_util_get_type() +{ + static GType type = 0; + + if (!type) + { + GType parent_type = g_type_from_name( "GailUtil" ); + + if( ! parent_type ) + { + g_warning( "Unknown type: GailUtil" ); + parent_type = ATK_TYPE_UTIL; + } + + GTypeQuery type_query; + g_type_query( parent_type, &type_query ); + + static const GTypeInfo typeInfo = + { + static_cast<guint16>(type_query.class_size), + nullptr, + nullptr, + reinterpret_cast<GClassInitFunc>(ooo_atk_util_class_init), + nullptr, + nullptr, + static_cast<guint16>(type_query.instance_size), + 0, + nullptr, + nullptr + } ; + + type = g_type_register_static (parent_type, "OOoUtil", &typeInfo, GTypeFlags(0)) ; + } + + return type; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkvalue.cxx b/vcl/unx/gtk3/a11y/gtk3atkvalue.cxx index 30057943bc44..f5e45d3b2556 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkvalue.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkvalue.cxx @@ -5,8 +5,134 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkvalue.cxx" +#include "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleValue.hpp> + +#include <string.h> + +using namespace ::com::sun::star; + +/// @throws uno::RuntimeException +static css::uno::Reference<css::accessibility::XAccessibleValue> + getValue( AtkValue *pValue ) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pValue ); + if( pWrap ) + { + if( !pWrap->mpValue.is() ) + { + pWrap->mpValue.set(pWrap->mpContext, css::uno::UNO_QUERY); + } + + return pWrap->mpValue; + } + + return css::uno::Reference<css::accessibility::XAccessibleValue>(); +} + +static void anyToGValue( const uno::Any& aAny, GValue *pValue ) +{ + // FIXME: expand to lots of types etc. + double aDouble=0; + aAny >>= aDouble; + + memset( pValue, 0, sizeof( GValue ) ); + g_value_init( pValue, G_TYPE_DOUBLE ); + g_value_set_double( pValue, aDouble ); +} + +extern "C" { + +static void +value_wrapper_get_current_value( AtkValue *value, + GValue *gval ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleValue> pValue + = getValue( value ); + if( pValue.is() ) + anyToGValue( pValue->getCurrentValue(), gval ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getCurrentValue()" ); + } +} + +static void +value_wrapper_get_maximum_value( AtkValue *value, + GValue *gval ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleValue> pValue + = getValue( value ); + if( pValue.is() ) + anyToGValue( pValue->getMaximumValue(), gval ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getCurrentValue()" ); + } +} + +static void +value_wrapper_get_minimum_value( AtkValue *value, + GValue *gval ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleValue> pValue + = getValue( value ); + if( pValue.is() ) + anyToGValue( pValue->getMinimumValue(), gval ); + } + catch(const uno::Exception&) { + g_warning( "Exception in getCurrentValue()" ); + } +} + +static gboolean +value_wrapper_set_current_value( AtkValue *value, + const GValue *gval ) +{ + try { + css::uno::Reference<css::accessibility::XAccessibleValue> pValue + = getValue( value ); + if( pValue.is() ) + { + // FIXME - this needs expanding + double aDouble = g_value_get_double( gval ); + return pValue->setCurrentValue( uno::Any(aDouble) ); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getCurrentValue()" ); + } + + return FALSE; +} + +} // extern "C" + +void +valueIfaceInit (AtkValueIface *iface) +{ + g_return_if_fail (iface != nullptr); + + iface->get_current_value = value_wrapper_get_current_value; + iface->get_maximum_value = value_wrapper_get_maximum_value; + iface->get_minimum_value = value_wrapper_get_minimum_value; + iface->set_current_value = value_wrapper_set_current_value; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkwindow.cxx b/vcl/unx/gtk3/a11y/gtk3atkwindow.cxx index cd8479cd4df7..eb72edf4908c 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkwindow.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkwindow.cxx @@ -5,8 +5,326 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkwindow.cxx" +#include <unx/gtk/gtkframe.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <vcl/popupmenuwindow.hxx> +#include <sal/log.hxx> + +#include "atkwindow.hxx" +#include "atkwrapper.hxx" +#include "atkregistry.hxx" + +#include <com/sun/star/accessibility/AccessibleRole.hpp> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + +extern "C" { + +static void (* window_real_initialize) (AtkObject *obj, gpointer data) = nullptr; +static void (* window_real_finalize) (GObject *obj) = nullptr; + +static void +init_from_window( AtkObject *accessible, vcl::Window const *pWindow ) +{ + static AtkRole aDefaultRole = ATK_ROLE_INVALID; + + // Special role for sub-menu and combo-box popups that are exposed directly + // by their parents already. + if( aDefaultRole == ATK_ROLE_INVALID ) + { + SAL_WNODEPRECATED_DECLARATIONS_PUSH + aDefaultRole = atk_role_register( "redundant object" ); + SAL_WNODEPRECATED_DECLARATIONS_POP + } + + AtkRole role = aDefaultRole; + + // Determine the appropriate role for the GtkWindow + switch( pWindow->GetAccessibleRole() ) + { + case AccessibleRole::ALERT: + role = ATK_ROLE_ALERT; + break; + + case AccessibleRole::DIALOG: + role = ATK_ROLE_DIALOG; + break; + + case AccessibleRole::FRAME: + role = ATK_ROLE_FRAME; + break; + + /* Ignore window objects for sub-menus, combo- and list boxes, + * which are exposed as children of their parents. + */ + case AccessibleRole::WINDOW: + { + WindowType type = WindowType::WINDOW; + bool parentIsMenuFloatingWindow = false; + + vcl::Window *pParent = pWindow->GetParent(); + if( pParent ) { + type = pParent->GetType(); + parentIsMenuFloatingWindow = pParent->IsMenuFloatingWindow(); + } + + if( (WindowType::LISTBOX != type) && (WindowType::COMBOBOX != type) && + (WindowType::MENUBARWINDOW != type) && ! parentIsMenuFloatingWindow ) + { + role = ATK_ROLE_WINDOW; + } + } + break; + + default: + { + vcl::Window *pChild = pWindow->GetWindow(GetWindowType::FirstChild); + if( pChild ) + { + if( WindowType::HELPTEXTWINDOW == pChild->GetType() ) + { + role = ATK_ROLE_TOOL_TIP; + pChild->SetAccessibleRole( AccessibleRole::LABEL ); + accessible->name = g_strdup( OUStringToOString( pChild->GetText(), RTL_TEXTENCODING_UTF8 ).getStr() ); + } + else if ( pWindow->GetType() == WindowType::BORDERWINDOW && pChild->GetType() == WindowType::FLOATINGWINDOW ) + { + PopupMenuFloatingWindow* p = dynamic_cast<PopupMenuFloatingWindow*>(pChild); + if (p && p->IsPopupMenu() && p->GetMenuStackLevel() == 0) + { + // This is a top-level menu popup. Register it. + role = ATK_ROLE_POPUP_MENU; + pChild->SetAccessibleRole( AccessibleRole::POPUP_MENU ); + accessible->name = g_strdup( OUStringToOString( pChild->GetText(), RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + } + break; + } + } + + accessible->role = role; +} + +/*****************************************************************************/ + +static gboolean +ooo_window_wrapper_clear_focus(gpointer) +{ + SolarMutexGuard aGuard; + SAL_WNODEPRECATED_DECLARATIONS_PUSH + atk_focus_tracker_notify( nullptr ); + SAL_WNODEPRECATED_DECLARATIONS_POP + return false; +} + +/*****************************************************************************/ + +static gboolean +ooo_window_wrapper_real_focus_gtk (GtkWidget *, GdkEventFocus *) +{ + g_idle_add( ooo_window_wrapper_clear_focus, nullptr ); + return false; +} + +static gboolean ooo_tooltip_map( GtkWidget* pToolTip, gpointer ) +{ + AtkObject* pAccessible = gtk_widget_get_accessible( pToolTip ); + if( pAccessible ) + atk_object_notify_state_change( pAccessible, ATK_STATE_SHOWING, TRUE ); + return FALSE; +} + +static gboolean ooo_tooltip_unmap( GtkWidget* pToolTip, gpointer ) +{ + AtkObject* pAccessible = gtk_widget_get_accessible( pToolTip ); + if( pAccessible ) + atk_object_notify_state_change( pAccessible, ATK_STATE_SHOWING, FALSE ); + return FALSE; +} + +/*****************************************************************************/ + +static bool +isChildPopupMenu(vcl::Window* pWindow) +{ + vcl::Window* pChild = pWindow->GetAccessibleChildWindow(0); + if (!pChild) + return false; + + if (WindowType::FLOATINGWINDOW != pChild->GetType()) + return false; + + PopupMenuFloatingWindow* p = dynamic_cast<PopupMenuFloatingWindow*>(pChild); + if (!p) + return false; + + return p->IsPopupMenu(); +} + +static void +ooo_window_wrapper_real_initialize(AtkObject *obj, gpointer data) +{ + window_real_initialize(obj, data); + + GtkSalFrame *pFrame = GtkSalFrame::getFromWindow( GTK_WINDOW( data ) ); + if( pFrame ) + { + vcl::Window *pWindow = pFrame->GetWindow(); + if( pWindow ) + { + init_from_window( obj, pWindow ); + + Reference< XAccessible > xAccessible( pWindow->GetAccessible() ); + + /* We need the wrapper object for the top-level XAccessible to be + * in the wrapper registry when atk traverses the hierarchy up on + * focus events + */ + if( WindowType::BORDERWINDOW == pWindow->GetType() ) + { + if ( isChildPopupMenu(pWindow) ) + { + AtkObject *child = atk_object_wrapper_new( xAccessible, obj ); + ooo_wrapper_registry_add( xAccessible, child ); + } + else + { + ooo_wrapper_registry_add( xAccessible, obj ); + g_object_set_data( G_OBJECT(obj), "ooo:atk-wrapper-key", xAccessible.get() ); + } + } + else + { + AtkObject *child = atk_object_wrapper_new( xAccessible, obj ); + child->role = ATK_ROLE_FILLER; + if( (ATK_ROLE_DIALOG == obj->role) || (ATK_ROLE_ALERT == obj->role) ) + child->role = ATK_ROLE_OPTION_PANE; + ooo_wrapper_registry_add( xAccessible, child ); + } + } + } + + g_signal_connect_after( GTK_WIDGET( data ), "focus-out-event", + G_CALLBACK (ooo_window_wrapper_real_focus_gtk), + nullptr); + + if( obj->role == ATK_ROLE_TOOL_TIP ) + { + g_signal_connect_after( GTK_WIDGET( data ), "map-event", + G_CALLBACK (ooo_tooltip_map), + nullptr); + g_signal_connect_after( GTK_WIDGET( data ), "unmap-event", + G_CALLBACK (ooo_tooltip_unmap), + nullptr); + } +} + +/*****************************************************************************/ + +static void +ooo_window_wrapper_real_finalize (GObject *obj) +{ + ooo_wrapper_registry_remove( static_cast<XAccessible *>(g_object_get_data( obj, "ooo:atk-wrapper-key" ))); + window_real_finalize( obj ); +} + +/*****************************************************************************/ + +static void +ooo_window_wrapper_class_init (AtkObjectClass *klass, gpointer) +{ + AtkObjectClass *atk_class; + GObjectClass *gobject_class; + gpointer data; + + /* + * Patch the gobject vtable of GailWindow to refer to our instance of + * "initialize". + */ + + data = g_type_class_peek_parent( klass ); + atk_class = ATK_OBJECT_CLASS (data); + + window_real_initialize = atk_class->initialize; + atk_class->initialize = ooo_window_wrapper_real_initialize; + + gobject_class = G_OBJECT_CLASS (data); + + window_real_finalize = gobject_class->finalize; + gobject_class->finalize = ooo_window_wrapper_real_finalize; +} + +} // extern "C" + +/*****************************************************************************/ + +GType +ooo_window_wrapper_get_type() +{ + static GType type = 0; + + if (!type) + { + GType parent_type = g_type_from_name( "GailWindow" ); + + if( ! parent_type ) + { + SAL_INFO("vcl.a11y", "Unknown type: GailWindow"); + parent_type = ATK_TYPE_OBJECT; + } + + GTypeQuery type_query; + g_type_query( parent_type, &type_query ); + + static const GTypeInfo typeInfo = + { + static_cast<guint16>(type_query.class_size), + nullptr, + nullptr, + reinterpret_cast<GClassInitFunc>(ooo_window_wrapper_class_init), + nullptr, + nullptr, + static_cast<guint16>(type_query.instance_size), + 0, + nullptr, + nullptr + } ; + + type = g_type_register_static (parent_type, "OOoWindowAtkObject", &typeInfo, GTypeFlags(0)) ; + } + + return type; +} + +void restore_gail_window_vtable() +{ + AtkObjectClass *atk_class; + gpointer data; + + GType type = g_type_from_name( "GailWindow" ); + + if( type == G_TYPE_INVALID ) + return; + + data = g_type_class_peek( type ); + atk_class = ATK_OBJECT_CLASS (data); + + atk_class->initialize = window_real_initialize; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/a11y/gtk3atkwrapper.cxx b/vcl/unx/gtk3/a11y/gtk3atkwrapper.cxx index 3b07e9536d77..0998553bd927 100644 --- a/vcl/unx/gtk3/a11y/gtk3atkwrapper.cxx +++ b/vcl/unx/gtk3/a11y/gtk3atkwrapper.cxx @@ -5,8 +5,955 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "../../gtk/a11y/atkwrapper.cxx" +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Type.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleRelation.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleText.hpp> +#include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp> +#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp> +#include <com/sun/star/accessibility/XAccessibleValue.hpp> +#include <com/sun/star/accessibility/XAccessibleAction.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleContext2.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp> +#include <com/sun/star/accessibility/XAccessibleStateSet.hpp> +#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp> +#include <com/sun/star/accessibility/XAccessibleTable.hpp> +#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> +#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp> +#include <com/sun/star/accessibility/XAccessibleImage.hpp> +#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp> +#include <com/sun/star/accessibility/XAccessibleHypertext.hpp> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/awt/XExtendedToolkit.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/awt/XTopWindowListener.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/beans/Property.hpp> + +#include <rtl/ref.hxx> +#include <rtl/strbuf.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include "atkwrapper.hxx" +#include "atkregistry.hxx" +#include "atklistener.hxx" +#include "atktextattributes.hxx" + +#include <string.h> +#include <vector> + +using namespace ::com::sun::star; + +static GObjectClass *parent_class = nullptr; + +static AtkRelationType mapRelationType( sal_Int16 nRelation ) +{ + AtkRelationType type = ATK_RELATION_NULL; + + switch( nRelation ) + { + case accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM: + type = ATK_RELATION_FLOWS_FROM; + break; + + case accessibility::AccessibleRelationType::CONTENT_FLOWS_TO: + type = ATK_RELATION_FLOWS_TO; + break; + + case accessibility::AccessibleRelationType::CONTROLLED_BY: + type = ATK_RELATION_CONTROLLED_BY; + break; + + case accessibility::AccessibleRelationType::CONTROLLER_FOR: + type = ATK_RELATION_CONTROLLER_FOR; + break; + + case accessibility::AccessibleRelationType::LABEL_FOR: + type = ATK_RELATION_LABEL_FOR; + break; + + case accessibility::AccessibleRelationType::LABELED_BY: + type = ATK_RELATION_LABELLED_BY; + break; + + case accessibility::AccessibleRelationType::MEMBER_OF: + type = ATK_RELATION_MEMBER_OF; + break; + + case accessibility::AccessibleRelationType::SUB_WINDOW_OF: + type = ATK_RELATION_SUBWINDOW_OF; + break; + + case accessibility::AccessibleRelationType::NODE_CHILD_OF: + type = ATK_RELATION_NODE_CHILD_OF; + break; + + default: + break; + } + + return type; +} + +AtkStateType mapAtkState( sal_Int16 nState ) +{ + AtkStateType type = ATK_STATE_INVALID; + + // A perfect / complete mapping ... + switch( nState ) + { +#define MAP_DIRECT( a ) \ + case accessibility::AccessibleStateType::a: \ + type = ATK_STATE_##a; break + + MAP_DIRECT( INVALID ); + MAP_DIRECT( ACTIVE ); + MAP_DIRECT( ARMED ); + MAP_DIRECT( BUSY ); + MAP_DIRECT( CHECKED ); + MAP_DIRECT( EDITABLE ); + MAP_DIRECT( ENABLED ); + MAP_DIRECT( EXPANDABLE ); + MAP_DIRECT( EXPANDED ); + MAP_DIRECT( FOCUSABLE ); + MAP_DIRECT( FOCUSED ); + MAP_DIRECT( HORIZONTAL ); + MAP_DIRECT( ICONIFIED ); + MAP_DIRECT( INDETERMINATE ); + MAP_DIRECT( MANAGES_DESCENDANTS ); + MAP_DIRECT( MODAL ); + MAP_DIRECT( MULTI_LINE ); + MAP_DIRECT( OPAQUE ); + MAP_DIRECT( PRESSED ); + MAP_DIRECT( RESIZABLE ); + MAP_DIRECT( SELECTABLE ); + MAP_DIRECT( SELECTED ); + MAP_DIRECT( SENSITIVE ); + MAP_DIRECT( SHOWING ); + MAP_DIRECT( SINGLE_LINE ); + MAP_DIRECT( STALE ); + MAP_DIRECT( TRANSIENT ); + MAP_DIRECT( VERTICAL ); + MAP_DIRECT( VISIBLE ); + MAP_DIRECT( DEFAULT ); + // a spelling error ... + case accessibility::AccessibleStateType::DEFUNC: + type = ATK_STATE_DEFUNCT; break; + case accessibility::AccessibleStateType::MULTI_SELECTABLE: + type = ATK_STATE_MULTISELECTABLE; break; + default: + //Mis-use ATK_STATE_LAST_DEFINED to check if a state is unmapped + //NOTE! Do not report it + type = ATK_STATE_LAST_DEFINED; + break; + } + + return type; +} + +static AtkRole getRoleForName( const gchar * name ) +{ + AtkRole ret = atk_role_for_name( name ); + if( ATK_ROLE_INVALID == ret ) + { + // this should only happen in old ATK versions + SAL_WNODEPRECATED_DECLARATIONS_PUSH + ret = atk_role_register( name ); + SAL_WNODEPRECATED_DECLARATIONS_POP + } + + return ret; +} + +static AtkRole mapToAtkRole( sal_Int16 nRole ) +{ + AtkRole role = ATK_ROLE_UNKNOWN; + + static AtkRole roleMap[] = { + ATK_ROLE_UNKNOWN, + ATK_ROLE_ALERT, + ATK_ROLE_COLUMN_HEADER, + ATK_ROLE_CANVAS, + ATK_ROLE_CHECK_BOX, + ATK_ROLE_CHECK_MENU_ITEM, + ATK_ROLE_COLOR_CHOOSER, + ATK_ROLE_COMBO_BOX, + ATK_ROLE_DATE_EDITOR, + ATK_ROLE_DESKTOP_ICON, + ATK_ROLE_DESKTOP_FRAME, // ? pane + ATK_ROLE_DIRECTORY_PANE, + ATK_ROLE_DIALOG, + ATK_ROLE_UNKNOWN, // DOCUMENT - registered below + ATK_ROLE_UNKNOWN, // EMBEDDED_OBJECT - registered below + ATK_ROLE_UNKNOWN, // END_NOTE - registered below + ATK_ROLE_FILE_CHOOSER, + ATK_ROLE_FILLER, + ATK_ROLE_FONT_CHOOSER, + ATK_ROLE_FOOTER, + ATK_ROLE_UNKNOWN, // FOOTNOTE - registered below + ATK_ROLE_FRAME, + ATK_ROLE_GLASS_PANE, + ATK_ROLE_IMAGE, // GRAPHIC + ATK_ROLE_UNKNOWN, // GROUP_BOX - registered below + ATK_ROLE_HEADER, + ATK_ROLE_HEADING, + ATK_ROLE_TEXT, // HYPER_LINK - registered below + ATK_ROLE_ICON, + ATK_ROLE_INTERNAL_FRAME, + ATK_ROLE_LABEL, + ATK_ROLE_LAYERED_PANE, + ATK_ROLE_LIST, + ATK_ROLE_LIST_ITEM, + ATK_ROLE_MENU, + ATK_ROLE_MENU_BAR, + ATK_ROLE_MENU_ITEM, + ATK_ROLE_OPTION_PANE, + ATK_ROLE_PAGE_TAB, + ATK_ROLE_PAGE_TAB_LIST, + ATK_ROLE_PANEL, + ATK_ROLE_PARAGRAPH, + ATK_ROLE_PASSWORD_TEXT, + ATK_ROLE_POPUP_MENU, + ATK_ROLE_PUSH_BUTTON, + ATK_ROLE_PROGRESS_BAR, + ATK_ROLE_RADIO_BUTTON, + ATK_ROLE_RADIO_MENU_ITEM, + ATK_ROLE_ROW_HEADER, + ATK_ROLE_ROOT_PANE, + ATK_ROLE_SCROLL_BAR, + ATK_ROLE_SCROLL_PANE, + ATK_ROLE_PANEL, // SHAPE + ATK_ROLE_SEPARATOR, + ATK_ROLE_SLIDER, + ATK_ROLE_SPIN_BUTTON, // SPIN_BOX ? + ATK_ROLE_SPLIT_PANE, + ATK_ROLE_STATUSBAR, + ATK_ROLE_TABLE, + ATK_ROLE_TABLE_CELL, + ATK_ROLE_TEXT, + ATK_ROLE_PANEL, // TEXT_FRAME + ATK_ROLE_TOGGLE_BUTTON, + ATK_ROLE_TOOL_BAR, + ATK_ROLE_TOOL_TIP, + ATK_ROLE_TREE, + ATK_ROLE_VIEWPORT, + ATK_ROLE_WINDOW, + ATK_ROLE_PUSH_BUTTON, // BUTTON_DROPDOWN + ATK_ROLE_PUSH_BUTTON, // BUTTON_MENU + ATK_ROLE_UNKNOWN, // CAPTION - registered below + ATK_ROLE_UNKNOWN, // CHART - registered below + ATK_ROLE_UNKNOWN, // EDIT_BAR - registered below + ATK_ROLE_UNKNOWN, // FORM - registered below + ATK_ROLE_UNKNOWN, // IMAGE_MAP - registered below + ATK_ROLE_UNKNOWN, // NOTE - registered below + ATK_ROLE_UNKNOWN, // PAGE - registered below + ATK_ROLE_RULER, + ATK_ROLE_UNKNOWN, // SECTION - registered below + ATK_ROLE_UNKNOWN, // TREE_ITEM - registered below + ATK_ROLE_TREE_TABLE, + ATK_ROLE_SCROLL_PANE, // COMMENT - mapped to atk_role_scroll_pane + ATK_ROLE_UNKNOWN // COMMENT_END - mapped to atk_role_unknown +#if defined(ATK_CHECK_VERSION) + //older ver that doesn't define ATK_CHECK_VERSION doesn't have the following + , ATK_ROLE_DOCUMENT_PRESENTATION + , ATK_ROLE_DOCUMENT_SPREADSHEET + , ATK_ROLE_DOCUMENT_TEXT +#if ATK_CHECK_VERSION(2,15,2) + , ATK_ROLE_STATIC +#else + , ATK_ROLE_LABEL +#endif +#else + //older version should fallback to DOCUMENT_FRAME role + , ATK_ROLE_DOCUMENT_FRAME + , ATK_ROLE_DOCUMENT_FRAME + , ATK_ROLE_DOCUMENT_FRAME + , ATK_ROLE_LABEL +#endif + }; + + static bool initialized = false; + + if( ! initialized ) + { + // the accessible roles below were added to ATK in later versions, + // with role_for_name we will know if they exist in runtime. + roleMap[accessibility::AccessibleRole::EDIT_BAR] = getRoleForName("edit bar"); + roleMap[accessibility::AccessibleRole::EMBEDDED_OBJECT] = getRoleForName("embedded"); + roleMap[accessibility::AccessibleRole::CHART] = getRoleForName("chart"); + roleMap[accessibility::AccessibleRole::CAPTION] = getRoleForName("caption"); + roleMap[accessibility::AccessibleRole::DOCUMENT] = getRoleForName("document frame"); + roleMap[accessibility::AccessibleRole::PAGE] = getRoleForName("page"); + roleMap[accessibility::AccessibleRole::SECTION] = getRoleForName("section"); + roleMap[accessibility::AccessibleRole::FORM] = getRoleForName("form"); + roleMap[accessibility::AccessibleRole::GROUP_BOX] = getRoleForName("grouping"); + roleMap[accessibility::AccessibleRole::COMMENT] = getRoleForName("comment"); + roleMap[accessibility::AccessibleRole::IMAGE_MAP] = getRoleForName("image map"); + roleMap[accessibility::AccessibleRole::TREE_ITEM] = getRoleForName("tree item"); + roleMap[accessibility::AccessibleRole::HYPER_LINK] = getRoleForName("link"); + roleMap[accessibility::AccessibleRole::END_NOTE] = getRoleForName("footnote"); + roleMap[accessibility::AccessibleRole::FOOTNOTE] = getRoleForName("footnote"); + roleMap[accessibility::AccessibleRole::NOTE] = getRoleForName("comment"); + + initialized = true; + } + + static const sal_Int32 nMapSize = SAL_N_ELEMENTS(roleMap); + if( 0 <= nRole && nMapSize > nRole ) + role = roleMap[nRole]; + + return role; +} + +/*****************************************************************************/ + +extern "C" { + +/*****************************************************************************/ + +static G_CONST_RETURN gchar* +wrapper_get_name( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + + if( obj->mpContext.is() ) + { + try { + OString aName = + OUStringToOString( + obj->mpContext->getAccessibleName(), + RTL_TEXTENCODING_UTF8); + + int nCmp = atk_obj->name ? rtl_str_compare( atk_obj->name, aName.getStr() ) : -1; + if( nCmp != 0 ) + { + if( atk_obj->name ) + g_free(atk_obj->name); + atk_obj->name = g_strdup(aName.getStr()); + } + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleName()" ); + } + } + + return ATK_OBJECT_CLASS (parent_class)->get_name(atk_obj); +} + +/*****************************************************************************/ + +static G_CONST_RETURN gchar* +wrapper_get_description( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + + if( obj->mpContext.is() ) + { + try { + OString aDescription = + OUStringToOString( + obj->mpContext->getAccessibleDescription(), + RTL_TEXTENCODING_UTF8); + + g_free(atk_obj->description); + atk_obj->description = g_strdup(aDescription.getStr()); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleDescription()" ); + } + } + + return ATK_OBJECT_CLASS (parent_class)->get_description(atk_obj); + +} + +/*****************************************************************************/ + +static AtkAttributeSet * +wrapper_get_attributes( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER( atk_obj ); + AtkAttributeSet *pSet = nullptr; + + try + { + uno::Reference< accessibility::XAccessibleExtendedAttributes > + xExtendedAttrs( obj->mpContext, uno::UNO_QUERY ); + if( xExtendedAttrs.is() ) + pSet = attribute_set_new_from_extended_attributes( xExtendedAttrs ); + } + catch(const uno::Exception&) + { + g_warning( "Exception in getAccessibleAttributes()" ); + } + + return pSet; +} + +/*****************************************************************************/ + +static gint +wrapper_get_n_children( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + gint n = 0; + + if( obj->mpContext.is() ) + { + try { + n = obj->mpContext->getAccessibleChildCount(); + } + catch(const uno::Exception&) { + OSL_FAIL("Exception in getAccessibleChildCount()" ); + } + } + + return n; +} + +/*****************************************************************************/ + +static AtkObject * +wrapper_ref_child( AtkObject *atk_obj, + gint i ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + AtkObject* child = nullptr; + + // see comments above atk_object_wrapper_remove_child + if( -1 < i && obj->index_of_child_about_to_be_removed == i ) + { + g_object_ref( obj->child_about_to_be_removed ); + return obj->child_about_to_be_removed; + } + + if( obj->mpContext.is() ) + { + try { + uno::Reference< accessibility::XAccessible > xAccessible = + obj->mpContext->getAccessibleChild( i ); + + child = atk_object_wrapper_ref( xAccessible ); + } + catch(const uno::Exception&) { + OSL_FAIL("Exception in getAccessibleChild"); + } + } + + return child; +} + +/*****************************************************************************/ + +static gint +wrapper_get_index_in_parent( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + + //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y + if (obj->mpOrig) + return atk_object_get_index_in_parent(obj->mpOrig); + + gint i = -1; + + if( obj->mpContext.is() ) + { + try { + i = obj->mpContext->getAccessibleIndexInParent(); + } + catch(const uno::Exception&) { + g_warning( "Exception in getAccessibleIndexInParent()" ); + } + } + return i; +} + +/*****************************************************************************/ + +AtkRelation* +atk_object_wrapper_relation_new(const accessibility::AccessibleRelation& rRelation) +{ + sal_uInt32 nTargetCount = rRelation.TargetSet.getLength(); + + std::vector<AtkObject*> aTargets; + + for (const auto& rTarget : rRelation.TargetSet) + { + uno::Reference< accessibility::XAccessible > xAccessible( rTarget, uno::UNO_QUERY ); + aTargets.push_back(atk_object_wrapper_ref(xAccessible)); + } + + AtkRelation *pRel = + atk_relation_new( + aTargets.data(), nTargetCount, + mapRelationType( rRelation.RelationType ) + ); + + return pRel; +} + +static AtkRelationSet * +wrapper_ref_relation_set( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + + //if we're a native GtkDrawingArea with custom a11y, use the default toolkit relation set impl + if (obj->mpOrig) + return atk_object_ref_relation_set(obj->mpOrig); + + AtkRelationSet *pSet = atk_relation_set_new(); + + if( obj->mpContext.is() ) + { + try { + uno::Reference< accessibility::XAccessibleRelationSet > xRelationSet( + obj->mpContext->getAccessibleRelationSet() + ); + + sal_Int32 nRelations = xRelationSet.is() ? xRelationSet->getRelationCount() : 0; + for( sal_Int32 n = 0; n < nRelations; n++ ) + { + AtkRelation *pRel = atk_object_wrapper_relation_new(xRelationSet->getRelation(n)); + atk_relation_set_add(pSet, pRel); + g_object_unref(pRel); + } + } + catch(const uno::Exception &) { + g_object_unref( G_OBJECT( pSet ) ); + pSet = nullptr; + } + } + + return pSet; +} + +static AtkStateSet * +wrapper_ref_state_set( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + AtkStateSet *pSet = atk_state_set_new(); + + if( obj->mpContext.is() ) + { + try { + uno::Reference< accessibility::XAccessibleStateSet > xStateSet( + obj->mpContext->getAccessibleStateSet()); + + if( xStateSet.is() ) + { + uno::Sequence< sal_Int16 > aStates = xStateSet->getStates(); + + for( const auto nState : aStates ) + { + // ATK_STATE_LAST_DEFINED is used to check if the state + // is unmapped, do not report it to Atk + if ( mapAtkState( nState ) != ATK_STATE_LAST_DEFINED ) + atk_state_set_add_state( pSet, mapAtkState( nState ) ); + } + + // We need to emulate FOCUS state for menus, menu-items etc. + if( atk_obj == atk_get_focus_object() ) + atk_state_set_add_state( pSet, ATK_STATE_FOCUSED ); +/* FIXME - should we do this ? + else + atk_state_set_remove_state( pSet, ATK_STATE_FOCUSED ); +*/ + } + } + + catch(const uno::Exception &) { + g_warning( "Exception in wrapper_ref_state_set" ); + atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT ); + } + } + else + atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT ); + + return pSet; +} + +/*****************************************************************************/ + +static void +atk_object_wrapper_finalize (GObject *obj) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER (obj); + + if( pWrap->mpAccessible.is() ) + { + ooo_wrapper_registry_remove( pWrap->mpAccessible ); + pWrap->mpAccessible.clear(); + } + + atk_object_wrapper_dispose( pWrap ); + + parent_class->finalize( obj ); +} + +static void +atk_object_wrapper_class_init (AtkObjectWrapperClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( klass ); + AtkObjectClass *atk_class = ATK_OBJECT_CLASS( klass ); + + parent_class = static_cast<GObjectClass *>(g_type_class_peek_parent (klass)); + + // GObject methods + gobject_class->finalize = atk_object_wrapper_finalize; + + // AtkObject methods + atk_class->get_name = wrapper_get_name; + atk_class->get_description = wrapper_get_description; + atk_class->get_attributes = wrapper_get_attributes; + atk_class->get_n_children = wrapper_get_n_children; + atk_class->ref_child = wrapper_ref_child; + atk_class->get_index_in_parent = wrapper_get_index_in_parent; + atk_class->ref_relation_set = wrapper_ref_relation_set; + atk_class->ref_state_set = wrapper_ref_state_set; +} + +static void +atk_object_wrapper_init (AtkObjectWrapper *wrapper, + AtkObjectWrapperClass*) +{ + wrapper->mpAction = nullptr; + wrapper->mpComponent = nullptr; + wrapper->mpEditableText = nullptr; + wrapper->mpHypertext = nullptr; + wrapper->mpImage = nullptr; + wrapper->mpSelection = nullptr; + wrapper->mpTable = nullptr; + wrapper->mpText = nullptr; + wrapper->mpValue = nullptr; +} + +} // extern "C" + +GType +atk_object_wrapper_get_type() +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo typeInfo = + { + sizeof (AtkObjectWrapperClass), + nullptr, + nullptr, + reinterpret_cast<GClassInitFunc>(atk_object_wrapper_class_init), + nullptr, + nullptr, + sizeof (AtkObjectWrapper), + 0, + reinterpret_cast<GInstanceInitFunc>(atk_object_wrapper_init), + nullptr + } ; + type = g_type_register_static (ATK_TYPE_OBJECT, + "OOoAtkObj", + &typeInfo, GTypeFlags(0)) ; + } + return type; +} + +static bool +isOfType( uno::XInterface *pInterface, const uno::Type & rType ) +{ + g_return_val_if_fail( pInterface != nullptr, false ); + + bool bIs = false; + try { + uno::Any aRet = pInterface->queryInterface( rType ); + + bIs = ( ( typelib_TypeClass_INTERFACE == aRet.pType->eTypeClass ) && + ( aRet.pReserved != nullptr ) ); + } catch( const uno::Exception &) { } + + return bIs; +} + +extern "C" { +typedef GType (* GetGIfaceType ) (); +} +const struct { + const char *name; + GInterfaceInitFunc const aInit; + GetGIfaceType const aGetGIfaceType; + const uno::Type & (*aGetUnoType) (); +} aTypeTable[] = { +// re-location heaven: + { + "Comp", reinterpret_cast<GInterfaceInitFunc>(componentIfaceInit), + atk_component_get_type, + cppu::UnoType<accessibility::XAccessibleComponent>::get + }, + { + "Act", reinterpret_cast<GInterfaceInitFunc>(actionIfaceInit), + atk_action_get_type, + cppu::UnoType<accessibility::XAccessibleAction>::get + }, + { + "Txt", reinterpret_cast<GInterfaceInitFunc>(textIfaceInit), + atk_text_get_type, + cppu::UnoType<accessibility::XAccessibleText>::get + }, + { + "Val", reinterpret_cast<GInterfaceInitFunc>(valueIfaceInit), + atk_value_get_type, + cppu::UnoType<accessibility::XAccessibleValue>::get + }, + { + "Tab", reinterpret_cast<GInterfaceInitFunc>(tableIfaceInit), + atk_table_get_type, + cppu::UnoType<accessibility::XAccessibleTable>::get + }, + { + "Edt", reinterpret_cast<GInterfaceInitFunc>(editableTextIfaceInit), + atk_editable_text_get_type, + cppu::UnoType<accessibility::XAccessibleEditableText>::get + }, + { + "Img", reinterpret_cast<GInterfaceInitFunc>(imageIfaceInit), + atk_image_get_type, + cppu::UnoType<accessibility::XAccessibleImage>::get + }, + { + "Hyp", reinterpret_cast<GInterfaceInitFunc>(hypertextIfaceInit), + atk_hypertext_get_type, + cppu::UnoType<accessibility::XAccessibleHypertext>::get + }, + { + "Sel", reinterpret_cast<GInterfaceInitFunc>(selectionIfaceInit), + atk_selection_get_type, + cppu::UnoType<accessibility::XAccessibleSelection>::get + } + // AtkDocument is a nastily broken interface (so far) + // we could impl. get_document_type perhaps though. +}; + +const int aTypeTableSize = G_N_ELEMENTS( aTypeTable ); + +static GType +ensureTypeFor( uno::XInterface *pAccessible ) +{ + int i; + bool bTypes[ aTypeTableSize ] = { false, }; + OStringBuffer aTypeNameBuf( "OOoAtkObj" ); + + for( i = 0; i < aTypeTableSize; i++ ) + { + if( isOfType( pAccessible, aTypeTable[i].aGetUnoType() ) ) + { + aTypeNameBuf.append(aTypeTable[i].name); + bTypes[i] = true; + } + } + + OString aTypeName = aTypeNameBuf.makeStringAndClear(); + GType nType = g_type_from_name( aTypeName.getStr() ); + if( nType == G_TYPE_INVALID ) + { + GTypeInfo aTypeInfo = { + sizeof( AtkObjectWrapperClass ), + nullptr, nullptr, nullptr, nullptr, nullptr, + sizeof( AtkObjectWrapper ), + 0, nullptr, nullptr + } ; + nType = g_type_register_static( ATK_TYPE_OBJECT_WRAPPER, + aTypeName.getStr(), &aTypeInfo, + GTypeFlags(0) ) ; + + for( int j = 0; j < aTypeTableSize; j++ ) + if( bTypes[j] ) + { + GInterfaceInfo aIfaceInfo = { nullptr, nullptr, nullptr }; + aIfaceInfo.interface_init = aTypeTable[j].aInit; + g_type_add_interface_static (nType, aTypeTable[j].aGetGIfaceType(), + &aIfaceInfo); + } + } + return nType; +} + +AtkObject * +atk_object_wrapper_ref( const uno::Reference< accessibility::XAccessible > &rxAccessible, bool create ) +{ + g_return_val_if_fail( rxAccessible.get() != nullptr, nullptr ); + + AtkObject *obj = ooo_wrapper_registry_get(rxAccessible); + if( obj ) + { + g_object_ref( obj ); + return obj; + } + + if( create ) + return atk_object_wrapper_new( rxAccessible ); + + return nullptr; +} + +AtkObject * +atk_object_wrapper_new( const css::uno::Reference< css::accessibility::XAccessible >& rxAccessible, + AtkObject* parent, AtkObject* orig ) +{ + g_return_val_if_fail( rxAccessible.get() != nullptr, nullptr ); + + AtkObjectWrapper *pWrap = nullptr; + + try { + uno::Reference< accessibility::XAccessibleContext > xContext(rxAccessible->getAccessibleContext()); + + g_return_val_if_fail( xContext.get() != nullptr, nullptr ); + + GType nType = ensureTypeFor( xContext.get() ); + gpointer obj = g_object_new( nType, nullptr); + + pWrap = ATK_OBJECT_WRAPPER( obj ); + pWrap->mpAccessible = rxAccessible; + + pWrap->index_of_child_about_to_be_removed = -1; + pWrap->child_about_to_be_removed = nullptr; + + pWrap->mpContext = xContext; + pWrap->mpOrig = orig; + + AtkObject* atk_obj = ATK_OBJECT(pWrap); + atk_obj->role = mapToAtkRole( xContext->getAccessibleRole() ); + atk_obj->accessible_parent = parent; + + ooo_wrapper_registry_add( rxAccessible, atk_obj ); + + if( parent ) + g_object_ref( atk_obj->accessible_parent ); + else + { + /* gail_focus_tracker remembers the focused object at the first + * parent in the hierarchy that is a Gtk+ widget, but at the time the + * event gets processed (at idle), it may be too late to create the + * hierarchy, so doing it now .. + */ + uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() ); + + if( xParent.is() ) + atk_obj->accessible_parent = atk_object_wrapper_ref( xParent ); + } + + // Attach a listener to the UNO object if it's not TRANSIENT + uno::Reference< accessibility::XAccessibleStateSet > xStateSet( xContext->getAccessibleStateSet() ); + if( xStateSet.is() && ! xStateSet->contains( accessibility::AccessibleStateType::TRANSIENT ) ) + { + uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY); + if( xBroadcaster.is() ) + { + uno::Reference<accessibility::XAccessibleEventListener> xListener(new AtkListener(pWrap)); + xBroadcaster->addAccessibleEventListener(xListener); + } + else + OSL_ASSERT( false ); + } + +#if ATK_CHECK_VERSION(2,33,1) + { + css::uno::Reference<css::accessibility::XAccessibleContext2> xContext2(xContext, css::uno::UNO_QUERY); + if( xContext2.is() ) + { + OString aId = OUStringToOString( xContext2->getAccessibleId(), RTL_TEXTENCODING_UTF8); + atk_object_set_accessible_id(atk_obj, aId.getStr()); + } + } +#endif + + return ATK_OBJECT( pWrap ); + } + catch (const uno::Exception &) + { + if( pWrap ) + g_object_unref( pWrap ); + + return nullptr; + } +} + +/*****************************************************************************/ + +void atk_object_wrapper_add_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index) +{ + AtkObject *atk_obj = ATK_OBJECT( wrapper ); + + atk_object_set_parent( child, atk_obj ); + g_signal_emit_by_name( atk_obj, "children_changed::add", index, child, nullptr ); +} + +/*****************************************************************************/ + +void atk_object_wrapper_remove_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index) +{ + /* + * the atk-bridge GTK+ module gets back to the event source to ref the child just + * vanishing, so we keep this reference because the semantic on OOo side is different. + */ + wrapper->child_about_to_be_removed = child; + wrapper->index_of_child_about_to_be_removed = index; + + g_signal_emit_by_name( ATK_OBJECT( wrapper ), "children_changed::remove", index, child, nullptr ); + + wrapper->index_of_child_about_to_be_removed = -1; + wrapper->child_about_to_be_removed = nullptr; +} + +/*****************************************************************************/ + +void atk_object_wrapper_set_role(AtkObjectWrapper* wrapper, sal_Int16 role) +{ + AtkObject *atk_obj = ATK_OBJECT( wrapper ); + atk_object_set_role( atk_obj, mapToAtkRole( role ) ); +} + +/*****************************************************************************/ + +void atk_object_wrapper_dispose(AtkObjectWrapper* wrapper) +{ + wrapper->mpContext.clear(); + wrapper->mpAction.clear(); + wrapper->mpComponent.clear(); + wrapper->mpEditableText.clear(); + wrapper->mpHypertext.clear(); + wrapper->mpImage.clear(); + wrapper->mpSelection.clear(); + wrapper->mpMultiLineText.clear(); + wrapper->mpTable.clear(); + wrapper->mpText.clear(); + wrapper->mpTextMarkup.clear(); + wrapper->mpTextAttributes.clear(); + wrapper->mpValue.clear(); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/fpicker/SalGtkFilePicker.cxx b/vcl/unx/gtk3/fpicker/SalGtkFilePicker.cxx index c106aa0ce4c4..2552bb16b253 100644 --- a/vcl/unx/gtk/fpicker/SalGtkFilePicker.cxx +++ b/vcl/unx/gtk3/fpicker/SalGtkFilePicker.cxx @@ -53,7 +53,7 @@ #include <set> #include <string.h> -#include <gtk/fpicker/SalGtkFilePicker.hxx> +#include "SalGtkFilePicker.hxx" using namespace ::com::sun::star; using namespace ::com::sun::star::ui::dialogs; @@ -437,15 +437,11 @@ dialog_remove_buttons( GtkDialog *pDialog ) { g_return_if_fail( GTK_IS_DIALOG( pDialog ) ); -#if GTK_CHECK_VERSION(3,0,0) GtkWidget *pHeaderBar = gtk_dialog_get_header_bar(pDialog); if( pHeaderBar != nullptr ) dialog_remove_buttons( pHeaderBar ); else dialog_remove_buttons(gtk_dialog_get_action_area(pDialog)); -#else - dialog_remove_buttons(pDialog->action_area); -#endif } namespace { @@ -798,14 +794,10 @@ uno::Sequence<OUString> SAL_CALL SalGtkFilePicker::getSelectedFiles() } if( bChangeFilter && bExtensionTypedIn ) { -#if GTK_CHECK_VERSION(3,0,0) gchar* pCurrentName = gtk_file_chooser_get_current_name(GTK_FILE_CHOOSER(m_pDialog)); setCurrentFilter( aNewFilter ); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(m_pDialog), pCurrentName); g_free(pCurrentName); -#else - setCurrentFilter( aNewFilter ); -#endif } } } @@ -1009,12 +1001,7 @@ sal_Int16 SAL_CALL SalGtkFilePicker::execute() gtk_window_set_title( GTK_WINDOW( dlg ), OUStringToOString(getResString(FILE_PICKER_TITLE_SAVE ), RTL_TEXTENCODING_UTF8 ).getStr() ); -#if GTK_CHECK_VERSION(3,0,0) gtk_window_set_transient_for(GTK_WINDOW(dlg), GTK_WINDOW(m_pDialog)); -#else - if (pParent) - gtk_window_set_transient_for(GTK_WINDOW(dlg), pParent); -#endif RunDialog* pAnotherDialog = new RunDialog(dlg, xToolkit, xDesktop); uno::Reference < awt::XTopWindowListener > xAnotherLifeCycle(pAnotherDialog); btn = pAnotherDialog->run(); diff --git a/vcl/unx/gtk/fpicker/SalGtkFilePicker.hxx b/vcl/unx/gtk3/fpicker/SalGtkFilePicker.hxx index ae3e1baa8275..db471d5e5c6c 100644 --- a/vcl/unx/gtk/fpicker/SalGtkFilePicker.hxx +++ b/vcl/unx/gtk3/fpicker/SalGtkFilePicker.hxx @@ -32,7 +32,7 @@ #include <memory> #include <rtl/ustring.hxx> -#include <gtk/fpicker/SalGtkPicker.hxx> +#include "SalGtkPicker.hxx" // Implementation class for the XFilePicker Interface diff --git a/vcl/unx/gtk/fpicker/SalGtkFolderPicker.cxx b/vcl/unx/gtk3/fpicker/SalGtkFolderPicker.cxx index ade5ff504539..13df53994dee 100644 --- a/vcl/unx/gtk/fpicker/SalGtkFolderPicker.cxx +++ b/vcl/unx/gtk3/fpicker/SalGtkFolderPicker.cxx @@ -34,7 +34,7 @@ #include <com/sun/star/ui/dialogs/TemplateDescription.hpp> #include <vcl/svapp.hxx> #include <unx/gtk/gtkinst.hxx> -#include <gtk/fpicker/SalGtkFolderPicker.hxx> +#include "SalGtkFolderPicker.hxx" #include <sal/log.hxx> #include <string.h> diff --git a/vcl/unx/gtk/fpicker/SalGtkFolderPicker.hxx b/vcl/unx/gtk3/fpicker/SalGtkFolderPicker.hxx index 46cf107a04ae..229bbe8b8749 100644 --- a/vcl/unx/gtk/fpicker/SalGtkFolderPicker.hxx +++ b/vcl/unx/gtk3/fpicker/SalGtkFolderPicker.hxx @@ -25,7 +25,7 @@ #include <rtl/ustring.hxx> #include <cppuhelper/implbase.hxx> -#include <gtk/fpicker/SalGtkPicker.hxx> +#include "SalGtkPicker.hxx" class SalGtkFolderPicker : public SalGtkPicker, diff --git a/vcl/unx/gtk/fpicker/SalGtkPicker.cxx b/vcl/unx/gtk3/fpicker/SalGtkPicker.cxx index 3e443ea04692..7db114069651 100644 --- a/vcl/unx/gtk/fpicker/SalGtkPicker.cxx +++ b/vcl/unx/gtk3/fpicker/SalGtkPicker.cxx @@ -36,7 +36,7 @@ #include <vcl/window.hxx> #include <unx/gtk/gtkframe.hxx> -#include <gtk/fpicker/SalGtkPicker.hxx> +#include "SalGtkPicker.hxx" using namespace ::rtl; using namespace ::com::sun::star; diff --git a/vcl/unx/gtk/fpicker/SalGtkPicker.hxx b/vcl/unx/gtk3/fpicker/SalGtkPicker.hxx index 0eb8720f771a..0eb8720f771a 100644 --- a/vcl/unx/gtk/fpicker/SalGtkPicker.hxx +++ b/vcl/unx/gtk3/fpicker/SalGtkPicker.hxx diff --git a/vcl/unx/gtk/fpicker/eventnotification.hxx b/vcl/unx/gtk3/fpicker/eventnotification.hxx index 88a472e0e8ce..88a472e0e8ce 100644 --- a/vcl/unx/gtk/fpicker/eventnotification.hxx +++ b/vcl/unx/gtk3/fpicker/eventnotification.hxx diff --git a/vcl/unx/gtk/fpicker/resourceprovider.cxx b/vcl/unx/gtk3/fpicker/resourceprovider.cxx index 745521ac596a..818018b4a7e5 100644 --- a/vcl/unx/gtk/fpicker/resourceprovider.cxx +++ b/vcl/unx/gtk3/fpicker/resourceprovider.cxx @@ -24,7 +24,7 @@ #include <strings.hrc> #include <svdata.hxx> -#include <gtk/fpicker/SalGtkPicker.hxx> +#include "SalGtkPicker.hxx" using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds; using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds; diff --git a/vcl/unx/gtk3/gtk3fpicker.cxx b/vcl/unx/gtk3/gtk3fpicker.cxx deleted file mode 100644 index 9b83c57daed8..000000000000 --- a/vcl/unx/gtk3/gtk3fpicker.cxx +++ /dev/null @@ -1,15 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.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 "../gtk/fpicker/resourceprovider.cxx" -#include "../gtk/fpicker/SalGtkPicker.cxx" -#include "../gtk/fpicker/SalGtkFilePicker.cxx" -#include "../gtk/fpicker/SalGtkFolderPicker.cxx" - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtk3gloactiongroup.cxx b/vcl/unx/gtk3/gtk3gloactiongroup.cxx index 749f5434a456..19b412faec38 100644 --- a/vcl/unx/gtk3/gtk3gloactiongroup.cxx +++ b/vcl/unx/gtk3/gtk3gloactiongroup.cxx @@ -1,5 +1,403 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ -#include "../gtk/gloactiongroup.cxx" +#include <unx/gtk/gtksalmenu.hxx> + +#include <unx/gtk/gloactiongroup.h> +#include <unx/gtk/gtkinst.hxx> +#include <unx/gtk/gtkframe.hxx> + +#include <sal/log.hxx> + +/* + * GLOAction + */ + +#define G_TYPE_LO_ACTION (g_lo_action_get_type ()) +#define G_LO_ACTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_LO_ACTION, GLOAction)) + +struct GLOAction +{ + GObject parent_instance; + + gint item_id; // Menu item ID. + gboolean submenu; // TRUE if action is a submenu action. + gboolean enabled; // TRUE if action is enabled. + GVariantType* parameter_type; // A GVariantType with the action parameter type. + GVariantType* state_type; // A GVariantType with item state type + GVariant* state_hint; // A GVariant with state hints. + GVariant* state; // A GVariant with current item state +}; + +typedef GObjectClass GLOActionClass; + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +G_DEFINE_TYPE (GLOAction, g_lo_action, G_TYPE_OBJECT); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +static GLOAction* +g_lo_action_new() +{ + return G_LO_ACTION (g_object_new (G_TYPE_LO_ACTION, nullptr)); +} + +static void +g_lo_action_init (GLOAction *action) +{ + action->item_id = -1; + action->submenu = FALSE; + action->enabled = TRUE; + action->parameter_type = nullptr; + action->state_type = nullptr; + action->state_hint = nullptr; + action->state = nullptr; +} + +static void +g_lo_action_finalize (GObject *object) +{ + GLOAction* action = G_LO_ACTION(object); + + if (action->parameter_type) + g_variant_type_free (action->parameter_type); + + if (action->state_type) + g_variant_type_free (action->state_type); + + if (action->state_hint) + g_variant_unref (action->state_hint); + + if (action->state) + g_variant_unref (action->state); + + G_OBJECT_CLASS (g_lo_action_parent_class)->finalize (object); +} + +static void +g_lo_action_class_init (GLOActionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = g_lo_action_finalize; +} + +/* + * GLOActionGroup + */ + +struct GLOActionGroupPrivate +{ + GHashTable *table; /* string -> GLOAction */ +}; + +static void g_lo_action_group_iface_init (GActionGroupInterface *); + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +G_DEFINE_TYPE_WITH_CODE (GLOActionGroup, + g_lo_action_group, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, + g_lo_action_group_iface_init)); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +static gchar ** +g_lo_action_group_list_actions (GActionGroup *group) +{ + GLOActionGroup *loGroup = G_LO_ACTION_GROUP (group); + GHashTableIter iter; + gint n, i = 0; + gchar **keys; + gpointer key; + + n = g_hash_table_size (loGroup->priv->table); + keys = g_new (gchar *, n + 1); + + g_hash_table_iter_init (&iter, loGroup->priv->table); + while (g_hash_table_iter_next (&iter, &key, nullptr)) + keys[i++] = g_strdup (static_cast<gchar*>(key)); + g_assert_cmpint (i, ==, n); + keys[n] = nullptr; + + return keys; +} + +static gboolean +g_lo_action_group_query_action (GActionGroup *group, + const gchar *action_name, + gboolean *enabled, + const GVariantType **parameter_type, + const GVariantType **state_type, + GVariant **state_hint, + GVariant **state) +{ + //SAL_INFO("vcl.unity", "g_lo_action_group_query_action on " << group); + GLOActionGroup *lo_group = G_LO_ACTION_GROUP (group); + GLOAction* action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name)); + + if (action == nullptr) + return FALSE; + + if (enabled) + { + *enabled = action->enabled; + } + + if (parameter_type) + *parameter_type = action->parameter_type; + + if (state_type) + *state_type = action->state_type; + + if (state_hint) + *state_hint = (action->state_hint) ? g_variant_ref (action->state_hint) : nullptr; + + if (state) + *state = (action->state) ? g_variant_ref (action->state) : nullptr; + + return TRUE; +} + +static void +g_lo_action_group_perform_submenu_action (GLOActionGroup *group, + const gchar *action_name, + GVariant *state) +{ + gboolean bState = g_variant_get_boolean (state); + SAL_INFO("vcl.unity", "g_lo_action_group_perform_submenu_action on " << group << " to " << bState); + + if (bState) + GtkSalMenu::Activate(action_name); + else + GtkSalMenu::Deactivate(action_name); +} + +static void +g_lo_action_group_change_state (GActionGroup *group, + const gchar *action_name, + GVariant *value) +{ + SAL_INFO("vcl.unity", "g_lo_action_group_change_state on " << group ); + g_return_if_fail (value != nullptr); + + g_variant_ref_sink (value); + + if (action_name != nullptr) + { + GLOActionGroup* lo_group = G_LO_ACTION_GROUP (group); + GLOAction* action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name)); + + if (action != nullptr) + { + if (action->submenu) + g_lo_action_group_perform_submenu_action (lo_group, action_name, value); + else + { + gboolean is_new = FALSE; + + /* If action already exists but has no state, it should be removed and added again. */ + if (action->state_type == nullptr) + { + g_action_group_action_removed (G_ACTION_GROUP (group), action_name); + action->state_type = g_variant_type_copy (g_variant_get_type(value)); + is_new = TRUE; + } + + if (g_variant_is_of_type (value, action->state_type)) + { + if (action->state) + g_variant_unref(action->state); + + action->state = g_variant_ref (value); + + if (is_new) + g_action_group_action_added (G_ACTION_GROUP (group), action_name); + else + g_action_group_action_state_changed (group, action_name, value); + } + } + } + } + + g_variant_unref (value); +} + +static void +g_lo_action_group_activate (GActionGroup *group, + const gchar *action_name, + GVariant *parameter) +{ + if (parameter != nullptr) + g_action_group_change_action_state(group, action_name, parameter); + GtkSalMenu::DispatchCommand(action_name); +} + +void +g_lo_action_group_insert (GLOActionGroup *group, + const gchar *action_name, + gint item_id, + gboolean submenu) +{ + g_lo_action_group_insert_stateful (group, action_name, item_id, submenu, nullptr, nullptr, nullptr, nullptr); +} + +void +g_lo_action_group_insert_stateful (GLOActionGroup *group, + const gchar *action_name, + gint item_id, + gboolean submenu, + const GVariantType *parameter_type, + const GVariantType *state_type, + GVariant *state_hint, + GVariant *state) +{ + g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); + + GLOAction* old_action = G_LO_ACTION (g_hash_table_lookup (group->priv->table, action_name)); + + if (old_action == nullptr || old_action->item_id != item_id) + { + if (old_action != nullptr) + g_lo_action_group_remove (group, action_name); + + GLOAction* action = g_lo_action_new(); + + g_hash_table_insert (group->priv->table, g_strdup (action_name), action); + + action->item_id = item_id; + action->submenu = submenu; + + if (parameter_type) + action->parameter_type = const_cast<GVariantType*>(parameter_type); + + if (state_type) + action->state_type = const_cast<GVariantType*>(state_type); + + if (state_hint) + action->state_hint = g_variant_ref_sink (state_hint); + + if (state) + action->state = g_variant_ref_sink (state); + + g_action_group_action_added (G_ACTION_GROUP (group), action_name); + } +} + +static void +g_lo_action_group_finalize (GObject *object) +{ + GLOActionGroup *lo_group = G_LO_ACTION_GROUP (object); + + g_hash_table_unref (lo_group->priv->table); + + G_OBJECT_CLASS (g_lo_action_group_parent_class)->finalize (object); +} + +static void +g_lo_action_group_init (GLOActionGroup *group) +{ + SAL_INFO("vcl.unity", "g_lo_action_group_init on " << group); + group->priv = G_TYPE_INSTANCE_GET_PRIVATE (group, + G_TYPE_LO_ACTION_GROUP, + GLOActionGroupPrivate); + group->priv->table = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_object_unref); +} + +static void +g_lo_action_group_class_init (GLOActionGroupClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = g_lo_action_group_finalize; +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + g_type_class_add_private (klass, sizeof (GLOActionGroupPrivate)); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +} + +static void +g_lo_action_group_iface_init (GActionGroupInterface *iface) +{ + iface->list_actions = g_lo_action_group_list_actions; + iface->query_action = g_lo_action_group_query_action; + iface->change_action_state = g_lo_action_group_change_state; + iface->activate_action = g_lo_action_group_activate; +} + +GLOActionGroup * +g_lo_action_group_new() +{ + GLOActionGroup* group = G_LO_ACTION_GROUP (g_object_new (G_TYPE_LO_ACTION_GROUP, nullptr)); + return group; +} + +void +g_lo_action_group_set_action_enabled (GLOActionGroup *group, + const gchar *action_name, + gboolean enabled) +{ + SAL_INFO("vcl.unity", "g_lo_action_group_set_action_enabled on " << group); + g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); + g_return_if_fail (action_name != nullptr); + + GLOAction* action = G_LO_ACTION (g_hash_table_lookup (group->priv->table, action_name)); + + if (action == nullptr) + return; + + action->enabled = enabled; + + g_action_group_action_enabled_changed (G_ACTION_GROUP (group), action_name, enabled); +} + +void +g_lo_action_group_remove (GLOActionGroup *group, + const gchar *action_name) +{ + SAL_INFO("vcl.unity", "g_lo_action_group_remove on " << group); + g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); + + if (action_name != nullptr) + { + g_action_group_action_removed (G_ACTION_GROUP (group), action_name); + g_hash_table_remove (group->priv->table, action_name); + } +} + +void +g_lo_action_group_clear (GLOActionGroup *group) +{ + SAL_INFO("vcl.unity", "g_lo_action_group_clear on " << group); + g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); + + GList* keys = g_hash_table_get_keys (group->priv->table); + + for (GList* element = g_list_first (keys); element != nullptr; element = g_list_next (element)) + { + g_lo_action_group_remove (group, static_cast<gchar*>(element->data)); + } + + g_list_free (keys); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtk3glomenu.cxx b/vcl/unx/gtk3/gtk3glomenu.cxx index e894b09320fd..f20903d0bd5a 100644 --- a/vcl/unx/gtk3/gtk3glomenu.cxx +++ b/vcl/unx/gtk3/gtk3glomenu.cxx @@ -1,5 +1,686 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ -#include "../gtk/glomenu.cxx" +#include <string.h> + +#include <unx/gtk/gtksalmenu.hxx> +#include <unx/gtk/glomenu.h> + +struct GLOMenu +{ + GMenuModel const parent_instance; + + GArray *items; +}; + +typedef GMenuModelClass GLOMenuClass; + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +G_DEFINE_TYPE (GLOMenu, g_lo_menu, G_TYPE_MENU_MODEL); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +struct item +{ + GHashTable* attributes; // Item attributes. + GHashTable* links; // Item links. +}; + +static void +g_lo_menu_struct_item_init (struct item *menu_item) +{ + menu_item->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, reinterpret_cast<GDestroyNotify>(g_variant_unref)); + menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); +} + +/* We treat attribute names the same as GSettings keys: + * - only lowercase ascii, digits and '-' + * - must start with lowercase + * - must not end with '-' + * - no consecutive '-' + * - not longer than 1024 chars + */ +static gboolean +valid_attribute_name (const gchar *name) +{ + gint i; + + if (!g_ascii_islower (name[0])) + return FALSE; + + for (i = 1; name[i]; i++) + { + if (name[i] != '-' && + !g_ascii_islower (name[i]) && + !g_ascii_isdigit (name[i])) + return FALSE; + + if (name[i] == '-' && name[i + 1] == '-') + return FALSE; + } + + if (name[i - 1] == '-') + return FALSE; + + if (i > 1024) + return FALSE; + + return TRUE; +} + +/* + * GLOMenu + */ + +static gboolean +g_lo_menu_is_mutable (GMenuModel*) +{ + // Menu is always mutable. + return TRUE; +} + +static gint +g_lo_menu_get_n_items (GMenuModel *model) +{ + g_return_val_if_fail (model != nullptr, 0); + GLOMenu *menu = G_LO_MENU (model); + g_return_val_if_fail (menu->items != nullptr, 0); + + return menu->items->len; +} + +gint +g_lo_menu_get_n_items_from_section (GLOMenu *menu, + gint section) +{ + g_return_val_if_fail (0 <= section && section < static_cast<gint>(menu->items->len), 0); + + GLOMenu *model = g_lo_menu_get_section (menu, section); + + g_return_val_if_fail (model != nullptr, 0); + + gint length = model->items->len; + + g_object_unref (model); + + return length; +} + +static void +g_lo_menu_get_item_attributes (GMenuModel *model, + gint position, + GHashTable **table) +{ + GLOMenu *menu = G_LO_MENU (model); + *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).attributes); +} + +static void +g_lo_menu_get_item_links (GMenuModel *model, + gint position, + GHashTable **table) +{ + GLOMenu *menu = G_LO_MENU (model); + *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).links); +} + +void +g_lo_menu_insert (GLOMenu *menu, + gint position, + const gchar *label) +{ + g_lo_menu_insert_section (menu, position, label, nullptr); +} + +void +g_lo_menu_insert_in_section (GLOMenu *menu, + gint section, + gint position, + const gchar *label) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + g_return_if_fail (0 <= section && section < static_cast<gint>(menu->items->len)); + + GLOMenu *model = g_lo_menu_get_section (menu, section); + + g_return_if_fail (model != nullptr); + + g_lo_menu_insert (model, position, label); + + g_object_unref (model); +} + +GLOMenu * +g_lo_menu_new() +{ + return G_LO_MENU( g_object_new (G_TYPE_LO_MENU, nullptr) ); +} + +static void +g_lo_menu_set_attribute_value (GLOMenu *menu, + gint position, + const gchar *attribute, + GVariant *value) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + g_return_if_fail (attribute != nullptr); + g_return_if_fail (valid_attribute_name (attribute)); + + if (position >= static_cast<gint>(menu->items->len)) + return; + + struct item menu_item = g_array_index (menu->items, struct item, position); + + if (value != nullptr) + g_hash_table_insert (menu_item.attributes, g_strdup (attribute), g_variant_ref_sink (value)); + else + g_hash_table_remove (menu_item.attributes, attribute); +} + +static GVariant* +g_lo_menu_get_attribute_value_from_item_in_section (GLOMenu *menu, + gint section, + gint position, + const gchar *attribute, + const GVariantType *type) +{ + GMenuModel *model = G_MENU_MODEL (g_lo_menu_get_section (menu, section)); + + g_return_val_if_fail (model != nullptr, nullptr); + + GVariant *value = g_menu_model_get_item_attribute_value (model, + position, + attribute, + type); + + g_object_unref (model); + + return value; +} + +void +g_lo_menu_set_label (GLOMenu *menu, + gint position, + const gchar *label) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + + GVariant *value; + + if (label != nullptr) + value = g_variant_new_string (label); + else + value = nullptr; + + g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_LABEL, value); +} + +void +g_lo_menu_set_icon (GLOMenu *menu, + gint position, + const GIcon *icon) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + + GVariant *value; + + if (icon != nullptr) + { +#if GLIB_CHECK_VERSION(2,38,0) + value = g_icon_serialize (const_cast<GIcon*>(icon)); +#else + value = nullptr; +#endif + } + else + value = nullptr; + +#ifndef G_MENU_ATTRIBUTE_ICON +# define G_MENU_ATTRIBUTE_ICON "icon" +#endif + + g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_ICON, value); + if (value) + g_variant_unref (value); +} + +void +g_lo_menu_set_label_to_item_in_section (GLOMenu *menu, + gint section, + gint position, + const gchar *label) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + + GLOMenu *model = g_lo_menu_get_section (menu, section); + + g_return_if_fail (model != nullptr); + + g_lo_menu_set_label (model, position, label); + + // Notify the update. + g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1); + + g_object_unref (model); +} + +void +g_lo_menu_set_icon_to_item_in_section (GLOMenu *menu, + gint section, + gint position, + const GIcon *icon) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + + GLOMenu *model = g_lo_menu_get_section (menu, section); + + g_return_if_fail (model != nullptr); + + g_lo_menu_set_icon (model, position, icon); + + // Notify the update. + g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1); + + g_object_unref (model); +} + +gchar * +g_lo_menu_get_label_from_item_in_section (GLOMenu *menu, + gint section, + gint position) +{ + g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr); + + GVariant *label_value = g_lo_menu_get_attribute_value_from_item_in_section (menu, + section, + position, + G_MENU_ATTRIBUTE_LABEL, + G_VARIANT_TYPE_STRING); + + gchar *label = nullptr; + + if (label_value) + { + label = g_variant_dup_string (label_value, nullptr); + g_variant_unref (label_value); + } + + return label; +} + +void +g_lo_menu_set_action_and_target_value (GLOMenu *menu, + gint position, + const gchar *action, + GVariant *target_value) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + + GVariant *action_value; + + if (action != nullptr) + { + action_value = g_variant_new_string (action); + } + else + { + action_value = nullptr; + target_value = nullptr; + } + + g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_ACTION, action_value); + g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_TARGET, target_value); + g_lo_menu_set_attribute_value (menu, position, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION, nullptr); + + g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 1); +} + +void +g_lo_menu_set_action_and_target_value_to_item_in_section (GLOMenu *menu, + gint section, + gint position, + const gchar *command, + GVariant *target_value) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + + GLOMenu *model = g_lo_menu_get_section (menu, section); + + g_return_if_fail (model != nullptr); + + g_lo_menu_set_action_and_target_value (model, position, command, target_value); + + g_object_unref (model); +} + +void +g_lo_menu_set_accelerator_to_item_in_section (GLOMenu *menu, + gint section, + gint position, + const gchar *accelerator) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + + GLOMenu *model = g_lo_menu_get_section (menu, section); + + g_return_if_fail (model != nullptr); + + GVariant *value; + + if (accelerator != nullptr) + value = g_variant_new_string (accelerator); + else + value = nullptr; + + g_lo_menu_set_attribute_value (model, position, G_LO_MENU_ATTRIBUTE_ACCELERATOR, value); + + // Notify the update. + g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1); + + g_object_unref (model); +} + +gchar * +g_lo_menu_get_accelerator_from_item_in_section (GLOMenu *menu, + gint section, + gint position) +{ + g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr); + + GVariant *accel_value = g_lo_menu_get_attribute_value_from_item_in_section (menu, + section, + position, + G_LO_MENU_ATTRIBUTE_ACCELERATOR, + G_VARIANT_TYPE_STRING); + + gchar *accel = nullptr; + + if (accel_value != nullptr) + { + accel = g_variant_dup_string (accel_value, nullptr); + g_variant_unref (accel_value); + } + + return accel; +} + +void +g_lo_menu_set_command_to_item_in_section (GLOMenu *menu, + gint section, + gint position, + const gchar *command) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + + GLOMenu *model = g_lo_menu_get_section (menu, section); + + g_return_if_fail (model != nullptr); + + GVariant *value; + + if (command != nullptr) + value = g_variant_new_string (command); + else + value = nullptr; + + g_lo_menu_set_attribute_value (model, position, G_LO_MENU_ATTRIBUTE_COMMAND, value); + + // Notify the update. + g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1); + + g_object_unref (model); +} + +gchar * +g_lo_menu_get_command_from_item_in_section (GLOMenu *menu, + gint section, + gint position) +{ + g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr); + + GVariant *command_value = g_lo_menu_get_attribute_value_from_item_in_section (menu, + section, + position, + G_LO_MENU_ATTRIBUTE_COMMAND, + G_VARIANT_TYPE_STRING); + + gchar *command = nullptr; + + if (command_value != nullptr) + { + command = g_variant_dup_string (command_value, nullptr); + g_variant_unref (command_value); + } + + return command; +} + +static void +g_lo_menu_set_link (GLOMenu *menu, + gint position, + const gchar *link, + GMenuModel *model) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + g_return_if_fail (link != nullptr); + g_return_if_fail (valid_attribute_name (link)); + + if (position < 0 || position >= static_cast<gint>(menu->items->len)) + position = menu->items->len - 1; + + struct item menu_item = g_array_index (menu->items, struct item, position); + + if (model != nullptr) + g_hash_table_insert (menu_item.links, g_strdup (link), g_object_ref (model)); + else + g_hash_table_remove (menu_item.links, link); +} + +void +g_lo_menu_insert_section (GLOMenu *menu, + gint position, + const gchar *label, + GMenuModel *section) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + + if (position < 0 || position > static_cast<gint>(menu->items->len)) + position = menu->items->len; + + struct item menu_item; + + g_lo_menu_struct_item_init(&menu_item); + + g_array_insert_val (menu->items, position, menu_item); + + g_lo_menu_set_label (menu, position, label); + g_lo_menu_set_link (menu, position, G_MENU_LINK_SECTION, section); + + g_menu_model_items_changed (G_MENU_MODEL (menu), position, 0, 1); +} + +void +g_lo_menu_new_section (GLOMenu *menu, + gint position, + const gchar *label) +{ + GMenuModel *section = G_MENU_MODEL (g_lo_menu_new()); + + g_lo_menu_insert_section (menu, position, label, section); + + g_object_unref (section); +} + +GLOMenu * +g_lo_menu_get_section (GLOMenu *menu, + gint section) +{ + g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr); + + return G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class) + ->get_item_link (G_MENU_MODEL (menu), section, G_MENU_LINK_SECTION)); +} + +void +g_lo_menu_new_submenu_in_item_in_section (GLOMenu *menu, + gint section, + gint position) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + g_return_if_fail (0 <= section && section < static_cast<gint>(menu->items->len)); + + GLOMenu* model = g_lo_menu_get_section (menu, section); + + g_return_if_fail (model != nullptr); + + if (0 <= position && position < static_cast<gint>(model->items->len)) { + GMenuModel* submenu = G_MENU_MODEL (g_lo_menu_new()); + + g_lo_menu_set_link (model, position, G_MENU_LINK_SUBMENU, submenu); + + g_object_unref (submenu); + + g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1); + + g_object_unref (model); + } +} + +GLOMenu * +g_lo_menu_get_submenu_from_item_in_section (GLOMenu *menu, + gint section, + gint position) +{ + g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr); + g_return_val_if_fail (0 <= section && section < static_cast<gint>(menu->items->len), nullptr); + + GLOMenu *model = g_lo_menu_get_section (menu, section); + + g_return_val_if_fail (model != nullptr, nullptr); + + GLOMenu *submenu = nullptr; + + if (0 <= position && position < static_cast<gint>(model->items->len)) + submenu = G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class) + ->get_item_link (G_MENU_MODEL (model), position, G_MENU_LINK_SUBMENU)); + //submenu = g_menu_model_get_item_link (G_MENU_MODEL (model), position, G_MENU_LINK_SUBMENU); + + g_object_unref (model); + + return submenu; +} + +void +g_lo_menu_set_submenu_action_to_item_in_section (GLOMenu *menu, + gint section, + gint position, + const gchar *action) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + + GMenuModel *model = G_MENU_MODEL (g_lo_menu_get_section (menu, section)); + + g_return_if_fail (model != nullptr); + + GVariant *value; + + if (action != nullptr) + value = g_variant_new_string (action); + else + value = nullptr; + + g_lo_menu_set_attribute_value (G_LO_MENU (model), position, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION, value); + + // Notify the update. + g_menu_model_items_changed (model, position, 1, 1); + + g_object_unref (model); +} + +static void +g_lo_menu_clear_item (struct item *menu_item) +{ + if (menu_item->attributes != nullptr) + g_hash_table_unref (menu_item->attributes); + if (menu_item->links != nullptr) + g_hash_table_unref (menu_item->links); +} + +void +g_lo_menu_remove (GLOMenu *menu, + gint position) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + g_return_if_fail (0 <= position && position < static_cast<gint>(menu->items->len)); + + g_lo_menu_clear_item (&g_array_index (menu->items, struct item, position)); + g_array_remove_index (menu->items, position); + g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 0); +} + +void +g_lo_menu_remove_from_section (GLOMenu *menu, + gint section, + gint position) +{ + g_return_if_fail (G_IS_LO_MENU (menu)); + g_return_if_fail (0 <= section && section < static_cast<gint>(menu->items->len)); + + GLOMenu *model = g_lo_menu_get_section (menu, section); + + g_return_if_fail (model != nullptr); + + g_lo_menu_remove (model, position); + + g_object_unref (model); +} + +static void +g_lo_menu_finalize (GObject *object) +{ + GLOMenu *menu = G_LO_MENU (object); + struct item *items; + gint n_items; + gint i; + + n_items = menu->items->len; + items = reinterpret_cast<struct item *>(g_array_free (menu->items, FALSE)); + for (i = 0; i < n_items; i++) + g_lo_menu_clear_item (&items[i]); + g_free (items); + + G_OBJECT_CLASS (g_lo_menu_parent_class) + ->finalize (object); +} + +static void +g_lo_menu_init (GLOMenu *menu) +{ + menu->items = g_array_new (FALSE, FALSE, sizeof (struct item)); +} + +static void +g_lo_menu_class_init (GLOMenuClass *klass) +{ + GMenuModelClass *model_class = G_MENU_MODEL_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = g_lo_menu_finalize; + + model_class->is_mutable = g_lo_menu_is_mutable; + model_class->get_n_items = g_lo_menu_get_n_items; + model_class->get_item_attributes = g_lo_menu_get_item_attributes; + model_class->get_item_links = g_lo_menu_get_item_links; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx index 333db6693d13..f5e26be83b59 100644 --- a/vcl/unx/gtk3/gtk3gtkinst.cxx +++ b/vcl/unx/gtk3/gtk3gtkinst.cxx @@ -7,8 +7,35 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../gtk/gtkinst.cxx" -#include "../gtk/a11y/atkwrapper.hxx" +#include <stack> +#include <string.h> +#include <osl/process.h> +#include <unx/gtk/gtkdata.hxx> +#include <unx/gtk/gtkinst.hxx> +#include <unx/salobj.h> +#include <unx/gtk/gtkgdi.hxx> +#include <unx/gtk/gtkframe.hxx> +#include <unx/gtk/gtkobject.hxx> +#include <unx/gtk/atkbridge.hxx> +#include <unx/gtk/gtkprn.hxx> +#include <unx/gtk/gtksalmenu.hxx> +#include <headless/svpvd.hxx> +#include <headless/svpbmp.hxx> +#include <vcl/inputtypes.hxx> +#include <unx/genpspgraphics.h> +#include <rtl/strbuf.hxx> +#include <sal/log.hxx> +#include <rtl/uri.hxx> + +#include <vcl/settings.hxx> + +#include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> + +#include <unx/gtk/gtkprintwrapper.hxx> + +#include "a11y/atkwrapper.hxx" #include <com/sun/star/lang/IllegalArgumentException.hpp> #include <com/sun/star/lang/XMultiServiceFactory.hpp> #include <com/sun/star/lang/XServiceInfo.hpp> @@ -56,6 +83,376 @@ using namespace com::sun::star; using namespace com::sun::star::uno; using namespace com::sun::star::lang; +extern "C" +{ + #define GET_YIELD_MUTEX() static_cast<GtkYieldMutex*>(GetSalData()->m_pInstance->GetYieldMutex()) + static void GdkThreadsEnter() + { + GtkYieldMutex *pYieldMutex = GET_YIELD_MUTEX(); + pYieldMutex->ThreadsEnter(); + } + static void GdkThreadsLeave() + { + GtkYieldMutex *pYieldMutex = GET_YIELD_MUTEX(); + pYieldMutex->ThreadsLeave(); + } + + VCLPLUG_GTK_PUBLIC SalInstance* create_SalInstance() + { + SAL_INFO( + "vcl.gtk", + "create vcl plugin instance with gtk version " << gtk_major_version + << " " << gtk_minor_version << " " << gtk_micro_version); + + if (gtk_major_version == 3 && gtk_minor_version < 18) + { + g_warning("require gtk >= 3.18 for theme expectations"); + return nullptr; + } + + // for gtk2 it is always built with X support, so this is always called + // for gtk3 it is normally built with X and Wayland support, if + // X is supported GDK_WINDOWING_X11 is defined and this is always + // called, regardless of if we're running under X or Wayland. + // We can't use (DLSYM_GDK_IS_X11_DISPLAY(pDisplay)) to only do it under + // X, because we need to do it earlier than we have a display +#if defined(GDK_WINDOWING_X11) + /* #i92121# workaround deadlocks in the X11 implementation + */ + static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" ); + /* #i90094# + from now on we know that an X connection will be + established, so protect X against itself + */ + if( ! ( pNoXInitThreads && *pNoXInitThreads ) ) + XInitThreads(); +#endif + + // init gdk thread protection + bool const sup = g_thread_supported(); + // extracted from the 'if' to avoid Clang -Wunreachable-code + if ( !sup ) + g_thread_init( nullptr ); + + gdk_threads_set_lock_functions (GdkThreadsEnter, GdkThreadsLeave); + SAL_INFO("vcl.gtk", "Hooked gdk threads locks"); + + auto pYieldMutex = std::make_unique<GtkYieldMutex>(); + + gdk_threads_init(); + + GtkInstance* pInstance = new GtkInstance( std::move(pYieldMutex) ); + SAL_INFO("vcl.gtk", "creating GtkInstance " << pInstance); + + // Create SalData, this does not leak + new GtkSalData( pInstance ); + + return pInstance; + } +} + +static VclInputFlags categorizeEvent(const GdkEvent *pEvent) +{ + VclInputFlags nType = VclInputFlags::NONE; + switch( pEvent->type ) + { + case GDK_MOTION_NOTIFY: + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + case GDK_SCROLL: + nType = VclInputFlags::MOUSE; + break; + case GDK_KEY_PRESS: + // case GDK_KEY_RELEASE: //similar to the X11SalInstance one + nType = VclInputFlags::KEYBOARD; + break; + case GDK_EXPOSE: + nType = VclInputFlags::PAINT; + break; + default: + nType = VclInputFlags::OTHER; + break; + } + return nType; +} + +GtkInstance::GtkInstance( std::unique_ptr<SalYieldMutex> pMutex ) + : SvpSalInstance( std::move(pMutex) ) + , m_pTimer(nullptr) + , bNeedsInit(true) + , m_pLastCairoFontOptions(nullptr) +{ +} + +//We want to defer initializing gtk until we are after uno has been +//bootstrapped so we can ask the config what the UI language is so that we can +//force that in as $LANGUAGE to get gtk to render widgets RTL if we have a RTL +//UI in a LTR locale +void GtkInstance::AfterAppInit() +{ + EnsureInit(); +} + +void GtkInstance::EnsureInit() +{ + if (!bNeedsInit) + return; + // initialize SalData + GtkSalData *pSalData = GetGtkSalData(); + pSalData->Init(); + GtkSalData::initNWF(); + + InitAtkBridge(); + + ImplSVData* pSVData = ImplGetSVData(); +#ifdef GTK_TOOLKIT_NAME + pSVData->maAppData.mxToolkitName = OUString(GTK_TOOLKIT_NAME); +#else + pSVData->maAppData.mxToolkitName = OUString("gtk3"); +#endif + + bNeedsInit = false; +} + +GtkInstance::~GtkInstance() +{ + assert( nullptr == m_pTimer ); + DeInitAtkBridge(); + ResetLastSeenCairoFontOptions(nullptr); +} + +SalFrame* GtkInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle ) +{ + EnsureInit(); + return new GtkSalFrame( pParent, nStyle ); +} + +SalFrame* GtkInstance::CreateChildFrame( SystemParentData* pParentData, SalFrameStyleFlags ) +{ + EnsureInit(); + return new GtkSalFrame( pParentData ); +} + +SalObject* GtkInstance::CreateObject( SalFrame* pParent, SystemWindowData* /*pWindowData*/, bool bShow ) +{ + EnsureInit(); + //FIXME: Missing CreateObject functionality ... + return new GtkSalObject( static_cast<GtkSalFrame*>(pParent), bShow ); +} + +extern "C" +{ + typedef void*(* getDefaultFnc)(); + typedef void(* addItemFnc)(void *, const char *); +} + +void GtkInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString&, const OUString&) +{ + EnsureInit(); + OString sGtkURL; + rtl_TextEncoding aSystemEnc = osl_getThreadTextEncoding(); + if ((aSystemEnc == RTL_TEXTENCODING_UTF8) || !rFileUrl.startsWith( "file://" )) + sGtkURL = OUStringToOString(rFileUrl, RTL_TEXTENCODING_UTF8); + else + { + //Non-utf8 locales are a bad idea if trying to work with non-ascii filenames + //Decode %XX components + OUString sDecodedUri = rtl::Uri::decode(rFileUrl.copy(7), rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8); + //Convert back to system locale encoding + OString sSystemUrl = OUStringToOString(sDecodedUri, aSystemEnc); + //Encode to an escaped ASCII-encoded URI + gchar *g_uri = g_filename_to_uri(sSystemUrl.getStr(), nullptr, nullptr); + sGtkURL = OString(g_uri); + g_free(g_uri); + } + GtkRecentManager *manager = gtk_recent_manager_get_default (); + gtk_recent_manager_add_item (manager, sGtkURL.getStr()); +} + +SalInfoPrinter* GtkInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo, + ImplJobSetup* pSetupData ) +{ + EnsureInit(); + mbPrinterInit = true; + // create and initialize SalInfoPrinter + PspSalInfoPrinter* pPrinter = new GtkSalInfoPrinter; + configurePspInfoPrinter(pPrinter, pQueueInfo, pSetupData); + return pPrinter; +} + +std::unique_ptr<SalPrinter> GtkInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) +{ + EnsureInit(); + mbPrinterInit = true; + return std::unique_ptr<SalPrinter>(new GtkSalPrinter( pInfoPrinter )); +} + +/* + * These methods always occur in pairs + * A ThreadsEnter is followed by a ThreadsLeave + * We need to queue up the recursive lock count + * for each pair, so we can accurately restore + * it later. + */ +thread_local std::stack<sal_uInt32> GtkYieldMutex::yieldCounts; + +void GtkYieldMutex::ThreadsEnter() +{ + acquire(); + if (!yieldCounts.empty()) { + auto n = yieldCounts.top(); + yieldCounts.pop(); + assert(n > 0); + n--; + if (n > 0) + acquire(n); + } +} + +void GtkYieldMutex::ThreadsLeave() +{ + assert(m_nCount != 0); + yieldCounts.push(m_nCount); + release(true); +} + +std::unique_ptr<SalVirtualDevice> GtkInstance::CreateVirtualDevice( SalGraphics *pG, + long &nDX, long &nDY, + DeviceFormat eFormat, + const SystemGraphicsData* /*pGd*/ ) +{ + EnsureInit(); + SvpSalGraphics *pSvpSalGraphics = dynamic_cast<SvpSalGraphics*>(pG); + assert(pSvpSalGraphics); + std::unique_ptr<SalVirtualDevice> pNew(new SvpSalVirtualDevice(eFormat, pSvpSalGraphics->getSurface())); + pNew->SetSize( nDX, nDY ); + return pNew; +} + +std::shared_ptr<SalBitmap> GtkInstance::CreateSalBitmap() +{ + EnsureInit(); + return SvpSalInstance::CreateSalBitmap(); +} + +std::unique_ptr<SalMenu> GtkInstance::CreateMenu( bool bMenuBar, Menu* pVCLMenu ) +{ + EnsureInit(); + GtkSalMenu* pSalMenu = new GtkSalMenu( bMenuBar ); + pSalMenu->SetMenu( pVCLMenu ); + return std::unique_ptr<SalMenu>(pSalMenu); +} + +std::unique_ptr<SalMenuItem> GtkInstance::CreateMenuItem( const SalItemParams & rItemData ) +{ + EnsureInit(); + return std::unique_ptr<SalMenuItem>(new GtkSalMenuItem( &rItemData )); +} + +SalTimer* GtkInstance::CreateSalTimer() +{ + EnsureInit(); + assert( nullptr == m_pTimer ); + if ( nullptr == m_pTimer ) + m_pTimer = new GtkSalTimer(); + return m_pTimer; +} + +void GtkInstance::RemoveTimer () +{ + EnsureInit(); + m_pTimer = nullptr; +} + +bool GtkInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents) +{ + EnsureInit(); + return GetGtkSalData()->Yield( bWait, bHandleAllCurrentEvents ); +} + +bool GtkInstance::IsTimerExpired() +{ + EnsureInit(); + return (m_pTimer && m_pTimer->Expired()); +} + +bool GtkInstance::AnyInput( VclInputFlags nType ) +{ + EnsureInit(); + if( (nType & VclInputFlags::TIMER) && IsTimerExpired() ) + return true; + if (!gdk_events_pending()) + return false; + + if (nType == VCL_INPUT_ANY) + return true; + + bool bRet = false; + std::stack<GdkEvent*> aEvents; + GdkEvent *pEvent = nullptr; + while ((pEvent = gdk_event_get())) + { + aEvents.push(pEvent); + VclInputFlags nEventType = categorizeEvent(pEvent); + if ( (nEventType & nType) || ( nEventType == VclInputFlags::NONE && (nType & VclInputFlags::OTHER) ) ) + { + bRet = true; + break; + } + } + + while (!aEvents.empty()) + { + pEvent = aEvents.top(); + gdk_event_put(pEvent); + gdk_event_free(pEvent); + aEvents.pop(); + } + return bRet; +} + +std::unique_ptr<GenPspGraphics> GtkInstance::CreatePrintGraphics() +{ + EnsureInit(); + return std::make_unique<GenPspGraphics>(); +} + +std::shared_ptr<vcl::unx::GtkPrintWrapper> const & +GtkInstance::getPrintWrapper() const +{ + if (!m_xPrintWrapper) + m_xPrintWrapper.reset(new vcl::unx::GtkPrintWrapper); + return m_xPrintWrapper; +} + +const cairo_font_options_t* GtkInstance::GetCairoFontOptions() +{ + const cairo_font_options_t* pCairoFontOptions = gdk_screen_get_font_options(gdk_screen_get_default()); + if (!m_pLastCairoFontOptions && pCairoFontOptions) + m_pLastCairoFontOptions = cairo_font_options_copy(pCairoFontOptions); + return pCairoFontOptions; +} + +const cairo_font_options_t* GtkInstance::GetLastSeenCairoFontOptions() const +{ + return m_pLastCairoFontOptions; +} + +void GtkInstance::ResetLastSeenCairoFontOptions(const cairo_font_options_t* pCairoFontOptions) +{ + if (m_pLastCairoFontOptions) + cairo_font_options_destroy(m_pLastCairoFontOptions); + if (pCairoFontOptions) + m_pLastCairoFontOptions = cairo_font_options_copy(pCairoFontOptions); + else + m_pLastCairoFontOptions = nullptr; +} + + namespace { struct TypeEntry diff --git a/vcl/unx/gtk3/gtk3gtkprintwrapper.cxx b/vcl/unx/gtk3/gtk3gtkprintwrapper.cxx index edefca9626ad..b1a8b9c66d13 100644 --- a/vcl/unx/gtk3/gtk3gtkprintwrapper.cxx +++ b/vcl/unx/gtk3/gtk3gtkprintwrapper.cxx @@ -7,6 +7,154 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../gtk/gtkprintwrapper.cxx" +#include <cassert> + +#include <rtl/ustring.hxx> + +#include <unx/gtk/gtkprintwrapper.hxx> + +namespace vcl +{ +namespace unx +{ + +GtkPrintWrapper::GtkPrintWrapper() +{ +} + +GtkPrintWrapper::~GtkPrintWrapper() +{ +} + +bool GtkPrintWrapper::supportsPrinting() const +{ + (void) this; // loplugin:staticmethods + return true; +} + +bool GtkPrintWrapper::supportsPrintSelection() const +{ + (void) this; // loplugin:staticmethods + return true; +} + +GtkPageSetup* GtkPrintWrapper::page_setup_new() const +{ + (void) this; // loplugin:staticmethods + return gtk_page_setup_new(); +} + +GtkPrintJob* GtkPrintWrapper::print_job_new(const gchar* title, GtkPrinter* printer, GtkPrintSettings* settings, GtkPageSetup* page_setup) const +{ + (void) this; // loplugin:staticmethods + return gtk_print_job_new(title, printer, settings, page_setup); +} + +void GtkPrintWrapper::print_job_send(GtkPrintJob* job, GtkPrintJobCompleteFunc callback, gpointer user_data, GDestroyNotify dnotify) const +{ + (void) this; // loplugin:staticmethods + gtk_print_job_send(job, callback, user_data, dnotify); +} + +gboolean GtkPrintWrapper::print_job_set_source_file(GtkPrintJob* job, const gchar* filename, GError** error) const +{ + (void) this; // loplugin:staticmethods + return gtk_print_job_set_source_file(job, filename, error); +} + +const gchar* GtkPrintWrapper::print_settings_get(GtkPrintSettings* settings, const gchar* key) const +{ + (void) this; // loplugin:staticmethods + return gtk_print_settings_get(settings, key); +} + +gboolean GtkPrintWrapper::print_settings_get_collate(GtkPrintSettings* settings) const +{ + (void) this; // loplugin:staticmethods + return gtk_print_settings_get_collate(settings); +} + +void GtkPrintWrapper::print_settings_set_collate(GtkPrintSettings* settings, gboolean collate) const +{ + (void) this; // loplugin:staticmethods + gtk_print_settings_set_collate(settings, collate); +} + +gint GtkPrintWrapper::print_settings_get_n_copies(GtkPrintSettings* settings) const +{ + (void) this; // loplugin:staticmethods + return gtk_print_settings_get_n_copies(settings); +} + +void GtkPrintWrapper::print_settings_set_n_copies(GtkPrintSettings* settings, gint num_copies) const +{ + (void) this; // loplugin:staticmethods + gtk_print_settings_set_n_copies(settings, num_copies); +} + +GtkPageRange* GtkPrintWrapper::print_settings_get_page_ranges(GtkPrintSettings* settings, gint* num_ranges) const +{ + (void) this; // loplugin:staticmethods + return gtk_print_settings_get_page_ranges(settings, num_ranges); +} + +void GtkPrintWrapper::print_settings_set_print_pages(GtkPrintSettings* settings, GtkPrintPages pages) const +{ + (void) this; // loplugin:staticmethods + gtk_print_settings_set_print_pages(settings, pages); +} + +GtkWidget* GtkPrintWrapper::print_unix_dialog_new() const +{ + (void) this; // loplugin:staticmethods + return gtk_print_unix_dialog_new(nullptr, nullptr); +} + +void GtkPrintWrapper::print_unix_dialog_add_custom_tab(GtkPrintUnixDialog* dialog, GtkWidget* child, GtkWidget* tab_label) const +{ + (void) this; // loplugin:staticmethods + gtk_print_unix_dialog_add_custom_tab(dialog, child, tab_label); +} + +GtkPrinter* GtkPrintWrapper::print_unix_dialog_get_selected_printer(GtkPrintUnixDialog* dialog) const +{ + (void) this; // loplugin:staticmethods + GtkPrinter* pRet = gtk_print_unix_dialog_get_selected_printer(dialog); + g_object_ref(G_OBJECT(pRet)); + return pRet; +} + +void GtkPrintWrapper::print_unix_dialog_set_manual_capabilities(GtkPrintUnixDialog* dialog, GtkPrintCapabilities capabilities) const +{ + (void) this; // loplugin:staticmethods + gtk_print_unix_dialog_set_manual_capabilities(dialog, capabilities); +} + +GtkPrintSettings* GtkPrintWrapper::print_unix_dialog_get_settings(GtkPrintUnixDialog* dialog) const +{ + (void) this; // loplugin:staticmethods + return gtk_print_unix_dialog_get_settings(dialog); +} + +void GtkPrintWrapper::print_unix_dialog_set_settings(GtkPrintUnixDialog* dialog, GtkPrintSettings* settings) const +{ + (void) this; // loplugin:staticmethods + gtk_print_unix_dialog_set_settings(dialog, settings); +} + +void GtkPrintWrapper::print_unix_dialog_set_support_selection(GtkPrintUnixDialog* dialog, gboolean support_selection) const +{ + (void) this; // loplugin:staticmethods + gtk_print_unix_dialog_set_support_selection(dialog, support_selection); +} + +void GtkPrintWrapper::print_unix_dialog_set_has_selection(GtkPrintUnixDialog* dialog, gboolean has_selection) const +{ + (void) this; // loplugin:staticmethods + gtk_print_unix_dialog_set_has_selection(dialog, has_selection); +} + +} +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtk3gtksalmenu.cxx b/vcl/unx/gtk3/gtk3gtksalmenu.cxx index d465594fc9bc..81d5d650dd26 100644 --- a/vcl/unx/gtk3/gtk3gtksalmenu.cxx +++ b/vcl/unx/gtk3/gtk3gtksalmenu.cxx @@ -1,5 +1,1396 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 "../gtk/gtksalmenu.cxx" +#include <unx/gtk/gtksalmenu.hxx> + +#include <unx/gendata.hxx> +#include <unx/saldisp.hxx> +#include <unx/gtk/gtkdata.hxx> +#include <unx/gtk/glomenu.h> +#include <unx/gtk/gloactiongroup.h> +#include <vcl/floatwin.hxx> +#include <vcl/menu.hxx> +#include <vcl/pngwrite.hxx> +#include <unx/gtk/gtkinst.hxx> + +#include <sal/log.hxx> +#include <tools/stream.hxx> +#include <window.h> +#include <strings.hrc> + +static bool bUnityMode = false; + +/* + * This function generates a unique command name for each menu item + */ +static gchar* GetCommandForItem(GtkSalMenu* pParentMenu, sal_uInt16 nItemId) +{ + OString aCommand("window-"); + aCommand = aCommand + OString::number(reinterpret_cast<unsigned long>(pParentMenu)); + aCommand = aCommand + "-" + OString::number(nItemId); + return g_strdup(aCommand.getStr()); +} + +static gchar* GetCommandForItem(GtkSalMenuItem* pSalMenuItem) +{ + return GetCommandForItem(pSalMenuItem->mpParentMenu, + pSalMenuItem->mnId); +} + +bool GtkSalMenu::PrepUpdate() +{ + return mpMenuModel && mpActionGroup; +} + +/* + * Menu updating methods + */ + +static void RemoveSpareItemsFromNativeMenu( GLOMenu* pMenu, GList** pOldCommandList, unsigned nSection, unsigned nValidItems ) +{ + sal_Int32 nSectionItems = g_lo_menu_get_n_items_from_section( pMenu, nSection ); + + while ( nSectionItems > static_cast<sal_Int32>(nValidItems) ) + { + gchar* aCommand = g_lo_menu_get_command_from_item_in_section( pMenu, nSection, --nSectionItems ); + + if ( aCommand != nullptr && pOldCommandList != nullptr ) + *pOldCommandList = g_list_append( *pOldCommandList, g_strdup( aCommand ) ); + + g_free( aCommand ); + + g_lo_menu_remove_from_section( pMenu, nSection, nSectionItems ); + } +} + +typedef std::pair<GtkSalMenu*, sal_uInt16> MenuAndId; + +namespace +{ + MenuAndId decode_command(const gchar *action_name) + { + OString sCommand(action_name); + + sal_Int32 nIndex = 0; + OString sWindow = sCommand.getToken(0, '-', nIndex); + OString sGtkSalMenu = sCommand.getToken(0, '-', nIndex); + OString sItemId = sCommand.getToken(0, '-', nIndex); + + GtkSalMenu* pSalSubMenu = reinterpret_cast<GtkSalMenu*>(sGtkSalMenu.toInt64()); + + assert(sWindow == "window" && pSalSubMenu); + (void) sWindow; + + return MenuAndId(pSalSubMenu, sItemId.toInt32()); + } +} + +static void RemoveDisabledItemsFromNativeMenu(GLOMenu* pMenu, GList** pOldCommandList, + sal_Int32 nSection, GActionGroup* pActionGroup) +{ + while (nSection >= 0) + { + sal_Int32 nSectionItems = g_lo_menu_get_n_items_from_section( pMenu, nSection ); + while (nSectionItems--) + { + gchar* pCommand = g_lo_menu_get_command_from_item_in_section(pMenu, nSection, nSectionItems); + // remove disabled entries + bool bRemove = !g_action_group_get_action_enabled(pActionGroup, pCommand); + if (!bRemove) + { + //also remove any empty submenus + GLOMenu* pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section(pMenu, nSection, nSectionItems); + if (pSubMenuModel) + { + gint nSubMenuSections = g_menu_model_get_n_items(G_MENU_MODEL(pSubMenuModel)); + if (nSubMenuSections == 0) + bRemove = true; + else if (nSubMenuSections == 1) + { + gint nItems = g_lo_menu_get_n_items_from_section(pSubMenuModel, 0); + if (nItems == 0) + bRemove = true; + else if (nItems == 1) + { + //If the only entry is the "No Selection Possible" entry, then we are allowed + //to removed it + gchar* pSubCommand = g_lo_menu_get_command_from_item_in_section(pSubMenuModel, 0, 0); + MenuAndId aMenuAndId(decode_command(pSubCommand)); + bRemove = aMenuAndId.second == 0xFFFF; + g_free(pSubCommand); + } + } + } + } + + if (bRemove) + { + //but tdf#86850 Always display clipboard functions + bRemove = g_strcmp0(pCommand, ".uno:Cut") && + g_strcmp0(pCommand, ".uno:Copy") && + g_strcmp0(pCommand, ".uno:Paste"); + } + + if (bRemove) + { + if (pCommand != nullptr && pOldCommandList != nullptr) + *pOldCommandList = g_list_append(*pOldCommandList, g_strdup(pCommand)); + g_lo_menu_remove_from_section(pMenu, nSection, nSectionItems); + } + + g_free(pCommand); + } + --nSection; + } +} + +static void RemoveSpareSectionsFromNativeMenu( GLOMenu* pMenu, GList** pOldCommandList, sal_Int32 nLastSection ) +{ + if ( pMenu == nullptr || pOldCommandList == nullptr ) + return; + + sal_Int32 n = g_menu_model_get_n_items( G_MENU_MODEL( pMenu ) ) - 1; + + for ( ; n > nLastSection; n--) + { + RemoveSpareItemsFromNativeMenu( pMenu, pOldCommandList, n, 0 ); + g_lo_menu_remove( pMenu, n ); + } +} + +static gint CompareStr( gpointer str1, gpointer str2 ) +{ + return g_strcmp0( static_cast<const gchar*>(str1), static_cast<const gchar*>(str2) ); +} + +static void RemoveUnusedCommands( GLOActionGroup* pActionGroup, GList* pOldCommandList, GList* pNewCommandList ) +{ + if ( pActionGroup == nullptr || pOldCommandList == nullptr ) + { + g_list_free_full( pOldCommandList, g_free ); + g_list_free_full( pNewCommandList, g_free ); + return; + } + + while ( pNewCommandList != nullptr ) + { + GList* pNewCommand = g_list_first( pNewCommandList ); + pNewCommandList = g_list_remove_link( pNewCommandList, pNewCommand ); + + gpointer aCommand = g_list_nth_data( pNewCommand, 0 ); + + GList* pOldCommand = g_list_find_custom( pOldCommandList, aCommand, reinterpret_cast<GCompareFunc>(CompareStr) ); + + if ( pOldCommand != nullptr ) + { + pOldCommandList = g_list_remove_link( pOldCommandList, pOldCommand ); + g_list_free_full( pOldCommand, g_free ); + } + + g_list_free_full( pNewCommand, g_free ); + } + + while ( pOldCommandList != nullptr ) + { + GList* pCommand = g_list_first( pOldCommandList ); + pOldCommandList = g_list_remove_link( pOldCommandList, pCommand ); + + gchar* aCommand = static_cast<gchar*>(g_list_nth_data( pCommand, 0 )); + + g_lo_action_group_remove( pActionGroup, aCommand ); + + g_list_free_full( pCommand, g_free ); + } +} + +void GtkSalMenu::ImplUpdate(bool bRecurse, bool bRemoveDisabledEntries) +{ + SolarMutexGuard aGuard; + + SAL_INFO("vcl.unity", "ImplUpdate pre PrepUpdate"); + if( !PrepUpdate() ) + return; + + if (mbNeedsUpdate) + { + mbNeedsUpdate = false; + if (mbMenuBar && maUpdateMenuBarIdle.IsActive()) + { + maUpdateMenuBarIdle.Stop(); + maUpdateMenuBarIdle.Invoke(); + return; + } + } + + Menu* pVCLMenu = mpVCLMenu; + GLOMenu* pLOMenu = G_LO_MENU( mpMenuModel ); + GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup ); + SAL_INFO("vcl.unity", "Syncing vcl menu " << pVCLMenu << " to menu model " << pLOMenu << " and action group " << pActionGroup); + GList *pOldCommandList = nullptr; + GList *pNewCommandList = nullptr; + + sal_uInt16 nLOMenuSize = g_menu_model_get_n_items( G_MENU_MODEL( pLOMenu ) ); + + if ( nLOMenuSize == 0 ) + g_lo_menu_new_section( pLOMenu, 0, nullptr ); + + sal_Int32 nSection = 0; + sal_Int32 nItemPos = 0; + sal_Int32 validItems = 0; + sal_Int32 nItem; + + for ( nItem = 0; nItem < static_cast<sal_Int32>(GetItemCount()); nItem++ ) { + if ( !IsItemVisible( nItem ) ) + continue; + + GtkSalMenuItem *pSalMenuItem = GetItemAtPos( nItem ); + sal_uInt16 nId = pSalMenuItem->mnId; + + // PopupMenu::ImplExecute might add <No Selection Possible> entry to top-level + // popup menu, but we have our own implementation below, so skip that one. + if ( nId == 0xFFFF ) + continue; + + if ( pSalMenuItem->mnType == MenuItemType::SEPARATOR ) + { + // Delete extra items from current section. + RemoveSpareItemsFromNativeMenu( pLOMenu, &pOldCommandList, nSection, validItems ); + + nSection++; + nItemPos = 0; + validItems = 0; + + if ( nLOMenuSize <= nSection ) + { + g_lo_menu_new_section( pLOMenu, nSection, nullptr ); + nLOMenuSize++; + } + + continue; + } + + if ( nItemPos >= g_lo_menu_get_n_items_from_section( pLOMenu, nSection ) ) + g_lo_menu_insert_in_section( pLOMenu, nSection, nItemPos, "EMPTY STRING" ); + + // Get internal menu item values. + OUString aText = pVCLMenu->GetItemText( nId ); + Image aImage = pVCLMenu->GetItemImage( nId ); + bool bEnabled = pVCLMenu->IsItemEnabled( nId ); + vcl::KeyCode nAccelKey = pVCLMenu->GetAccelKey( nId ); + bool bChecked = pVCLMenu->IsItemChecked( nId ); + MenuItemBits itemBits = pVCLMenu->GetItemBits( nId ); + + // Store current item command in command list. + gchar *aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pLOMenu, nSection, nItemPos ); + + if ( aCurrentCommand != nullptr ) + pOldCommandList = g_list_append( pOldCommandList, aCurrentCommand ); + + // Get the new command for the item. + gchar* aNativeCommand = GetCommandForItem(pSalMenuItem); + + // Force updating of native menu labels. + NativeSetItemText( nSection, nItemPos, aText ); + NativeSetItemIcon( nSection, nItemPos, aImage ); + NativeSetAccelerator( nSection, nItemPos, nAccelKey, nAccelKey.GetName( GetFrame()->GetWindow() ) ); + + if ( g_strcmp0( aNativeCommand, "" ) != 0 && pSalMenuItem->mpSubMenu == nullptr ) + { + NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, bChecked, false ); + NativeCheckItem( nSection, nItemPos, itemBits, bChecked ); + NativeSetEnableItem( aNativeCommand, bEnabled ); + + pNewCommandList = g_list_append( pNewCommandList, g_strdup( aNativeCommand ) ); + } + + GtkSalMenu* pSubmenu = pSalMenuItem->mpSubMenu; + + if ( pSubmenu && pSubmenu->GetMenu() ) + { + bool bNonMenuChangedToMenu = NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, false, true ); + pNewCommandList = g_list_append( pNewCommandList, g_strdup( aNativeCommand ) ); + + GLOMenu* pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section( pLOMenu, nSection, nItemPos ); + + if ( pSubMenuModel == nullptr ) + { + g_lo_menu_new_submenu_in_item_in_section( pLOMenu, nSection, nItemPos ); + pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section( pLOMenu, nSection, nItemPos ); + } + + g_object_unref( pSubMenuModel ); + + if (bRecurse || bNonMenuChangedToMenu) + { + SAL_INFO("vcl.unity", "preparing submenu " << pSubMenuModel << " to menu model " << G_MENU_MODEL(pSubMenuModel) << " and action group " << G_ACTION_GROUP(pActionGroup)); + pSubmenu->SetMenuModel( G_MENU_MODEL( pSubMenuModel ) ); + pSubmenu->SetActionGroup( G_ACTION_GROUP( pActionGroup ) ); + pSubmenu->ImplUpdate(true, bRemoveDisabledEntries); + } + } + + g_free( aNativeCommand ); + + ++nItemPos; + ++validItems; + } + + if (bRemoveDisabledEntries) + { + // Delete disabled items in last section. + RemoveDisabledItemsFromNativeMenu(pLOMenu, &pOldCommandList, nSection, G_ACTION_GROUP(pActionGroup)); + } + + // Delete extra items in last section. + RemoveSpareItemsFromNativeMenu( pLOMenu, &pOldCommandList, nSection, validItems ); + + // Delete extra sections. + RemoveSpareSectionsFromNativeMenu( pLOMenu, &pOldCommandList, nSection ); + + // Delete unused commands. + RemoveUnusedCommands( pActionGroup, pOldCommandList, pNewCommandList ); + + // Resolves: tdf#103166 if the menu is empty, add a disabled + // <No Selection Possible> placeholder. + sal_Int32 nSectionsCount = g_menu_model_get_n_items(G_MENU_MODEL(pLOMenu)); + gint nItemsCount = 0; + for (nSection = 0; nSection < nSectionsCount; ++nSection) + { + nItemsCount += g_lo_menu_get_n_items_from_section(pLOMenu, nSection); + if (nItemsCount) + break; + } + if (!nItemsCount) + { + gchar* aNativeCommand = GetCommandForItem(this, 0xFFFF); + OUString aPlaceholderText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE)); + g_lo_menu_insert_in_section(pLOMenu, nSection-1, 0, + OUStringToOString(aPlaceholderText, RTL_TEXTENCODING_UTF8).getStr()); + NativeSetItemCommand(nSection-1, 0, 0xFFFF, aNativeCommand, MenuItemBits::NONE, false, false); + NativeSetEnableItem(aNativeCommand, false); + g_free(aNativeCommand); + } +} + +void GtkSalMenu::Update() +{ + //find out if top level is a menubar or not, if not, then it's a popup menu + //hierarchy and in those we hide (most) disabled entries + const GtkSalMenu* pMenu = this; + while (pMenu->mpParentSalMenu) + pMenu = pMenu->mpParentSalMenu; + ImplUpdate(false, !pMenu->mbMenuBar); +} + +static void MenuPositionFunc(GtkMenu* menu, gint* x, gint* y, gboolean* push_in, gpointer user_data) +{ + Point *pPos = static_cast<Point*>(user_data); + *x = pPos->X(); + if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL) + { + GtkRequisition natural_size; + gtk_widget_get_preferred_size(GTK_WIDGET(menu), nullptr, &natural_size); + *x -= natural_size.width; + } + *y = pPos->Y(); + *push_in = false; +} + +bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangle& rRect, + FloatWinPopupFlags nFlags) +{ + VclPtr<vcl::Window> xParent = pWin->ImplGetWindowImpl()->mpRealParent; + mpFrame = static_cast<GtkSalFrame*>(xParent->ImplGetFrame()); + + GLOActionGroup* pActionGroup = g_lo_action_group_new(); + mpActionGroup = G_ACTION_GROUP(pActionGroup); + mpMenuModel = G_MENU_MODEL(g_lo_menu_new()); + // Generate the main menu structure, populates mpMenuModel + UpdateFull(); + + GtkWidget *pWidget = gtk_menu_new_from_model(mpMenuModel); + gtk_menu_attach_to_widget(GTK_MENU(pWidget), mpFrame->getMouseEventWidget(), nullptr); + gtk_widget_insert_action_group(mpFrame->getMouseEventWidget(), "win", mpActionGroup); + + //run in a sub main loop because we need to keep vcl PopupMenu alive to use + //it during DispatchCommand, returning now to the outer loop causes the + //launching PopupMenu to be destroyed, instead run the subloop here + //until the gtk menu is destroyed + GMainLoop* pLoop = g_main_loop_new(nullptr, true); + g_signal_connect_swapped(G_OBJECT(pWidget), "deactivate", G_CALLBACK(g_main_loop_quit), pLoop); + +#if GTK_CHECK_VERSION(3,22,0) + if (gtk_check_version(3, 22, 0) == nullptr) + { + GdkGravity rect_anchor = GDK_GRAVITY_SOUTH_WEST, menu_anchor = GDK_GRAVITY_NORTH_WEST; + + if (nFlags & FloatWinPopupFlags::Left) + { + rect_anchor = GDK_GRAVITY_NORTH_WEST; + menu_anchor = GDK_GRAVITY_NORTH_EAST; + } + else if (nFlags & FloatWinPopupFlags::Up) + { + rect_anchor = GDK_GRAVITY_NORTH_WEST; + menu_anchor = GDK_GRAVITY_SOUTH_WEST; + } + else if (nFlags & FloatWinPopupFlags::Right) + { + rect_anchor = GDK_GRAVITY_NORTH_EAST; + } + + tools::Rectangle aFloatRect = FloatingWindow::ImplConvertToAbsPos(xParent, rRect); + aFloatRect.Move(-mpFrame->maGeometry.nX, -mpFrame->maGeometry.nY); + GdkRectangle rect {static_cast<int>(aFloatRect.Left()), static_cast<int>(aFloatRect.Top()), + static_cast<int>(aFloatRect.GetWidth()), static_cast<int>(aFloatRect.GetHeight())}; + + GdkWindow* gdkWindow = widget_get_window(mpFrame->getMouseEventWidget()); + gtk_menu_popup_at_rect(GTK_MENU(pWidget), gdkWindow, &rect, rect_anchor, menu_anchor, nullptr); + } + else +#endif + { + guint nButton; + guint32 nTime; + + //typically there is an event, and we can then distinguish if this was + //launched from the keyboard (gets auto-mnemoniced) or the mouse (which + //doesn't) + GdkEvent *pEvent = gtk_get_current_event(); + if (pEvent) + { + gdk_event_get_button(pEvent, &nButton); + nTime = gdk_event_get_time(pEvent); + } + else + { + nButton = 0; + nTime = GtkSalFrame::GetLastInputEventTime(); + } + + // do the same strange semantics as vcl popup windows to arrive at a frame geometry + // in mirrored UI case; best done by actually executing the same code + sal_uInt16 nArrangeIndex; + Point aPos = FloatingWindow::ImplCalcPos(pWin, rRect, nFlags, nArrangeIndex); + aPos = FloatingWindow::ImplConvertToAbsPos(xParent, aPos); + + gtk_menu_popup(GTK_MENU(pWidget), nullptr, nullptr, MenuPositionFunc, + &aPos, nButton, nTime); + } + + if (g_main_loop_is_running(pLoop)) + { + gdk_threads_leave(); + g_main_loop_run(pLoop); + gdk_threads_enter(); + } + g_main_loop_unref(pLoop); + + mpVCLMenu->Deactivate(); + + gtk_widget_insert_action_group(mpFrame->getMouseEventWidget(), "win", nullptr); + + gtk_widget_destroy(pWidget); + + g_object_unref(mpActionGroup); + ClearActionGroupAndMenuModel(); + + mpFrame = nullptr; + + return true; +} + +/* + * GtkSalMenu + */ + +GtkSalMenu::GtkSalMenu( bool bMenuBar ) : + mbInActivateCallback( false ), + mbMenuBar( bMenuBar ), + mbNeedsUpdate( false ), + mbReturnFocusToDocument( false ), + mbAddedGrab( false ), + mpMenuBarContainerWidget( nullptr ), + mpMenuAllowShrinkWidget( nullptr ), + mpMenuBarWidget( nullptr ), + mpMenuBarContainerProvider( nullptr ), + mpMenuBarProvider( nullptr ), + mpCloseButton( nullptr ), + mpVCLMenu( nullptr ), + mpParentSalMenu( nullptr ), + mpFrame( nullptr ), + mpMenuModel( nullptr ), + mpActionGroup( nullptr ) +{ + //typically this only gets called after the menu has been customized on the + //next idle slot, in the normal case of a new menubar SetFrame is called + //directly long before this idle would get called. + maUpdateMenuBarIdle.SetPriority(TaskPriority::HIGHEST); + maUpdateMenuBarIdle.SetInvokeHandler(LINK(this, GtkSalMenu, MenuBarHierarchyChangeHandler)); + maUpdateMenuBarIdle.SetDebugName("Native Gtk Menu Update Idle"); +} + +IMPL_LINK_NOARG(GtkSalMenu, MenuBarHierarchyChangeHandler, Timer *, void) +{ + SAL_WARN_IF(!mpFrame, "vcl.gtk", "MenuBar layout changed, but no frame for some reason!"); + if (!mpFrame) + return; + SetFrame(mpFrame); +} + +void GtkSalMenu::SetNeedsUpdate() +{ + GtkSalMenu* pMenu = this; + // start that the menu and its parents are in need of an update + // on the next activation + while (pMenu && !pMenu->mbNeedsUpdate) + { + pMenu->mbNeedsUpdate = true; + pMenu = pMenu->mpParentSalMenu; + } + // only if a menubar is directly updated do we force in a full + // structure update + if (mbMenuBar && !maUpdateMenuBarIdle.IsActive()) + maUpdateMenuBarIdle.Start(); +} + +void GtkSalMenu::SetMenuModel(GMenuModel* pMenuModel) +{ + if (mpMenuModel) + g_object_unref(mpMenuModel); + mpMenuModel = pMenuModel; + if (mpMenuModel) + g_object_ref(mpMenuModel); +} + +GtkSalMenu::~GtkSalMenu() +{ + SolarMutexGuard aGuard; + + DestroyMenuBarWidget(); + + if (mpMenuModel) + g_object_unref(mpMenuModel); + + maItems.clear(); + + if (mpFrame) + mpFrame->SetMenu(nullptr); +} + +bool GtkSalMenu::VisibleMenuBar() +{ + return mbMenuBar && (bUnityMode || mpMenuBarContainerWidget); +} + +void GtkSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos ) +{ + SolarMutexGuard aGuard; + GtkSalMenuItem *pItem = static_cast<GtkSalMenuItem*>( pSalMenuItem ); + + if ( nPos == MENU_APPEND ) + maItems.push_back( pItem ); + else + maItems.insert( maItems.begin() + nPos, pItem ); + + pItem->mpParentMenu = this; + + SetNeedsUpdate(); +} + +void GtkSalMenu::RemoveItem( unsigned nPos ) +{ + SolarMutexGuard aGuard; + maItems.erase( maItems.begin() + nPos ); + SetNeedsUpdate(); +} + +void GtkSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned ) +{ + SolarMutexGuard aGuard; + GtkSalMenuItem *pItem = static_cast< GtkSalMenuItem* >( pSalMenuItem ); + GtkSalMenu *pGtkSubMenu = static_cast< GtkSalMenu* >( pSubMenu ); + + if ( pGtkSubMenu == nullptr ) + return; + + pGtkSubMenu->mpParentSalMenu = this; + pItem->mpSubMenu = pGtkSubMenu; + + SetNeedsUpdate(); +} + +static void CloseMenuBar(GtkWidget *, gpointer pMenu) +{ + Application::PostUserEvent(static_cast<MenuBar*>(pMenu)->GetCloseButtonClickHdl()); +} + +void GtkSalMenu::ShowCloseButton(bool bShow) +{ + assert(mbMenuBar); + if (!mpMenuBarContainerWidget) + return; + + if (!bShow) + { + if (mpCloseButton) + gtk_widget_destroy(mpCloseButton); + return; + } + + MenuBar *pVclMenuBar = static_cast<MenuBar*>(mpVCLMenu.get()); + mpCloseButton = gtk_button_new(); + g_signal_connect(mpCloseButton, "clicked", G_CALLBACK(CloseMenuBar), pVclMenuBar); + + gtk_button_set_relief(GTK_BUTTON(mpCloseButton), GTK_RELIEF_NONE); + gtk_button_set_focus_on_click(GTK_BUTTON(mpCloseButton), false); + gtk_widget_set_can_focus(mpCloseButton, false); + + GtkStyleContext *pButtonContext = gtk_widget_get_style_context(GTK_WIDGET(mpCloseButton)); + + GtkCssProvider *pProvider = gtk_css_provider_new(); + static const gchar data[] = "* { " + "padding: 0;" + "margin-left: 8px;" + "margin-right: 8px;" + "min-width: 18px;" + "min-height: 18px;" + "}"; + const gchar olddata[] = "* { " + "padding: 0;" + "margin-left: 8px;" + "margin-right: 8px;" + "}"; + gtk_css_provider_load_from_data(pProvider, gtk_check_version(3, 20, 0) == nullptr ? data : olddata, -1, nullptr); + gtk_style_context_add_provider(pButtonContext, + GTK_STYLE_PROVIDER(pProvider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + gtk_style_context_add_class(pButtonContext, "flat"); + gtk_style_context_add_class(pButtonContext, "small-button"); + + GIcon* icon = g_themed_icon_new_with_default_fallbacks("window-close-symbolic"); + GtkWidget* image = gtk_image_new_from_gicon(icon, GTK_ICON_SIZE_MENU); + gtk_widget_show(image); + g_object_unref(icon); + + OUString sToolTip(VclResId(SV_HELPTEXT_CLOSEDOCUMENT)); + gtk_widget_set_tooltip_text(mpCloseButton, + OUStringToOString(sToolTip, RTL_TEXTENCODING_UTF8).getStr()); + + gtk_widget_set_valign(mpCloseButton, GTK_ALIGN_CENTER); + + gtk_container_add(GTK_CONTAINER(mpCloseButton), image); + gtk_grid_attach(GTK_GRID(mpMenuBarContainerWidget), GTK_WIDGET(mpCloseButton), 1, 0, 1, 1); + gtk_widget_show_all(mpCloseButton); +} + +//Typically when the menubar is deactivated we want the focus to return +//to where it came from. If the menubar was activated because of F6 +//moving focus into the associated VCL menubar then on pressing ESC +//or any other normal reason for deactivation we want focus to return +//to the document, definitely not still stuck in the associated +//VCL menubar. But if F6 is pressed while the menubar is activated +//we want to pass that F6 back to the VCL menubar which will move +//focus to the next pane by itself. +void GtkSalMenu::ReturnFocus() +{ + if (mbAddedGrab) + { + gtk_grab_remove(mpMenuBarWidget); + mbAddedGrab = false; + } + if (!mbReturnFocusToDocument) + gtk_widget_grab_focus(GTK_WIDGET(mpFrame->getEventBox())); + else + mpFrame->GetWindow()->GrabFocusToDocument(); + mbReturnFocusToDocument = false; +} + +gboolean GtkSalMenu::SignalKey(GdkEventKey const * pEvent) +{ + if (pEvent->keyval == GDK_KEY_F6) + { + mbReturnFocusToDocument = false; + gtk_menu_shell_cancel(GTK_MENU_SHELL(mpMenuBarWidget)); + //because we return false here, the keypress will continue + //to propagate and in the case that vcl focus is in + //the vcl menubar then that will also process F6 and move + //to the next pane + } + return false; +} + +//The GtkSalMenu is owner by a Vcl Menu/MenuBar. In the menubar +//case the vcl menubar is present and "visible", but with a 0 height +//so it not apparent. Normally it acts as though it is not there when +//a Native menubar is active. If we return true here, then for keyboard +//activation and traversal with F6 through panes then the vcl menubar +//acts as though it *is* present and we translate its take focus and F6 +//traversal key events into the gtk menubar equivalents. +bool GtkSalMenu::CanGetFocus() const +{ + return mpMenuBarWidget != nullptr; +} + +bool GtkSalMenu::TakeFocus() +{ + if (!mpMenuBarWidget) + return false; + + //Send a keyboard event to the gtk menubar to let it know it has been + //activated via the keyboard. Doesn't do anything except cause the gtk + //menubar "keyboard_mode" member to get set to true, so typically mnemonics + //are shown which will serve as indication that the menubar has focus + //(given that we want to show it with no menus popped down) + GdkEvent *event = GtkSalFrame::makeFakeKeyPress(mpMenuBarWidget); + gtk_widget_event(mpMenuBarWidget, event); + gdk_event_free(event); + + //this pairing results in a menubar with keyboard focus with no menus + //auto-popped down + gtk_grab_add(mpMenuBarWidget); + mbAddedGrab = true; + gtk_menu_shell_select_first(GTK_MENU_SHELL(mpMenuBarWidget), false); + gtk_menu_shell_deselect(GTK_MENU_SHELL(mpMenuBarWidget)); + mbReturnFocusToDocument = true; + return true; +} + +static void MenuBarReturnFocus(GtkMenuShell*, gpointer menu) +{ + GtkSalFrame::UpdateLastInputEventTime(gtk_get_current_event_time()); + GtkSalMenu* pMenu = static_cast<GtkSalMenu*>(menu); + pMenu->ReturnFocus(); +} + +static gboolean MenuBarSignalKey(GtkWidget*, GdkEventKey* pEvent, gpointer menu) +{ + GtkSalMenu* pMenu = static_cast<GtkSalMenu*>(menu); + return pMenu->SignalKey(pEvent); +} + +void GtkSalMenu::CreateMenuBarWidget() +{ + if (mpMenuBarContainerWidget) + return; + + GtkGrid* pGrid = mpFrame->getTopLevelGridWidget(); + mpMenuBarContainerWidget = gtk_grid_new(); + + gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarContainerWidget), true); + gtk_grid_insert_row(pGrid, 0); + gtk_grid_attach(pGrid, mpMenuBarContainerWidget, 0, 0, 1, 1); + + mpMenuAllowShrinkWidget = gtk_scrolled_window_new(nullptr, nullptr); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(mpMenuAllowShrinkWidget), GTK_SHADOW_NONE); + // tdf#116290 external policy on scrolledwindow will not show a scrollbar, + // but still allow scrolled window to not be sized to the child content. + // So the menubar can be shrunk past its nominal smallest width. + // Unlike a hack using GtkFixed/GtkLayout the correct placement of the menubar occurs under RTL + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mpMenuAllowShrinkWidget), GTK_POLICY_EXTERNAL, GTK_POLICY_NEVER); + gtk_grid_attach(GTK_GRID(mpMenuBarContainerWidget), mpMenuAllowShrinkWidget, 0, 0, 1, 1); + + mpMenuBarWidget = gtk_menu_bar_new_from_model(mpMenuModel); + + gtk_widget_insert_action_group(mpMenuBarWidget, "win", mpActionGroup); + gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarWidget), true); + gtk_widget_set_hexpand(mpMenuAllowShrinkWidget, true); + gtk_container_add(GTK_CONTAINER(mpMenuAllowShrinkWidget), mpMenuBarWidget); + + g_signal_connect(G_OBJECT(mpMenuBarWidget), "deactivate", G_CALLBACK(MenuBarReturnFocus), this); + g_signal_connect(G_OBJECT(mpMenuBarWidget), "key-press-event", G_CALLBACK(MenuBarSignalKey), this); + + gtk_widget_show_all(mpMenuBarContainerWidget); + + ShowCloseButton( static_cast<MenuBar*>(mpVCLMenu.get())->HasCloseButton() ); + + ApplyPersona(); +} + +void GtkSalMenu::ApplyPersona() +{ + if (!mpMenuBarContainerWidget) + return; + assert(mbMenuBar); + // I'm dubious about the persona theming feature, but as it exists, lets try and support + // it, apply the image to the mpMenuBarContainerWidget + const BitmapEx& rPersonaBitmap = Application::GetSettings().GetStyleSettings().GetPersonaHeader(); + + GtkStyleContext *pMenuBarContainerContext = gtk_widget_get_style_context(GTK_WIDGET(mpMenuBarContainerWidget)); + if (mpMenuBarContainerProvider) + { + gtk_style_context_remove_provider(pMenuBarContainerContext, GTK_STYLE_PROVIDER(mpMenuBarContainerProvider)); + mpMenuBarContainerProvider = nullptr; + } + GtkStyleContext *pMenuBarContext = gtk_widget_get_style_context(GTK_WIDGET(mpMenuBarWidget)); + if (mpMenuBarProvider) + { + gtk_style_context_remove_provider(pMenuBarContext, GTK_STYLE_PROVIDER(mpMenuBarProvider)); + mpMenuBarProvider = nullptr; + } + + if (!rPersonaBitmap.IsEmpty()) + { + if (maPersonaBitmap != rPersonaBitmap) + { + vcl::PNGWriter aPNGWriter(rPersonaBitmap); + mxPersonaImage.reset(new utl::TempFile); + mxPersonaImage->EnableKillingFile(true); + SvStream* pStream = mxPersonaImage->GetStream(StreamMode::WRITE); + aPNGWriter.Write(*pStream); + mxPersonaImage->CloseStream(); + } + + mpMenuBarContainerProvider = gtk_css_provider_new(); + OUString aBuffer = "* { background-image: url(\"" + mxPersonaImage->GetURL() + "\"); background-position: top right; }"; + OString aResult = OUStringToOString(aBuffer, RTL_TEXTENCODING_UTF8); + gtk_css_provider_load_from_data(mpMenuBarContainerProvider, aResult.getStr(), aResult.getLength(), nullptr); + gtk_style_context_add_provider(pMenuBarContainerContext, GTK_STYLE_PROVIDER(mpMenuBarContainerProvider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + + // force the menubar to be transparent when persona is active otherwise for + // me the menubar becomes gray when its in the backdrop + mpMenuBarProvider = gtk_css_provider_new(); + static const gchar data[] = "* { " + "background-image: none;" + "background-color: transparent;" + "}"; + gtk_css_provider_load_from_data(mpMenuBarProvider, data, -1, nullptr); + gtk_style_context_add_provider(pMenuBarContext, + GTK_STYLE_PROVIDER(mpMenuBarProvider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } + maPersonaBitmap = rPersonaBitmap; +} + +void GtkSalMenu::DestroyMenuBarWidget() +{ + if (mpMenuBarContainerWidget) + { + gtk_widget_destroy(mpMenuBarContainerWidget); + mpMenuBarContainerWidget = nullptr; + mpCloseButton = nullptr; + } +} + +void GtkSalMenu::SetFrame(const SalFrame* pFrame) +{ + SolarMutexGuard aGuard; + assert(mbMenuBar); + SAL_INFO("vcl.unity", "GtkSalMenu set to frame"); + mpFrame = const_cast<GtkSalFrame*>(static_cast<const GtkSalFrame*>(pFrame)); + + // if we had a menu on the GtkSalMenu we have to free it as we generate a + // full menu anyway and we might need to reuse an existing model and + // actiongroup + mpFrame->SetMenu( this ); + mpFrame->EnsureAppMenuWatch(); + + // Clean menu model and action group if needed. + GtkWidget* pWidget = mpFrame->getWindow(); + GdkWindow* gdkWindow = gtk_widget_get_window( pWidget ); + + GLOMenu* pMenuModel = G_LO_MENU( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) ); + GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-action-group" ) ); + SAL_INFO("vcl.unity", "Found menu model: " << pMenuModel << " and action group: " << pActionGroup); + + if ( pMenuModel ) + { + if ( g_menu_model_get_n_items( G_MENU_MODEL( pMenuModel ) ) > 0 ) + g_lo_menu_remove( pMenuModel, 0 ); + + mpMenuModel = G_MENU_MODEL( g_lo_menu_new() ); + } + + if ( pActionGroup ) + { + g_lo_action_group_clear( pActionGroup ); + mpActionGroup = G_ACTION_GROUP( pActionGroup ); + } + + // Generate the main menu structure. + if ( PrepUpdate() ) + UpdateFull(); + + g_lo_menu_insert_section( pMenuModel, 0, nullptr, mpMenuModel ); + + if (!bUnityMode && static_cast<MenuBar*>(mpVCLMenu.get())->IsDisplayable()) + { + DestroyMenuBarWidget(); + CreateMenuBarWidget(); + } +} + +const GtkSalFrame* GtkSalMenu::GetFrame() const +{ + SolarMutexGuard aGuard; + const GtkSalMenu* pMenu = this; + while( pMenu && ! pMenu->mpFrame ) + pMenu = pMenu->mpParentSalMenu; + return pMenu ? pMenu->mpFrame : nullptr; +} + +void GtkSalMenu::NativeCheckItem( unsigned nSection, unsigned nItemPos, MenuItemBits bits, gboolean bCheck ) +{ + SolarMutexGuard aGuard; + + if ( mpActionGroup == nullptr ) + return; + + gchar* aCommand = g_lo_menu_get_command_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos ); + + if ( aCommand != nullptr || g_strcmp0( aCommand, "" ) != 0 ) + { + GVariant *pCheckValue = nullptr; + GVariant *pCurrentState = g_action_group_get_action_state( mpActionGroup, aCommand ); + + if ( bits & MenuItemBits::RADIOCHECK ) + pCheckValue = bCheck ? g_variant_new_string( aCommand ) : g_variant_new_string( "" ); + else + { + // By default, all checked items are checkmark buttons. + if (bCheck || pCurrentState != nullptr) + pCheckValue = g_variant_new_boolean( bCheck ); + } + + if ( pCheckValue != nullptr ) + { + if ( pCurrentState == nullptr || g_variant_equal( pCurrentState, pCheckValue ) == FALSE ) + { + g_action_group_change_action_state( mpActionGroup, aCommand, pCheckValue ); + } + else + { + g_variant_unref (pCheckValue); + } + } + + if ( pCurrentState != nullptr ) + g_variant_unref( pCurrentState ); + } + + if ( aCommand ) + g_free( aCommand ); +} + +void GtkSalMenu::NativeSetEnableItem( gchar const * aCommand, gboolean bEnable ) +{ + SolarMutexGuard aGuard; + GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup ); + + if ( g_action_group_get_action_enabled( G_ACTION_GROUP( pActionGroup ), aCommand ) != bEnable ) + g_lo_action_group_set_action_enabled( pActionGroup, aCommand, bEnable ); +} + +void GtkSalMenu::NativeSetItemText( unsigned nSection, unsigned nItemPos, const OUString& rText ) +{ + SolarMutexGuard aGuard; + // Escape all underscores so that they don't get interpreted as hotkeys + OUString aText = rText.replaceAll( "_", "__" ); + // Replace the LibreOffice hotkey identifier with an underscore + aText = aText.replace( '~', '_' ); + OString aConvertedText = OUStringToOString( aText, RTL_TEXTENCODING_UTF8 ); + + // Update item text only when necessary. + gchar* aLabel = g_lo_menu_get_label_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos ); + + if ( aLabel == nullptr || g_strcmp0( aLabel, aConvertedText.getStr() ) != 0 ) + g_lo_menu_set_label_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aConvertedText.getStr() ); + + if ( aLabel ) + g_free( aLabel ); +} + +namespace +{ + void DestroyMemoryStream(gpointer data) + { + SvMemoryStream* pMemStm = static_cast<SvMemoryStream*>(data); + delete pMemStm; + } +} + +void GtkSalMenu::NativeSetItemIcon( unsigned nSection, unsigned nItemPos, const Image& rImage ) +{ +#if GLIB_CHECK_VERSION(2,38,0) + if (!!rImage && mbHasNullItemIcon) + return; + + SolarMutexGuard aGuard; + + if (!!rImage) + { + SvMemoryStream* pMemStm = new SvMemoryStream; + vcl::PNGWriter aWriter(rImage.GetBitmapEx()); + aWriter.Write(*pMemStm); + + GBytes *pBytes = g_bytes_new_with_free_func(pMemStm->GetData(), + pMemStm->TellEnd(), + DestroyMemoryStream, + pMemStm); + + GIcon *pIcon = g_bytes_icon_new(pBytes); + + g_lo_menu_set_icon_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, pIcon ); + g_object_unref(pIcon); + g_bytes_unref(pBytes); + mbHasNullItemIcon = false; + } + else + { + g_lo_menu_set_icon_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, nullptr ); + mbHasNullItemIcon = true; + } +#else + (void)nSection; + (void)nItemPos; + (void)rImage; +#endif +} + +void GtkSalMenu::NativeSetAccelerator( unsigned nSection, unsigned nItemPos, const vcl::KeyCode& rKeyCode, const OUString& rKeyName ) +{ + SolarMutexGuard aGuard; + + if ( rKeyName.isEmpty() ) + return; + + guint nKeyCode; + GdkModifierType nModifiers; + GtkSalFrame::KeyCodeToGdkKey(rKeyCode, &nKeyCode, &nModifiers); + + gchar* aAccelerator = gtk_accelerator_name( nKeyCode, nModifiers ); + + gchar* aCurrentAccel = g_lo_menu_get_accelerator_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos ); + + if ( aCurrentAccel == nullptr && g_strcmp0( aCurrentAccel, aAccelerator ) != 0 ) + g_lo_menu_set_accelerator_to_item_in_section ( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aAccelerator ); + + g_free( aAccelerator ); + g_free( aCurrentAccel ); +} + +bool GtkSalMenu::NativeSetItemCommand( unsigned nSection, + unsigned nItemPos, + sal_uInt16 nId, + const gchar* aCommand, + MenuItemBits nBits, + bool bChecked, + bool bIsSubmenu ) +{ + bool bSubMenuAddedOrRemoved = false; + + SolarMutexGuard aGuard; + GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup ); + + GVariant *pTarget = nullptr; + + if (g_action_group_has_action(mpActionGroup, aCommand)) + g_lo_action_group_remove(pActionGroup, aCommand); + + if ( ( nBits & MenuItemBits::CHECKABLE ) || bIsSubmenu ) + { + // Item is a checkmark button. + GVariantType* pStateType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_BOOLEAN) ); + GVariant* pState = g_variant_new_boolean( bChecked ); + + g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, bIsSubmenu, nullptr, pStateType, nullptr, pState ); + } + else if ( nBits & MenuItemBits::RADIOCHECK ) + { + // Item is a radio button. + GVariantType* pParameterType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_STRING) ); + GVariantType* pStateType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_STRING) ); + GVariant* pState = g_variant_new_string( "" ); + pTarget = g_variant_new_string( aCommand ); + + g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, FALSE, pParameterType, pStateType, nullptr, pState ); + } + else + { + // Item is not special, so insert a stateless action. + g_lo_action_group_insert( pActionGroup, aCommand, nId, FALSE ); + } + + GLOMenu* pMenu = G_LO_MENU( mpMenuModel ); + + // Menu item is not updated unless it's necessary. + gchar* aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pMenu, nSection, nItemPos ); + + if ( aCurrentCommand == nullptr || g_strcmp0( aCurrentCommand, aCommand ) != 0 ) + { + bool bOldHasSubmenu = g_lo_menu_get_submenu_from_item_in_section(pMenu, nSection, nItemPos) != nullptr; + bSubMenuAddedOrRemoved = bOldHasSubmenu != bIsSubmenu; + if (bSubMenuAddedOrRemoved) + { + //tdf#98636 it's not good enough to unset the "submenu-action" attribute to change something + //from a submenu to a non-submenu item, so remove the old one entirely and re-add it to + //support achieving that + gchar* pLabel = g_lo_menu_get_label_from_item_in_section(pMenu, nSection, nItemPos); + g_lo_menu_remove_from_section(pMenu, nSection, nItemPos); + g_lo_menu_insert_in_section(pMenu, nSection, nItemPos, pLabel); + g_free(pLabel); + } + + g_lo_menu_set_command_to_item_in_section( pMenu, nSection, nItemPos, aCommand ); + + gchar* aItemCommand = g_strconcat("win.", aCommand, nullptr ); + + if ( bIsSubmenu ) + g_lo_menu_set_submenu_action_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand ); + else + { + g_lo_menu_set_action_and_target_value_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand, pTarget ); + pTarget = nullptr; + } + + g_free( aItemCommand ); + } + + if ( aCurrentCommand ) + g_free( aCurrentCommand ); + + if (pTarget) + g_variant_unref(pTarget); + + return bSubMenuAddedOrRemoved; +} + +GtkSalMenu* GtkSalMenu::GetTopLevel() +{ + GtkSalMenu *pMenu = this; + while (pMenu->mpParentSalMenu) + pMenu = pMenu->mpParentSalMenu; + return pMenu; +} + +void GtkSalMenu::DispatchCommand(const gchar *pCommand) +{ + SolarMutexGuard aGuard; + MenuAndId aMenuAndId = decode_command(pCommand); + GtkSalMenu* pSalSubMenu = aMenuAndId.first; + GtkSalMenu* pTopLevel = pSalSubMenu->GetTopLevel(); + if (pTopLevel->mpMenuBarWidget) + { + // tdf#125803 spacebar will toggle radios and checkbuttons without automatically + // closing the menu. To handle this properly I imagine we need to set groups for the + // radiobuttons so the others visually untoggle when the active one is toggled and + // we would further need to teach vcl that the state can change more than once. + // + // or we could unconditionally deactivate the menus if regardless of what particular + // type of menu item got activated + gtk_menu_shell_deactivate(GTK_MENU_SHELL(pTopLevel->mpMenuBarWidget)); + } + pTopLevel->GetMenu()->HandleMenuCommandEvent(pSalSubMenu->GetMenu(), aMenuAndId.second); +} + +void GtkSalMenu::ActivateAllSubmenus(Menu* pMenuBar) +{ + for (GtkSalMenuItem* pSalItem : maItems) + { + if ( pSalItem->mpSubMenu != nullptr ) + { + // We can re-enter this method via the new event loop that gets created + // in GtkClipboardTransferable::getTransferDataFlavorsAsVector, so use the InActivateCallback + // flag to detect that and skip some startup work. + if (!pSalItem->mpSubMenu->mbInActivateCallback) + { + pSalItem->mpSubMenu->mbInActivateCallback = true; + pMenuBar->HandleMenuActivateEvent(pSalItem->mpSubMenu->GetMenu()); + pSalItem->mpSubMenu->mbInActivateCallback = false; + pSalItem->mpSubMenu->ActivateAllSubmenus(pMenuBar); + pSalItem->mpSubMenu->Update(); + pMenuBar->HandleMenuDeActivateEvent(pSalItem->mpSubMenu->GetMenu()); + } + } + } +} + +void GtkSalMenu::ClearActionGroupAndMenuModel() +{ + SetMenuModel(nullptr); + mpActionGroup = nullptr; + for (GtkSalMenuItem* pSalItem : maItems) + { + if ( pSalItem->mpSubMenu != nullptr ) + { + pSalItem->mpSubMenu->ClearActionGroupAndMenuModel(); + } + } +} + +void GtkSalMenu::Activate(const gchar* pCommand) +{ + MenuAndId aMenuAndId = decode_command(pCommand); + GtkSalMenu* pSalMenu = aMenuAndId.first; + GtkSalMenu* pTopLevel = pSalMenu->GetTopLevel(); + Menu* pVclMenu = pSalMenu->GetMenu(); + Menu* pVclSubMenu = pVclMenu->GetPopupMenu(aMenuAndId.second); + GtkSalMenu* pSubMenu = pSalMenu->GetItemAtPos(pVclMenu->GetItemPos(aMenuAndId.second))->mpSubMenu; + + pSubMenu->mbInActivateCallback = true; + pTopLevel->GetMenu()->HandleMenuActivateEvent(pVclSubMenu); + pSubMenu->mbInActivateCallback = false; + pVclSubMenu->UpdateNativeMenu(); +} + +void GtkSalMenu::Deactivate(const gchar* pCommand) +{ + MenuAndId aMenuAndId = decode_command(pCommand); + GtkSalMenu* pSalMenu = aMenuAndId.first; + GtkSalMenu* pTopLevel = pSalMenu->GetTopLevel(); + Menu* pVclMenu = pSalMenu->GetMenu(); + Menu* pVclSubMenu = pVclMenu->GetPopupMenu(aMenuAndId.second); + pTopLevel->GetMenu()->HandleMenuDeActivateEvent(pVclSubMenu); +} + +void GtkSalMenu::EnableUnity(bool bEnable) +{ + bUnityMode = bEnable; + + MenuBar* pMenuBar(static_cast<MenuBar*>(mpVCLMenu.get())); + bool bDisplayable(pMenuBar->IsDisplayable()); + + if (bEnable) + { + DestroyMenuBarWidget(); + UpdateFull(); + if (!bDisplayable) + ShowMenuBar(false); + } + else + { + Update(); + ShowMenuBar(bDisplayable); + } + + pMenuBar->LayoutChanged(); +} + +void GtkSalMenu::ShowMenuBar( bool bVisible ) +{ + // Unity tdf#106271: Can't hide global menu, so empty it instead when user wants to hide menubar, + if (bUnityMode) + { + if (bVisible) + Update(); + else if (mpMenuModel && g_menu_model_get_n_items(G_MENU_MODEL(mpMenuModel)) > 0) + g_lo_menu_remove(G_LO_MENU(mpMenuModel), 0); + } + else if (bVisible) + CreateMenuBarWidget(); + else + DestroyMenuBarWidget(); +} + +bool GtkSalMenu::IsItemVisible( unsigned nPos ) +{ + SolarMutexGuard aGuard; + bool bVisible = false; + + if ( nPos < maItems.size() ) + bVisible = maItems[ nPos ]->mbVisible; + + return bVisible; +} + +void GtkSalMenu::CheckItem( unsigned, bool ) +{ +} + +void GtkSalMenu::EnableItem( unsigned nPos, bool bEnable ) +{ + SolarMutexGuard aGuard; + if ( bUnityMode && !mbInActivateCallback && !mbNeedsUpdate && GetTopLevel()->mbMenuBar && ( nPos < maItems.size() ) ) + { + gchar* pCommand = GetCommandForItem( GetItemAtPos( nPos ) ); + NativeSetEnableItem( pCommand, bEnable ); + g_free( pCommand ); + } +} + +void GtkSalMenu::ShowItem( unsigned nPos, bool bShow ) +{ + SolarMutexGuard aGuard; + if ( nPos < maItems.size() ) + { + maItems[ nPos ]->mbVisible = bShow; + if ( bUnityMode && !mbInActivateCallback && !mbNeedsUpdate && GetTopLevel()->mbMenuBar ) + Update(); + } +} + +void GtkSalMenu::SetItemText( unsigned nPos, SalMenuItem* pSalMenuItem, const OUString& rText ) +{ + SolarMutexGuard aGuard; + if ( bUnityMode && !mbInActivateCallback && !mbNeedsUpdate && GetTopLevel()->mbMenuBar && ( nPos < maItems.size() ) ) + { + gchar* pCommand = GetCommandForItem( static_cast< GtkSalMenuItem* >( pSalMenuItem ) ); + + gint nSectionsCount = g_menu_model_get_n_items( mpMenuModel ); + for ( gint nSection = 0; nSection < nSectionsCount; ++nSection ) + { + gint nItemsCount = g_lo_menu_get_n_items_from_section( G_LO_MENU( mpMenuModel ), nSection ); + for ( gint nItem = 0; nItem < nItemsCount; ++nItem ) + { + gchar* pCommandFromModel = g_lo_menu_get_command_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItem ); + + if ( !g_strcmp0( pCommandFromModel, pCommand ) ) + { + NativeSetItemText( nSection, nItem, rText ); + g_free( pCommandFromModel ); + g_free( pCommand ); + return; + } + + g_free( pCommandFromModel ); + } + } + + g_free( pCommand ); + } +} + +void GtkSalMenu::SetItemImage( unsigned, SalMenuItem*, const Image& ) +{ +} + +void GtkSalMenu::SetAccelerator( unsigned, SalMenuItem*, const vcl::KeyCode&, const OUString& ) +{ +} + +void GtkSalMenu::GetSystemMenuData( SystemMenuData* ) +{ +} + +int GtkSalMenu::GetMenuBarHeight() const +{ + return mpMenuBarWidget ? gtk_widget_get_allocated_height(mpMenuBarWidget) : 0; +} + +/* + * GtkSalMenuItem + */ + +GtkSalMenuItem::GtkSalMenuItem( const SalItemParams* pItemData ) : + mpParentMenu( nullptr ), + mpSubMenu( nullptr ), + mnType( pItemData->eType ), + mnId( pItemData->nId ), + mbVisible( true ) +{ +} + +GtkSalMenuItem::~GtkSalMenuItem() +{ +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtk3gtksys.cxx b/vcl/unx/gtk3/gtk3gtksys.cxx index 2406e0aa3118..229a718c75a7 100644 --- a/vcl/unx/gtk3/gtk3gtksys.cxx +++ b/vcl/unx/gtk3/gtk3gtksys.cxx @@ -5,8 +5,279 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.h> +#include <gmodule.h> +#include <gtk/gtk.h> +#include <unx/gtk/gtkinst.hxx> +#include <unx/gtk/gtksys.hxx> +#include <unx/gtk/gtkbackend.hxx> +#include <osl/module.h> + +GtkSalSystem *GtkSalSystem::GetSingleton() +{ + static GtkSalSystem *pSingleton = new GtkSalSystem(); + return pSingleton; +} + +SalSystem *GtkInstance::CreateSalSystem() +{ + return GtkSalSystem::GetSingleton(); +} + +GtkSalSystem::GtkSalSystem() : SalGenericSystem() +{ + mpDisplay = gdk_display_get_default(); + countScreenMonitors(); + // rhbz#1285356, native look will be gtk2, which crashes + // when gtk3 is already loaded. Until there is a solution + // java-side force look and feel to something that doesn't + // crash when we are using gtk3 + setenv("STOC_FORCE_SYSTEM_LAF", "true", 1); +} + +GtkSalSystem::~GtkSalSystem() +{ +} + +int +GtkSalSystem::GetDisplayXScreenCount() +{ + return gdk_display_get_n_screens (mpDisplay); +} + +namespace +{ + +struct GdkRectangleCoincidentLess +{ + // fdo#78799 - detect and elide overlaying monitors of different sizes + bool operator()(GdkRectangle const& rLeft, GdkRectangle const& rRight) + { + return + rLeft.x < rRight.x + || rLeft.y < rRight.y + ; + } +}; +struct GdkRectangleCoincident +{ + // fdo#78799 - detect and elide overlaying monitors of different sizes + bool operator()(GdkRectangle const& rLeft, GdkRectangle const& rRight) + { + return + rLeft.x == rRight.x + && rLeft.y == rRight.y + ; + } +}; + +} + +/** + * GtkSalSystem::countScreenMonitors() + * + * This method builds the vector which allows us to map from VCL's + * idea of linear integer ScreenNumber to gtk+'s rather more + * complicated screen + monitor concept. */ +void +GtkSalSystem::countScreenMonitors() +{ + maScreenMonitors.clear(); + for (gint i = 0; i < gdk_display_get_n_screens(mpDisplay); i++) + { + GdkScreen* const pScreen(gdk_display_get_screen(mpDisplay, i)); + gint nMonitors(pScreen ? gdk_screen_get_n_monitors(pScreen) : 0); + if (nMonitors > 1) + { + std::vector<GdkRectangle> aGeometries; + aGeometries.reserve(nMonitors); + for (gint j(0); j != nMonitors; ++j) + { + GdkRectangle aGeometry; + gdk_screen_get_monitor_geometry(pScreen, j, &aGeometry); + aGeometries.push_back(aGeometry); + } + std::sort(aGeometries.begin(), aGeometries.end(), + GdkRectangleCoincidentLess()); + const std::vector<GdkRectangle>::iterator aUniqueEnd( + std::unique(aGeometries.begin(), aGeometries.end(), + GdkRectangleCoincident())); + nMonitors = std::distance(aGeometries.begin(), aUniqueEnd); + } + maScreenMonitors.emplace_back(pScreen, nMonitors); + } +} + +SalX11Screen +GtkSalSystem::getXScreenFromDisplayScreen(unsigned int nScreen) +{ + gint nMonitor; + + GdkScreen *pScreen = getScreenMonitorFromIdx (nScreen, nMonitor); + if (!pScreen) + return SalX11Screen (0); + if (!DLSYM_GDK_IS_X11_DISPLAY(mpDisplay)) + return SalX11Screen (0); + return SalX11Screen (gdk_x11_screen_get_screen_number (pScreen)); +} + +GdkScreen * +GtkSalSystem::getScreenMonitorFromIdx (int nIdx, gint &nMonitor) +{ + GdkScreen *pScreen = nullptr; + for (auto const& screenMonitor : maScreenMonitors) + { + pScreen = screenMonitor.first; + if (!pScreen) + break; + if (nIdx >= screenMonitor.second) + nIdx -= screenMonitor.second; + else + break; + } + nMonitor = nIdx; + + // handle invalid monitor indexes as non-existent screens + if (nMonitor < 0 || (pScreen && nMonitor >= gdk_screen_get_n_monitors (pScreen))) + pScreen = nullptr; + + return pScreen; +} + +int +GtkSalSystem::getScreenIdxFromPtr (GdkScreen *pScreen) +{ + int nIdx = 0; + for (auto const& screenMonitor : maScreenMonitors) + { + if (screenMonitor.first == pScreen) + return nIdx; + nIdx += screenMonitor.second; + } + g_warning ("failed to find screen %p", pScreen); + return 0; +} + +int GtkSalSystem::getScreenMonitorIdx (GdkScreen *pScreen, + int nX, int nY) +{ + // TODO: this will fail horribly for exotic combinations like two + // monitors in mirror mode and one extra. Hopefully such + // abominations are not used (or, even better, not possible) in + // practice .-) + return getScreenIdxFromPtr (pScreen) + + gdk_screen_get_monitor_at_point (pScreen, nX, nY); +} + +unsigned int GtkSalSystem::GetDisplayScreenCount() +{ + gint nMonitor; + (void)getScreenMonitorFromIdx (G_MAXINT, nMonitor); + return G_MAXINT - nMonitor; +} + +bool GtkSalSystem::IsUnifiedDisplay() +{ + return gdk_display_get_n_screens (mpDisplay) == 1; +} + +namespace { +int _fallback_get_primary_monitor (GdkScreen *pScreen) +{ + // Use monitor name as primacy heuristic + int max = gdk_screen_get_n_monitors (pScreen); + for (int i = 0; i < max; ++i) + { + char *name = gdk_screen_get_monitor_plug_name (pScreen, i); + bool bLaptop = (name && !g_ascii_strncasecmp (name, "LVDS", 4)); + g_free (name); + if (bLaptop) + return i; + } + return 0; +} + +int _get_primary_monitor (GdkScreen *pScreen) +{ + static int (*get_fn) (GdkScreen *) = nullptr; + get_fn = gdk_screen_get_primary_monitor; + // Perhaps we have a newer gtk+ with this symbol: + if (!get_fn) + { + get_fn = reinterpret_cast<int(*)(GdkScreen*)>(osl_getAsciiFunctionSymbol(nullptr, + "gdk_screen_get_primary_monitor")); + } + if (!get_fn) + get_fn = _fallback_get_primary_monitor; + if (get_fn) + return get_fn (pScreen); + else + return 0; +} +} // end anonymous namespace + +unsigned int GtkSalSystem::GetDisplayBuiltInScreen() +{ + GdkScreen *pDefault = gdk_display_get_default_screen (mpDisplay); + int idx = getScreenIdxFromPtr (pDefault); + return idx + _get_primary_monitor (pDefault); +} + +tools::Rectangle GtkSalSystem::GetDisplayScreenPosSizePixel (unsigned int nScreen) +{ + gint nMonitor; + GdkScreen *pScreen; + GdkRectangle aRect; + pScreen = getScreenMonitorFromIdx (nScreen, nMonitor); + if (!pScreen) + return tools::Rectangle(); + gdk_screen_get_monitor_geometry (pScreen, nMonitor, &aRect); + return tools::Rectangle (Point(aRect.x, aRect.y), Size(aRect.width, aRect.height)); +} + +// convert ~ to indicate mnemonic to '_' +static OString MapToGtkAccelerator(const OUString &rStr) +{ + return OUStringToOString(rStr.replaceFirst("~", "_"), RTL_TEXTENCODING_UTF8); +} + +int GtkSalSystem::ShowNativeDialog (const OUString& rTitle, const OUString& rMessage, + const std::vector< OUString >& rButtonNames) +{ + OString aTitle (OUStringToOString (rTitle, RTL_TEXTENCODING_UTF8)); + OString aMessage (OUStringToOString (rMessage, RTL_TEXTENCODING_UTF8)); + + GtkDialog *pDialog = GTK_DIALOG ( + g_object_new (GTK_TYPE_MESSAGE_DIALOG, + "title", aTitle.getStr(), + "message-type", int(GTK_MESSAGE_WARNING), + "text", aMessage.getStr(), + nullptr)); + int nButton = 0; + for (auto const& buttonName : rButtonNames) + gtk_dialog_add_button (pDialog, MapToGtkAccelerator(buttonName).getStr(), nButton++); + gtk_dialog_set_default_response (pDialog, 0/*nDefaultButton*/); + + nButton = gtk_dialog_run (pDialog); + if (nButton < 0) + nButton = -1; + + gtk_widget_destroy (GTK_WIDGET (pDialog)); -#include "../gtk/gtksys.cxx" + return nButton; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtk3hudawareness.cxx b/vcl/unx/gtk3/gtk3hudawareness.cxx index 3d928f0fd0b1..0aa5878a1afe 100644 --- a/vcl/unx/gtk3/gtk3hudawareness.cxx +++ b/vcl/unx/gtk3/gtk3hudawareness.cxx @@ -1,4 +1,107 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -#include "../gtk/hudawareness.cxx" +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <string.h> + +#include <unx/gtk/gtksalmenu.hxx> +#include <unx/gtk/hudawareness.h> + +struct HudAwarenessHandle +{ + GDBusConnection *connection; + HudAwarenessCallback callback; + gpointer user_data; + GDestroyNotify notify; +}; + +static void +hud_awareness_method_call (GDBusConnection * /* connection */, + const gchar * /* sender */, + const gchar * /* object_path */, + const gchar * /* interface_name */, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + HudAwarenessHandle *handle = static_cast<HudAwarenessHandle*>(user_data); + + if (g_str_equal (method_name, "HudActiveChanged")) + { + gboolean active; + + g_variant_get (parameters, "(b)", &active); + + (* handle->callback) (active, handle->user_data); + } + + g_dbus_method_invocation_return_value (invocation, nullptr); +} + +guint +hud_awareness_register (GDBusConnection *connection, + const gchar *object_path, + HudAwarenessCallback callback, + gpointer user_data, + GDestroyNotify notify, + GError **error) +{ + static GDBusInterfaceInfo *iface; + static GDBusNodeInfo *info; + GDBusInterfaceVTable vtable; + HudAwarenessHandle *handle; + guint object_id; + + memset (static_cast<void *>(&vtable), 0, sizeof (vtable)); + vtable.method_call = hud_awareness_method_call; + + if G_UNLIKELY (iface == nullptr) + { + GError *local_error = nullptr; + + info = g_dbus_node_info_new_for_xml ("<node>" + "<interface name='com.canonical.hud.Awareness'>" + "<method name='CheckAwareness'/>" + "<method name='HudActiveChanged'>" + "<arg type='b'/>" + "</method>" + "</interface>" + "</node>", + &local_error); + g_assert_no_error (local_error); + iface = g_dbus_node_info_lookup_interface (info, "com.canonical.hud.Awareness"); + g_assert (iface != nullptr); + } + + handle = static_cast<HudAwarenessHandle*>(g_malloc (sizeof (HudAwarenessHandle))); + + object_id = g_dbus_connection_register_object (connection, object_path, iface, &vtable, handle, &g_free, error); + + if (object_id == 0) + { + g_free (handle); + return 0; + } + + handle->connection = static_cast<GDBusConnection*>(g_object_ref (connection)); + handle->callback = callback; + handle->user_data = user_data; + handle->notify = notify; + + return object_id; +} + +void +hud_awareness_unregister (GDBusConnection *connection, + guint subscription_id) +{ + g_dbus_connection_unregister_object (connection, subscription_id); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtk3salprn-gtk.cxx b/vcl/unx/gtk3/gtk3salprn-gtk.cxx index 16bf17c8562a..aeb9b1de246c 100644 --- a/vcl/unx/gtk3/gtk3salprn-gtk.cxx +++ b/vcl/unx/gtk3/gtk3salprn-gtk.cxx @@ -7,6 +7,956 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../gtk/salprn-gtk.cxx" +#include <unx/gtk/gtkprintwrapper.hxx> + +#include <unx/gtk/gtkdata.hxx> +#include <unx/gtk/gtkframe.hxx> +#include <unx/gtk/gtkinst.hxx> +#include <unx/gtk/gtkprn.hxx> + +#include <vcl/configsettings.hxx> +#include <vcl/help.hxx> +#include <vcl/print.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> + +#include <gtk/gtk.h> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <com/sun/star/sheet/XSpreadsheetView.hpp> +#include <com/sun/star/view/PrintableState.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> + +#include <officecfg/Office/Common.hxx> + +#include <rtl/ustring.hxx> +#include <sal/log.hxx> + +#include <unotools/streamwrap.hxx> + +#include <cstring> +#include <map> + +namespace beans = com::sun::star::beans; +namespace uno = com::sun::star::uno; +namespace view = com::sun::star::view; + +using vcl::unx::GtkPrintWrapper; + +using uno::UNO_QUERY; + +class GtkPrintDialog +{ +public: + explicit GtkPrintDialog(vcl::PrinterController& io_rController); + bool run(); + GtkPrinter* getPrinter() const + { + return m_xWrapper->print_unix_dialog_get_selected_printer(GTK_PRINT_UNIX_DIALOG(m_pDialog)); + } + GtkPrintSettings* getSettings() const + { + return m_xWrapper->print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(m_pDialog)); + } + void updateControllerPrintRange(); + + ~GtkPrintDialog(); + + static void UIOption_CheckHdl(GtkWidget* i_pWidget, GtkPrintDialog* io_pThis) + { + io_pThis->impl_UIOption_CheckHdl(i_pWidget); + } + static void UIOption_RadioHdl(GtkWidget* i_pWidget, GtkPrintDialog* io_pThis) + { + io_pThis->impl_UIOption_RadioHdl(i_pWidget); + } + static void UIOption_SelectHdl(GtkWidget* i_pWidget, GtkPrintDialog* io_pThis) + { + io_pThis->impl_UIOption_SelectHdl(i_pWidget); + } + +private: + beans::PropertyValue* impl_queryPropertyValue(GtkWidget* i_pWidget) const; + void impl_checkOptionalControlDependencies(); + + void impl_UIOption_CheckHdl(GtkWidget* i_pWidget); + void impl_UIOption_RadioHdl(GtkWidget* i_pWidget); + void impl_UIOption_SelectHdl(GtkWidget* i_pWidget); + + void impl_initDialog(); + void impl_initCustomTab(); + void impl_initPrintContent(uno::Sequence<sal_Bool> const& i_rDisabled); + + void impl_readFromSettings(); + void impl_storeToSettings() const; + +private: + GtkWidget* m_pDialog; + vcl::PrinterController& m_rController; + std::map<GtkWidget*, OUString> m_aControlToPropertyMap; + std::map<GtkWidget*, sal_Int32> m_aControlToNumValMap; + std::shared_ptr<GtkPrintWrapper> m_xWrapper; +}; + +struct GtkSalPrinter_Impl +{ + OString m_sSpoolFile; + OUString m_sJobName; + GtkPrinter* m_pPrinter; + GtkPrintSettings* m_pSettings; + + GtkSalPrinter_Impl(); + ~GtkSalPrinter_Impl(); +}; + +GtkSalPrinter_Impl::GtkSalPrinter_Impl() + : m_pPrinter(nullptr) + , m_pSettings(nullptr) +{ +} + +GtkSalPrinter_Impl::~GtkSalPrinter_Impl() +{ + if (m_pPrinter) + { + g_object_unref(G_OBJECT(m_pPrinter)); + m_pPrinter = nullptr; + } + if (m_pSettings) + { + g_object_unref(G_OBJECT(m_pSettings)); + m_pSettings = nullptr; + } +} + +namespace +{ + +GtkInstance const& +lcl_getGtkSalInstance() +{ + // we _know_ this is GtkInstance + return *static_cast<GtkInstance*>(GetGtkSalData()->m_pInstance); +} + +bool +lcl_useSystemPrintDialog() +{ + return officecfg::Office::Common::Misc::UseSystemPrintDialog::get() + && officecfg::Office::Common::Misc::ExperimentalMode::get() + && lcl_getGtkSalInstance().getPrintWrapper()->supportsPrinting(); +} + +} + +GtkSalPrinter::GtkSalPrinter(SalInfoPrinter* const i_pInfoPrinter) + : PspSalPrinter(i_pInfoPrinter) +{ +} + +GtkSalPrinter::~GtkSalPrinter() = default; + +bool +GtkSalPrinter::impl_doJob( + const OUString* const i_pFileName, + const OUString& i_rJobName, + const OUString& i_rAppName, + ImplJobSetup* const io_pSetupData, + const bool i_bCollate, + vcl::PrinterController& io_rController) +{ + io_rController.setJobState(view::PrintableState_JOB_STARTED); + io_rController.jobStarted(); + const bool bJobStarted( + PspSalPrinter::StartJob(i_pFileName, i_rJobName, i_rAppName, + 1/*i_nCopies*/, i_bCollate, true, io_pSetupData)) + ; + + if (bJobStarted) + { + io_rController.createProgressDialog(); + const int nPages(io_rController.getFilteredPageCount()); + for (int nPage(0); nPage != nPages; ++nPage) + { + if (nPage == nPages - 1) + io_rController.setLastPage(true); + io_rController.printFilteredPage(nPage); + } + io_rController.setJobState(view::PrintableState_JOB_COMPLETED); + } + + return bJobStarted; +} + +bool +GtkSalPrinter::StartJob( + const OUString* const i_pFileName, + const OUString& i_rJobName, + const OUString& i_rAppName, + ImplJobSetup* io_pSetupData, + vcl::PrinterController& io_rController) +{ + if (!lcl_useSystemPrintDialog()) + return PspSalPrinter::StartJob(i_pFileName, i_rJobName, i_rAppName, io_pSetupData, io_rController); + + assert(!m_xImpl); + + m_xImpl.reset(new GtkSalPrinter_Impl()); + m_xImpl->m_sJobName = i_rJobName; + + OString sFileName; + if (i_pFileName) + sFileName = OUStringToOString(*i_pFileName, osl_getThreadTextEncoding()); + + GtkPrintDialog aDialog(io_rController); + if (!aDialog.run()) + { + io_rController.abortJob(); + return false; + } + aDialog.updateControllerPrintRange(); + m_xImpl->m_pPrinter = aDialog.getPrinter(); + m_xImpl->m_pSettings = aDialog.getSettings(); + + //To-Do proper name, watch for encodings + sFileName = OString("/tmp/hacking.ps"); + m_xImpl->m_sSpoolFile = sFileName; + + OUString aFileName = OStringToOUString(sFileName, osl_getThreadTextEncoding()); + + //To-Do, swap ps/pdf for gtk_printer_accepts_ps()/gtk_printer_accepts_pdf() ? + + return impl_doJob(&aFileName, i_rJobName, i_rAppName, io_pSetupData, /*bCollate*/false, io_rController); +} + +bool +GtkSalPrinter::EndJob() +{ + bool bRet = PspSalPrinter::EndJob(); + + if (!lcl_useSystemPrintDialog()) + return bRet; + + assert(m_xImpl); + + if (!bRet || m_xImpl->m_sSpoolFile.isEmpty()) + return bRet; + + std::shared_ptr<GtkPrintWrapper> const xWrapper(lcl_getGtkSalInstance().getPrintWrapper()); + + GtkPageSetup* pPageSetup = xWrapper->page_setup_new(); + + GtkPrintJob* const pJob = xWrapper->print_job_new( + OUStringToOString(m_xImpl->m_sJobName, RTL_TEXTENCODING_UTF8).getStr(), + m_xImpl->m_pPrinter, m_xImpl->m_pSettings, pPageSetup); + + GError* error = nullptr; + bRet = xWrapper->print_job_set_source_file(pJob, m_xImpl->m_sSpoolFile.getStr(), &error); + if (bRet) + xWrapper->print_job_send(pJob, nullptr, nullptr, nullptr); + else + { + //To-Do, do something with this + fprintf(stderr, "error was %s\n", error->message); + g_error_free(error); + } + + g_object_unref(pPageSetup); + m_xImpl.reset(); + + //To-Do, remove temp spool file + + return bRet; +} + +namespace +{ + +void +lcl_setHelpText( + GtkWidget* const io_pWidget, + const uno::Sequence<OUString>& i_rHelpTexts, + const sal_Int32 i_nIndex) +{ + if (i_nIndex >= 0 && i_nIndex < i_rHelpTexts.getLength()) + gtk_widget_set_tooltip_text(io_pWidget, + OUStringToOString(i_rHelpTexts.getConstArray()[i_nIndex], RTL_TEXTENCODING_UTF8).getStr()); +} + +GtkWidget* +lcl_makeFrame( + GtkWidget* const i_pChild, + const OUString &i_rText, + const uno::Sequence<OUString> &i_rHelpTexts, + sal_Int32* const io_pCurHelpText) +{ + GtkWidget* const pLabel = gtk_label_new(nullptr); + lcl_setHelpText(pLabel, i_rHelpTexts, !io_pCurHelpText ? 0 : (*io_pCurHelpText)++); + gtk_misc_set_alignment(GTK_MISC(pLabel), 0.0, 0.5); + + { + gchar* const pText = g_markup_printf_escaped("<b>%s</b>", + OUStringToOString(i_rText, RTL_TEXTENCODING_UTF8).getStr()); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(pLabel), pText); + g_free(pText); + } + + GtkWidget* const pFrame = gtk_vbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(pFrame), pLabel, FALSE, FALSE, 0); + + GtkWidget* const pAlignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); + gtk_alignment_set_padding(GTK_ALIGNMENT(pAlignment), 0, 0, 12, 0); + gtk_box_pack_start(GTK_BOX(pFrame), pAlignment, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(pAlignment), i_pChild); + return pFrame; +} + +void +lcl_extractHelpTextsOrIds( + const beans::PropertyValue& rEntry, + uno::Sequence<OUString>& rHelpStrings) +{ + if (!(rEntry.Value >>= rHelpStrings)) + { + OUString aHelpString; + if (rEntry.Value >>= aHelpString) + { + rHelpStrings.realloc(1); + *rHelpStrings.getArray() = aHelpString; + } + } +} + +GtkWidget* +lcl_combo_box_text_new() +{ + return gtk_combo_box_text_new(); +} + +void +lcl_combo_box_text_append(GtkWidget* const pWidget, gchar const* const pText) +{ + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(pWidget), pText); +} + +} + +GtkPrintDialog::GtkPrintDialog(vcl::PrinterController& io_rController) + : m_rController(io_rController) + , m_xWrapper(lcl_getGtkSalInstance().getPrintWrapper()) +{ + assert(m_xWrapper->supportsPrinting()); + impl_initDialog(); + impl_initCustomTab(); + impl_readFromSettings(); +} + +void +GtkPrintDialog::impl_initDialog() +{ + //To-Do, like fpicker, set UI language + m_pDialog = m_xWrapper->print_unix_dialog_new(); + + vcl::Window* const pTopWindow(Application::GetActiveTopWindow()); + if (pTopWindow) + { + GtkSalFrame* const pFrame(dynamic_cast<GtkSalFrame*>(pTopWindow->ImplGetFrame())); + if (pFrame) + { + GtkWindow* const pParent(GTK_WINDOW(pFrame->getWindow())); + if (pParent) + gtk_window_set_transient_for(GTK_WINDOW(m_pDialog), pParent); + } + } + + m_xWrapper->print_unix_dialog_set_manual_capabilities(GTK_PRINT_UNIX_DIALOG(m_pDialog), + GtkPrintCapabilities(GTK_PRINT_CAPABILITY_COPIES + | GTK_PRINT_CAPABILITY_COLLATE + | GTK_PRINT_CAPABILITY_REVERSE + | GTK_PRINT_CAPABILITY_GENERATE_PS + | GTK_PRINT_CAPABILITY_NUMBER_UP + | GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT + )); +} + +void +GtkPrintDialog::impl_initCustomTab() +{ + typedef std::vector<std::pair<GtkWidget*, OUString> > CustomTabs_t; + + const uno::Sequence<beans::PropertyValue>& rOptions(m_rController.getUIOptions()); + std::map<OUString, GtkWidget*> aPropertyToDependencyRowMap; + CustomTabs_t aCustomTabs; + GtkWidget* pCurParent = nullptr; + GtkWidget* pCurTabPage = nullptr; + GtkWidget* pCurSubGroup = nullptr; + bool bIgnoreSubgroup = false; + for (const auto& rOption : rOptions) + { + uno::Sequence<beans::PropertyValue> aOptProp; + rOption.Value >>= aOptProp; + + OUString aCtrlType; + OUString aText; + OUString aPropertyName; + uno::Sequence<OUString> aChoices; + uno::Sequence<sal_Bool> aChoicesDisabled; + uno::Sequence<OUString> aHelpTexts; + sal_Int64 nMinValue = 0, nMaxValue = 0; + sal_Int32 nCurHelpText = 0; + OUString aDependsOnName; + sal_Int32 nDependsOnValue = 0; + bool bUseDependencyRow = false; + bool bIgnore = false; + GtkWidget* pGroup = nullptr; + bool bGtkInternal = false; + + //Fix fdo#69381 + //Next options if this one is empty + if (!aOptProp.hasElements()) + continue; + + for (const beans::PropertyValue& rEntry : std::as_const(aOptProp)) + { + if ( rEntry.Name == "Text" ) + { + OUString aValue; + rEntry.Value >>= aValue; + aText = aValue.replace('~', '_'); + } + else if ( rEntry.Name == "ControlType" ) + rEntry.Value >>= aCtrlType; + else if ( rEntry.Name == "Choices" ) + rEntry.Value >>= aChoices; + else if ( rEntry.Name == "ChoicesDisabled" ) + rEntry.Value >>= aChoicesDisabled; + else if ( rEntry.Name == "Property" ) + { + beans::PropertyValue aVal; + rEntry.Value >>= aVal; + aPropertyName = aVal.Name; + } + else if ( rEntry.Name == "DependsOnName" ) + rEntry.Value >>= aDependsOnName; + else if ( rEntry.Name == "DependsOnEntry" ) + rEntry.Value >>= nDependsOnValue; + else if ( rEntry.Name == "AttachToDependency" ) + rEntry.Value >>= bUseDependencyRow; + else if ( rEntry.Name == "MinValue" ) + rEntry.Value >>= nMinValue; + else if ( rEntry.Name == "MaxValue" ) + rEntry.Value >>= nMaxValue; + else if ( rEntry.Name == "HelpId" ) + { + uno::Sequence<OUString> aHelpIds; + lcl_extractHelpTextsOrIds(rEntry, aHelpIds); + Help* const pHelp = Application::GetHelp(); + if (pHelp) + { + const int nLen = aHelpIds.getLength(); + aHelpTexts.realloc(nLen); + std::transform(aHelpIds.begin(), aHelpIds.end(), aHelpTexts.begin(), + [&pHelp](const OUString& rHelpId) { return pHelp->GetHelpText(rHelpId, static_cast<weld::Widget*>(nullptr)); }); + } + else // fallback + aHelpTexts = aHelpIds; + } + else if ( rEntry.Name == "HelpText" ) + lcl_extractHelpTextsOrIds(rEntry, aHelpTexts); + else if ( rEntry.Name == "InternalUIOnly" ) + rEntry.Value >>= bIgnore; + else if ( rEntry.Name == "Enabled" ) + { + // Ignore this. We use UIControlOptions::isUIOptionEnabled + // to check whether a control should be enabled. + } + else if ( rEntry.Name == "GroupingHint" ) + { + // Ignore this. We cannot add/modify controls to/on existing + // tabs of the Gtk print dialog. + } + else + { + SAL_INFO("vcl.gtk", "unhandled UI option entry: " << rEntry.Name); + } + } + + if ( aPropertyName == "PrintContent" ) + bGtkInternal = true; + + if (aCtrlType == "Group" || !pCurParent) + { + pCurTabPage = gtk_vbox_new(FALSE, 12); + gtk_container_set_border_width(GTK_CONTAINER(pCurTabPage), 6); + lcl_setHelpText(pCurTabPage, aHelpTexts, 0); + + pCurParent = pCurTabPage; + aCustomTabs.emplace_back(pCurTabPage, aText); + } + else if (aCtrlType == "Subgroup") + { + bIgnoreSubgroup = bIgnore; + if (bIgnore) + continue; + pCurParent = gtk_vbox_new(FALSE, 12); + gtk_container_set_border_width(GTK_CONTAINER(pCurParent), 0); + + pCurSubGroup = lcl_makeFrame(pCurParent, aText, aHelpTexts, nullptr); + gtk_box_pack_start(GTK_BOX(pCurTabPage), pCurSubGroup, FALSE, FALSE, 0); + } + // special case: we need to map these to controls of the gtk print dialog + else if (bGtkInternal) + { + if ( aPropertyName == "PrintContent" ) + { + // What to print? And, more importantly, is there a selection? + impl_initPrintContent(aChoicesDisabled); + } + } + else if (bIgnoreSubgroup || bIgnore) + continue; + else + { + // change handlers for all the controls set up in this block + // should be set _after_ the control has been made (in)active, + // because: + // 1. value of the property is _known_--we are using it to + // _set_ the control, right?--no need to change it back .-) + // 2. it may cause warning because the widget may not + // have been placed in m_aControlToPropertyMap yet + + GtkWidget* pWidget = nullptr; + beans::PropertyValue* pVal = nullptr; + if (aCtrlType == "Bool" && pCurParent) + { + pWidget = gtk_check_button_new_with_mnemonic( + OUStringToOString(aText, RTL_TEXTENCODING_UTF8).getStr()); + lcl_setHelpText(pWidget, aHelpTexts, 0); + m_aControlToPropertyMap[pWidget] = aPropertyName; + + bool bVal = false; + pVal = m_rController.getValue(aPropertyName); + if (pVal) + pVal->Value >>= bVal; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pWidget), bVal); + gtk_widget_set_sensitive(pWidget, + m_rController.isUIOptionEnabled(aPropertyName) && pVal != nullptr); + g_signal_connect(pWidget, "toggled", G_CALLBACK(GtkPrintDialog::UIOption_CheckHdl), this); + } + else if (aCtrlType == "Radio" && pCurParent) + { + GtkWidget* const pVbox = gtk_vbox_new(FALSE, 12); + gtk_container_set_border_width(GTK_CONTAINER(pVbox), 0); + + if (!aText.isEmpty()) + pGroup = lcl_makeFrame(pVbox, aText, aHelpTexts, &nCurHelpText); + + sal_Int32 nSelectVal = 0; + pVal = m_rController.getValue(aPropertyName); + if (pVal && pVal->Value.hasValue()) + pVal->Value >>= nSelectVal; + + for (sal_Int32 m = 0; m != aChoices.getLength(); m++) + { + pWidget = gtk_radio_button_new_with_mnemonic_from_widget( + GTK_RADIO_BUTTON(m == 0 ? nullptr : pWidget), + OUStringToOString(aChoices[m].replace('~', '_'), RTL_TEXTENCODING_UTF8).getStr()); + lcl_setHelpText(pWidget, aHelpTexts, nCurHelpText++); + m_aControlToPropertyMap[pWidget] = aPropertyName; + m_aControlToNumValMap[pWidget] = m; + GtkWidget* const pRow = gtk_hbox_new(FALSE, 12); + gtk_box_pack_start(GTK_BOX(pVbox), pRow, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(pRow), pWidget, FALSE, FALSE, 0); + aPropertyToDependencyRowMap[aPropertyName + OUString::number(m)] = pRow; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pWidget), m == nSelectVal); + gtk_widget_set_sensitive(pWidget, + m_rController.isUIOptionEnabled(aPropertyName) && pVal != nullptr); + g_signal_connect(pWidget, "toggled", + G_CALLBACK(GtkPrintDialog::UIOption_RadioHdl), this); + } + + if (pGroup) + pWidget = pGroup; + else + pWidget = pVbox; + } + else if ((aCtrlType == "List" || + aCtrlType == "Range" || + aCtrlType == "Edit" + ) && pCurParent) + { + GtkWidget* const pHbox = gtk_hbox_new(FALSE, 12); + gtk_container_set_border_width(GTK_CONTAINER(pHbox), 0); + + if ( aCtrlType == "List" ) + { + pWidget = lcl_combo_box_text_new(); + + for (const auto& rChoice : std::as_const(aChoices)) + { + lcl_combo_box_text_append(pWidget, + OUStringToOString(rChoice, RTL_TEXTENCODING_UTF8).getStr()); + } + + sal_Int32 nSelectVal = 0; + pVal = m_rController.getValue(aPropertyName); + if (pVal && pVal->Value.hasValue()) + pVal->Value >>= nSelectVal; + gtk_combo_box_set_active(GTK_COMBO_BOX(pWidget), nSelectVal); + g_signal_connect(pWidget, "changed", G_CALLBACK(GtkPrintDialog::UIOption_SelectHdl), this); + } + else if (aCtrlType == "Edit" && pCurParent) + { + pWidget = gtk_entry_new(); + + OUString aCurVal; + pVal = m_rController.getValue(aPropertyName); + if (pVal && pVal->Value.hasValue()) + pVal->Value >>= aCurVal; + gtk_entry_set_text(GTK_ENTRY(pWidget), + OUStringToOString(aCurVal, RTL_TEXTENCODING_UTF8).getStr()); + } + else if (aCtrlType == "Range" && pCurParent) + { + pWidget = gtk_spin_button_new_with_range(nMinValue, nMaxValue, 1.0); + + sal_Int64 nCurVal = 0; + pVal = m_rController.getValue(aPropertyName); + if (pVal && pVal->Value.hasValue()) + pVal->Value >>= nCurVal; + gtk_spin_button_set_value(GTK_SPIN_BUTTON(pWidget), nCurVal); + } + + lcl_setHelpText(pWidget, aHelpTexts, 0); + m_aControlToPropertyMap[pWidget] = aPropertyName; + + gtk_widget_set_sensitive(pWidget, + m_rController.isUIOptionEnabled(aPropertyName) && pVal != nullptr); + + if (!aText.isEmpty()) + { + GtkWidget* const pLabel = gtk_label_new_with_mnemonic( + OUStringToOString(aText, RTL_TEXTENCODING_UTF8).getStr()); + gtk_label_set_mnemonic_widget(GTK_LABEL(pLabel), pWidget); + gtk_box_pack_start(GTK_BOX(pHbox), pLabel, FALSE, FALSE, 0); + } + + gtk_box_pack_start(GTK_BOX(pHbox), pWidget, FALSE, FALSE, 0); + + pWidget = pHbox; + + } + else + SAL_INFO("vcl.gtk", "unhandled option type: " << aCtrlType); + + GtkWidget* pRow = nullptr; + if (pWidget) + { + if (bUseDependencyRow && !aDependsOnName.isEmpty()) + { + pRow = aPropertyToDependencyRowMap[aDependsOnName + OUString::number(nDependsOnValue)]; + if (!pRow) + { + gtk_widget_destroy(pWidget); + pWidget = nullptr; + } + } + } + if (pWidget) + { + if (!pRow) + { + pRow = gtk_hbox_new(FALSE, 12); + gtk_box_pack_start(GTK_BOX(pCurParent), pRow, FALSE, FALSE, 0); + } + if (!pGroup) + aPropertyToDependencyRowMap[aPropertyName + OUString::number(0)] = pRow; + gtk_box_pack_start(GTK_BOX(pRow), pWidget, FALSE, FALSE, 0); + } + } + } + + CustomTabs_t::const_reverse_iterator aEnd = aCustomTabs.rend(); + for (CustomTabs_t::const_reverse_iterator aI = aCustomTabs.rbegin(); aI != aEnd; ++aI) + { + gtk_widget_show_all(aI->first); + m_xWrapper->print_unix_dialog_add_custom_tab(GTK_PRINT_UNIX_DIALOG(m_pDialog), aI->first, + gtk_label_new(OUStringToOString(aI->second, RTL_TEXTENCODING_UTF8).getStr())); + } +} + +void +GtkPrintDialog::impl_initPrintContent(uno::Sequence<sal_Bool> const& i_rDisabled) +{ + SAL_WARN_IF(i_rDisabled.getLength() != 3, "vcl.gtk", "there is more choices than we expected"); + if (i_rDisabled.getLength() != 3) + return; + + GtkPrintUnixDialog* const pDialog(GTK_PRINT_UNIX_DIALOG(m_pDialog)); + + // XXX: This is a hack that depends on the number and the ordering of + // the controls in the rDisabled sequence (cf. the initialization of + // the "PrintContent" UI option in SwPrintUIOptions::SwPrintUIOptions, + // sw/source/core/view/printdata.cxx) + if (m_xWrapper->supportsPrintSelection() && !i_rDisabled[2]) + { + m_xWrapper->print_unix_dialog_set_support_selection(pDialog, TRUE); + m_xWrapper->print_unix_dialog_set_has_selection(pDialog, TRUE); + } + + beans::PropertyValue* const pPrintContent( + m_rController.getValue(OUString("PrintContent"))); + + if (pPrintContent) + { + sal_Int32 nSelectionType(0); + pPrintContent->Value >>= nSelectionType; + GtkPrintSettings* const pSettings(getSettings()); + GtkPrintPages ePrintPages(GTK_PRINT_PAGES_ALL); + switch (nSelectionType) + { + case 0: + ePrintPages = GTK_PRINT_PAGES_ALL; + break; + case 1: + ePrintPages = GTK_PRINT_PAGES_RANGES; + break; + case 2: + if (m_xWrapper->supportsPrintSelection()) + ePrintPages = GTK_PRINT_PAGES_SELECTION; + else + SAL_INFO("vcl.gtk", "the application wants to print a selection, but the present gtk version does not support it"); + break; + default: + SAL_WARN("vcl.gtk", "unexpected selection type: " << nSelectionType); + } + m_xWrapper->print_settings_set_print_pages(pSettings, ePrintPages); + m_xWrapper->print_unix_dialog_set_settings(pDialog, pSettings); + g_object_unref(G_OBJECT(pSettings)); + } +} + +void +GtkPrintDialog::impl_checkOptionalControlDependencies() +{ + for (auto& rEntry : m_aControlToPropertyMap) + { + gtk_widget_set_sensitive(rEntry.first, m_rController.isUIOptionEnabled(rEntry.second)); + } +} + +beans::PropertyValue* +GtkPrintDialog::impl_queryPropertyValue(GtkWidget* const i_pWidget) const +{ + beans::PropertyValue* pVal(nullptr); + std::map<GtkWidget*, OUString>::const_iterator aIt(m_aControlToPropertyMap.find(i_pWidget)); + if (aIt != m_aControlToPropertyMap.end()) + { + pVal = m_rController.getValue(aIt->second); + SAL_WARN_IF(!pVal, "vcl.gtk", "property value not found"); + } + else + { + SAL_WARN("vcl.gtk", "changed control not in property map"); + } + return pVal; +} + +void +GtkPrintDialog::impl_UIOption_CheckHdl(GtkWidget* const i_pWidget) +{ + beans::PropertyValue* const pVal = impl_queryPropertyValue(i_pWidget); + if (pVal) + { + const bool bVal = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(i_pWidget)); + pVal->Value <<= bVal; + + impl_checkOptionalControlDependencies(); + } +} + +void +GtkPrintDialog::impl_UIOption_RadioHdl(GtkWidget* const i_pWidget) +{ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(i_pWidget))) + { + beans::PropertyValue* const pVal = impl_queryPropertyValue(i_pWidget); + std::map<GtkWidget*, sal_Int32>::const_iterator it = m_aControlToNumValMap.find(i_pWidget); + if (pVal && it != m_aControlToNumValMap.end()) + { + + const sal_Int32 nVal = it->second; + pVal->Value <<= nVal; + + impl_checkOptionalControlDependencies(); + } + } +} + +void +GtkPrintDialog::impl_UIOption_SelectHdl(GtkWidget* const i_pWidget) +{ + beans::PropertyValue* const pVal = impl_queryPropertyValue(i_pWidget); + if (pVal) + { + const sal_Int32 nVal(gtk_combo_box_get_active(GTK_COMBO_BOX(i_pWidget))); + pVal->Value <<= nVal; + + impl_checkOptionalControlDependencies(); + } +} + +bool +GtkPrintDialog::run() +{ + bool bDoJob = false; + bool bContinue = true; + while (bContinue) + { + bContinue = false; + const gint nStatus = gtk_dialog_run(GTK_DIALOG(m_pDialog)); + switch (nStatus) + { + case GTK_RESPONSE_HELP: + fprintf(stderr, "To-Do: Help ?\n"); + bContinue = true; + break; + case GTK_RESPONSE_OK: + bDoJob = true; + break; + default: + break; + } + } + gtk_widget_hide(m_pDialog); + impl_storeToSettings(); + return bDoJob; +} + +void +GtkPrintDialog::updateControllerPrintRange() +{ + GtkPrintSettings* const pSettings(getSettings()); + // TODO: use get_print_pages + if (const gchar* const pStr = m_xWrapper->print_settings_get(pSettings, GTK_PRINT_SETTINGS_PRINT_PAGES)) + { + beans::PropertyValue* pVal = m_rController.getValue(OUString("PrintRange")); + if (!pVal) + pVal = m_rController.getValue(OUString("PrintContent")); + SAL_WARN_IF(!pVal, "vcl.gtk", "Nothing to map standard print options to!"); + if (pVal) + { + sal_Int32 nVal = 0; + if (!strcmp(pStr, "all")) + nVal = 0; + else if (!strcmp(pStr, "ranges")) + nVal = 1; + else if (!strcmp(pStr, "selection")) + nVal = 2; + pVal->Value <<= nVal; + + if (nVal == 1) + { + pVal = m_rController.getValue(OUString("PageRange")); + SAL_WARN_IF(!pVal, "vcl.gtk", "PageRange doesn't exist!"); + if (pVal) + { + OUStringBuffer sBuf; + gint num_ranges; + const GtkPageRange* const pRanges = m_xWrapper->print_settings_get_page_ranges(pSettings, &num_ranges); + for (gint i = 0; i != num_ranges && pRanges; ++i) + { + sBuf.append(sal_Int32(pRanges[i].start+1)); + if (pRanges[i].start != pRanges[i].end) + { + sBuf.append('-'); + sBuf.append(sal_Int32(pRanges[i].end+1)); + } + + if (i != num_ranges-1) + sBuf.append(','); + } + pVal->Value <<= sBuf.makeStringAndClear(); + } + } + } + } + g_object_unref(G_OBJECT(pSettings)); +} + +GtkPrintDialog::~GtkPrintDialog() +{ + gtk_widget_destroy(m_pDialog); +} + +void +GtkPrintDialog::impl_readFromSettings() +{ + vcl::SettingsConfigItem* const pItem(vcl::SettingsConfigItem::get()); + GtkPrintSettings* const pSettings(getSettings()); + + const OUString aPrintDialogStr("PrintDialog"); + const OUString aCopyCount(pItem->getValue(aPrintDialogStr, + "CopyCount")); + const OUString aCollate(pItem->getValue(aPrintDialogStr, + "Collate")); + + const gint nOldCopyCount(m_xWrapper->print_settings_get_n_copies(pSettings)); + const sal_Int32 nCopyCount(aCopyCount.toInt32()); + if (nCopyCount > 0 && nOldCopyCount != nCopyCount) + { + m_xWrapper->print_settings_set_n_copies(pSettings, sal::static_int_cast<gint>(nCopyCount)); + } + + const bool bOldCollate(m_xWrapper->print_settings_get_collate(pSettings)); + const bool bCollate(aCollate.equalsIgnoreAsciiCase("true")); + if (bOldCollate != bCollate) + { + m_xWrapper->print_settings_set_collate(pSettings, bCollate); + } + + m_xWrapper->print_unix_dialog_set_settings(GTK_PRINT_UNIX_DIALOG(m_pDialog), pSettings); + g_object_unref(G_OBJECT(pSettings)); +} + +void +GtkPrintDialog::impl_storeToSettings() +const +{ + vcl::SettingsConfigItem* const pItem(vcl::SettingsConfigItem::get()); + GtkPrintSettings* const pSettings(getSettings()); + + const OUString aPrintDialogStr("PrintDialog"); + pItem->setValue(aPrintDialogStr, + "CopyCount", + OUString::number(m_xWrapper->print_settings_get_n_copies(pSettings))); + pItem->setValue(aPrintDialogStr, + "Collate", + m_xWrapper->print_settings_get_collate(pSettings) + ? OUString("true") + : OUString("false")) + ; + // pItem->setValue(aPrintDialog, OUString("ToFile"), ); + g_object_unref(G_OBJECT(pSettings)); + pItem->Commit(); +} + +sal_uInt32 +GtkSalInfoPrinter::GetCapabilities( + const ImplJobSetup* const i_pSetupData, + const PrinterCapType i_nType) +{ + if (i_nType == PrinterCapType::ExternalDialog && lcl_useSystemPrintDialog()) + return 1; + return PspSalInfoPrinter::GetCapabilities(i_pSetupData, i_nType); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkaction.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkaction.cxx index a3fa632d08a1..b05929b90571 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkaction.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkaction.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkaction.cxx" +#include "../../gtk3/a11y/gtk3atkaction.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkbridge.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkbridge.cxx index d8e087956067..d31e5e429597 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkbridge.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkbridge.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkbridge.cxx" +#include "../../gtk3/a11y/gtk3atkbridge.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkcomponent.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkcomponent.cxx index e4eabec20fb8..63f44a3a14e9 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkcomponent.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkcomponent.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkcomponent.cxx" +#include "../../gtk3/a11y/gtk3atkcomponent.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkeditabletext.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkeditabletext.cxx index ea3f0895fb8e..064e72d98612 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkeditabletext.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkeditabletext.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkeditabletext.cxx" +#include "../../gtk3/a11y/gtk3atkeditabletext.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkfactory.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkfactory.cxx index c60db2fcc074..86d9ac43adb5 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkfactory.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkfactory.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkfactory.cxx" +#include "../../gtk3/a11y/gtk3atkfactory.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkhypertext.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkhypertext.cxx index bb9749c36af3..d2ce059aad62 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkhypertext.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkhypertext.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkhypertext.cxx" +#include "../../gtk3/a11y/gtk3atkhypertext.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkimage.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkimage.cxx index 4e2c77e9f2b1..3a1234e07462 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkimage.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkimage.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkimage.cxx" +#include "../../gtk3/a11y/gtk3atkimage.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atklistener.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atklistener.cxx index eca1cd7ec1d0..51d53bf9163b 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atklistener.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atklistener.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atklistener.cxx" +#include "../../gtk3/a11y/gtk3atklistener.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkregistry.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkregistry.cxx index 126e97a808ae..f5e79172084a 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkregistry.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkregistry.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkregistry.cxx" +#include "../../gtk3/a11y/gtk3atkregistry.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkselection.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkselection.cxx index f67b665304d7..ab39c0e132f0 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkselection.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkselection.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkselection.cxx" +#include "../../gtk3/a11y/gtk3atkselection.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktable.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktable.cxx index d886ac07296a..194791b68c10 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktable.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktable.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atktable.cxx" +#include "../../gtk3/a11y/gtk3atktable.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktext.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktext.cxx index e4bbd5a38980..8d668e6e69d0 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktext.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktext.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atktext.cxx" +#include "../../gtk3/a11y/gtk3atktext.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktextattributes.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktextattributes.cxx index b0edad06a65c..c767a95d0448 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktextattributes.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktextattributes.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atktextattributes.cxx" +#include "../../gtk3/a11y/gtk3atktextattributes.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkutil.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkutil.cxx index 8c1eeaf9882b..39eb5aeebd60 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkutil.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkutil.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkutil.cxx" +#include "../../gtk3/a11y/gtk3atkutil.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkvalue.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkvalue.cxx index 30057943bc44..5a526e1cf909 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkvalue.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkvalue.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkvalue.cxx" +#include "../../gtk3/a11y/gtk3atkvalue.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkwindow.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkwindow.cxx index cd8479cd4df7..194e10999657 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkwindow.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkwindow.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkwindow.cxx" +#include "../../gtk3/a11y/gtk3atkwindow.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkwrapper.cxx b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkwrapper.cxx index 3b07e9536d77..b0029f273ee2 100644 --- a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkwrapper.cxx +++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkwrapper.cxx @@ -7,6 +7,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "../../gtk/a11y/atkwrapper.cxx" +#include "../../gtk3/a11y/gtk3atkwrapper.cxx" /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |