drop gtk2 support
Change-Id: Ie838cabfecfef7e3225c1555536d5c9cf3b43f15
Reviewed-on: https://gerrit.libreoffice.org/77405
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <[email protected]>
Reviewed-by: Caolán McNamara <[email protected]>
Tested-by: Caolán McNamara <[email protected]>
diff --git a/Repository.mk b/Repository.mk
index 8068e52..9658891 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -224,14 +224,6 @@
) \
))
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,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 57f66b8..4d99f79 100644
--- a/RepositoryExternal.mk
+++ b/RepositoryExternal.mk
@@ -2605,34 +2605,6 @@
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 5492aaf..f64044d 100755
--- a/bin/check-elf-dynamic-objects
+++ b/bin/check-elf-dynamic-objects
@@ -93,7 +93,6 @@
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 @@
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 4cbe0be..ef016e3 100755
--- a/bin/distro-install-file-lists
+++ b/bin/distro-install-file-lists
@@ -324,9 +324,6 @@
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 6c8cff7..ca20283 100755
--- a/bin/moveglobalheaders.sh
+++ b/bin/moveglobalheaders.sh
@@ -36,8 +36,7 @@
# 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 122c9de..29b8568 100644
--- a/config_host.mk.in
+++ b/config_host.mk.in
@@ -142,8 +142,6 @@
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 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 694334d..8ff5a9c 100644
--- a/config_host/config_vclplug.h.in
+++ b/config_host/config_vclplug.h.in
@@ -7,7 +7,6 @@
#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 cf0f6e5..ce79ad5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -574,7 +574,6 @@
case "$host_os" in
solaris*)
test_gtk=yes
build_gstreamer_1_0=yes
test_freetype=yes
_os=SunOS
@@ -600,7 +599,6 @@
;;
linux-gnu*|k*bsd*-gnu*)
test_gtk=yes
build_gstreamer_1_0=yes
test_kf5=yes
test_gtk3_kde5=yes
@@ -649,7 +647,6 @@
;;
darwin*) # macOS or iOS
test_gtk=yes
test_randr=no
test_xrender=no
test_freetype=no
@@ -662,7 +659,6 @@
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 @@
;;
freebsd*)
test_gtk=yes
build_gstreamer_1_0=yes
test_kf5=yes
test_gtk3_kde5=yes
@@ -722,7 +717,6 @@
;;
*netbsd*)
test_gtk=yes
build_gstreamer_1_0=yes
test_kf5=yes
test_gtk3_kde5=yes
@@ -739,7 +733,6 @@
;;
openbsd*)
test_gtk=yes
test_freetype=yes
PTHREAD_CFLAGS="-D_THREAD_SAFE"
PTHREAD_LIBS="-pthread"
@@ -747,7 +740,6 @@
;;
dragonfly*)
test_gtk=yes
build_gstreamer_1_0=yes
test_kf5=yes
test_gtk3_kde5=yes
@@ -770,7 +762,6 @@
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 @@
'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-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 @@
./configure \
--disable-cups \
--disable-gstreamer-1-0 \
--disable-gtk \
--disable-gtk3 \
--disable-pdfimport \
--disable-postgresql-sdbc \
@@ -4849,7 +4834,6 @@
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 ===================================================================
R=""
if test "$USING_X11" != TRUE; then
enable_gtk=no
enable_gtk3=no
fi
GTK3_CFLAGS=""
@@ -10369,18 +10352,6 @@
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(SYSTEM_BLUEZ)
dnl ===================================================================
dnl Check whether the gtk 2.0 libraries are available.
dnl Check whether to enable GIO support
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])
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 8a2bf42..54e3733 100644
--- a/cui/Library_cui.mk
+++ b/cui/Library_cui.mk
@@ -20,7 +20,6 @@
$(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 bcf1d7a..c0b8403 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 @@
m_xFileDlgCB->set_sensitive(false);
}
#if ! ENABLE_GTK
#if !ENABLE_GTK3
m_xPrintDlgFrame->hide();
#else
if (!SvtMiscOptions().IsExperimentalMode())
@@ -269,7 +270,7 @@
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 ca5873d..c1661a3 100644
--- a/desktop/Pagein_common.mk
+++ b/desktop/Pagein_common.mk
@@ -28,7 +28,7 @@
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 5bdba75..0d0f9ce 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 a4f913c..fc7291c 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 75b885a..a007b85 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 1cca285..ab9ec3b 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 bd144c8..a2988d0 100644
--- a/onlineupdate/Executable_test_updater_dialog.mk
+++ b/onlineupdate/Executable_test_updater_dialog.mk
@@ -15,6 +15,7 @@
-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_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 7c24b7b..ab15a94 100644
--- a/onlineupdate/Executable_updater.mk
+++ b/onlineupdate/Executable_updater.mk
@@ -14,6 +14,7 @@
-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_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 47d27ea..7c3bcfc 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 @@
{
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 0c3b97b..6f7b582 100644
--- a/scp2/InstallScript_setup_osl.mk
+++ b/scp2/InstallScript_setup_osl.mk
@@ -33,7 +33,7 @@
$(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 aa06d63..25ae816 100644
--- a/scp2/Module_scp2.mk
+++ b/scp2/Module_scp2.mk
@@ -37,7 +37,7 @@
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 4a36cc6..94229cc 100644
--- a/sd/CppunitTest_sd_uimpress.mk
+++ b/sd/CppunitTest_sd_uimpress.mk
@@ -71,7 +71,6 @@
$(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 c89b686..d694f0b 100644
--- a/solenv/clang-format/blacklist
+++ b/solenv/clang-format/blacklist
@@ -18113,51 +18113,13 @@
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/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 7c5c251..0d40bd6 100644
--- a/solenv/gbuild/CppunitTest.mk
+++ b/solenv/gbuild/CppunitTest.mk
@@ -224,7 +224,6 @@
$(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 296436a..0000000
--- 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 360eabf..0000000
--- 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 c0ad6cd..9f6504c 100644
--- a/vcl/Library_vclplug_gtk3.mk
+++ b/vcl/Library_vclplug_gtk3.mk
@@ -46,7 +46,6 @@
$(eval $(call gb_Library_add_libs,vclplug_gtk3,\
$(GTK3_LIBS) \
$(GTHREAD_LIBS) \
-lX11 \
-lXext \
-lSM \
@@ -97,10 +96,13 @@
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 d67c53e..2317264 100644
--- a/vcl/Library_vclplug_gtk3_kde5.mk
+++ b/vcl/Library_vclplug_gtk3_kde5.mk
@@ -51,7 +51,6 @@
$(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 54e3fbd6..859702c 100644
--- a/vcl/Module_vcl.mk
+++ b/vcl/Module_vcl.mk
@@ -66,12 +66,6 @@
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 f1750b7..84e2d19 100644
--- a/vcl/inc/unx/gtk/gtkdata.hxx
+++ b/vcl/inc/unx/gtk/gtkdata.hxx
@@ -42,44 +42,22 @@
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 @@
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 @@
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 @@
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 084339f..f9a41c6 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 @@
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 @@
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 @@
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 @@
GtkDropTarget* m_pDropTarget;
GtkDragSource* m_pDragSource;
bool m_bGeometryIsProvisional;
#else
GdkRegion* m_pRegion;
bool m_bSetFocusOnMap;
#endif
GtkSalMenu* m_pSalMenu;
@@ -242,12 +227,7 @@
// 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 @@
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 @@
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 @@
Idle m_aSmoothScrollIdle;
int m_nGrabLevel;
bool m_bSalObjectSetPosSize;
#endif
GtkSalFrame( SalFrame* pParent, SalFrameStyleFlags nStyle );
GtkSalFrame( SystemParentData* pSysData );
@@ -374,9 +347,7 @@
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 @@
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 @@
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 @@
// 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 @@
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 c9b7be4..e19d012 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 @@
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 65caa38..78dd9bc 100644
--- a/vcl/inc/unx/gtk/gtkinst.hxx
+++ b/vcl/inc/unx/gtk/gtkinst.hxx
@@ -57,7 +57,6 @@
void ThreadsLeave();
};
#if GTK_CHECK_VERSION(3,0,0)
class GtkSalFrame;
struct VclToGtkHelper
@@ -182,20 +181,10 @@
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 @@
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 @@
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 @@
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 4523e99..f54d7af 100644
--- a/vcl/inc/unx/gtk/gtkobject.hxx
+++ b/vcl/inc/unx/gtk/gtkobject.hxx
@@ -29,12 +29,8 @@
{
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 4862c8c..589c800 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 @@
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 @@
// 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 2d812ca..ddef9ef 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 20604ed..ce3fb55 100644
--- a/vcl/source/app/salplug.cxx
+++ b/vcl/source/app/salplug.cxx
@@ -95,7 +95,7 @@
* 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 @@
#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 @@
#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 14a172f..0000000
--- 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 a74f1a9..0000000
--- 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 da5a48e..0000000
--- 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 49d3eb9..0000000
--- 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 3a776b2..0000000
--- 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 ab94126..0000000
--- 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 acd43f4..0000000
--- 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 c30bc63..0000000
--- 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 ff96378..0000000
--- 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 1d9772b..0000000
--- 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 221e55d..0000000
--- 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 1406cee..0000000
--- 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 0ba7fd5..0000000
--- 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 50ed279..0000000
--- 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 f5e45d3..0000000
--- 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 eb72edf..0000000
--- 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 cd43902..0000000
--- 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 56782e2..0000000
--- 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 5457ac6..0000000
--- 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 ca7a567..0000000
--- 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 2a31b87..0000000
--- 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 38302c6..0000000
--- 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 7e22413..0000000
--- 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 2601d7d..0000000
--- 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 d050351..0000000
--- 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 3734595..0000000
--- 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 b7985fd..0000000
--- 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 eb2f594..0000000
--- 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 e5e17a5..0000000
--- 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 558ede5..0000000
--- 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
similarity index 100%
rename from vcl/unx/gtk/a11y/TODO
rename to vcl/unx/gtk3/a11y/TODO
diff --git a/vcl/unx/gtk/a11y/atkfactory.hxx b/vcl/unx/gtk3/a11y/atkfactory.hxx
similarity index 100%
rename from vcl/unx/gtk/a11y/atkfactory.hxx
rename to vcl/unx/gtk3/a11y/atkfactory.hxx
diff --git a/vcl/unx/gtk/a11y/atklistener.hxx b/vcl/unx/gtk3/a11y/atklistener.hxx
similarity index 100%
rename from vcl/unx/gtk/a11y/atklistener.hxx
rename to vcl/unx/gtk3/a11y/atklistener.hxx
diff --git a/vcl/unx/gtk/a11y/atkregistry.hxx b/vcl/unx/gtk3/a11y/atkregistry.hxx
similarity index 100%
rename from vcl/unx/gtk/a11y/atkregistry.hxx
rename to vcl/unx/gtk3/a11y/atkregistry.hxx
diff --git a/vcl/unx/gtk/a11y/atktextattributes.hxx b/vcl/unx/gtk3/a11y/atktextattributes.hxx
similarity index 100%
rename from vcl/unx/gtk/a11y/atktextattributes.hxx
rename to vcl/unx/gtk3/a11y/atktextattributes.hxx
diff --git a/vcl/unx/gtk/a11y/atkutil.hxx b/vcl/unx/gtk3/a11y/atkutil.hxx
similarity index 100%
rename from vcl/unx/gtk/a11y/atkutil.hxx
rename to vcl/unx/gtk3/a11y/atkutil.hxx
diff --git a/vcl/unx/gtk/a11y/atkwindow.hxx b/vcl/unx/gtk3/a11y/atkwindow.hxx
similarity index 100%
rename from vcl/unx/gtk/a11y/atkwindow.hxx
rename to vcl/unx/gtk3/a11y/atkwindow.hxx
diff --git a/vcl/unx/gtk/a11y/atkwrapper.hxx b/vcl/unx/gtk3/a11y/atkwrapper.hxx
similarity index 97%
rename from vcl/unx/gtk/a11y/atkwrapper.hxx
rename to vcl/unx/gtk3/a11y/atkwrapper.hxx
index 8725e54..7ae0379 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 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 a3fa632..81e7432 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 d8e0879..0e2781c 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 e4eabec..e904b12 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 ea3f089..49d3eb9 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 c60db2f..d7c8bf9 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 "../../gtk/a11y/atkfactory.cxx"
#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 )
{
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 bb9749c..ab94126 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 4e2c77e..1c9bc2c 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 eca1cd7..c30bc63 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 126e97a..ff96378 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 f67b665..1d9772b 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 d886ac0..29ffa48 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 e4bbd5a..532b5501 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 b0edad0..0ba7fd5 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 8c1eeaf..cac3ac6 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 3005794..f5e45d3 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 cd8479c..eb72edf 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 3b07e95..0998553 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
similarity index 99%
rename from vcl/unx/gtk/fpicker/SalGtkFilePicker.cxx
rename to vcl/unx/gtk3/fpicker/SalGtkFilePicker.cxx
index c106aa0..2552bb1 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 @@
{
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 @@
}
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 @@
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
similarity index 99%
rename from vcl/unx/gtk/fpicker/SalGtkFilePicker.hxx
rename to vcl/unx/gtk3/fpicker/SalGtkFilePicker.hxx
index ae3e1ba..db471d5 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
similarity index 98%
rename from vcl/unx/gtk/fpicker/SalGtkFolderPicker.cxx
rename to vcl/unx/gtk3/fpicker/SalGtkFolderPicker.cxx
index ade5ff5..13df539 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
similarity index 97%
rename from vcl/unx/gtk/fpicker/SalGtkFolderPicker.hxx
rename to vcl/unx/gtk3/fpicker/SalGtkFolderPicker.hxx
index 46cf107..229bbe8 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
similarity index 99%
rename from vcl/unx/gtk/fpicker/SalGtkPicker.cxx
rename to vcl/unx/gtk3/fpicker/SalGtkPicker.cxx
index 3e443ea..7db1140 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
similarity index 100%
rename from vcl/unx/gtk/fpicker/SalGtkPicker.hxx
rename to vcl/unx/gtk3/fpicker/SalGtkPicker.hxx
diff --git a/vcl/unx/gtk/fpicker/eventnotification.hxx b/vcl/unx/gtk3/fpicker/eventnotification.hxx
similarity index 100%
rename from vcl/unx/gtk/fpicker/eventnotification.hxx
rename to vcl/unx/gtk3/fpicker/eventnotification.hxx
diff --git a/vcl/unx/gtk/fpicker/resourceprovider.cxx b/vcl/unx/gtk3/fpicker/resourceprovider.cxx
similarity index 98%
rename from vcl/unx/gtk/fpicker/resourceprovider.cxx
rename to vcl/unx/gtk3/fpicker/resourceprovider.cxx
index 745521a..818018b 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 9b83c57..0000000
--- 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 749f543..19b412f 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 e894b09..f20903d 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 333db66..f5e26be 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::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 edefca9..b1a8b9c 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 d465594..81d5d65 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 2406e0a..229a718 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 "../gtk/gtksys.cxx"
#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));
return nButton;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/gtk3/gtk3hudawareness.cxx b/vcl/unx/gtk3/gtk3hudawareness.cxx
index 3d928f0..0aa5878 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 16bf17c..aeb9b1d 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 a3fa632..b05929b 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 d8e0879..d31e5e4 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 e4eabec..63f44a3 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 ea3f089..064e72d 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 c60db2f..86d9ac4 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 bb9749c..d2ce059 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 4e2c77e..3a1234e 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 eca1cd7..51d53bf 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 126e97a..f5e7917 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 f67b665..ab39c0e 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 d886ac0..194791b 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 e4bbd5a..8d668e6 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 b0edad0..c767a95d 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 8c1eeaf..39eb5ae 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 3005794..5a526e1 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 cd8479c..194e109 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 3b07e95..b0029f2 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: */