Kaydet (Commit) ecb5fcff authored tarafından Milian Wolff's avatar Milian Wolff Kaydeden (comit) Thorsten Behrens

Introduce gtk3_kde5 vcl plugin

This is a hybrid plugin which mostly wraps the GTK3 vclplug. Only
the file and folder picker are replaced by KDE dialogs. This gives
us a well-maintained GTK LO base with basic KDE integration with
minimum effort.

To prevent issues with nested event loops, the KDE dialogs are
launched from a separate process, the new lo_kde5filepicker helper
executable. A trivial stdin/stdout IPC mechanism transfers the data
between LO and the Qt/KDE helper. The usage of an external process
also allows us to copy'n'paste between LO and the KDE file dialog
without freezing the UI, as would happen when one would do this
in-process. This is in general also the architecture applied by the
kmozillahelper, which is used to integrate KDE file dialogs into
Firefox.

While the KDE dialog is shown, the GTK3 main window is disabled and
close requests are ignored. The KDE dialog in turn also sets the LO
window as transient parent. Together, this makes the illusion perfect
and the KDE dialog behaves like a modal dialog. This works properly
also with multiple LO main windows, and only individual windows will
get blocked as one would expect.

Functionality wise, most of the features of the KDE4 dialog are
supported. You can pick files and folders, and save files under a new
name. Some custom checkbox widgets are supported, but lists, buttons
and preview widgets are not yet implemented. Also, loading remote
files via KIO is not possible yet.

Change-Id: I1a97cf7c272307a19ace4222d5f12253bc722829
Reviewed-on: https://gerrit.libreoffice.org/47718Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarThorsten Behrens <Thorsten.Behrens@CIB.de>
üst f811979b
...@@ -302,8 +302,14 @@ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,kde, \ ...@@ -302,8 +302,14 @@ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,kde, \
$(if $(ENABLE_KDE4),vclplug_kde4) \ $(if $(ENABLE_KDE4),vclplug_kde4) \
$(if $(ENABLE_KDE5),vclplug_kde5) \ $(if $(ENABLE_KDE5),vclplug_kde5) \
$(if $(ENABLE_QT5),vclplug_qt5) \ $(if $(ENABLE_QT5),vclplug_qt5) \
$(if $(ENABLE_GTK3_KDE5),vclplug_gtk3_kde5) \
) \ ) \
)) ))
ifneq ($(ENABLE_GTK3_KDE5),)
$(eval $(call gb_Helper_register_executables_for_install,OOO,kde, \
lo_kde5filepicker \
))
endif
$(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,math, \ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,math, \
sm \ sm \
......
...@@ -158,6 +158,7 @@ export ENABLE_PDFIMPORT=@ENABLE_PDFIMPORT@ ...@@ -158,6 +158,7 @@ export ENABLE_PDFIMPORT=@ENABLE_PDFIMPORT@
export ENABLE_PDFIUM=@ENABLE_PDFIUM@ export ENABLE_PDFIUM=@ENABLE_PDFIUM@
export ENABLE_QT5=@ENABLE_QT5@ export ENABLE_QT5=@ENABLE_QT5@
export ENABLE_KDE5=@ENABLE_KDE5@ export ENABLE_KDE5=@ENABLE_KDE5@
export ENABLE_GTK3_KDE5=@ENABLE_GTK3_KDE5@
export ENABLE_RANDR=@ENABLE_RANDR@ export ENABLE_RANDR=@ENABLE_RANDR@
export ENABLE_RELEASE_BUILD=@ENABLE_RELEASE_BUILD@ export ENABLE_RELEASE_BUILD=@ENABLE_RELEASE_BUILD@
export ENABLE_REPORTBUILDER=@ENABLE_REPORTBUILDER@ export ENABLE_REPORTBUILDER=@ENABLE_REPORTBUILDER@
......
/*
Settings for GTK3/KDE5 integration.
*/
#ifndef CONFIG_GTK3_KDE5_H
#define CONFIG_GTK3_KDE5_H
#define ENABLE_GTK3_KDE5 0
#endif
...@@ -10,5 +10,6 @@ Settings about which X11 desktops have support enabled. ...@@ -10,5 +10,6 @@ Settings about which X11 desktops have support enabled.
#define ENABLE_GTK 0 #define ENABLE_GTK 0
#define ENABLE_KDE4 0 #define ENABLE_KDE4 0
#define ENABLE_KDE5 0 #define ENABLE_KDE5 0
#define ENABLE_GTK3_KDE5 0
#endif #endif
...@@ -595,6 +595,7 @@ linux-gnu*|k*bsd*-gnu*) ...@@ -595,6 +595,7 @@ linux-gnu*|k*bsd*-gnu*)
test_kde4=yes test_kde4=yes
test_kde5=yes test_kde5=yes
test_qt5=yes test_qt5=yes
test_gtk3_kde5=yes
if test "$enable_fuzzers" != yes; then if test "$enable_fuzzers" != yes; then
test_freetype=yes test_freetype=yes
test_fontconfig=yes test_fontconfig=yes
...@@ -691,6 +692,7 @@ freebsd*) ...@@ -691,6 +692,7 @@ freebsd*)
test_kde4=yes test_kde4=yes
test_kde5=yes test_kde5=yes
test_qt5=yes test_qt5=yes
test_gtk3_kde5=yes
test_freetype=yes test_freetype=yes
AC_MSG_CHECKING([the FreeBSD operating system release]) AC_MSG_CHECKING([the FreeBSD operating system release])
if test -n "$with_os_version"; then if test -n "$with_os_version"; then
...@@ -721,6 +723,7 @@ freebsd*) ...@@ -721,6 +723,7 @@ freebsd*)
test_kde4=yes test_kde4=yes
test_kde5=yes test_kde5=yes
test_qt5=yes test_qt5=yes
test_gtk3_kde5=yes
test_freetype=yes test_freetype=yes
PTHREAD_LIBS="-pthread -lpthread" PTHREAD_LIBS="-pthread -lpthread"
_os=NetBSD _os=NetBSD
...@@ -748,6 +751,7 @@ dragonfly*) ...@@ -748,6 +751,7 @@ dragonfly*)
test_kde4=yes test_kde4=yes
test_kde5=yes test_kde5=yes
test_qt5=yes test_qt5=yes
test_gtk3_kde5=yes
test_freetype=yes test_freetype=yes
PTHREAD_LIBS="-pthread" PTHREAD_LIBS="-pthread"
_os=DragonFly _os=DragonFly
...@@ -773,6 +777,7 @@ linux-android*) ...@@ -773,6 +777,7 @@ linux-android*)
test_kde4=no test_kde4=no
test_kde5=no test_kde5=no
test_qt5=no test_qt5=no
test_gtk3_kde5=no
test_randr=no test_randr=no
test_xrender=no test_xrender=no
_os=Android _os=Android
...@@ -1227,6 +1232,12 @@ AC_ARG_ENABLE(kde5, ...@@ -1227,6 +1232,12 @@ AC_ARG_ENABLE(kde5,
KF5 are available.]), KF5 are available.]),
,) ,)
AC_ARG_ENABLE(gtk3_kde5,
AS_HELP_STRING([--enable-gtk3-kde5],
[Determines whether to use Gtk3 vclplug with KDE file dialogs on
platforms where Gtk3, Qt5 and Plasma is available.]),
,)
libo_FUZZ_ARG_ENABLE(gui, libo_FUZZ_ARG_ENABLE(gui,
AS_HELP_STRING([--disable-gui], AS_HELP_STRING([--disable-gui],
[Disable use of X11 or Wayland to reduce dependencies. Not related to the --headless [Disable use of X11 or Wayland to reduce dependencies. Not related to the --headless
...@@ -4542,6 +4553,7 @@ if test "$USING_X11" != TRUE; then ...@@ -4542,6 +4553,7 @@ if test "$USING_X11" != TRUE; then
test_kde4=no test_kde4=no
test_kde5=no test_kde5=no
test_qt5=no test_qt5=no
test_gtk3_kde5=no
enable_cairo_canvas=no enable_cairo_canvas=no
fi fi
...@@ -9797,6 +9809,14 @@ if test "x$enable_kde5" = "xyes"; then ...@@ -9797,6 +9809,14 @@ if test "x$enable_kde5" = "xyes"; then
fi fi
AC_SUBST(ENABLE_KDE5) AC_SUBST(ENABLE_KDE5)
ENABLE_GTK3_KDE5=""
if test "x$enable_gtk3_kde5" = "xyes"; then
ENABLE_GTK3_KDE5="TRUE"
AC_DEFINE(ENABLE_GTK3_KDE5)
R="$R gtk3_kde5"
fi
AC_SUBST(ENABLE_GTK3_KDE5)
build_vcl_plugins="$R" build_vcl_plugins="$R"
if test -z "$build_vcl_plugins"; then if test -z "$build_vcl_plugins"; then
build_vcl_plugins="none" build_vcl_plugins="none"
...@@ -10804,7 +10824,8 @@ QT5_GLIB_CFLAGS="" ...@@ -10804,7 +10824,8 @@ QT5_GLIB_CFLAGS=""
QT5_GLIB_LIBS="" QT5_GLIB_LIBS=""
QT5_HAVE_GLIB="" QT5_HAVE_GLIB=""
if test \( "$test_kde5" = "yes" -a "$ENABLE_KDE5" = "TRUE" \) -o \ if test \( "$test_kde5" = "yes" -a "$ENABLE_KDE5" = "TRUE" \) -o \
\( "$test_qt5" = "yes" -a "$ENABLE_QT5" = "TRUE" \) \( "$test_qt5" = "yes" -a "$ENABLE_QT5" = "TRUE" \) -o \
\( "$test_gtk3_kde5" = "yes" -a "$ENABLE_GTK3_KDE5" = "TRUE" \)
then then
qt5_incdirs="$QT5INC /usr/include/qt5 /usr/include $x_includes" qt5_incdirs="$QT5INC /usr/include/qt5 /usr/include $x_includes"
qt5_libdirs="$QT5LIB /usr/lib/qt5 /usr/lib $x_libraries" qt5_libdirs="$QT5LIB /usr/lib/qt5 /usr/lib $x_libraries"
...@@ -10892,7 +10913,8 @@ KF5_CFLAGS="" ...@@ -10892,7 +10913,8 @@ KF5_CFLAGS=""
KF5_LIBS="" KF5_LIBS=""
KF5_CONFIG="kf5-config" KF5_CONFIG="kf5-config"
if test \( "$test_kde5" = "yes" -a "$ENABLE_KDE5" = "TRUE" \) -o \ if test \( "$test_kde5" = "yes" -a "$ENABLE_KDE5" = "TRUE" \) -o \
\( "$test_kf5" = "yes" -a "$ENABLE_KF5" = "TRUE" \) \( "$test_kf5" = "yes" -a "$ENABLE_KF5" = "TRUE" \) -o \
\( "$test_gtk3_kde5" = "yes" -a "$ENABLE_GTK3_KDE5" = "TRUE" \)
then then
kf5_incdirs="$KF5INC /usr/include/ $x_includes" kf5_incdirs="$KF5INC /usr/include/ $x_includes"
kf5_libdirs="$KF5LIB /usr/lib /usr/lib/kf5 /usr/lib/kf5/devel $x_libraries" kf5_libdirs="$KF5LIB /usr/lib /usr/lib/kf5 /usr/lib/kf5/devel $x_libraries"
...@@ -10942,8 +10964,8 @@ then ...@@ -10942,8 +10964,8 @@ then
PKG_CHECK_MODULES(KF5_XCB,[xcb],,[AC_MSG_ERROR([XCB not installed])]) PKG_CHECK_MODULES(KF5_XCB,[xcb],,[AC_MSG_ERROR([XCB not installed])])
KF5_CFLAGS="-I$kf5_incdir -I$kf5_incdir/KCoreAddons -I$kf5_incdir/KI18n -I$kf5_incdir/KConfigCore -I$kf5_incdir/KWindowSystem -I$kf5_incdir/KIOCore -I$qt5_incdir -I$qt5_incdir/QtCore -I$qt5_incdir/QtGui -I$qt5_incdir/QtWidgets -I$qt5_incdir/QtNetwork -DQT_CLEAN_NAMESPACE -DQT_THREAD_SUPPORT $KF5_XCB_CFLAGS" KF5_CFLAGS="-I$kf5_incdir -I$kf5_incdir/KCoreAddons -I$kf5_incdir/KI18n -I$kf5_incdir/KConfigCore -I$kf5_incdir/KWindowSystem -I$kf5_incdir/KIOCore -I$kf5_incdir/KIOWidgets -I$kf5_incdir/KIOFileWidgets -I$qt5_incdir -I$qt5_incdir/QtCore -I$qt5_incdir/QtGui -I$qt5_incdir/QtWidgets -I$qt5_incdir/QtNetwork -DQT_CLEAN_NAMESPACE -DQT_THREAD_SUPPORT $KF5_XCB_CFLAGS"
KF5_LIBS="-L$kf5_libdir -lKF5CoreAddons -lKF5I18n -lKF5ConfigCore -lKF5WindowSystem -lKF5KIOCore -L$qt5_libdir -lQt5Core -lQt5Gui -lQt5Widgets -lQt5Network -lQt5X11Extras $KF5_XCB_LIBS" KF5_LIBS="-L$kf5_libdir -lKF5CoreAddons -lKF5I18n -lKF5ConfigCore -lKF5WindowSystem -lKF5KIOCore -lKF5KIOWidgets -lKF5KIOFileWidgets -L$qt5_libdir -lQt5Core -lQt5Gui -lQt5Widgets -lQt5Network -lQt5X11Extras $KF5_XCB_LIBS"
KF5_CFLAGS=$(printf '%s' "$KF5_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g") KF5_CFLAGS=$(printf '%s' "$KF5_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g")
AC_LANG_PUSH([C++]) AC_LANG_PUSH([C++])
...@@ -12513,6 +12535,7 @@ AC_CONFIG_HEADERS([config_host/config_mpl.h]) ...@@ -12513,6 +12535,7 @@ AC_CONFIG_HEADERS([config_host/config_mpl.h])
AC_CONFIG_HEADERS([config_host/config_kde4.h]) AC_CONFIG_HEADERS([config_host/config_kde4.h])
AC_CONFIG_HEADERS([config_host/config_qt5.h]) AC_CONFIG_HEADERS([config_host/config_qt5.h])
AC_CONFIG_HEADERS([config_host/config_kde5.h]) AC_CONFIG_HEADERS([config_host/config_kde5.h])
AC_CONFIG_HEADERS([config_host/config_gtk3_kde5.h])
AC_CONFIG_HEADERS([config_host/config_oox.h]) AC_CONFIG_HEADERS([config_host/config_oox.h])
AC_CONFIG_HEADERS([config_host/config_options.h]) AC_CONFIG_HEADERS([config_host/config_options.h])
AC_CONFIG_HEADERS([config_host/config_options_calc.h]) AC_CONFIG_HEADERS([config_host/config_options_calc.h])
......
# -*- 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_CustomTarget_CustomTarget,vcl/unx/gtk3_kde5))
$(call gb_CustomTarget_get_target,vcl/unx/gtk3_kde5) : \
$(call gb_CustomTarget_get_workdir,vcl/unx/gtk3_kde5)/kde5_filepicker.moc \
$(call gb_CustomTarget_get_workdir,vcl/unx/gtk3_kde5)/kde5_filepicker_ipc.moc \
$(call gb_CustomTarget_get_workdir,vcl/unx/gtk3_kde5)/%.moc : \
$(SRCDIR)/vcl/unx/gtk3_kde5/%.hxx \
| $(call gb_CustomTarget_get_workdir,vcl/unx/gtk3_kde5)/.dir
$(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),MOC,1)
$(MOC5) $< -o $@
# vim: set noet sw=4:
# -*- 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_Executable_Executable,lo_kde5filepicker))
# FIXME: how to find the moc files automatically?!
$(eval $(call gb_Executable_set_include,lo_kde5filepicker,\
$$(INCLUDE) \
-I$(SRCDIR)/vcl/inc \
-I$(WORKDIR)/CustomTarget/vcl/unx/gtk3_kde5 \
))
$(eval $(call gb_Executable_add_cxxflags,lo_kde5filepicker,\
$$(INCLUDE) \
$$(BOOST_CXXFLAGS) \
))
$(eval $(call gb_Executable_use_custom_headers,lo_kde5filepicker,\
officecfg/registry \
))
$(eval $(call gb_Executable_use_sdk_api,lo_kde5filepicker))
$(eval $(call gb_Executable_add_libs,lo_kde5filepicker,\
-lX11 \
-lXext \
-lSM \
-lICE \
))
$(eval $(call gb_Executable_use_libraries,lo_kde5filepicker,\
vclplug_gen \
vcl \
tl \
utl \
sot \
ucbhelper \
basegfx \
comphelper \
cppuhelper \
i18nlangtag \
i18nutil \
$(if $(ENABLE_JAVA), \
jvmaccess) \
cppu \
sal \
))
$(eval $(call gb_Executable_use_externals,lo_kde5filepicker,\
boost_headers \
epoxy \
kde5 \
dbus \
))
$(eval $(call gb_Executable_add_defs,lo_kde5filepicker,\
$(QT5_CFLAGS) \
$(QT5_GLIB_CFLAGS) \
$(KF5_CFLAGS) \
))
$(eval $(call gb_Executable_add_libs,lo_kde5filepicker,\
$(QT5_LIBS) \
$(QT5_GLIB_LIBS) \
$(KF5_LIBS) \
$(BOOST_PROCESS_LIB) \
$(BOOST_FILESYSTEM_LIB) \
))
$(eval $(call gb_Executable_add_exception_objects,lo_kde5filepicker,\
vcl/unx/gtk3_kde5/kde5_lo_filepicker_main \
vcl/unx/gtk3_kde5/kde5_filepicker \
vcl/unx/gtk3_kde5/kde5_filepicker_ipc \
))
ifeq ($(OS),LINUX)
$(eval $(call gb_Executable_add_libs,lo_kde5filepicker,\
-lm \
-ldl \
-lpthread \
))
endif
# vim: set noet sw=4 ts=4:
# -*- 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_gtk3_kde5))
# Silence deprecation warnings wholesale as long as vcl/unx/gtk3/*.cxx just
# forward to vcl/unx/gtk/*.cxx:
$(eval $(call gb_Library_add_cxxflags,vclplug_gtk3_kde5, \
-Wno-deprecated-declarations \
))
$(eval $(call gb_Library_set_include,vclplug_gtk3_kde5,\
$$(INCLUDE) \
-I$(SRCDIR)/vcl/inc \
-I$(SRCDIR)/vcl/unx \
-I$(SRCDIR)/vcl/unx/gtk3 \
))
$(eval $(call gb_Library_add_cxxflags,vclplug_gtk3_kde5,\
$$(INCLUDE) \
$$(GTK3_CFLAGS) \
))
$(eval $(call gb_Library_add_defs,vclplug_gtk3_kde5,\
-DVCLPLUG_GTK_IMPLEMENTATION -DVCLPLUG_GTK3_KDE5_IMPLEMENTATION \
))
$(eval $(call gb_Library_use_custom_headers,vclplug_gtk3_kde5,\
officecfg/registry \
))
$(eval $(call gb_Library_use_sdk_api,vclplug_gtk3_kde5))
$(eval $(call gb_Library_add_libs,vclplug_gtk3_kde5,\
$(GTK3_LIBS) \
$(GTHREAD_LIBS) \
-lX11 \
-lXext \
-lSM \
-lICE \
))
$(eval $(call gb_Library_use_libraries,vclplug_gtk3_kde5,\
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_gtk3_kde5,\
boost_headers \
boost_filesystem \
epoxy \
dbus \
))
$(eval $(call gb_Library_add_exception_objects,vclplug_gtk3_kde5,\
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkaction \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkbridge \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkcomponent \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkeditabletext \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkfactory \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkhypertext \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkimage \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atklistener \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkregistry \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkselection \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktable \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktextattributes \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktext \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkutil \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkvalue \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkwindow \
vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkwrapper \
vcl/unx/gtk3_kde5/gtk3_kde5_gtkdata \
vcl/unx/gtk3_kde5/gtk3_kde5_gtkinst \
vcl/unx/gtk3_kde5/gtk3_kde5_gtksys \
vcl/unx/gtk3_kde5/gtk3_kde5_filepicker \
vcl/unx/gtk3_kde5/gtk3_kde5_filepicker_ipc \
vcl/unx/gtk3_kde5/gtk3_kde5_folderpicker \
vcl/unx/gtk3_kde5/gtk3_kde5_cairo \
vcl/unx/gtk3_kde5/gtk3_kde5_printwrapper \
vcl/unx/gtk3_kde5/gtk3_kde5_salnativewidgets-gtk \
vcl/unx/gtk3_kde5/gtk3_kde5_salprn-gtk \
vcl/unx/gtk3_kde5/gtk3_kde5_gtkframe \
vcl/unx/gtk3_kde5/gtk3_kde5_gtkobject \
vcl/unx/gtk3_kde5/gtk3_kde5_gtksalmenu \
vcl/unx/gtk3_kde5/gtk3_kde5_glomenu \
vcl/unx/gtk3_kde5/gtk3_kde5_gloactiongroup \
vcl/unx/gtk3_kde5/gtk3_kde5_hudawareness \
))
ifeq ($(OS),LINUX)
$(eval $(call gb_Library_add_libs,vclplug_gtk3_kde5,\
-lm \
-ldl \
-lpthread \
))
endif
# vim: set noet sw=4 ts=4:
...@@ -94,6 +94,13 @@ $(eval $(call gb_Module_add_targets,vcl,\ ...@@ -94,6 +94,13 @@ $(eval $(call gb_Module_add_targets,vcl,\
Library_vclplug_qt5 \ Library_vclplug_qt5 \
)) ))
endif endif
ifneq ($(ENABLE_GTK3_KDE5),)
$(eval $(call gb_Module_add_targets,vcl,\
CustomTarget_gtk3_kde5_moc \
Library_vclplug_gtk3_kde5 \
Executable_lo_kde5filepicker \
))
endif
endif endif
ifeq ($(OS),MACOSX) ifeq ($(OS),MACOSX)
......
...@@ -99,7 +99,7 @@ static SalInstance* tryInstance( const OUString& rModuleBase, bool bForce = fals ...@@ -99,7 +99,7 @@ static SalInstance* tryInstance( const OUString& rModuleBase, bool bForce = fals
* #i109007# KDE3 seems to have the same problem. * #i109007# KDE3 seems to have the same problem.
* And same applies for KDE4. * And same applies for KDE4.
*/ */
if( rModuleBase == "gtk" || rModuleBase == "gtk3" || rModuleBase == "kde4" ) if( rModuleBase == "gtk" || rModuleBase == "gtk3" || rModuleBase == "kde4" || rModuleBase == "gtk3_kde5")
{ {
pCloseModule = nullptr; pCloseModule = nullptr;
} }
...@@ -163,6 +163,9 @@ static SalInstance* autodetect_plugin() ...@@ -163,6 +163,9 @@ static SalInstance* autodetect_plugin()
{ {
static const char* const pKDEFallbackList[] = static const char* const pKDEFallbackList[] =
{ {
#if ENABLE_GTK3_KDE5
"gtk3_kde5",
#endif
#if ENABLE_KDE4 #if ENABLE_KDE4
"kde4", "kde4",
#endif #endif
......
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
// the service names
#define FILE_PICKER_SERVICE_NAME "com.sun.star.ui.dialogs.Gtk3KDE5FilePicker"
// the implementation names
#define FILE_PICKER_IMPL_NAME "com.sun.star.ui.dialogs.Gtk3KDE5FilePicker"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkaction.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkbridge.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkcomponent.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkeditabletext.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkfactory.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkhypertext.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkimage.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atklistener.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkregistry.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkselection.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atktable.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atktext.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atktextattributes.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkutil.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkvalue.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkwindow.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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/a11y/atkwrapper.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
#include <cstdint>
#include <iostream>
#include <vector>
#include <sal/types.h>
#include <com/sun/star/uno/Sequence.hxx>
// #define DEBUG_FILEPICKER_IPC
namespace rtl
{
class OUString;
}
class QString;
enum class Commands : uint16_t
{
SetTitle,
SetWinId,
Execute,
SetMultiSelectionMode,
SetDefaultName,
SetDisplayDirectory,
GetDisplayDirectory,
GetSelectedFiles,
AppendFilter,
SetCurrentFilter,
GetCurrentFilter,
SetValue,
GetValue,
EnableControl,
SetLabel,
GetLabel,
AddCheckBox,
Initialize,
Quit,
EnablePickFolderMode,
};
inline std::vector<char> readIpcStringArg(std::istream& stream)
{
uint32_t length = 0;
stream >> length;
stream.ignore(); // skip space separator
std::vector<char> buffer(length, '\0');
stream.read(buffer.data(), length);
return buffer;
}
void readIpcArg(std::istream& stream, rtl::OUString& string);
void readIpcArg(std::istream& stream, QString& string);
void readIpcArg(std::istream& stream, css::uno::Sequence<rtl::OUString>& seq);
inline void readIpcArg(std::istream& stream, Commands& value)
{
uint16_t v = 0;
stream >> v;
stream.ignore(); // skip space
value = static_cast<Commands>(v);
}
inline void readIpcArg(std::istream& stream, sal_Bool& value)
{
bool v = false;
stream >> v;
value = v;
stream.ignore(); // skip space
}
inline void readIpcArg(std::istream& stream, bool& value)
{
stream >> value;
stream.ignore(); // skip space
}
inline void readIpcArg(std::istream& stream, sal_Int16& value)
{
stream >> value;
stream.ignore(); // skip space
}
inline void readIpcArg(std::istream& stream, sal_uIntPtr& value)
{
stream >> value;
stream.ignore(); // skip space
}
inline void readIpcArgs(std::istream& /*stream*/)
{
// end of arguments, nothing to do
}
template <typename T, typename... Args>
inline void readIpcArgs(std::istream& stream, T& arg, Args&... args)
{
readIpcArg(stream, arg);
readIpcArgs(stream, args...);
}
void sendIpcArg(std::ostream& stream, const rtl::OUString& string);
void sendIpcArg(std::ostream& stream, const QString& string);
inline void sendIpcStringArg(std::ostream& stream, uint32_t length, const char* string)
{
stream << length << ' ';
stream.write(string, length);
stream << ' ';
}
inline void sendIpcArg(std::ostream& stream, Commands value)
{
stream << static_cast<uint16_t>(value) << ' ';
}
inline void sendIpcArg(std::ostream& stream, sal_Bool value)
{
stream << static_cast<bool>(value) << ' ';
}
inline void sendIpcArg(std::ostream& stream, bool value) { stream << value << ' '; }
inline void sendIpcArg(std::ostream& stream, sal_Int16 value) { stream << value << ' '; }
inline void sendIpcArg(std::ostream& stream, sal_uIntPtr value) { stream << value << ' '; }
inline void sendIpcArgsImpl(std::ostream& stream)
{
// end of arguments, flush stream
stream << std::endl;
}
template <typename T, typename... Args>
inline void sendIpcArgsImpl(std::ostream& stream, const T& arg, const Args&... args)
{
sendIpcArg(stream, arg);
sendIpcArgsImpl(stream, args...);
}
template <typename T, typename... Args>
inline void sendIpcArgs(std::ostream& stream, const T& arg, const Args&... args)
{
sendIpcArgsImpl(stream, arg, args...);
#ifdef DEBUG_FILEPICKER_IPC
std::cerr << "IPC MSG: ";
sendIpcArgsImpl(std::cerr, arg, args...);
#endif
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/a11y/gtk3atkaction.cxx"
#include "../gtk3/a11y/gtk3atkbridge.cxx"
#include "../gtk3/a11y/gtk3atkcomponent.cxx"
#include "../gtk3/a11y/gtk3atkeditabletext.cxx"
#include "../gtk3/a11y/gtk3atkfactory.cxx"
#include "../gtk3/a11y/gtk3atkhypertext.cxx"
#include "../gtk3/a11y/gtk3atkimage.cxx"
#include "../gtk3/a11y/gtk3atklistener.cxx"
#include "../gtk3/a11y/gtk3atkregistry.cxx"
#include "../gtk3/a11y/gtk3atkselection.cxx"
#include "../gtk3/a11y/gtk3atktable.cxx"
#include "../gtk3/a11y/gtk3atktextattributes.cxx"
#include "../gtk3/a11y/gtk3atktext.cxx"
#include "../gtk3/a11y/gtk3atkutil.cxx"
#include "../gtk3/a11y/gtk3atkvalue.cxx"
#include "../gtk3/a11y/gtk3atkwindow.cxx"
#include "../gtk3/a11y/gtk3atkwrapper.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/cairo_gtk3_cairo.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
This diff is collapsed.
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
#include <cppuhelper/compbase.hxx>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <osl/conditn.hxx>
#include <osl/mutex.hxx>
#include <rtl/ustrbuf.hxx>
#include <boost/process/child.hpp>
#include <boost/process/pipe.hpp>
#include "gtk3_kde5_filepicker_ipc.hxx"
#include <functional>
typedef ::cppu::WeakComponentImplHelper<css::ui::dialogs::XFilePicker3,
css::ui::dialogs::XFilePickerControlAccess
// TODO css::ui::dialogs::XFilePreview
,
css::lang::XInitialization, css::lang::XServiceInfo>
Gtk3KDE5FilePicker_Base;
class Gtk3KDE5FilePicker : public Gtk3KDE5FilePicker_Base
{
protected:
css::uno::Reference<css::ui::dialogs::XFilePickerListener> m_xListener;
osl::Mutex _helperMutex;
Gtk3KDE5FilePickerIpc m_ipc;
public:
explicit Gtk3KDE5FilePicker(const css::uno::Reference<css::uno::XComponentContext>&);
virtual ~Gtk3KDE5FilePicker();
// XFilePickerNotifier
virtual void SAL_CALL addFilePickerListener(
const css::uno::Reference<css::ui::dialogs::XFilePickerListener>& xListener) override;
virtual void SAL_CALL removeFilePickerListener(
const css::uno::Reference<css::ui::dialogs::XFilePickerListener>& xListener) override;
// XExecutableDialog functions
virtual void SAL_CALL setTitle(const OUString& rTitle) override;
virtual sal_Int16 SAL_CALL execute() override;
// XFilePicker functions
virtual void SAL_CALL setMultiSelectionMode(sal_Bool bMode) override;
virtual void SAL_CALL setDefaultName(const OUString& rName) override;
virtual void SAL_CALL setDisplayDirectory(const OUString& rDirectory) override;
virtual OUString SAL_CALL getDisplayDirectory() override;
virtual css::uno::Sequence<OUString> SAL_CALL getFiles() override;
// XFilterManager functions
virtual void SAL_CALL appendFilter(const OUString& rTitle, const OUString& rFilter) override;
virtual void SAL_CALL setCurrentFilter(const OUString& rTitle) override;
virtual OUString SAL_CALL getCurrentFilter() override;
// XFilterGroupManager functions
virtual void SAL_CALL
appendFilterGroup(const OUString& rGroupTitle,
const css::uno::Sequence<css::beans::StringPair>& rFilters) override;
// XFilePickerControlAccess functions
virtual void SAL_CALL setValue(sal_Int16 nControlId, sal_Int16 nControlAction,
const css::uno::Any& rValue) override;
virtual css::uno::Any SAL_CALL getValue(sal_Int16 nControlId,
sal_Int16 nControlAction) override;
virtual void SAL_CALL enableControl(sal_Int16 nControlId, sal_Bool bEnable) override;
virtual void SAL_CALL setLabel(sal_Int16 nControlId, const OUString& rLabel) override;
virtual OUString SAL_CALL getLabel(sal_Int16 nControlId) override;
/* TODO XFilePreview
virtual css::uno::Sequence< sal_Int16 > SAL_CALL getSupportedImageFormats( );
virtual sal_Int32 SAL_CALL getTargetColorDepth( );
virtual sal_Int32 SAL_CALL getAvailableWidth( );
virtual sal_Int32 SAL_CALL getAvailableHeight( );
virtual void SAL_CALL setImage( sal_Int16 aImageFormat, const css::uno::Any &rImage );
virtual sal_Bool SAL_CALL setShowState( sal_Bool bShowState );
virtual sal_Bool SAL_CALL getShowState( );
*/
// XFilePicker2 functions
virtual css::uno::Sequence<OUString> SAL_CALL getSelectedFiles() override;
// XInitialization
virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override;
// XCancellable
virtual void SAL_CALL cancel() override;
// XEventListener
virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent);
using cppu::WeakComponentImplHelperBase::disposing;
// XServiceInfo
virtual OUString SAL_CALL getImplementationName() override;
virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
private:
Gtk3KDE5FilePicker(const Gtk3KDE5FilePicker&) = delete;
Gtk3KDE5FilePicker& operator=(const Gtk3KDE5FilePicker&) = delete;
//add a custom control widget to the file dialog
void addCustomControl(sal_Int16 controlId);
// emit XFilePickerListener controlStateChanged event
void filterChanged();
// emit XFilePickerListener fileSelectionChanged event
void selectionChanged();
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "gtk3_kde5_filepicker_ipc.hxx"
#undef Region
#include "unx/geninst.h"
#include "strings.hrc"
#include <future>
#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
#include <vcl/svapp.hxx>
#include <vcl/sysdata.hxx>
#include <vcl/syswin.hxx>
#include <osl/file.h>
#include <osl/process.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <unx/gtk/gtkdata.hxx>
#include <boost/filesystem/path.hpp>
#include <boost/process/environment.hpp>
#include <boost/process/search_path.hpp>
#include <boost/process/io.hpp>
using namespace ::com::sun::star::ui::dialogs;
namespace bp = boost::process;
namespace bf = boost::filesystem;
// helper functions
namespace
{
bf::path applicationDirPath()
{
OUString applicationFilePath;
osl_getExecutableFile(&applicationFilePath.pData);
OUString applicationSystemPath;
osl_getSystemPathFromFileURL(applicationFilePath.pData, &applicationSystemPath.pData);
auto sysPath = applicationSystemPath.toUtf8();
auto ret = bf::path(sysPath.getStr(), sysPath.getStr() + sysPath.getLength());
ret.remove_filename();
return ret;
}
bf::path findPickerExecutable()
{
auto paths = boost::this_process::path();
paths.insert(paths.begin(), applicationDirPath());
auto ret = bp::search_path("lo_kde5filepicker", paths);
if (ret.empty())
throw bp::process_error(std::make_error_code(std::errc::no_such_file_or_directory),
"could not find lo_kde5filepicker executable");
return ret;
}
}
void readIpcArg(std::istream& stream, OUString& str)
{
const auto buffer = readIpcStringArg(stream);
str = OUString::fromUtf8(OString(buffer.data(), buffer.size()));
}
void readIpcArg(std::istream& stream, css::uno::Sequence<OUString>& seq)
{
uint32_t numFiles = 0;
stream >> numFiles;
stream.ignore(); // skip space;
seq.realloc(numFiles);
for (size_t i = 0; i < numFiles; ++i)
{
readIpcArg(stream, seq[i]);
}
}
void sendIpcArg(std::ostream& stream, const OUString& string)
{
const auto utf8 = string.toUtf8();
sendIpcStringArg(stream, utf8.getLength(), utf8.getStr());
}
OUString getResString(const char* pResId)
{
if (pResId == nullptr)
return {};
return VclResId(pResId);
}
// Gtk3KDE5FilePicker
Gtk3KDE5FilePickerIpc::Gtk3KDE5FilePickerIpc()
// workaround: specify some non-empty argument, otherwise the Qt app will see argc == 0
: m_process(findPickerExecutable(), "dummy", bp::std_out > m_stdout, bp::std_in < m_stdin)
{
}
Gtk3KDE5FilePickerIpc::~Gtk3KDE5FilePickerIpc()
{
sendCommand(Commands::Quit);
if (m_process.running())
m_process.wait_for(std::chrono::milliseconds(100));
}
sal_Int16 SAL_CALL Gtk3KDE5FilePickerIpc::execute()
{
auto restoreMainWindow = blockMainWindow();
auto id = sendCommand(Commands::Execute);
sal_Bool accepted = false;
readResponse(id, accepted);
if (restoreMainWindow)
restoreMainWindow();
return accepted ? ExecutableDialogResults::OK : ExecutableDialogResults::CANCEL;
}
static gboolean ignoreDeleteEvent(GtkWidget* /*widget*/, GdkEvent* /*event*/,
gpointer /*user_data*/)
{
return true;
}
std::function<void()> SAL_CALL Gtk3KDE5FilePickerIpc::blockMainWindow()
{
vcl::Window* pParentWin = Application::GetDefDialogParent();
if (!pParentWin)
return {};
const SystemEnvData* pSysData = static_cast<SystemWindow*>(pParentWin)->GetSystemData();
if (!pSysData)
return {};
sendCommand(Commands::SetWinId, pSysData->aWindow);
auto* pMainWindow = reinterpret_cast<GtkWidget*>(pSysData->pWidget);
if (!pMainWindow)
return {};
SolarMutexGuard guard;
auto deleteEventSignalId = g_signal_lookup("delete_event", gtk_widget_get_type());
// disable the mainwindow
gtk_widget_set_sensitive(pMainWindow, false);
// block the GtkSalFrame delete_event handler
auto blockedHandler = g_signal_handler_find(
pMainWindow, static_cast<GSignalMatchType>(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
deleteEventSignalId, 0, nullptr, nullptr, pSysData->pSalFrame);
g_signal_handler_block(pMainWindow, blockedHandler);
// prevent the window from being closed
auto ignoreDeleteEventHandler
= g_signal_connect(pMainWindow, "delete_event", G_CALLBACK(ignoreDeleteEvent), nullptr);
return [pMainWindow, ignoreDeleteEventHandler, blockedHandler] {
SolarMutexGuard cleanupGuard;
// re-enable window
gtk_widget_set_sensitive(pMainWindow, true);
// allow it to be closed again
g_signal_handler_disconnect(pMainWindow, ignoreDeleteEventHandler);
// unblock the GtkSalFrame handler
g_signal_handler_unblock(pMainWindow, blockedHandler);
};
}
void SAL_CALL Gtk3KDE5FilePickerIpc::await(const std::future<void>& future)
{
while (future.wait_for(std::chrono::milliseconds(1)) != std::future_status::ready)
{
GetGtkSalData()->Yield(false, true);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
#include <cppuhelper/compbase.hxx>
#include <osl/conditn.hxx>
#include <osl/mutex.hxx>
#include <rtl/ustrbuf.hxx>
#include <boost/process/child.hpp>
#include <boost/process/pipe.hpp>
#include "filepicker_ipc_commands.hxx"
#include <functional>
#include <future>
#include <mutex>
#include <thread>
void readIpcArg(std::istream& stream, OUString& str);
void readIpcArg(std::istream& stream, css::uno::Sequence<OUString>& seq);
void sendIpcArg(std::ostream& stream, const OUString& string);
OUString getResString(const char* pResId);
class Gtk3KDE5FilePickerIpc
{
protected:
boost::process::ipstream m_stdout;
boost::process::opstream m_stdin;
boost::process::child m_process;
// simple multiplexing: every command gets it's own ID that can be used to
// read the corresponding response
uint64_t m_msgId = 1;
std::mutex m_mutex;
uint64_t m_incomingResponse = 0;
public:
explicit Gtk3KDE5FilePickerIpc();
~Gtk3KDE5FilePickerIpc();
sal_Int16 SAL_CALL execute();
template <typename... Args> uint64_t sendCommand(Commands command, const Args&... args)
{
auto id = m_msgId;
++m_msgId;
sendIpcArgs(m_stdin, id, command, args...);
return id;
}
template <typename... Args> void readResponse(uint64_t id, Args&... args)
{
// read synchronously from a background thread and run the eventloop until the value becomes available
// this allows us to keep the GUI responsive and also enables access to the LO clipboard
await(std::async(std::launch::async, [&]() {
while (true)
{
// only let one thread read at any given time
std::lock_guard<std::mutex> lock(m_mutex);
// check if we need to read (and potentially wait) a response ID
if (m_incomingResponse == 0)
readIpcArgs(m_stdout, m_incomingResponse);
if (m_incomingResponse == id)
{
// the response we are waiting for came in
readIpcArgs(m_stdout, args...);
m_incomingResponse = 0;
break;
}
else
{
// the next response answers some other request, yield
std::this_thread::yield();
}
}
}));
}
private:
std::function<void()> SAL_CALL blockMainWindow();
static void SAL_CALL await(const std::future<void>& future);
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "gtk3_kde5_folderpicker.hxx"
#include <com/sun/star/awt/Toolkit.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
#include <vcl/svapp.hxx>
#include <strings.hrc>
using namespace ::com::sun::star;
using namespace ::com::sun::star::ui::dialogs;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::uno;
// constructor
Gtk3KDE5FolderPicker::Gtk3KDE5FolderPicker(
const uno::Reference<uno::XComponentContext>& /*xContext*/)
{
m_ipc.sendCommand(Commands::EnablePickFolderMode);
setTitle(getResString(STR_FPICKER_FOLDER_DEFAULT_TITLE));
}
Gtk3KDE5FolderPicker::~Gtk3KDE5FolderPicker() = default;
void SAL_CALL Gtk3KDE5FolderPicker::setDisplayDirectory(const OUString& aDirectory)
{
m_ipc.sendCommand(Commands::SetDisplayDirectory, aDirectory);
}
OUString SAL_CALL Gtk3KDE5FolderPicker::getDisplayDirectory()
{
auto id = m_ipc.sendCommand(Commands::GetDisplayDirectory);
OUString ret;
m_ipc.readResponse(id, ret);
return ret;
}
OUString SAL_CALL Gtk3KDE5FolderPicker::getDirectory()
{
auto id = m_ipc.sendCommand(Commands::GetSelectedFiles);
uno::Sequence<OUString> seq;
m_ipc.readResponse(id, seq);
return seq.hasElements() ? seq[0] : OUString();
}
void SAL_CALL Gtk3KDE5FolderPicker::setDescription(const OUString& /*rDescription*/) {}
// XExecutableDialog functions
void SAL_CALL Gtk3KDE5FolderPicker::setTitle(const OUString& aTitle)
{
m_ipc.sendCommand(Commands::SetTitle, aTitle);
}
sal_Int16 SAL_CALL Gtk3KDE5FolderPicker::execute() { return m_ipc.execute(); }
// XCancellable
void SAL_CALL Gtk3KDE5FolderPicker::cancel()
{
// TODO
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
#include <list>
#include <memory>
#include <rtl/ustring.hxx>
#include <cppuhelper/implbase.hxx>
#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include "gtk3_kde5_filepicker_ipc.hxx"
class Gtk3KDE5FolderPicker : public cppu::WeakImplHelper<css::ui::dialogs::XFolderPicker2>
{
protected:
Gtk3KDE5FilePickerIpc m_ipc;
public:
// constructor
explicit Gtk3KDE5FolderPicker(
const css::uno::Reference<css::uno::XComponentContext>& xServiceMgr);
virtual ~Gtk3KDE5FolderPicker();
// XExecutableDialog functions
virtual void SAL_CALL setTitle(const OUString& aTitle) override;
virtual sal_Int16 SAL_CALL execute() override;
// XFolderPicker functions
virtual void SAL_CALL setDisplayDirectory(const OUString& rDirectory) override;
virtual OUString SAL_CALL getDisplayDirectory() override;
virtual OUString SAL_CALL getDirectory() override;
virtual void SAL_CALL setDescription(const OUString& rDescription) override;
// XCancellable
virtual void SAL_CALL cancel() override;
private:
Gtk3KDE5FolderPicker(const Gtk3KDE5FolderPicker&) = delete;
Gtk3KDE5FolderPicker& operator=(const Gtk3KDE5FolderPicker&) = delete;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3gloactiongroup.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3glomenu.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3gtkdata.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3gtkframe.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3gtkinst.cxx"
#include "gtk3_kde5_filepicker.hxx"
#include "gtk3_kde5_folderpicker.hxx"
#include <boost/process/exception.hpp>
uno::Reference<ui::dialogs::XFilePicker2>
GtkInstance::createFilePicker(const uno::Reference<uno::XComponentContext>& xMSF)
{
try
{
return uno::Reference<ui::dialogs::XFilePicker2>(new Gtk3KDE5FilePicker(xMSF));
}
catch (const boost::process::process_error& error)
{
OSL_FAIL(error.what());
return { nullptr };
}
}
uno::Reference<ui::dialogs::XFolderPicker2>
GtkInstance::createFolderPicker(const uno::Reference<uno::XComponentContext>& xMSF)
{
try
{
return uno::Reference<ui::dialogs::XFolderPicker2>(new Gtk3KDE5FolderPicker(xMSF));
}
catch (const boost::process::process_error& error)
{
OSL_FAIL(error.what());
return { nullptr };
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3gtkobject.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3gtksalmenu.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3gtksys.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3hudawareness.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3gtkprintwrapper.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3salnativewidgets-gtk.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "../gtk3/gtk3salprn-gtk.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "kde5_filepicker.hxx"
#include <KWindowSystem>
#include <KFileWidget>
#include <QtCore/QDebug>
#include <QtCore/QUrl>
#include <QtGui/QClipboard>
#include <QtGui/QWindow>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QWidget>
#include <QtWidgets/QApplication>
// The dialog should check whether LO also supports the protocol
// provided by KIO, and KFileWidget::dirOperator() is only 4.3+ .
// Moreover it's only in this somewhat internal KFileWidget class,
// which may not necessarily be what QFileDialog::fileWidget() returns,
// but that's hopefully not a problem in practice.
//#if Qt_VERSION_MAJOR == 4 && Qt_VERSION_MINOR >= 2
//#define ALLOW_REMOTE_URLS 1
//#else
#define ALLOW_REMOTE_URLS 0
//#endif
// KDE5FilePicker
KDE5FilePicker::KDE5FilePicker(QObject* parent)
: QObject(parent)
, _dialog(new QFileDialog(nullptr, {}, QDir::homePath()))
, _extraControls(new QWidget)
, _layout(new QFormLayout(_extraControls))
, _winId(0)
, allowRemoteUrls(false)
{
#if ALLOW_REMOTE_URLS
if (KFileWidget* fileWidget = dynamic_cast<KFileWidget*>(_dialog->fileWidget()))
{
allowRemoteUrls = true;
// Use finishedLoading signal rather than e.g. urlEntered, because if there's a problem
// such as the URL being mistyped, there's no way to prevent two message boxes about it,
// one from us and one from Qt code.
connect(fileWidget->dirOperator(), SIGNAL(finishedLoading()), SLOT(checkProtocol()));
}
#endif
setMultiSelectionMode(false);
connect(_dialog, &QFileDialog::filterSelected, this, &KDE5FilePicker::filterChanged);
connect(_dialog, &QFileDialog::fileSelected, this, &KDE5FilePicker::selectionChanged);
qApp->installEventFilter(this);
}
void KDE5FilePicker::enableFolderMode()
{
_dialog->setOption(QFileDialog::ShowDirsOnly, true);
_dialog->setFileMode(QFileDialog::Directory);
}
KDE5FilePicker::~KDE5FilePicker()
{
delete _extraControls;
delete _dialog;
}
void SAL_CALL KDE5FilePicker::setTitle(const QString& title) { _dialog->setWindowTitle(title); }
bool SAL_CALL KDE5FilePicker::execute()
{
if (!_filters.isEmpty())
_dialog->setNameFilters(_filters);
if (!_currentFilter.isEmpty())
_dialog->selectNameFilter(_currentFilter);
_dialog->show();
//block and wait for user input
return _dialog->exec() == QFileDialog::Accepted;
}
void SAL_CALL KDE5FilePicker::setMultiSelectionMode(bool multiSelect)
{
_dialog->setFileMode(multiSelect ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile);
}
void SAL_CALL KDE5FilePicker::setDefaultName(const QString& name)
{
_dialog->selectUrl(QUrl(name));
}
void SAL_CALL KDE5FilePicker::setDisplayDirectory(const QString& dir)
{
_dialog->selectUrl(QUrl(dir));
}
QString SAL_CALL KDE5FilePicker::getDisplayDirectory() const
{
return _dialog->directoryUrl().url();
}
QList<QUrl> SAL_CALL KDE5FilePicker::getSelectedFiles() const { return _dialog->selectedUrls(); }
void SAL_CALL KDE5FilePicker::appendFilter(const QString& title, const QString& filter)
{
QString t = title;
QString f = filter;
// '/' need to be escaped else they are assumed to be mime types by kfiledialog
//see the docs
t.replace("/", "\\/");
// openoffice gives us filters separated by ';' qt dialogs just want space separated
f.replace(";", " ");
// make sure "*.*" is not used as "all files"
f.replace("*.*", "*");
_filters << QStringLiteral("%1 (%2)").arg(t, f);
_titleToFilters[t] = _filters.constLast();
}
void SAL_CALL KDE5FilePicker::setCurrentFilter(const QString& title)
{
_currentFilter = _titleToFilters.value(title);
}
QString SAL_CALL KDE5FilePicker::getCurrentFilter() const
{
QString filter = _titleToFilters.key(_dialog->selectedNameFilter());
//default if not found
if (filter.isEmpty())
filter = "ODF Text Document (.odt)";
return filter;
}
void SAL_CALL KDE5FilePicker::setValue(sal_Int16 controlId, sal_Int16 /*nControlAction*/,
bool value)
{
if (_customWidgets.contains(controlId))
{
QCheckBox* cb = dynamic_cast<QCheckBox*>(_customWidgets.value(controlId));
if (cb)
cb->setChecked(value);
}
else
qWarning() << "set value on unknown control" << controlId;
}
bool SAL_CALL KDE5FilePicker::getValue(sal_Int16 controlId, sal_Int16 /*nControlAction*/) const
{
bool ret = false;
if (_customWidgets.contains(controlId))
{
QCheckBox* cb = dynamic_cast<QCheckBox*>(_customWidgets.value(controlId));
if (cb)
ret = cb->isChecked();
}
else
qWarning() << "get value on unknown control" << controlId;
return ret;
}
void SAL_CALL KDE5FilePicker::enableControl(sal_Int16 controlId, sal_Bool enable)
{
if (_customWidgets.contains(controlId))
_customWidgets.value(controlId)->setEnabled(enable);
else
qWarning() << "enable on unknown control" << controlId;
}
void SAL_CALL KDE5FilePicker::setLabel(sal_Int16 controlId, const QString& label)
{
if (_customWidgets.contains(controlId))
{
QCheckBox* cb = dynamic_cast<QCheckBox*>(_customWidgets.value(controlId));
if (cb)
cb->setText(label);
}
else
qWarning() << "set label on unknown control" << controlId;
}
QString SAL_CALL KDE5FilePicker::getLabel(sal_Int16 controlId) const
{
QString label;
if (_customWidgets.contains(controlId))
{
QCheckBox* cb = dynamic_cast<QCheckBox*>(_customWidgets.value(controlId));
if (cb)
label = cb->text();
}
else
qWarning() << "get label on unknown control" << controlId;
return label;
}
void KDE5FilePicker::addCheckBox(sal_Int16 controlId, const QString& label, bool hidden)
{
auto widget = new QCheckBox(_extraControls);
widget->setHidden(hidden);
if (!hidden)
{
auto resString = label;
resString.replace('~', '&');
_layout->addRow(resString, widget);
}
_customWidgets.insert(controlId, widget);
}
void SAL_CALL KDE5FilePicker::initialize(bool saveDialog)
{
//default is opening
QFileDialog::AcceptMode operationMode
= saveDialog ? QFileDialog::AcceptSave : QFileDialog::AcceptOpen;
_dialog->setAcceptMode(operationMode);
if (saveDialog)
{
_dialog->setConfirmOverwrite(true);
_dialog->setFileMode(QFileDialog::AnyFile);
}
}
void KDE5FilePicker::checkProtocol()
{
// There's no libreoffice.desktop :(, so find a matching one.
/*
KService::List services = KServiceTypeTrader::self()->query( "Application", "Exec =~ 'libreoffice %U'" );
QStringList protocols;
if( !services.isEmpty())
protocols = services[ 0 ]->property( "X-Qt-Protocols" ).toStringList();
if( protocols.isEmpty()) // incorrect (developer?) installation ?
protocols << "file" << "http";
if( !protocols.contains( _dialog->baseUrl().protocol()) && !protocols.contains( "KIO" ))
KMessageBox::error( _dialog, KIO::buildErrorString( KIO::ERR_UNSUPPORTED_PROTOCOL, _dialog->baseUrl().protocol()));
*/
}
void KDE5FilePicker::setWinId(sal_uIntPtr winId) { _winId = winId; }
bool KDE5FilePicker::eventFilter(QObject* o, QEvent* e)
{
if (e->type() == QEvent::Show && o->isWidgetType())
{
auto* w = static_cast<QWidget*>(o);
if (!w->parentWidget() && w->isModal())
{
KWindowSystem::setMainWindow(w, _winId);
if (auto* fileWidget = w->findChild<KFileWidget*>({}, Qt::FindDirectChildrenOnly))
fileWidget->setCustomWidget(_extraControls);
return false;
}
}
return QObject::eventFilter(o, e);
}
#include "kde5_filepicker.moc"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QHash>
#include <sal/types.h>
class QFileDialog;
class QWidget;
class QFormLayout;
class KDE5FilePicker : public QObject
{
Q_OBJECT
protected:
//the dialog to display
QFileDialog* _dialog;
//running filter string to add to dialog
QStringList _filters;
// map of filter titles to full filter for selection
QHash<QString, QString> _titleToFilters;
// string to set the current filter
QString _currentFilter;
//mapping of SAL control ID's to created custom controls
QHash<sal_Int16, QWidget*> _customWidgets;
//widget to contain extra custom controls
QWidget* _extraControls;
//layout for extra custom controls
QFormLayout* _layout;
sal_uIntPtr _winId;
bool allowRemoteUrls;
public:
explicit KDE5FilePicker(QObject* parent = nullptr);
~KDE5FilePicker();
void enableFolderMode();
// XExecutableDialog functions
void SAL_CALL setTitle(const QString& rTitle);
bool SAL_CALL execute();
// XFilePicker functions
void SAL_CALL setMultiSelectionMode(bool bMode);
void SAL_CALL setDefaultName(const QString& rName);
void SAL_CALL setDisplayDirectory(const QString& rDirectory);
QString SAL_CALL getDisplayDirectory() const;
// XFilterManager functions
void SAL_CALL appendFilter(const QString& rTitle, const QString& rFilter);
void SAL_CALL setCurrentFilter(const QString& rTitle);
QString SAL_CALL getCurrentFilter() const;
// XFilePickerControlAccess functions
void SAL_CALL setValue(sal_Int16 nControlId, sal_Int16 nControlAction, bool rValue);
bool SAL_CALL getValue(sal_Int16 nControlId, sal_Int16 nControlAction) const;
void SAL_CALL enableControl(sal_Int16 nControlId, sal_Bool bEnable);
void SAL_CALL setLabel(sal_Int16 nControlId, const QString& rLabel);
QString SAL_CALL getLabel(sal_Int16 nControlId) const;
// XFilePicker2 functions
QList<QUrl> getSelectedFiles() const;
// XInitialization
void initialize(bool saveDialog);
//add a custom control widget to the file dialog
void addCheckBox(sal_Int16 nControlId, const QString& label, bool hidden);
void setWinId(sal_uIntPtr winId);
private:
Q_DISABLE_COPY(KDE5FilePicker)
protected:
bool eventFilter(QObject* watched, QEvent* event) override;
private Q_SLOTS:
void checkProtocol();
Q_SIGNALS:
void filterChanged();
void selectionChanged();
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "kde5_filepicker_ipc.hxx"
#include <QSocketNotifier>
#include <QUrl>
#include <QThread>
#include <QApplication>
#include <QDebug>
#include <iostream>
#include "filepicker_ipc_commands.hxx"
#include "kde5_filepicker.hxx"
#include <rtl/ustring.h>
void readIpcArg(std::istream& stream, QString& string)
{
const auto buffer = readIpcStringArg(stream);
string = QString::fromUtf8(buffer.data(), buffer.size());
}
void sendIpcArg(std::ostream& stream, const QString& string)
{
const auto utf8 = string.toUtf8();
sendIpcStringArg(stream, utf8.size(), utf8.data());
}
void sendIpcArg(std::ostream& stream, const QList<QUrl>& urls)
{
stream << static_cast<uint32_t>(urls.size()) << ' ';
for (const auto& url : urls)
{
sendIpcArg(stream, url.toString());
}
}
FilePickerIpc::FilePickerIpc(KDE5FilePicker* filePicker, QObject* parent)
: QObject(parent)
, m_filePicker(filePicker)
, m_stdinNotifier(new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this))
{
connect(m_stdinNotifier, &QSocketNotifier::activated, this, &FilePickerIpc::readCommands);
}
FilePickerIpc::~FilePickerIpc() = default;
void FilePickerIpc::readCommands()
{
while (!std::cin.eof())
{
readCommand();
}
}
void FilePickerIpc::readCommand()
{
uint64_t messageId = 0;
Commands command;
readIpcArgs(std::cin, messageId, command);
switch (command)
{
case Commands::SetTitle:
{
QString title;
readIpcArgs(std::cin, title);
m_filePicker->setTitle(title);
return;
}
case Commands::SetWinId:
{
sal_uIntPtr winId = 0;
readIpcArgs(std::cin, winId);
m_filePicker->setWinId(winId);
return;
}
case Commands::Execute:
{
sendIpcArgs(std::cout, messageId, m_filePicker->execute());
return;
}
case Commands::SetMultiSelectionMode:
{
bool multiSelection = false;
readIpcArgs(std::cin, multiSelection);
m_filePicker->setMultiSelectionMode(multiSelection);
return;
}
case Commands::SetDefaultName:
{
QString name;
readIpcArgs(std::cin, name);
m_filePicker->setDefaultName(name);
return;
}
case Commands::SetDisplayDirectory:
{
QString dir;
readIpcArgs(std::cin, dir);
m_filePicker->setDisplayDirectory(dir);
return;
}
case Commands::GetDisplayDirectory:
{
sendIpcArgs(std::cout, messageId, m_filePicker->getDisplayDirectory());
return;
}
case Commands::GetSelectedFiles:
{
sendIpcArgs(std::cout, messageId, m_filePicker->getSelectedFiles());
return;
}
case Commands::AppendFilter:
{
QString title, filter;
readIpcArgs(std::cin, title, filter);
m_filePicker->appendFilter(title, filter);
return;
}
case Commands::SetCurrentFilter:
{
QString title;
readIpcArgs(std::cin, title);
m_filePicker->setCurrentFilter(title);
return;
}
case Commands::GetCurrentFilter:
{
sendIpcArgs(std::cout, messageId, m_filePicker->getCurrentFilter());
return;
}
case Commands::SetValue:
{
sal_Int16 controlId = 0;
sal_Int16 nControlAction = 0;
bool value = false;
readIpcArgs(std::cin, controlId, nControlAction, value);
m_filePicker->setValue(controlId, nControlAction, value);
return;
}
case Commands::GetValue:
{
sal_Int16 controlId = 0;
sal_Int16 nControlAction = 0;
readIpcArgs(std::cin, controlId, nControlAction);
sendIpcArgs(std::cout, messageId, m_filePicker->getValue(controlId, nControlAction));
return;
}
case Commands::EnableControl:
{
sal_Int16 controlId = 0;
bool enabled = false;
readIpcArgs(std::cin, controlId, enabled);
m_filePicker->enableControl(controlId, enabled);
return;
}
case Commands::SetLabel:
{
sal_Int16 controlId = 0;
QString label;
readIpcArgs(std::cin, controlId, label);
m_filePicker->setLabel(controlId, label);
return;
}
case Commands::GetLabel:
{
sal_Int16 controlId = 0;
readIpcArgs(std::cin, controlId);
sendIpcArgs(std::cout, messageId, m_filePicker->getLabel(controlId));
return;
}
case Commands::AddCheckBox:
{
sal_Int16 controlId = 0;
bool hidden = false;
QString label;
readIpcArgs(std::cin, controlId, hidden, label);
m_filePicker->addCheckBox(controlId, label, hidden);
return;
}
case Commands::Initialize:
{
bool saveDialog = false;
readIpcArgs(std::cin, saveDialog);
m_filePicker->initialize(saveDialog);
return;
}
case Commands::EnablePickFolderMode:
{
m_filePicker->enableFolderMode();
return;
}
case Commands::Quit:
{
qApp->quit();
return;
}
}
qWarning() << "unhandled command " << static_cast<uint16_t>(command);
}
#include "kde5_filepicker_ipc.moc"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
#include <QObject>
class KDE5FilePicker;
class WinIdEmbedder;
class QSocketNotifier;
class FilePickerIpc : public QObject
{
Q_OBJECT
public:
explicit FilePickerIpc(KDE5FilePicker* filePicker, QObject* parent = nullptr);
~FilePickerIpc();
private Q_SLOTS:
void readCommands();
private:
void readCommand();
KDE5FilePicker* m_filePicker = nullptr;
QSocketNotifier* m_stdinNotifier = nullptr;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this 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 "kde5_filepicker.hxx"
#include "kde5_filepicker_ipc.hxx"
#include <QApplication>
#include <iostream>
int main(int argc, char** argv)
{
QApplication::setApplicationName(QStringLiteral("lo_kde5filepicker"));
QApplication app(argc, argv);
app.setQuitOnLastWindowClosed(false);
KDE5FilePicker filePicker;
FilePickerIpc ipc(&filePicker);
return app.exec();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment