summaryrefslogtreecommitdiffstats
path: root/fpicker
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /fpicker
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fpicker')
-rw-r--r--fpicker/AllLangMoTarget_fps.mk13
-rw-r--r--fpicker/CppunitTest_fpicker_dialogs_test.mk63
-rw-r--r--fpicker/IwyuFilter_fpicker.yaml2
-rw-r--r--fpicker/Library_fps.mk65
-rw-r--r--fpicker/Library_fps_aqua.mk50
-rw-r--r--fpicker/Library_fps_office.mk63
-rw-r--r--fpicker/Makefile14
-rw-r--r--fpicker/Module_fpicker.mk39
-rw-r--r--fpicker/README.md3
-rw-r--r--fpicker/UIConfig_fps.mk19
-rw-r--r--fpicker/inc/bitmaps.hlst16
-rw-r--r--fpicker/inc/strings.hrc36
-rw-r--r--fpicker/qa/unit/data/fpicker-dialogs-test.txt40
-rw-r--r--fpicker/qa/unit/fpicker-dialogs-test.cxx61
-rw-r--r--fpicker/source/aqua/AquaFilePickerDelegate.hxx48
-rw-r--r--fpicker/source/aqua/AquaFilePickerDelegate.mm115
-rw-r--r--fpicker/source/aqua/ControlHelper.hxx184
-rw-r--r--fpicker/source/aqua/ControlHelper.mm937
-rw-r--r--fpicker/source/aqua/FilterHelper.hxx124
-rw-r--r--fpicker/source/aqua/FilterHelper.mm443
-rw-r--r--fpicker/source/aqua/NSString_OOoAdditions.hxx33
-rw-r--r--fpicker/source/aqua/NSString_OOoAdditions.mm47
-rw-r--r--fpicker/source/aqua/NSURL_OOoAdditions.hxx38
-rw-r--r--fpicker/source/aqua/NSURL_OOoAdditions.mm80
-rw-r--r--fpicker/source/aqua/SalAquaConstants.h54
-rw-r--r--fpicker/source/aqua/SalAquaFilePicker.hxx160
-rw-r--r--fpicker/source/aqua/SalAquaFilePicker.mm590
-rw-r--r--fpicker/source/aqua/SalAquaFolderPicker.hxx95
-rw-r--r--fpicker/source/aqua/SalAquaFolderPicker.mm179
-rw-r--r--fpicker/source/aqua/SalAquaPicker.hxx81
-rw-r--r--fpicker/source/aqua/SalAquaPicker.mm209
-rw-r--r--fpicker/source/aqua/fps_aqua.component30
-rw-r--r--fpicker/source/aqua/resourceprovider.hxx46
-rw-r--r--fpicker/source/aqua/resourceprovider.mm116
-rw-r--r--fpicker/source/office/OfficeControlAccess.cxx770
-rw-r--r--fpicker/source/office/OfficeControlAccess.hxx134
-rw-r--r--fpicker/source/office/OfficeFilePicker.cxx1108
-rw-r--r--fpicker/source/office/OfficeFilePicker.hxx239
-rw-r--r--fpicker/source/office/OfficeFolderPicker.cxx175
-rw-r--r--fpicker/source/office/OfficeFolderPicker.hxx89
-rw-r--r--fpicker/source/office/PlacesListBox.cxx158
-rw-r--r--fpicker/source/office/PlacesListBox.hxx66
-rw-r--r--fpicker/source/office/QueryFolderName.hxx39
-rw-r--r--fpicker/source/office/RemoteFilesDialog.cxx1201
-rw-r--r--fpicker/source/office/RemoteFilesDialog.hxx186
-rw-r--r--fpicker/source/office/asyncfilepicker.cxx183
-rw-r--r--fpicker/source/office/asyncfilepicker.hxx93
-rw-r--r--fpicker/source/office/autocmpledit.cxx104
-rw-r--r--fpicker/source/office/autocmpledit.hxx53
-rw-r--r--fpicker/source/office/breadcrumb.cxx246
-rw-r--r--fpicker/source/office/breadcrumb.hxx70
-rw-r--r--fpicker/source/office/commonpicker.cxx476
-rw-r--r--fpicker/source/office/commonpicker.hxx190
-rw-r--r--fpicker/source/office/contentenumeration.cxx326
-rw-r--r--fpicker/source/office/contentenumeration.hxx237
-rw-r--r--fpicker/source/office/fileview.cxx1816
-rw-r--r--fpicker/source/office/fileview.hxx184
-rw-r--r--fpicker/source/office/foldertree.cxx178
-rw-r--r--fpicker/source/office/foldertree.hxx48
-rw-r--r--fpicker/source/office/fpdialogbase.hxx112
-rw-r--r--fpicker/source/office/fpinteraction.cxx142
-rw-r--r--fpicker/source/office/fpinteraction.hxx80
-rw-r--r--fpicker/source/office/fps_office.component34
-rw-r--r--fpicker/source/office/fpsmartcontent.cxx325
-rw-r--r--fpicker/source/office/fpsmartcontent.hxx195
-rw-r--r--fpicker/source/office/iodlg.cxx2331
-rw-r--r--fpicker/source/office/iodlg.hxx302
-rw-r--r--fpicker/source/office/iodlgimp.cxx190
-rw-r--r--fpicker/source/office/iodlgimp.hxx205
-rw-r--r--fpicker/source/office/pickercallbacks.hxx51
-rw-r--r--fpicker/source/win32/FilterContainer.cxx275
-rw-r--r--fpicker/source/win32/FilterContainer.hxx107
-rw-r--r--fpicker/source/win32/IVistaFilePickerInternalNotify.hxx48
-rw-r--r--fpicker/source/win32/VistaFilePicker.cxx541
-rw-r--r--fpicker/source/win32/VistaFilePicker.hxx224
-rw-r--r--fpicker/source/win32/VistaFilePickerEventHandler.cxx313
-rw-r--r--fpicker/source/win32/VistaFilePickerEventHandler.hxx192
-rw-r--r--fpicker/source/win32/VistaFilePickerImpl.cxx1249
-rw-r--r--fpicker/source/win32/VistaFilePickerImpl.hxx310
-rw-r--r--fpicker/source/win32/WinImplHelper.cxx161
-rw-r--r--fpicker/source/win32/WinImplHelper.hxx49
-rw-r--r--fpicker/source/win32/fps.component29
-rw-r--r--fpicker/source/win32/platform_vista.h43
-rw-r--r--fpicker/source/win32/requests.hxx76
-rw-r--r--fpicker/source/win32/resourceprovider.cxx103
-rw-r--r--fpicker/source/win32/resourceprovider.hxx35
-rw-r--r--fpicker/source/win32/shared.hxx29
-rw-r--r--fpicker/source/win32/vistatypes.h43
-rw-r--r--fpicker/source/win32/workbench/Test_fps.cxx356
-rw-r--r--fpicker/source/win32/workbench/makefile.mk55
-rw-r--r--fpicker/uiconfig/ui/breadcrumb.ui33
-rw-r--r--fpicker/uiconfig/ui/explorerfiledialog.ui896
-rw-r--r--fpicker/uiconfig/ui/foldernamedialog.ui136
-rw-r--r--fpicker/uiconfig/ui/remotefilesdialog.ui562
94 files changed, 22294 insertions, 0 deletions
diff --git a/fpicker/AllLangMoTarget_fps.mk b/fpicker/AllLangMoTarget_fps.mk
new file mode 100644
index 000000000..cd4495b0b
--- /dev/null
+++ b/fpicker/AllLangMoTarget_fps.mk
@@ -0,0 +1,13 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+$(eval $(call gb_AllLangMoTarget_AllLangMoTarget,fps))
+
+$(eval $(call gb_AllLangMoTarget_set_polocation,fps,fpicker))
+
+# vim: set noet sw=4 ts=4:
diff --git a/fpicker/CppunitTest_fpicker_dialogs_test.mk b/fpicker/CppunitTest_fpicker_dialogs_test.mk
new file mode 100644
index 000000000..398150060
--- /dev/null
+++ b/fpicker/CppunitTest_fpicker_dialogs_test.mk
@@ -0,0 +1,63 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitScreenShot,fpicker_dialogs_test))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,fpicker_dialogs_test, \
+ fpicker/qa/unit/fpicker-dialogs-test \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,fpicker_dialogs_test))
+
+$(eval $(call gb_CppunitTest_use_libraries,fpicker_dialogs_test, \
+ basegfx \
+ comphelper \
+ cppu \
+ cppuhelper \
+ drawinglayer \
+ editeng \
+ i18nlangtag \
+ i18nutil \
+ msfilter \
+ oox \
+ sal \
+ salhelper \
+ sax \
+ sfx \
+ sot \
+ svl \
+ svt \
+ test \
+ tl \
+ tk \
+ ucbhelper \
+ unotest \
+ utl \
+ vcl \
+ xo \
+))
+
+$(eval $(call gb_CppunitTest_use_external,fpicker_dialogs_test,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,fpicker_dialogs_test))
+
+$(eval $(call gb_CppunitTest_use_ure,fpicker_dialogs_test))
+$(eval $(call gb_CppunitTest_use_vcl_non_headless_with_windows,fpicker_dialogs_test))
+
+$(eval $(call gb_CppunitTest_use_rdb,fpicker_dialogs_test,services))
+
+$(eval $(call gb_CppunitTest_use_configuration,fpicker_dialogs_test))
+
+$(eval $(call gb_CppunitTest_use_uiconfigs,fpicker_dialogs_test,\
+ fps \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/fpicker/IwyuFilter_fpicker.yaml b/fpicker/IwyuFilter_fpicker.yaml
new file mode 100644
index 000000000..978f04862
--- /dev/null
+++ b/fpicker/IwyuFilter_fpicker.yaml
@@ -0,0 +1,2 @@
+---
+assumeFilename: fpicker/source/office/iodlg.cxx
diff --git a/fpicker/Library_fps.mk b/fpicker/Library_fps.mk
new file mode 100644
index 000000000..3e14f03b7
--- /dev/null
+++ b/fpicker/Library_fps.mk
@@ -0,0 +1,65 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,fps))
+
+$(eval $(call gb_Library_use_custom_headers,fps,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_Library_set_componentfile,fps,fpicker/source/win32/fps,services))
+
+$(eval $(call gb_Library_set_include,fps,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/fpicker/inc \
+))
+
+$(eval $(call gb_Library_use_external,fps,boost_headers))
+
+$(eval $(call gb_Library_use_sdk_api,fps))
+
+$(eval $(call gb_Library_use_libraries,fps,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ i18nlangtag \
+ tl \
+ utl \
+ vcl \
+))
+
+$(eval $(call gb_Library_use_system_win32_libs,fps,\
+ advapi32 \
+ comdlg32 \
+ gdi32 \
+ kernel32 \
+ ole32 \
+ oleaut32 \
+ shell32 \
+ uuid \
+))
+
+ifeq ($(COM),MSC)
+$(eval $(call gb_Library_add_libs,fps,\
+ Delayimp.lib /DELAYLOAD:shell32.dll \
+))
+endif
+
+$(eval $(call gb_Library_add_exception_objects,fps,\
+ fpicker/source/win32/FilterContainer \
+ fpicker/source/win32/VistaFilePicker \
+ fpicker/source/win32/VistaFilePickerEventHandler \
+ fpicker/source/win32/VistaFilePickerImpl \
+ fpicker/source/win32/resourceprovider \
+ fpicker/source/win32/WinImplHelper \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/fpicker/Library_fps_aqua.mk b/fpicker/Library_fps_aqua.mk
new file mode 100644
index 000000000..0b8db2e37
--- /dev/null
+++ b/fpicker/Library_fps_aqua.mk
@@ -0,0 +1,50 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,fps_aqua))
+
+$(eval $(call gb_Library_set_componentfile,fps_aqua,fpicker/source/aqua/fps_aqua,services))
+
+$(eval $(call gb_Library_set_include,fps_aqua,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/fpicker/inc \
+))
+
+$(eval $(call gb_Library_use_external,fps_aqua,boost_headers))
+
+$(eval $(call gb_Library_use_sdk_api,fps_aqua))
+
+$(eval $(call gb_Library_use_system_darwin_frameworks,fps_aqua,\
+ Cocoa \
+ CoreFoundation \
+))
+
+$(eval $(call gb_Library_use_libraries,fps_aqua,\
+ cppu \
+ cppuhelper \
+ i18nlangtag \
+ sal \
+ utl \
+ vcl \
+))
+
+$(eval $(call gb_Library_add_objcxxobjects,fps_aqua,\
+ fpicker/source/aqua/AquaFilePickerDelegate \
+ fpicker/source/aqua/ControlHelper \
+ fpicker/source/aqua/FilterHelper \
+ fpicker/source/aqua/NSString_OOoAdditions \
+ fpicker/source/aqua/NSURL_OOoAdditions \
+ fpicker/source/aqua/resourceprovider \
+ fpicker/source/aqua/SalAquaFilePicker \
+ fpicker/source/aqua/SalAquaFolderPicker \
+ fpicker/source/aqua/SalAquaPicker \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/fpicker/Library_fps_office.mk b/fpicker/Library_fps_office.mk
new file mode 100644
index 000000000..d7e41e66e
--- /dev/null
+++ b/fpicker/Library_fps_office.mk
@@ -0,0 +1,63 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Library_Library,fps_office))
+
+$(eval $(call gb_Library_set_componentfile,fps_office,fpicker/source/office/fps_office,services))
+
+$(eval $(call gb_Library_set_include,fps_office,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/fpicker/inc \
+))
+
+$(eval $(call gb_Library_use_external,fps_office,boost_headers))
+
+$(eval $(call gb_Library_use_custom_headers,fps_office,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_Library_use_sdk_api,fps_office))
+
+$(eval $(call gb_Library_use_libraries,fps_office,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ salhelper \
+ svl \
+ svt \
+ tk \
+ tl \
+ ucbhelper \
+ utl \
+ vcl \
+ i18nlangtag \
+))
+
+$(eval $(call gb_Library_add_exception_objects,fps_office,\
+ fpicker/source/office/asyncfilepicker \
+ fpicker/source/office/autocmpledit \
+ fpicker/source/office/breadcrumb \
+ fpicker/source/office/commonpicker \
+ fpicker/source/office/contentenumeration \
+ fpicker/source/office/fileview \
+ fpicker/source/office/foldertree \
+ fpicker/source/office/fpinteraction \
+ fpicker/source/office/fpsmartcontent \
+ fpicker/source/office/iodlg \
+ fpicker/source/office/iodlgimp \
+ fpicker/source/office/OfficeControlAccess \
+ fpicker/source/office/OfficeFilePicker \
+ fpicker/source/office/OfficeFolderPicker \
+ fpicker/source/office/PlacesListBox \
+ fpicker/source/office/RemoteFilesDialog \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/fpicker/Makefile b/fpicker/Makefile
new file mode 100644
index 000000000..0997e6284
--- /dev/null
+++ b/fpicker/Makefile
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
+
+include $(module_directory)/../solenv/gbuild/partial_build.mk
+
+# vim: set noet sw=4 ts=4:
diff --git a/fpicker/Module_fpicker.mk b/fpicker/Module_fpicker.mk
new file mode 100644
index 000000000..d05b4b20d
--- /dev/null
+++ b/fpicker/Module_fpicker.mk
@@ -0,0 +1,39 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Module_Module,fpicker))
+
+$(eval $(call gb_Module_add_targets,fpicker,\
+ Library_fps_office \
+ UIConfig_fps \
+))
+
+$(eval $(call gb_Module_add_l10n_targets,fpicker,\
+ AllLangMoTarget_fps \
+))
+
+ifeq ($(OS),MACOSX)
+$(eval $(call gb_Module_add_targets,fpicker,\
+ Library_fps_aqua \
+))
+endif
+
+ifeq ($(OS),WNT)
+$(eval $(call gb_Module_add_targets,fpicker,\
+ Library_fps \
+))
+endif
+
+# screenshots
+$(eval $(call gb_Module_add_screenshot_targets,fpicker,\
+ CppunitTest_fpicker_dialogs_test \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/fpicker/README.md b/fpicker/README.md
new file mode 100644
index 000000000..6f4bae6c4
--- /dev/null
+++ b/fpicker/README.md
@@ -0,0 +1,3 @@
+# Native File Picker
+
+Native file pickers for macOS and Windows (file open dialog).
diff --git a/fpicker/UIConfig_fps.mk b/fpicker/UIConfig_fps.mk
new file mode 100644
index 000000000..2c179b296
--- /dev/null
+++ b/fpicker/UIConfig_fps.mk
@@ -0,0 +1,19 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_UIConfig_UIConfig,fps))
+
+$(eval $(call gb_UIConfig_add_uifiles,fps,\
+ fpicker/uiconfig/ui/breadcrumb \
+ fpicker/uiconfig/ui/explorerfiledialog \
+ fpicker/uiconfig/ui/foldernamedialog \
+ fpicker/uiconfig/ui/remotefilesdialog \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/fpicker/inc/bitmaps.hlst b/fpicker/inc/bitmaps.hlst
new file mode 100644
index 000000000..5771d6134
--- /dev/null
+++ b/fpicker/inc/bitmaps.hlst
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+inline constexpr OUStringLiteral BMP_FILEDLG_PLACE_LOCAL = u"fpicker/res/fp015.png";
+inline constexpr OUStringLiteral BMP_FILEDLG_PLACE_REMOTE = u"fpicker/res/fp016.png";
+inline constexpr OUStringLiteral RID_BMP_FOLDER = u"svtools/res/folder.png";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/fpicker/inc/strings.hrc b/fpicker/inc/strings.hrc
new file mode 100644
index 000000000..042ea35c4
--- /dev/null
+++ b/fpicker/inc/strings.hrc
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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
+
+#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))
+
+#define STR_EXPLORERFILE_OPEN NC_("STR_EXPLORERFILE_OPEN", "Open")
+#define STR_EXPLORERFILE_SAVE NC_("STR_EXPLORERFILE_SAVE", "Save as")
+#define STR_EXPLORERFILE_BUTTONSAVE NC_("STR_EXPLORERFILE_BUTTONSAVE", "~Save")
+#define STR_PATHNAME NC_("STR_PATHNAME", "~Path:")
+#define STR_PATHSELECT NC_("STR_PATHSELECT", "Select path")
+#define STR_BUTTONSELECT NC_("STR_BUTTONSELECT", "~Select")
+#define STR_PREVIEW NC_("STR_PREVIEW", "File Preview")
+#define STR_DEFAULT_DIRECTORY NC_("STR_DEFAULT_DIRECTORY", "My Documents")
+#define RID_FILEOPEN_NOTEXISTENTFILE NC_("RID_FILEOPEN_NOTEXISTENTFILE", "The file $name$ does not exist.\nMake sure you have entered the correct file name.")
+#define STR_SVT_NEW_FOLDER NC_("STR_SVT_NEW_FOLDER", "Folder")
+#define STR_SVT_NOREMOVABLEDEVICE NC_("STR_SVT_NOREMOVABLEDEVICE", "No removable storage device detected.\nMake sure it is plugged in properly and try again.")
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/qa/unit/data/fpicker-dialogs-test.txt b/fpicker/qa/unit/data/fpicker-dialogs-test.txt
new file mode 100644
index 000000000..563802ee2
--- /dev/null
+++ b/fpicker/qa/unit/data/fpicker-dialogs-test.txt
@@ -0,0 +1,40 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+# This file contains all dialogs that the unit tests in the module
+# will work on if it is in script mode. It will read one-by-one,
+# try to open it and create a screenshot that will be saved in
+# workdir/screenshots using the pattern of the ui-file name.
+#
+# Syntax:
+# - empty lines are allowed
+# - lines starting with '#' are treated as comment
+# - all other lines should contain a *.ui filename in the same
+# notation as in the dialog constructors (see code)
+
+#
+# The 'known' dialogs which have a hard-coded representation
+# in registerKnownDialogsByID/createDialogByID
+#
+
+# No known dialogs in fpicker for now
+
+#
+# Dialogs without a hard-coded representation. These will
+# be visualized using a fallback based on weld::Builder
+#
+
+# currently deactivated, leads to problems and the test to not work
+# This is typically a hint that these should be hard-coded in the
+# test case since they need some document and model data to work
+
+fps/ui/foldernamedialog.ui
+fps/ui/explorerfiledialog.ui
+fps/ui/explorerfiledialog.ui
+fps/ui/remotefilesdialog.ui
diff --git a/fpicker/qa/unit/fpicker-dialogs-test.cxx b/fpicker/qa/unit/fpicker-dialogs-test.cxx
new file mode 100644
index 000000000..dfce816df
--- /dev/null
+++ b/fpicker/qa/unit/fpicker-dialogs-test.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+#include <test/screenshot_test.hxx>
+#include <vcl/abstdlg.hxx>
+
+using namespace ::com::sun::star;
+
+/// Test opening a dialog in fpicker
+class FpickerDialogsTest : public ScreenshotTest
+{
+private:
+ /// helper method to populate KnownDialogs, called in setUp(). Needs to be
+ /// written and has to add entries to KnownDialogs
+ virtual void registerKnownDialogsByID(mapType& rKnownDialogs) override;
+
+ /// dialog creation for known dialogs by ID. Has to be implemented for
+ /// each registered known dialog
+ virtual VclPtr<VclAbstractDialog> createDialogByID(sal_uInt32 nID) override;
+
+public:
+ FpickerDialogsTest();
+
+ // try to open a dialog
+ void openAnyDialog();
+
+ CPPUNIT_TEST_SUITE(FpickerDialogsTest);
+ CPPUNIT_TEST(openAnyDialog);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+FpickerDialogsTest::FpickerDialogsTest() {}
+
+void FpickerDialogsTest::registerKnownDialogsByID(mapType& /*rKnownDialogs*/)
+{
+ // fill map of known dialogs
+}
+
+VclPtr<VclAbstractDialog> FpickerDialogsTest::createDialogByID(sal_uInt32 /*nID*/)
+{
+ return nullptr;
+}
+
+void FpickerDialogsTest::openAnyDialog()
+{
+ /// process input file containing the UXMLDescriptions of the dialogs to dump
+ processDialogBatchFile(u"fpicker/qa/unit/data/fpicker-dialogs-test.txt");
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(FpickerDialogsTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/AquaFilePickerDelegate.hxx b/fpicker/source/aqua/AquaFilePickerDelegate.hxx
new file mode 100644
index 000000000..51c34b71b
--- /dev/null
+++ b/fpicker/source/aqua/AquaFilePickerDelegate.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <premac.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+class SalAquaFilePicker;
+class FilterHelper;
+
+@interface AquaFilePickerDelegate : NSObject <NSOpenSavePanelDelegate>
+{
+ SalAquaFilePicker* filePicker;
+ FilterHelper* filterHelper;
+}
+
+- (id)initWithFilePicker:(SalAquaFilePicker*)fPicker;
+
+- (void)setFilterHelper:(FilterHelper*)filterHelper;
+
+- (BOOL)panel:(id)sender shouldShowFilename:(NSString*)filename;
+- (void)panelSelectionDidChange:(id)sender;
+- (void)panel:(id)sender directoryDidChange:(NSString*)path;
+
+- (void)filterSelectedAtIndex:(id)sender;
+- (void)autoextensionChanged:(id)sender;
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/AquaFilePickerDelegate.mm b/fpicker/source/aqua/AquaFilePickerDelegate.mm
new file mode 100644
index 000000000..d9506c2c7
--- /dev/null
+++ b/fpicker/source/aqua/AquaFilePickerDelegate.mm
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/uno/Any.hxx>
+
+#include "SalAquaFilePicker.hxx"
+#include "FilterHelper.hxx"
+#include "AquaFilePickerDelegate.hxx"
+
+@implementation AquaFilePickerDelegate
+
+- (id)initWithFilePicker:(SalAquaFilePicker*)fPicker
+{
+ if ((self = [super init])) {
+ filePicker = fPicker;
+ filterHelper = nullptr;
+ return self;
+ }
+ return nil;
+}
+
+- (void)setFilterHelper:(FilterHelper*)helper
+{
+ filterHelper = helper;
+}
+
+#pragma mark NSSavePanel delegate methods
+
+- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
+{
+ (void)sender;
+ if( filterHelper == nullptr )
+ return true;
+ if( filename == nil )
+ return false;
+ return filterHelper->filenameMatchesFilter(filename);
+}
+
+- (void)panelSelectionDidChange:(id)sender
+{
+ (void)sender;
+ if (filePicker != nullptr) {
+ css::ui::dialogs::FilePickerEvent evt;
+ filePicker->fileSelectionChanged(evt);
+ }
+}
+
+- (void)panel:(id)sender directoryDidChange:(NSString *)path
+{
+ (void)sender;
+ (void)path;
+ if (filePicker != nullptr) {
+ css::ui::dialogs::FilePickerEvent evt;
+ filePicker->directoryChanged(evt);
+ }
+}
+
+
+#pragma mark UIActions
+- (void)filterSelectedAtIndex:(id)sender
+{
+ if (sender == nil) {
+ return;
+ }
+
+ if ([sender class] != [NSPopUpButton class]) {
+ return;
+ }
+
+ if (filterHelper == nullptr) {
+ return;
+ }
+
+ NSPopUpButton *popup = static_cast<NSPopUpButton*>(sender);
+ unsigned int selectedIndex = [popup indexOfSelectedItem];
+
+ filterHelper->SetFilterAtIndex(selectedIndex);
+
+ filePicker->filterControlChanged();
+}
+
+- (void)autoextensionChanged:(id)sender
+{
+ if (sender == nil) {
+ return;
+ }
+
+ if ([sender class] != [NSButton class]) {
+ return;
+ }
+ uno::Any aValue;
+ aValue <<= ([static_cast<NSButton*>(sender) state] == NSControlStateValueOn);
+
+ filePicker->setValue(css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0, aValue);
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/ControlHelper.hxx b/fpicker/source/aqua/ControlHelper.hxx
new file mode 100644
index 000000000..5da540df6
--- /dev/null
+++ b/fpicker/source/aqua/ControlHelper.hxx
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+#include <list>
+#include <map>
+
+#include <premac.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+#include "SalAquaConstants.h"
+#include "FilterHelper.hxx"
+#include "AquaFilePickerDelegate.hxx"
+
+using namespace com::sun::star;
+
+class ControlHelper {
+
+public:
+
+
+ // Constructor / Destructor
+
+ ControlHelper();
+ virtual ~ControlHelper();
+
+
+ // XInitialization delegate
+
+ void initialize( sal_Int16 templateId );
+
+
+ // XFilePickerControlAccess function delegates
+
+ void setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue );
+ uno::Any getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const;
+ void enableControl( sal_Int16 nControlId, bool bEnable ) const;
+ OUString getLabel( sal_Int16 nControlId );
+ void setLabel( sal_Int16 nControlId, NSString* aLabel );
+
+
+ // other stuff
+
+ void updateFilterUI();
+
+
+ // Type definitions
+
+ enum ToggleType {
+ AUTOEXTENSION, //but autoextension is handled differently on MacOSX
+ PASSWORD,
+ FILTEROPTIONS,
+ READONLY,
+ LINK,
+ PREVIEW,
+ SELECTION,
+ TOGGLE_LAST
+ };
+
+ enum ListType {
+ VERSION,
+ TEMPLATE,
+ IMAGE_TEMPLATE,
+ IMAGE_ANCHOR,
+ LIST_LAST
+ };
+
+
+ // inline functions
+
+ NSView* getUserPane() {
+ if (!m_bIsUserPaneLaidOut) {
+ createUserPane();
+ }
+ return m_pUserPane;
+ }
+
+ bool getVisibility(ToggleType tToggle) {
+ return m_bToggleVisibility[tToggle];
+ }
+
+ void setFilterControlNeeded(bool bNeeded) {
+ m_bIsFilterControlNeeded = bNeeded;
+ if (bNeeded) {
+ m_bUserPaneNeeded = true;
+ }
+ }
+
+ void setFilterHelper(FilterHelper* pFilterHelper) {
+ m_pFilterHelper = pFilterHelper;
+ }
+
+ void setFilePickerDelegate(AquaFilePickerDelegate* pDelegate) {
+ m_pDelegate = pDelegate;
+ }
+
+ bool isAutoExtensionEnabled() {
+ return ([static_cast<NSButton*>(m_pToggles[AUTOEXTENSION]) state] == NSControlStateValueOn);
+ }
+
+private:
+
+ // private member variables
+
+
+ /** the native view object */
+ NSView* m_pUserPane;
+
+ /** the checkbox controls */
+ NSControl* m_pToggles[ TOGGLE_LAST ];
+
+ /** the visibility flags for the checkboxes */
+ bool m_bToggleVisibility[TOGGLE_LAST];
+
+ /** the special filter control */
+ NSPopUpButton *m_pFilterControl;
+
+ /** the popup menu controls (except for the filter control) */
+ NSControl* m_pListControls[ LIST_LAST ];
+
+ /** a map to store a control's label text */
+ ::std::map<NSControl *, NSString *> m_aMapListLabels;
+
+ /** a map to store a popup menu's label text field */
+ ::std::map<NSPopUpButton *, NSTextField *> m_aMapListLabelFields;
+
+ /** the visibility flags for the popup menus */
+ bool m_bListVisibility[ LIST_LAST ];
+
+ /** indicates if a user pane is needed */
+ bool m_bUserPaneNeeded;
+
+ /** indicates if the user pane was laid out already */
+ bool m_bIsUserPaneLaidOut;
+
+ /** indicates if a filter control is needed */
+ bool m_bIsFilterControlNeeded;
+
+ /** a list with all actively used controls */
+ ::std::list<NSControl*> m_aActiveControls;
+
+ /** the filter helper */
+ FilterHelper *m_pFilterHelper;
+
+ /** the save or open panel's delegate */
+ AquaFilePickerDelegate *m_pDelegate;
+
+
+ // private methods
+
+ void HandleSetListValue(const NSControl* pControl, const sal_Int16 nControlAction, const uno::Any& rValue);
+
+ void createControls();
+ void createFilterControl();
+ void createUserPane();
+
+ static int getControlElementName(const Class clazz, const int nControlId);
+ NSControl* getControl( const sal_Int16 nControlId ) const;
+ static int getVerticalDistance(const NSControl* first, const NSControl* second);
+
+ void layoutControls();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/ControlHelper.mm b/fpicker/source/aqua/ControlHelper.mm
new file mode 100644
index 000000000..88f0b655c
--- /dev/null
+++ b/fpicker/source/aqua/ControlHelper.mm
@@ -0,0 +1,937 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ControlActions.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+#include "resourceprovider.hxx"
+#include "NSString_OOoAdditions.hxx"
+#include <sal/log.hxx>
+
+#include "ControlHelper.hxx"
+
+#pragma mark DEFINES
+#define POPUP_WIDTH_MIN 200
+#define POPUP_WIDTH_MAX 350
+
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
+using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
+using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
+
+namespace {
+
+uno::Any HandleGetListValue(const NSControl* pControl, const sal_Int16 nControlAction)
+{
+ uno::Any aAny;
+
+ if ([pControl class] != [NSPopUpButton class]) {
+ SAL_INFO("fpicker.aqua","not a popup button");
+ return aAny;
+ }
+
+ NSPopUpButton *pButton = static_cast<NSPopUpButton*>(pControl);
+ NSMenu *rMenu = [pButton menu];
+ if (nil == rMenu) {
+ SAL_INFO("fpicker.aqua","button has no menu");
+ return aAny;
+ }
+
+ switch (nControlAction)
+ {
+ case ControlActions::GET_ITEMS:
+ {
+ SAL_INFO("fpicker.aqua","GET_ITEMS");
+ uno::Sequence< OUString > aItemList;
+
+ int nItems = [rMenu numberOfItems];
+ if (nItems > 0) {
+ aItemList.realloc(nItems);
+ OUString* pItemList = aItemList.getArray();
+ for (int i = 0; i < nItems; i++) {
+ NSString* sCFItem = [pButton itemTitleAtIndex:i];
+ if (nil != sCFItem) {
+ pItemList[i] = [sCFItem OUString];
+ SAL_INFO("fpicker.aqua","Return value[" << (i - 1) << "]: " << aItemList[i - 1]);
+ }
+ }
+ }
+
+ aAny <<= aItemList;
+ }
+ break;
+ case ControlActions::GET_SELECTED_ITEM:
+ {
+ SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM");
+ NSString* sCFItem = [pButton titleOfSelectedItem];
+ if (nil != sCFItem) {
+ OUString sString = [sCFItem OUString];
+ SAL_INFO("fpicker.aqua","Return value: " << sString);
+ aAny <<= sString;
+ }
+ }
+ break;
+ case ControlActions::GET_SELECTED_ITEM_INDEX:
+ {
+ SAL_INFO("fpicker.aqua","GET_SELECTED_ITEM_INDEX");
+ sal_Int32 nActive = [pButton indexOfSelectedItem];
+ SAL_INFO("fpicker.aqua","Return value: " << nActive);
+ aAny <<= nActive;
+ }
+ break;
+ default:
+ SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
+ break;
+ }
+
+ return aAny;
+}
+
+NSTextField* createLabelWithString(NSString* labelString)
+{
+ NSTextField *textField = [NSTextField new];
+ [textField setEditable:NO];
+ [textField setSelectable:NO];
+ [textField setDrawsBackground:NO];
+ [textField setBordered:NO];
+ [[textField cell] setTitle:labelString];
+
+ return textField;
+}
+
+}
+
+#pragma mark Constructor / Destructor
+
+// Constructor / Destructor
+
+ControlHelper::ControlHelper()
+: m_pUserPane(nullptr)
+, m_pFilterControl(nil)
+, m_bUserPaneNeeded( false )
+, m_bIsUserPaneLaidOut(false)
+, m_bIsFilterControlNeeded(false)
+, m_pFilterHelper(nullptr)
+{
+ int i;
+
+ for( i = 0; i < TOGGLE_LAST; i++ ) {
+ m_bToggleVisibility[i] = false;
+ }
+
+ for( i = 0; i < LIST_LAST; i++ ) {
+ m_bListVisibility[i] = false;
+ }
+}
+
+ControlHelper::~ControlHelper()
+{
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ if (nullptr != m_pUserPane) {
+ [m_pUserPane release];
+ }
+
+ if (m_pFilterControl != nullptr) {
+ [m_pFilterControl setTarget:nil];
+ }
+
+ for (auto const& activeControl : m_aActiveControls)
+ {
+ NSString* sLabelName = m_aMapListLabels[activeControl];
+ if (sLabelName != nil) {
+ [sLabelName release];
+ }
+ if ([activeControl class] == [NSPopUpButton class]) {
+ NSTextField* pField = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)];
+ if (pField != nil) {
+ [pField release];
+ }
+ }
+ [activeControl release];
+ }
+
+ [pool release];
+}
+
+#pragma mark XInitialization delegate
+
+// XInitialization delegate
+
+void ControlHelper::initialize( sal_Int16 nTemplateId )
+{
+ switch( nTemplateId )
+ {
+ case FILESAVE_AUTOEXTENSION_PASSWORD:
+ m_bToggleVisibility[AUTOEXTENSION] = true;
+ m_bToggleVisibility[PASSWORD] = true;
+ break;
+ case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
+ m_bToggleVisibility[AUTOEXTENSION] = true;
+ m_bToggleVisibility[PASSWORD] = true;
+ m_bToggleVisibility[FILTEROPTIONS] = true;
+ break;
+ case FILESAVE_AUTOEXTENSION_SELECTION:
+ m_bToggleVisibility[AUTOEXTENSION] = true;
+ m_bToggleVisibility[SELECTION] = true;
+ break;
+ case FILESAVE_AUTOEXTENSION_TEMPLATE:
+ m_bToggleVisibility[AUTOEXTENSION] = true;
+ m_bListVisibility[TEMPLATE] = true;
+ break;
+ case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
+ m_bToggleVisibility[LINK] = true;
+ m_bToggleVisibility[PREVIEW] = true;
+ m_bListVisibility[IMAGE_TEMPLATE] = true;
+ break;
+ case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
+ m_bToggleVisibility[LINK] = true;
+ m_bToggleVisibility[PREVIEW] = true;
+ m_bListVisibility[IMAGE_ANCHOR] = true;
+ break;
+ case FILEOPEN_READONLY_VERSION:
+ m_bToggleVisibility[READONLY] = true;
+ m_bListVisibility[VERSION] = true;
+ break;
+ case FILEOPEN_LINK_PREVIEW:
+ m_bToggleVisibility[LINK] = true;
+ m_bToggleVisibility[PREVIEW] = true;
+ break;
+ case FILESAVE_AUTOEXTENSION:
+ m_bToggleVisibility[AUTOEXTENSION] = true;
+ break;
+ case FILEOPEN_PREVIEW:
+ m_bToggleVisibility[PREVIEW] = true;
+ break;
+ case FILEOPEN_LINK_PLAY:
+ m_bToggleVisibility[LINK] = true;
+ }
+
+ createControls();
+}
+
+#pragma mark XFilePickerControlAccess delegates
+
+// XFilePickerControlAccess functions
+
+
+void ControlHelper::enableControl( const sal_Int16 nControlId, const bool bEnable ) const
+{
+ SolarMutexGuard aGuard;
+
+ if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
+ SAL_INFO("fpicker.aqua"," preview checkbox cannot be changed");
+ return;
+ }
+
+ NSControl* pControl = getControl(nControlId);
+
+ if( pControl != nil ) {
+ if( bEnable ) {
+ SAL_INFO("fpicker.aqua", "enable" );
+ } else {
+ SAL_INFO("fpicker.aqua", "disable" );
+ }
+ [pControl setEnabled:bEnable];
+ } else {
+ SAL_INFO("fpicker.aqua","enable unknown control " << nControlId );
+ }
+}
+
+OUString ControlHelper::getLabel( sal_Int16 nControlId )
+{
+ SolarMutexGuard aGuard;
+
+ NSControl* pControl = getControl( nControlId );
+
+ if( pControl == nil ) {
+ SAL_INFO("fpicker.aqua","Get label for unknown control " << nControlId);
+ return OUString();
+ }
+
+ OUString retVal;
+ if ([pControl class] == [NSPopUpButton class]) {
+ NSString *temp = m_aMapListLabels[pControl];
+ if (temp != nil)
+ retVal = [temp OUString];
+ }
+ else {
+ NSString* sLabel = [[pControl cell] title];
+ retVal = [sLabel OUString];
+ }
+
+ return retVal;
+}
+
+void ControlHelper::setLabel( sal_Int16 nControlId, NSString* aLabel )
+{
+ SolarMutexGuard aGuard;
+
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ NSControl* pControl = getControl(nControlId);
+
+ if (nil != pControl) {
+ if ([pControl class] == [NSPopUpButton class]) {
+ NSString *sOldName = m_aMapListLabels[pControl];
+ if (sOldName != nullptr && sOldName != aLabel) {
+ [sOldName release];
+ }
+
+ m_aMapListLabels[pControl] = [aLabel retain];
+ } else if ([pControl class] == [NSButton class]) {
+ [[pControl cell] setTitle:aLabel];
+ }
+ } else {
+ SAL_INFO("fpicker.aqua","Control not found to set label for");
+ }
+
+ layoutControls();
+
+ [pool release];
+}
+
+void ControlHelper::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
+{
+ SolarMutexGuard aGuard;
+
+ if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_PREVIEW) {
+ SAL_INFO("fpicker.aqua"," value for preview is unchangeable");
+ }
+ else {
+ NSControl* pControl = getControl( nControlId );
+
+ if( pControl == nil ) {
+ SAL_INFO("fpicker.aqua","enable unknown control " << nControlId);
+ } else {
+ if( [pControl class] == [NSPopUpButton class] ) {
+ HandleSetListValue(pControl, nControlAction, rValue);
+ } else if( [pControl class] == [NSButton class] ) {
+ bool bChecked = false;
+ rValue >>= bChecked;
+ SAL_INFO("fpicker.aqua"," value is a bool: " << bChecked);
+ [static_cast<NSButton*>(pControl) setState:(bChecked ? NSControlStateValueOn : NSControlStateValueOff)];
+ } else
+ {
+ SAL_INFO("fpicker.aqua","Can't set value on button / list " << nControlId << " " << nControlAction);
+ }
+ }
+ }
+}
+
+uno::Any ControlHelper::getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRetval;
+
+ NSControl* pControl = getControl( nControlId );
+
+ if( pControl == nil ) {
+ SAL_INFO("fpicker.aqua","get value for unknown control " << nControlId);
+ } else {
+ if( [pControl class] == [NSPopUpButton class] ) {
+ aRetval = HandleGetListValue(pControl, nControlAction);
+ } else if( [pControl class] == [NSButton class] ) {
+ //NSLog(@"control: %@", [[pControl cell] title]);
+ bool bValue = [static_cast<NSButton*>(pControl) state] == NSControlStateValueOn;
+ aRetval <<= bValue;
+ SAL_INFO("fpicker.aqua","value is a bool (checkbox): " << bValue);
+ }
+ }
+
+ return aRetval;
+}
+
+void ControlHelper::createUserPane()
+{
+ if (!m_bUserPaneNeeded) {
+ SAL_INFO("fpicker.aqua","no user pane needed");
+ return;
+ }
+
+ if (nil != m_pUserPane) {
+ SAL_INFO("fpicker.aqua","user pane already exists");
+ return;
+ }
+
+ if (m_bIsFilterControlNeeded && m_pFilterControl == nil) {
+ createFilterControl();
+ }
+
+ NSRect minRect = NSMakeRect(0,0,300,33);
+ m_pUserPane = [[NSView alloc] initWithFrame:minRect];
+
+ int currentHeight = kAquaSpaceBoxFrameViewDiffTop + kAquaSpaceBoxFrameViewDiffBottom;
+ int currentWidth = 300;
+
+ bool bPopupControlPresent = false;
+ bool bButtonControlPresent = false;
+
+ int nCheckboxMaxWidth = 0;
+ int nPopupMaxWidth = 0;
+ int nPopupLabelMaxWidth = 0;
+
+ size_t nLoop = 0;
+ for (auto const& activeControl : m_aActiveControls)
+ {
+ SAL_INFO("fpicker.aqua","currentHeight: " << currentHeight);
+
+ //let the control calculate its size
+ [activeControl sizeToFit];
+
+ NSRect frame = [activeControl frame];
+ SAL_INFO("fpicker.aqua","frame for control " << [[activeControl description] UTF8String] << " is {" << frame.origin.x << ", " << frame.origin.y << ", " << frame.size.width << ", " << frame.size.height << "}");
+
+ int nControlHeight = frame.size.height;
+ int nControlWidth = frame.size.width;
+
+ // Note: controls are grouped by kind, first all popup menus, then checkboxes
+ if ([activeControl class] == [NSPopUpButton class]) {
+ if (bPopupControlPresent) {
+ //this is not the first popup
+ currentHeight += kAquaSpaceBetweenPopupMenus;
+ }
+ else if (nLoop)
+ {
+ currentHeight += kAquaSpaceBetweenControls;
+ }
+
+ bPopupControlPresent = true;
+
+ // we have to add the label text width
+ NSString *label = m_aMapListLabels[activeControl];
+
+ NSTextField *textField = createLabelWithString(label);
+ [textField sizeToFit];
+ m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)] = textField;
+ [m_pUserPane addSubview:textField];
+
+ NSRect tfRect = [textField frame];
+ SAL_INFO("fpicker.aqua","frame for textfield " << [[textField description] UTF8String] << " is {" << tfRect.origin.x << ", " << tfRect.origin.y << ", " << tfRect.size.width << ", " << tfRect.size.height << "}");
+
+ int tfWidth = tfRect.size.width;
+
+ if (nPopupLabelMaxWidth < tfWidth) {
+ nPopupLabelMaxWidth = tfWidth;
+ }
+
+ frame.origin.x += (kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft) + tfWidth;
+
+ if (nControlWidth < POPUP_WIDTH_MIN) {
+ nControlWidth = POPUP_WIDTH_MIN;
+ frame.size.width = nControlWidth;
+ [activeControl setFrame:frame];
+ }
+
+ if (nControlWidth > POPUP_WIDTH_MAX) {
+ nControlWidth = POPUP_WIDTH_MAX;
+ frame.size.width = nControlWidth;
+ [activeControl setFrame:frame];
+ }
+
+ //set the max size
+ if (nPopupMaxWidth < nControlWidth) {
+ nPopupMaxWidth = nControlWidth;
+ }
+
+ nControlWidth += tfWidth + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
+ if (nControlHeight < kAquaPopupButtonDefaultHeight) {
+ //maybe the popup has no menu item yet, so set a default height
+ nControlHeight = kAquaPopupButtonDefaultHeight;
+ }
+
+ nControlHeight -= kAquaSpacePopupMenuFrameBoundsDiffV;
+ }
+ else if ([activeControl class] == [NSButton class]) {
+ if (nLoop)
+ {
+ currentHeight += kAquaSpaceBetweenControls;
+ }
+
+ if (nCheckboxMaxWidth < nControlWidth) {
+ nCheckboxMaxWidth = nControlWidth;
+ }
+
+ bButtonControlPresent = true;
+ nControlWidth -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
+ nControlHeight -= 2 * kAquaSpaceSwitchButtonFrameBoundsDiff;
+ }
+
+ // if ((nControlWidth + 2 * kAquaSpaceInsideGroupH) > currentWidth) {
+ // currentWidth = nControlWidth + 2 * kAquaSpaceInsideGroupH;
+ // }
+
+ currentHeight += nControlHeight;
+
+ [m_pUserPane addSubview:activeControl];
+ ++nLoop;
+ }
+
+ SAL_INFO("fpicker.aqua","height after adding all controls: " << currentHeight);
+
+ if (bPopupControlPresent && bButtonControlPresent)
+ {
+ //after a popup button (array) and before a different kind of control we need some extra space instead of the standard
+ currentHeight -= kAquaSpaceBetweenControls;
+ currentHeight += kAquaSpaceAfterPopupButtonsV;
+ SAL_INFO("fpicker.aqua","popup extra space added, currentHeight: " << currentHeight);
+ }
+
+ int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
+
+ currentWidth = nLongestPopupWidth > nCheckboxMaxWidth ? nLongestPopupWidth : nCheckboxMaxWidth;
+ SAL_INFO("fpicker.aqua","longest control width: " << currentWidth);
+
+ currentWidth += 2* kAquaSpaceInsideGroupH;
+
+ if (currentWidth < minRect.size.width)
+ currentWidth = minRect.size.width;
+
+ if (currentHeight < minRect.size.height)
+ currentHeight = minRect.size.height;
+
+ NSRect upRect = NSMakeRect(0, 0, currentWidth, currentHeight );
+ SAL_INFO("fpicker.aqua","setting user pane rect to {" << upRect.origin.x << ", " << upRect.origin.y << ", " << upRect.size.width << ", " << upRect.size.height << "}");
+
+ [m_pUserPane setFrame:upRect];
+
+ layoutControls();
+}
+
+#pragma mark Private / Misc
+
+// Private / Misc
+
+void ControlHelper::createControls()
+{
+ for (int i = 0; i < LIST_LAST; i++) {
+ if (m_bListVisibility[i]) {
+ m_bUserPaneNeeded = true;
+
+ int elementName = getControlElementName([NSPopUpButton class], i);
+ NSString* sLabel = CResourceProvider::getResString(elementName);
+
+ m_pListControls[i] = [NSPopUpButton new];
+
+#define MAP_LIST_( elem ) \
+ case elem: \
+ setLabel(ExtendedFilePickerElementIds::LISTBOX_##elem, sLabel); \
+ break
+
+ switch(i) {
+ MAP_LIST_(VERSION);
+ MAP_LIST_(TEMPLATE);
+ MAP_LIST_(IMAGE_TEMPLATE);
+ MAP_LIST_(IMAGE_ANCHOR);
+ }
+
+ m_aActiveControls.push_back(m_pListControls[i]);
+ } else {
+ m_pListControls[i] = nil;
+ }
+ }
+
+ for (int i = 0/*#i102102*/; i < TOGGLE_LAST; i++) {
+ if (m_bToggleVisibility[i]) {
+ m_bUserPaneNeeded = true;
+
+ int elementName = getControlElementName([NSButton class], i);
+ NSString* sLabel = CResourceProvider::getResString(elementName);
+
+ NSButton *button = [NSButton new];
+ [button setTitle:sLabel];
+
+ [button setButtonType:NSButtonTypeSwitch];
+
+ [button setState:NSControlStateValueOff];
+
+ if (i == AUTOEXTENSION) {
+ [button setTarget:m_pDelegate];
+ [button setAction:@selector(autoextensionChanged:)];
+ }
+
+ m_pToggles[i] = button;
+
+ m_aActiveControls.push_back(m_pToggles[i]);
+ } else {
+ m_pToggles[i] = nil;
+ }
+ }
+
+ //preview is always on with macOS
+ NSControl *pPreviewBox = m_pToggles[PREVIEW];
+ if (pPreviewBox != nil) {
+ [pPreviewBox setEnabled:NO];
+ [static_cast<NSButton*>(pPreviewBox) setState:NSControlStateValueOn];
+ }
+}
+
+#define TOGGLE_ELEMENT( elem ) \
+case elem: \
+ nReturn = CHECKBOX_##elem; \
+ return nReturn
+#define LIST_ELEMENT( elem ) \
+case elem: \
+ nReturn = LISTBOX_##elem##_LABEL; \
+ return nReturn
+
+int ControlHelper::getControlElementName(const Class aClazz, const int nControlId)
+{
+ int nReturn = -1;
+ if (aClazz == [NSButton class])
+ {
+ switch (nControlId) {
+ TOGGLE_ELEMENT( AUTOEXTENSION );
+ TOGGLE_ELEMENT( PASSWORD );
+ TOGGLE_ELEMENT( FILTEROPTIONS );
+ TOGGLE_ELEMENT( READONLY );
+ TOGGLE_ELEMENT( LINK );
+ TOGGLE_ELEMENT( PREVIEW );
+ TOGGLE_ELEMENT( SELECTION );
+ }
+ }
+ else if (aClazz == [NSPopUpButton class])
+ {
+ switch (nControlId) {
+ LIST_ELEMENT( VERSION );
+ LIST_ELEMENT( TEMPLATE );
+ LIST_ELEMENT( IMAGE_TEMPLATE );
+ LIST_ELEMENT( IMAGE_ANCHOR );
+ }
+ }
+
+ return nReturn;
+}
+
+void ControlHelper::HandleSetListValue(const NSControl* pControl, const sal_Int16 nControlAction, const uno::Any& rValue)
+{
+ if ([pControl class] != [NSPopUpButton class]) {
+ SAL_INFO("fpicker.aqua","not a popup menu");
+ return;
+ }
+
+ NSPopUpButton *pButton = static_cast<NSPopUpButton*>(pControl);
+ NSMenu *rMenu = [pButton menu];
+ if (nil == rMenu) {
+ SAL_INFO("fpicker.aqua","button has no menu");
+ return;
+ }
+
+ switch (nControlAction)
+ {
+ case ControlActions::ADD_ITEM:
+ {
+ SAL_INFO("fpicker.aqua","ADD_ITEMS");
+ OUString sItem;
+ rValue >>= sItem;
+
+ NSString* sCFItem = [NSString stringWithOUString:sItem];
+ SAL_INFO("fpicker.aqua","Adding menu item: " << sItem);
+ [pButton addItemWithTitle:sCFItem];
+ }
+ break;
+ case ControlActions::ADD_ITEMS:
+ {
+ SAL_INFO("fpicker.aqua","ADD_ITEMS");
+ uno::Sequence< OUString > aStringList;
+ rValue >>= aStringList;
+ sal_Int32 nItemCount = aStringList.getLength();
+ for (sal_Int32 i = 0; i < nItemCount; ++i)
+ {
+ NSString* sCFItem = [NSString stringWithOUString:aStringList[i]];
+ SAL_INFO("fpicker.aqua","Adding menu item: " << aStringList[i]);
+ [pButton addItemWithTitle:sCFItem];
+ }
+ }
+ break;
+ case ControlActions::DELETE_ITEM:
+ {
+ SAL_INFO("fpicker.aqua","DELETE_ITEM");
+ sal_Int32 nPos = -1;
+ rValue >>= nPos;
+ SAL_INFO("fpicker.aqua","Deleting item at position " << (nPos));
+ [rMenu removeItemAtIndex:nPos];
+ }
+ break;
+ case ControlActions::DELETE_ITEMS:
+ {
+ SAL_INFO("fpicker.aqua","DELETE_ITEMS");
+ int nItems = [rMenu numberOfItems];
+ if (nItems == 0) {
+ SAL_INFO("fpicker.aqua","no menu items to delete");
+ return;
+ }
+ for(sal_Int32 i = 0; i < nItems; i++) {
+ [rMenu removeItemAtIndex:i];
+ }
+ }
+ break;
+ case ControlActions::SET_SELECT_ITEM:
+ {
+ sal_Int32 nPos = -1;
+ rValue >>= nPos;
+ SAL_INFO("fpicker.aqua","Selecting item at position " << nPos);
+ [pButton selectItemAtIndex:nPos];
+ }
+ break;
+ default:
+ SAL_INFO("fpicker.aqua","undocumented/unimplemented ControlAction for a list");
+ break;
+ }
+
+ layoutControls();
+}
+
+// cf. offapi/com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.idl
+NSControl* ControlHelper::getControl( const sal_Int16 nControlId ) const
+{
+ NSControl* pWidget = nil;
+
+#define MAP_TOGGLE( elem ) \
+case ExtendedFilePickerElementIds::CHECKBOX_##elem: \
+ pWidget = m_pToggles[elem]; \
+ break
+
+#define MAP_LIST( elem ) \
+case ExtendedFilePickerElementIds::LISTBOX_##elem: \
+ pWidget = m_pListControls[elem]; \
+ break
+
+#define MAP_LIST_LABEL( elem ) \
+case ExtendedFilePickerElementIds::LISTBOX_##elem##_LABEL: \
+ pWidget = m_pListControls[elem]; \
+ break
+
+ switch( nControlId )
+ {
+ MAP_TOGGLE( AUTOEXTENSION );
+ MAP_TOGGLE( PASSWORD );
+ MAP_TOGGLE( FILTEROPTIONS );
+ MAP_TOGGLE( READONLY );
+ MAP_TOGGLE( LINK );
+ MAP_TOGGLE( PREVIEW );
+ MAP_TOGGLE( SELECTION );
+ //MAP_BUTTON( PLAY );
+ MAP_LIST( VERSION );
+ MAP_LIST( TEMPLATE );
+ MAP_LIST( IMAGE_TEMPLATE );
+ MAP_LIST( IMAGE_ANCHOR );
+ MAP_LIST_LABEL( VERSION );
+ MAP_LIST_LABEL( TEMPLATE );
+ MAP_LIST_LABEL( IMAGE_TEMPLATE );
+ MAP_LIST_LABEL( IMAGE_ANCHOR );
+ default:
+ SAL_INFO("fpicker.aqua","Handle unknown control " << nControlId);
+ break;
+ }
+#undef MAP
+
+ return pWidget;
+}
+
+void ControlHelper::layoutControls()
+{
+ SolarMutexGuard aGuard;
+
+ if (nil == m_pUserPane) {
+ SAL_INFO("fpicker.aqua","no user pane to layout");
+ return;
+ }
+
+ if (m_bIsUserPaneLaidOut) {
+ SAL_INFO("fpicker.aqua","user pane already laid out");
+ return;
+ }
+
+ NSRect userPaneRect = [m_pUserPane frame];
+ SAL_INFO("fpicker.aqua","userPane frame: {" << userPaneRect.origin.x << ", " << userPaneRect.origin.y << ", " << userPaneRect.size.width << ", " << userPaneRect.size.height << "}");
+
+ int nUsableWidth = userPaneRect.size.width;
+
+ //NOTE: NSView's coordinate system starts in the lower left hand corner but we start adding controls from the top,
+ // so we subtract from the vertical position as we make our way down the pane.
+ int currenttop = userPaneRect.size.height;
+ int nCheckboxMaxWidth = 0;
+ int nPopupMaxWidth = 0;
+ int nPopupLabelMaxWidth = 0;
+
+ //first loop to determine max sizes
+ for (auto const& activeControl : m_aActiveControls)
+ {
+
+ NSRect controlRect = [activeControl frame];
+ int nControlWidth = controlRect.size.width;
+
+ Class aSubType = [activeControl class];
+ if (aSubType == [NSPopUpButton class]) {
+ if (nPopupMaxWidth < nControlWidth) {
+ nPopupMaxWidth = nControlWidth;
+ }
+ NSTextField *label = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)];
+ NSRect labelFrame = [label frame];
+ int nLabelWidth = labelFrame.size.width;
+ if (nPopupLabelMaxWidth < nLabelWidth) {
+ nPopupLabelMaxWidth = nLabelWidth;
+ }
+ } else {
+ if (nCheckboxMaxWidth < nControlWidth) {
+ nCheckboxMaxWidth = nControlWidth;
+ }
+ }
+ }
+
+ int nLongestPopupWidth = nPopupMaxWidth + nPopupLabelMaxWidth + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
+ SAL_INFO("fpicker.aqua","longest popup width: " << nLongestPopupWidth);
+
+ NSControl* previousControl = nil;
+
+ int nDistBetweenControls = 0;
+
+ for (auto const& activeControl : m_aActiveControls)
+ {
+ //get the control's bounds
+ NSRect controlRect = [activeControl frame];
+ int nControlHeight = controlRect.size.height;
+
+ //subtract the height from the current vertical position, because the control's bounds origin rect will be its lower left hand corner
+ currenttop -= nControlHeight;
+
+ Class aSubType = [activeControl class];
+
+ //add space between the previous control and this control according to Apple's HIG
+ nDistBetweenControls = getVerticalDistance(previousControl, activeControl);
+ SAL_INFO("fpicker.aqua","vertical distance: " << nDistBetweenControls);
+ currenttop -= nDistBetweenControls;
+
+ previousControl = activeControl;
+
+ if (aSubType == [NSPopUpButton class]) {
+ //move vertically up some pixels to space the controls between their real (visual) bounds
+ currenttop += kAquaSpacePopupMenuFrameBoundsDiffTop;//from top
+
+ //get the corresponding popup label
+ NSTextField *label = m_aMapListLabelFields[static_cast<NSPopUpButton*>(activeControl)];
+ NSRect labelFrame = [label frame];
+ int totalWidth = nPopupMaxWidth + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpacePopupMenuFrameBoundsDiffLeft - kAquaSpaceLabelFrameBoundsDiffH;
+ SAL_INFO("fpicker.aqua","totalWidth: " << totalWidth);
+ //let's center popups
+ int left = (nUsableWidth + nLongestPopupWidth) / 2 - totalWidth;
+ SAL_INFO("fpicker.aqua","left: " << left);
+ labelFrame.origin.x = left;
+ labelFrame.origin.y = currenttop + kAquaSpaceLabelPopupDiffV;
+ SAL_INFO("fpicker.aqua","setting label at: {" << labelFrame.origin.x << ", " << labelFrame.origin.y << ", " << labelFrame.size.width << ", " << labelFrame.size.height << "}");
+ [label setFrame:labelFrame];
+
+ controlRect.origin.x = left + labelFrame.size.width + kAquaSpaceBetweenControls - kAquaSpaceLabelFrameBoundsDiffH - kAquaSpacePopupMenuFrameBoundsDiffLeft;
+ controlRect.origin.y = currenttop;
+ controlRect.size.width = nPopupMaxWidth;
+ SAL_INFO("fpicker.aqua","setting popup at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}");
+ [activeControl setFrame:controlRect];
+
+ //add some space to place the vertical position right below the popup's visual bounds
+ currenttop += kAquaSpacePopupMenuFrameBoundsDiffBottom;
+ } else {
+ currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;//from top
+
+ int left = (nUsableWidth - nCheckboxMaxWidth) / 2;
+ controlRect.origin.x = left;
+ controlRect.origin.y = currenttop;
+ controlRect.size.width = nPopupMaxWidth;
+ [activeControl setFrame:controlRect];
+ SAL_INFO("fpicker.aqua","setting checkbox at: {" << controlRect.origin.x << ", " << controlRect.origin.y << ", " << controlRect.size.width << ", " << controlRect.size.height << "}");
+
+ currenttop += kAquaSpaceSwitchButtonFrameBoundsDiff;
+ }
+ }
+
+ m_bIsUserPaneLaidOut = true;
+}
+
+void ControlHelper::createFilterControl()
+{
+ NSString* sLabel = CResourceProvider::getResString(CommonFilePickerElementIds::LISTBOX_FILTER_LABEL);
+
+ m_pFilterControl = [NSPopUpButton new];
+
+ [m_pFilterControl setAction:@selector(filterSelectedAtIndex:)];
+ [m_pFilterControl setTarget:m_pDelegate];
+
+ NSMenu *menu = [m_pFilterControl menu];
+
+ for (auto const& filterName : *m_pFilterHelper->getFilterNames())
+ {
+ SAL_INFO("fpicker.aqua","adding filter name: " << [filterName UTF8String]);
+ if ([filterName isEqualToString:@"-"]) {
+ [menu addItem:[NSMenuItem separatorItem]];
+ }
+ else {
+ [m_pFilterControl addItemWithTitle:filterName];
+ }
+ }
+
+ // always add the filter as first item
+ m_aActiveControls.push_front(m_pFilterControl);
+ m_aMapListLabels[m_pFilterControl] = [sLabel retain];
+}
+
+int ControlHelper::getVerticalDistance(const NSControl* first, const NSControl* second)
+{
+ if (first == nil) {
+ return kAquaSpaceBoxFrameViewDiffTop;
+ }
+ else if (second == nil) {
+ return kAquaSpaceBoxFrameViewDiffBottom;
+ }
+ else {
+ Class firstClass = [first class];
+ Class secondClass = [second class];
+
+ if (firstClass == [NSPopUpButton class]) {
+ if (secondClass == [NSPopUpButton class]) {
+ return kAquaSpaceBetweenPopupMenus;
+ }
+ else {
+ return kAquaSpaceAfterPopupButtonsV;
+ }
+ }
+
+ return kAquaSpaceBetweenControls;
+ }
+}
+
+void ControlHelper::updateFilterUI()
+{
+ if (!m_bIsFilterControlNeeded || m_pFilterHelper == nullptr) {
+ SAL_INFO("fpicker.aqua","no filter control needed or no filter helper present");
+ return;
+ }
+
+ int index = m_pFilterHelper->getCurrentFilterIndex();
+
+ if (m_pFilterControl == nil) {
+ createFilterControl();
+ }
+
+ [m_pFilterControl selectItemAtIndex:index];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/FilterHelper.hxx b/fpicker/source/aqua/FilterHelper.hxx
new file mode 100644
index 000000000..21cb9c4b4
--- /dev/null
+++ b/fpicker/source/aqua/FilterHelper.hxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <com/sun/star/uno/RuntimeException.hpp>
+
+#include <list>
+#include <string_view>
+#include <vector>
+
+#include <premac.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+typedef css::beans::StringPair UnoFilterEntry;
+typedef css::uno::Sequence< UnoFilterEntry > UnoFilterList; // can be transported more effectively
+typedef ::std::list<NSString *> NSStringList;
+typedef ::std::list<OUString> OUStringList;
+
+struct FilterEntry
+{
+protected:
+ OUString m_sTitle;
+ OUStringList m_sFilterSuffixList;
+ UnoFilterList m_aSubFilters;
+
+public:
+ FilterEntry( const OUString& _rTitle, const OUStringList _rFilter )
+ : m_sTitle( _rTitle )
+ , m_sFilterSuffixList( _rFilter )
+ {
+ }
+
+ FilterEntry( const OUString& _rTitle, const UnoFilterList& _rSubFilters );
+
+ OUString const & getTitle() const { return m_sTitle; }
+ OUStringList const & getFilterSuffixList() const { return m_sFilterSuffixList; }
+
+ /// determines if the filter has sub filter (i.e., the filter is a filter group in real)
+ bool hasSubFilters( ) const;
+
+ /** retrieves the filters belonging to the entry
+ @return
+ the number of sub filters
+ */
+ sal_Int32 getSubFilters( UnoFilterList& _rSubFilterList );
+
+ // helpers for iterating the sub filters
+ const UnoFilterEntry* beginSubFilters() const { return m_aSubFilters.getConstArray(); }
+ const UnoFilterEntry* endSubFilters() const { return m_aSubFilters.getConstArray() + m_aSubFilters.getLength(); }
+};
+
+typedef ::std::vector < FilterEntry > FilterList;
+
+class FilterHelper {
+
+public:
+ FilterHelper();
+ virtual ~FilterHelper();
+
+ //XFilterManager delegates
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void appendFilter( const OUString& aTitle, std::u16string_view aFilter );
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void setCurrentFilter( const OUString& aTitle );
+
+ /// @throws css::uno::RuntimeException
+ OUString getCurrentFilter( );
+
+ //XFilterGroupManager delegates
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void appendFilterGroup( const css::uno::Sequence< css::beans::StringPair >& aFilters );
+
+
+ //accessor
+ FilterList* getFilterList();
+ NSStringList* getFilterNames();
+
+ //misc
+ void SetCurFilter( const OUString& rFilter );
+ void SetFilterAtIndex(unsigned index);
+ OUStringList getCurrentFilterSuffixList();
+ int getCurrentFilterIndex();
+ void SetFilters();
+ bool filenameMatchesFilter(NSString * sFilename);
+
+private:
+ FilterList *m_pFilterList;
+ OUString m_aCurrentFilter;
+ NSStringList *m_pFilterNames;
+
+ bool FilterNameExists( const OUString& rTitle );
+ bool FilterNameExists( const UnoFilterList& _rGroupedFilters );
+
+ void ensureFilterList( const OUString& _rInitialCurrentFilter );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/FilterHelper.mm b/fpicker/source/aqua/FilterHelper.mm
new file mode 100644
index 000000000..f11d8447d
--- /dev/null
+++ b/fpicker/source/aqua/FilterHelper.mm
@@ -0,0 +1,443 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <cstddef>
+#include <string_view>
+#include <o3tl/string_view.hxx>
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+
+#include "NSString_OOoAdditions.hxx"
+#include "NSURL_OOoAdditions.hxx"
+
+#include "FilterHelper.hxx"
+
+namespace {
+
+void fillSuffixList(OUStringList& aSuffixList, std::u16string_view suffixString) {
+ std::size_t nIndex = 0;
+ do {
+ std::u16string_view aToken = o3tl::getToken( suffixString, u';', nIndex );
+ aSuffixList.push_back(OUString(aToken.substr(1)));
+ } while ( nIndex != std::u16string_view::npos );
+}
+
+}
+
+#pragma mark DEFINES
+
+#pragma mark FilterEntry
+
+FilterEntry::FilterEntry( const OUString& _rTitle, const UnoFilterList& _rSubFilters )
+:m_sTitle( _rTitle )
+,m_aSubFilters( _rSubFilters )
+{
+}
+
+
+bool FilterEntry::hasSubFilters() const
+{
+ bool bReturn = ( 0 < m_aSubFilters.getLength() );
+
+ return bReturn;
+}
+
+
+sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
+{
+ _rSubFilterList = m_aSubFilters;
+ sal_Int32 nReturn = m_aSubFilters.getLength();
+
+ return nReturn;
+}
+
+#pragma mark statics
+static bool
+isFilterString( std::u16string_view rFilterString, std::u16string_view pMatch )
+{
+ std::size_t nIndex = 0;
+ std::u16string_view aToken;
+ bool bIsFilter = true;
+
+ do
+ {
+ aToken = o3tl::getToken( rFilterString, u';', nIndex );
+ if( !o3tl::starts_with( aToken, pMatch ) )
+ {
+ bIsFilter = false;
+ break;
+ }
+ }
+ while( nIndex != std::u16string_view::npos );
+
+ return bIsFilter;
+}
+
+
+
+static OUString
+shrinkFilterName( const OUString& aFilterName, bool bAllowNoStar = false )
+{
+ sal_Int32 nBracketEnd = -1;
+ OUString aRealName(aFilterName);
+
+ for( sal_Int32 i = aRealName.getLength() - 1; i > 0; i-- )
+ {
+ if( aFilterName[i] == ')' )
+ nBracketEnd = i;
+ else if( aFilterName[i] == '(' )
+ {
+ sal_Int32 nBracketLen = nBracketEnd - i;
+ if( nBracketEnd <= 0 )
+ continue;
+ if( isFilterString( aFilterName.subView( i + 1, nBracketLen - 1 ), u"*." ) )
+ aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
+ else if (bAllowNoStar)
+ {
+ if( isFilterString( aFilterName.subView( i + 1, nBracketLen - 1 ), u".") )
+ aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
+ }
+ }
+ }
+
+ return aRealName;
+}
+
+
+namespace {
+
+ struct FilterTitleMatch
+ {
+protected:
+ const OUString rTitle;
+
+public:
+ FilterTitleMatch( const OUString& _rTitle ) : rTitle( _rTitle ) { }
+
+
+ bool operator () ( const FilterEntry& _rEntry )
+ {
+ bool bMatch;
+ if( !_rEntry.hasSubFilters() ) {
+ //first try the complete filter name
+ OUString title = _rEntry.getTitle();
+ bMatch = title.equals(rTitle);
+ if (!bMatch) {
+ //we didn't find a match using the full name, let's give it another
+ //try using the shrunk version
+ OUString aShrunkName = shrinkFilterName( _rEntry.getTitle() ).trim();
+ bMatch = aShrunkName.equals(rTitle);
+ }
+ }
+ else
+ // a filter group -> search the sub filters
+ bMatch =
+ ::std::any_of(_rEntry.beginSubFilters(),
+ _rEntry.endSubFilters(),
+ *this);
+
+ return bMatch;
+ }
+
+ bool operator () ( const UnoFilterEntry& _rEntry )
+ {
+ OUString aShrunkName = shrinkFilterName( _rEntry.First );
+ bool retVal = aShrunkName.equals(rTitle);
+ return retVal;
+ }
+ };
+}
+
+FilterHelper::FilterHelper()
+: m_pFilterList(nullptr)
+, m_pFilterNames(nullptr)
+{
+}
+
+FilterHelper::~FilterHelper()
+{
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ if (nullptr != m_pFilterList) {
+ delete m_pFilterList;
+ }
+
+ if (nullptr != m_pFilterNames) {
+ //we called retain when we added the strings to the list, so we should release them now
+ for (NSStringList::iterator iter = m_pFilterNames->begin(); iter != m_pFilterNames->end(); ++iter) {
+ [*iter release];
+ }
+ delete m_pFilterNames;
+ }
+
+ [pool release];
+}
+
+
+bool FilterHelper::FilterNameExists( const OUString& rTitle )
+{
+ bool bRet = false;
+
+ if( m_pFilterList )
+ bRet =
+ ::std::any_of(m_pFilterList->begin(),
+ m_pFilterList->end(),
+ FilterTitleMatch( rTitle ));
+
+ return bRet;
+}
+
+
+bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
+{
+ bool bRet = false;
+
+ if( m_pFilterList )
+ {
+ const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
+ const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
+ for( ; pStart != pEnd; ++pStart )
+ if( ::std::any_of(m_pFilterList->begin(),
+ m_pFilterList->end(),
+ FilterTitleMatch( pStart->First ) ) )
+ break;
+
+ bRet = (pStart != pEnd);
+ }
+
+ return bRet;
+}
+
+
+void FilterHelper::ensureFilterList( const OUString& _rInitialCurrentFilter )
+{
+ if( nullptr == m_pFilterList )
+ {
+ m_pFilterList = new FilterList;
+
+ // set the first filter to the current filter
+ m_aCurrentFilter = _rInitialCurrentFilter;
+ }
+}
+
+void FilterHelper::SetCurFilter( const OUString& rFilter )
+{
+ SolarMutexGuard aGuard;
+
+ if(!m_aCurrentFilter.equals(rFilter))
+ {
+ m_aCurrentFilter = rFilter;
+ }
+
+}
+
+void FilterHelper::SetFilters()
+{
+ // set the default filter
+ if( m_aCurrentFilter.getLength() > 0 )
+ {
+ SetCurFilter( m_aCurrentFilter );
+ }
+}
+
+void FilterHelper::appendFilter(const OUString& aTitle, std::u16string_view aFilterString)
+{
+ SolarMutexGuard aGuard;
+
+ if( FilterNameExists( aTitle ) ) {
+ throw css::lang::IllegalArgumentException();
+ }
+
+ // ensure that we have a filter list
+ ensureFilterList( aTitle );
+
+ // append the filter
+ OUStringList suffixList;
+ fillSuffixList(suffixList, aFilterString);
+ m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
+}
+
+void FilterHelper::setCurrentFilter( const OUString& aTitle )
+{
+ SetCurFilter(aTitle);
+}
+
+OUString FilterHelper::getCurrentFilter( )
+{
+ OUString sReturn = m_aCurrentFilter;
+
+ return sReturn;
+}
+
+void FilterHelper::appendFilterGroup( const css::uno::Sequence< css::beans::StringPair >& aFilters )
+{
+ SolarMutexGuard aGuard;
+
+ //add a separator if this is not the first group to be added
+ bool bPrependSeparator = m_pFilterList != nullptr;
+
+ // ensure that we have a filter list
+ OUString sInitialCurrentFilter;
+ if( aFilters.getLength() > 0)
+ sInitialCurrentFilter = aFilters[0].First;
+ ensureFilterList( sInitialCurrentFilter );
+
+ // append the filter
+ if (bPrependSeparator) {
+ OUStringList emptyList;
+ m_pFilterList->push_back(FilterEntry("-", emptyList));
+ }
+
+ const css::beans::StringPair* pSubFilters = aFilters.getConstArray();
+ const css::beans::StringPair* pSubFiltersEnd = pSubFilters + aFilters.getLength();
+ for( ; pSubFilters != pSubFiltersEnd; ++pSubFilters ) {
+ appendFilter(pSubFilters->First, pSubFilters->Second);
+ }
+}
+
+bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
+{
+ if (m_aCurrentFilter.isEmpty()) {
+ SAL_WARN("fpicker", "filter name is empty");
+ return true;
+ }
+
+ NSFileManager *manager = [NSFileManager defaultManager];
+ NSDictionary* pAttribs = [manager attributesOfItemAtPath: sFilename error: nil];
+ if( pAttribs )
+ {
+ NSObject* pType = [pAttribs objectForKey: NSFileType];
+ if( pType && [pType isKindOfClass: [NSString class]] )
+ {
+ NSString* pT = static_cast<NSString*>(pType);
+ if( [pT isEqualToString: NSFileTypeDirectory] ||
+ [pT isEqualToString: NSFileTypeSymbolicLink] )
+ return true;
+ }
+ }
+
+ FilterList::iterator filter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
+ if (filter == m_pFilterList->end()) {
+ SAL_WARN("fpicker", "filter not found in list");
+ return true;
+ }
+
+ OUStringList suffixList = filter->getFilterSuffixList();
+
+ {
+ OUString aName = [sFilename OUString];
+ for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); ++iter) {
+ if (*iter == ".*" || aName.endsWithIgnoreAsciiCase(*iter)) {
+ return true;
+ }
+ }
+ }
+
+ // might be an alias
+ NSString* pResolved = resolveAlias( sFilename );
+ if( pResolved )
+ {
+ bool bResult = filenameMatchesFilter( pResolved );
+ [pResolved autorelease];
+ if( bResult )
+ return true;
+ }
+
+ return false;
+}
+
+FilterList* FilterHelper::getFilterList()
+{
+ return m_pFilterList;
+}
+
+NSStringList* FilterHelper::getFilterNames()
+{
+ if (nullptr == m_pFilterList)
+ return nullptr;
+ if (nullptr == m_pFilterNames) {
+ //build filter names list
+ m_pFilterNames = new NSStringList;
+ for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter) {
+ m_pFilterNames->push_back([[NSString stringWithOUString:iter->getTitle()] retain]);
+ }
+ }
+
+ return m_pFilterNames;
+}
+
+void FilterHelper::SetFilterAtIndex(unsigned index)
+{
+ if (m_pFilterList->size() <= index) {
+ index = 0;
+ }
+ FilterEntry entry = m_pFilterList->at(index);
+ SetCurFilter(entry.getTitle());
+}
+
+int FilterHelper::getCurrentFilterIndex()
+{
+ int result = 0;//default to first filter
+ if (m_aCurrentFilter.getLength() > 0) {
+ int i = 0;
+ for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter, ++i) {
+ OUString aTitle = iter->getTitle();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ result = i;
+ break;
+ } else {
+ aTitle = shrinkFilterName(aTitle).trim();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ result = i;
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+OUStringList FilterHelper::getCurrentFilterSuffixList()
+{
+ OUStringList retVal;
+ if (m_aCurrentFilter.getLength() > 0) {
+ for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter) {
+ OUString aTitle = iter->getTitle();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ retVal = iter->getFilterSuffixList();
+ break;
+ } else {
+ aTitle = shrinkFilterName(aTitle).trim();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ retVal = iter->getFilterSuffixList();
+ break;
+ }
+ }
+ }
+ }
+
+ return retVal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/NSString_OOoAdditions.hxx b/fpicker/source/aqua/NSString_OOoAdditions.hxx
new file mode 100644
index 000000000..eac18d46f
--- /dev/null
+++ b/fpicker/source/aqua/NSString_OOoAdditions.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#include <postmac.h>
+#include <rtl/ustring.hxx>
+
+//for Cocoa types
+@interface NSString (OOoAdditions)
++ (id)stringWithOUString:(const OUString&)ouString;
+- (OUString)OUString;
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/NSString_OOoAdditions.mm b/fpicker/source/aqua/NSString_OOoAdditions.mm
new file mode 100644
index 000000000..23ae6bc5c
--- /dev/null
+++ b/fpicker/source/aqua/NSString_OOoAdditions.mm
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "NSString_OOoAdditions.hxx"
+
+@implementation NSString (OOoAdditions)
+
++ (id) stringWithOUString:(const OUString&)ouString
+{
+ NSString *string = [[NSString alloc] initWithCharacters:reinterpret_cast<unichar const *>(ouString.getStr()) length:ouString.getLength()];
+
+ return [string autorelease];
+}
+
+- (OUString) OUString
+{
+ unsigned int nFileNameLength = [self length];
+
+ UniChar unichars[nFileNameLength+1];
+
+ //'close' the string buffer correctly
+ unichars[nFileNameLength] = '\0';
+
+ [self getCharacters:unichars];
+
+ return OUString(reinterpret_cast<sal_Unicode *>(unichars));
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/NSURL_OOoAdditions.hxx b/fpicker/source/aqua/NSURL_OOoAdditions.hxx
new file mode 100644
index 000000000..f63ccfdcc
--- /dev/null
+++ b/fpicker/source/aqua/NSURL_OOoAdditions.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <premac.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <postmac.h>
+#include <rtl/ustring.hxx>
+
+@interface NSURL (OOoAdditions)
+- (OUString)OUString;
+@end
+
+/*
+ returns the resolved string if there was an alias
+ if there was no alias, nil is returned
+*/
+
+NSString* resolveAlias(NSString* i_pSystemPath);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/NSURL_OOoAdditions.mm b/fpicker/source/aqua/NSURL_OOoAdditions.mm
new file mode 100644
index 000000000..5a3737e9b
--- /dev/null
+++ b/fpicker/source/aqua/NSURL_OOoAdditions.mm
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "NSString_OOoAdditions.hxx"
+#include "NSURL_OOoAdditions.hxx"
+#include <sal/log.hxx>
+
+@implementation NSURL (OOoAdditions)
+- (OUString) OUString
+{
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ NSString *sURLString = nil;
+
+ SAL_INFO("fpicker.aqua","Extracting the full path of an item");
+ sURLString = [self absoluteString];
+ [sURLString retain];
+
+ OUString sResult = [sURLString OUString];
+ [sURLString release];
+
+ [pool release];
+
+ return sResult;
+}
+@end
+
+NSString* resolveAlias( NSString* i_pSystemPath )
+{
+ NSString* pResolvedPath = nil;
+ CFURLRef rUrl = CFURLCreateWithFileSystemPath( kCFAllocatorDefault,
+ reinterpret_cast<CFStringRef>(i_pSystemPath),
+ kCFURLPOSIXPathStyle, false);
+ if( rUrl != nullptr )
+ {
+ CFErrorRef rError;
+ CFDataRef rBookmark = CFURLCreateBookmarkDataFromFile( nullptr, rUrl, &rError );
+ CFRelease( rUrl );
+ if( rBookmark == nullptr )
+ {
+ CFRelease( rError );
+ }
+ else
+ {
+ Boolean bIsStale;
+ CFURLRef rResolvedUrl = CFURLCreateByResolvingBookmarkData( kCFAllocatorDefault, rBookmark, kCFBookmarkResolutionWithoutUIMask,
+ nullptr, nullptr, &bIsStale, &rError );
+ CFRelease( rBookmark );
+ if( rResolvedUrl == nullptr )
+ {
+ CFRelease( rError );
+ }
+ else
+ {
+ pResolvedPath = const_cast<NSString*>(reinterpret_cast<NSString const *>(CFURLCopyFileSystemPath( rResolvedUrl, kCFURLPOSIXPathStyle )));
+ CFRelease( rResolvedUrl );
+ }
+ }
+ }
+
+ return pResolvedPath;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaConstants.h b/fpicker/source/aqua/SalAquaConstants.h
new file mode 100644
index 000000000..3fd14d536
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaConstants.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#define kAppFourCharCode 'LibO'
+#define kControlPropertyTracking 'Trck'
+#define kControlPropertyLastPartCode 'LsPc'
+#define kControlPropertySubType 'SuTy'
+#define kPopupControlPropertyTitleWidth 'PoTW'
+
+#define kAquaSpaceBetweenControls (8)
+#define kAquaSpaceBetweenPopupMenus (10)
+
+#define kAquaSpaceInsideGroupH (16)
+#define kAquaSpaceInsideGroupV (11)
+
+#define kAquaSpaceBoxFrameViewDiffTop (7)
+#define kAquaSpaceBoxFrameViewDiffLeft (7)
+#define kAquaSpaceBoxFrameViewDiffBottom (9)
+#define kAquaSpaceBoxFrameViewDiffRight (7)
+
+#define kAquaSpaceButtonFrameBoundsDiff (6)
+#define kAquaSpaceSwitchButtonFrameBoundsDiff (2)
+
+#define kAquaSpacePopupMenuFrameBoundsDiffTop (2)
+#define kAquaSpacePopupMenuFrameBoundsDiffBottom (4)
+#define kAquaSpacePopupMenuFrameBoundsDiffV \
+ (kAquaSpacePopupMenuFrameBoundsDiffTop + kAquaSpacePopupMenuFrameBoundsDiffBottom)
+#define kAquaSpacePopupMenuFrameBoundsDiffLeft (3)
+
+#define kAquaSpaceLabelFrameBoundsDiffH (3)
+#define kAquaSpaceLabelPopupDiffV (6)
+#define kAquaSpaceAfterPopupButtonsV (20)
+
+#define kAquaPopupButtonDefaultHeight (26)
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaFilePicker.hxx b/fpicker/source/aqua/SalAquaFilePicker.hxx
new file mode 100644
index 000000000..3cf13fedb
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaFilePicker.hxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+
+#include "SalAquaPicker.hxx"
+
+#include <rtl/ustring.hxx>
+#include "FilterHelper.hxx"
+#include "AquaFilePickerDelegate.hxx"
+
+// Implementation class for the XFilePicker Interface
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::ui::dialogs::XFilePicker3,
+ css::ui::dialogs::XFilePickerControlAccess,
+ css::lang::XInitialization,
+ css::lang::XServiceInfo > SalAquaFilePicker_Base;
+
+class SalAquaFilePicker :
+ public SalAquaPicker,
+ public SalAquaFilePicker_Base
+{
+public:
+
+ // constructor
+ SalAquaFilePicker();
+
+ // 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& aTitle ) 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& aName ) override;
+
+ virtual void SAL_CALL setDisplayDirectory( const OUString& aDirectory ) override;
+
+ virtual OUString SAL_CALL getDisplayDirectory( ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getFiles( ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSelectedFiles( ) override;
+
+ // XFilterManager functions
+
+ virtual void SAL_CALL appendFilter( const OUString& aTitle, const OUString& aFilter ) override;
+
+ virtual void SAL_CALL setCurrentFilter( const OUString& aTitle ) override;
+
+ virtual OUString SAL_CALL getCurrentFilter( ) override;
+
+ // XFilterGroupManager functions
+
+ virtual void SAL_CALL appendFilterGroup( const OUString& sGroupTitle, const css::uno::Sequence< css::beans::StringPair >& aFilters ) override;
+
+ // XFilePickerControlAccess functions
+
+ virtual void SAL_CALL setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const css::uno::Any& aValue ) override;
+
+ virtual css::uno::Any SAL_CALL getValue( sal_Int16 aControlId, sal_Int16 aControlAction ) override;
+
+ virtual void SAL_CALL enableControl( sal_Int16 nControlId, sal_Bool bEnable ) override;
+
+ virtual void SAL_CALL setLabel( sal_Int16 nControlId, const OUString& aLabel ) override;
+
+ virtual OUString SAL_CALL getLabel( sal_Int16 nControlId ) override;
+
+ // XInitialization
+
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XCancellable
+
+ virtual void SAL_CALL cancel( ) override;
+
+ // XEventListener
+
+ using cppu::WeakComponentImplHelperBase::disposing;
+ /// @throws css::uno::RuntimeException
+ virtual void disposing( const css::lang::EventObject& aEvent );
+
+ // XServiceInfo
+
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // FilePicker Event functions
+
+ void fileSelectionChanged( css::ui::dialogs::FilePickerEvent aEvent );
+ void directoryChanged( css::ui::dialogs::FilePickerEvent aEvent );
+ // OUString SAL_CALL helpRequested( css::ui::dialogs::FilePickerEvent aEvent ) const;
+ void controlStateChanged( css::ui::dialogs::FilePickerEvent aEvent );
+ void dialogSizeChanged( );
+
+ AquaFilePickerDelegate * getDelegate() {
+ return m_pDelegate;
+ }
+
+ OUString const & getSaveFileName() {
+ return m_sSaveFileName;
+ }
+
+private:
+ SalAquaFilePicker( const SalAquaFilePicker& ) = delete;
+ SalAquaFilePicker& operator=( const SalAquaFilePicker& ) = delete;
+
+ virtual void ensureFilterHelper();
+
+ css::uno::Reference< css::ui::dialogs::XFilePickerListener > m_xListener;
+ FilterHelper *m_pFilterHelper;
+ OUString m_sSaveFileName;
+ AquaFilePickerDelegate *m_pDelegate;
+
+ void updateFilterUI();
+ void updateSaveFileNameExtension();
+
+public:
+
+ virtual ~SalAquaFilePicker() override;
+
+ void filterControlChanged();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaFilePicker.mm b/fpicker/source/aqua/SalAquaFilePicker.mm
new file mode 100644
index 000000000..01e6f80e9
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaFilePicker.mm
@@ -0,0 +1,590 @@
+/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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_features.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.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 <cppuhelper/interfacecontainer.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/ControlActions.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+
+#include "resourceprovider.hxx"
+
+#include <osl/file.hxx>
+#include "NSString_OOoAdditions.hxx"
+#include "NSURL_OOoAdditions.hxx"
+
+#include <iostream>
+
+#include "SalAquaFilePicker.hxx"
+
+#include <objc/objc-runtime.h>
+
+#pragma mark DEFINES
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
+using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
+using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+
+namespace
+{
+ uno::Sequence<OUString> FilePicker_getSupportedServiceNames()
+ {
+ return { "com.sun.star.ui.dialogs.FilePicker",
+ "com.sun.star.ui.dialogs.SystemFilePicker",
+ "com.sun.star.ui.dialogs.AquaFilePicker" };
+ }
+}
+
+#pragma mark Constructor
+
+SalAquaFilePicker::SalAquaFilePicker()
+ : SalAquaFilePicker_Base( m_rbHelperMtx )
+ , m_pFilterHelper( nullptr )
+{
+ m_pDelegate = [[AquaFilePickerDelegate alloc] initWithFilePicker:this];
+ m_pControlHelper->setFilePickerDelegate(m_pDelegate);
+}
+
+SalAquaFilePicker::~SalAquaFilePicker()
+{
+ if (nullptr != m_pFilterHelper)
+ delete m_pFilterHelper;
+
+ [m_pDelegate release];
+}
+
+
+#pragma mark XFilePickerNotifier
+
+void SAL_CALL SalAquaFilePicker::addFilePickerListener( const uno::Reference<XFilePickerListener>& xListener )
+{
+ SolarMutexGuard aGuard;
+ m_xListener = xListener;
+}
+
+void SAL_CALL SalAquaFilePicker::removeFilePickerListener( const uno::Reference<XFilePickerListener>& )
+{
+ SolarMutexGuard aGuard;
+ m_xListener.clear();
+}
+
+#pragma mark XAsynchronousExecutableDialog
+
+void SAL_CALL SalAquaFilePicker::setTitle( const OUString& aTitle )
+{
+ SolarMutexGuard aGuard;
+ implsetTitle(aTitle);
+}
+
+sal_Int16 SAL_CALL SalAquaFilePicker::execute()
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int16 retVal = 0;
+
+ implInitialize();
+
+ // if m_pDialog is nil after initialization, something must have gone wrong before
+ // or there was no initialization (see issue https://bz.apache.org/ooo/show_bug.cgi?id=100214)
+ if (m_pDialog == nil) {
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ }
+
+ if (m_pFilterHelper) {
+ m_pFilterHelper->SetFilters();
+ }
+
+ if (m_nDialogType == NAVIGATIONSERVICES_SAVE) {
+ if (m_sSaveFileName.getLength() == 0) {
+ //if no filename is set, NavigationServices will set the name to "untitled". We don't want this!
+ //So let's try to get the window title to get the real untitled name
+ NSWindow *frontWindow = [NSApp keyWindow];
+ if (nullptr != frontWindow) {
+ NSString *windowTitle = [frontWindow title];
+ if (windowTitle != nil) {
+ OUString ouName = [windowTitle OUString];
+ //a window title will typically be something like "Untitled1 - OpenOffice.org Writer"
+ //but we only want the "Untitled1" part of it
+ sal_Int32 indexOfDash = ouName.indexOf(" - ");
+ if (indexOfDash > -1) {
+ m_sSaveFileName = ouName.copy(0,indexOfDash);
+ if (m_sSaveFileName.getLength() > 0) {
+ setDefaultName(m_sSaveFileName);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //Set the delegate to be notified of certain events
+
+ [m_pDialog setDelegate:m_pDelegate];
+
+ int nStatus = runandwaitforresult();
+
+ [m_pDialog setDelegate:nil];
+
+ switch( nStatus )
+ {
+ case NSModalResponseOK:
+ retVal = ExecutableDialogResults::OK;
+ break;
+
+ case NSModalResponseCancel:
+ retVal = ExecutableDialogResults::CANCEL;
+ break;
+
+ default:
+ throw uno::RuntimeException(
+ "The dialog returned with an unknown result!",
+ static_cast<XFilePicker*>( static_cast<XFilePicker3*>( this ) ));
+ break;
+ }
+
+ return retVal;
+}
+
+
+#pragma mark XFilePicker
+
+void SAL_CALL SalAquaFilePicker::setMultiSelectionMode( sal_Bool /* bMode */ )
+{
+ SolarMutexGuard aGuard;
+
+ if (m_nDialogType == NAVIGATIONSERVICES_OPEN) {
+ [static_cast<NSOpenPanel*>(m_pDialog) setAllowsMultipleSelection:YES];
+ }
+}
+
+void SAL_CALL SalAquaFilePicker::setDefaultName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ m_sSaveFileName = aName;
+}
+
+void SAL_CALL SalAquaFilePicker::setDisplayDirectory( const OUString& rDirectory )
+{
+ SolarMutexGuard aGuard;
+
+ implsetDisplayDirectory(rDirectory);
+}
+
+OUString SAL_CALL SalAquaFilePicker::getDisplayDirectory()
+{
+ OUString retVal = implgetDisplayDirectory();
+
+ return retVal;
+}
+
+uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getFiles()
+{
+ uno::Sequence< OUString > aSelectedFiles = getSelectedFiles();
+ // multiselection doesn't really work with getFiles
+ // so just retrieve the first url
+ if (aSelectedFiles.getLength() > 1)
+ aSelectedFiles.realloc(1);
+
+ return aSelectedFiles;
+}
+
+uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getSelectedFiles()
+{
+ SolarMutexGuard aGuard;
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ static NSUserDefaults *userDefaults;
+ static bool triedUserDefaults = false;
+
+ if (!triedUserDefaults)
+ {
+ userDefaults = [NSUserDefaults standardUserDefaults];
+ triedUserDefaults = true;
+ }
+#endif
+
+ NSArray *files = nil;
+ if (m_nDialogType == NAVIGATIONSERVICES_OPEN) {
+ files = [static_cast<NSOpenPanel*>(m_pDialog) URLs];
+ }
+ else if (m_nDialogType == NAVIGATIONSERVICES_SAVE) {
+ files = [NSArray arrayWithObjects:[m_pDialog URL], nil];
+ }
+
+ NSUInteger nFiles = [files count];
+ SAL_INFO("fpicker.aqua", "# of items: " << nFiles);
+
+ uno::Sequence< OUString > aSelectedFiles(nFiles);
+ OUString* pSelectedFiles = aSelectedFiles.getArray();
+
+ for(NSUInteger nIndex = 0; nIndex < nFiles; nIndex += 1)
+ {
+ NSURL *url = [files objectAtIndex:nIndex];
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ if (userDefaults != NULL &&
+ [url respondsToSelector:@selector(bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:)])
+ {
+ // In the case of "Save As" when the user has input a new
+ // file name, this call will return nil, as bookmarks can
+ // (naturally) only be created for existing file system
+ // objects. In that case, code at a much lower level, in
+ // sal, takes care of creating a bookmark when a new file
+ // has been created outside the sandbox.
+ NSData *data = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
+ includingResourceValuesForKeys:nil
+ relativeToURL:nil
+ error:nil];
+ if (data != NULL)
+ {
+ [userDefaults setObject:data
+ forKey:[@"bookmarkFor:" stringByAppendingString:[url absoluteString]]];
+ }
+ }
+#endif
+
+ OUString sFileOrDirURL = [url OUString];
+
+ pSelectedFiles[nIndex] = sFileOrDirURL;
+ }
+
+ return aSelectedFiles;
+}
+
+#pragma mark XFilterManager
+
+void SAL_CALL SalAquaFilePicker::appendFilter( const OUString& aTitle, const OUString& aFilter )
+{
+ SolarMutexGuard aGuard;
+
+ ensureFilterHelper();
+ m_pFilterHelper->appendFilter( aTitle, aFilter );
+ m_pControlHelper->setFilterControlNeeded(true);
+}
+
+void SAL_CALL SalAquaFilePicker::setCurrentFilter( const OUString& aTitle )
+{
+ SolarMutexGuard aGuard;
+
+ ensureFilterHelper();
+ m_pFilterHelper->setCurrentFilter(aTitle);
+ updateFilterUI();
+
+ updateSaveFileNameExtension();
+}
+
+OUString SAL_CALL SalAquaFilePicker::getCurrentFilter()
+{
+ SolarMutexGuard aGuard;
+
+ ensureFilterHelper();
+
+ return m_pFilterHelper->getCurrentFilter();
+}
+
+#pragma mark XFilterGroupManager
+
+void SAL_CALL SalAquaFilePicker::appendFilterGroup( const OUString&, const uno::Sequence<beans::StringPair>& aFilters )
+{
+ SolarMutexGuard aGuard;
+
+ ensureFilterHelper();
+ m_pFilterHelper->appendFilterGroup(aFilters);
+ m_pControlHelper->setFilterControlNeeded(true);
+}
+
+#pragma mark XFilePickerControlAccess
+
+void SAL_CALL SalAquaFilePicker::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const uno::Any& rValue )
+{
+ SolarMutexGuard aGuard;
+
+ m_pControlHelper->setValue(nControlId, nControlAction, rValue);
+
+ if (nControlId == ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION && m_nDialogType == NAVIGATIONSERVICES_SAVE) {
+ updateSaveFileNameExtension();
+ }
+}
+
+uno::Any SAL_CALL SalAquaFilePicker::getValue( sal_Int16 nControlId, sal_Int16 nControlAction )
+{
+ uno::Any aValue = m_pControlHelper->getValue(nControlId, nControlAction);
+
+ return aValue;
+}
+
+void SAL_CALL SalAquaFilePicker::enableControl( sal_Int16 nControlId, sal_Bool bEnable )
+{
+ m_pControlHelper->enableControl(nControlId, bEnable);
+}
+
+void SAL_CALL SalAquaFilePicker::setLabel( sal_Int16 nControlId, const OUString& aLabel )
+{
+ SolarMutexGuard aGuard;
+
+ NSString* sLabel = [NSString stringWithOUString:aLabel];
+ m_pControlHelper->setLabel( nControlId, sLabel ) ;
+}
+
+OUString SAL_CALL SalAquaFilePicker::getLabel( sal_Int16 nControlId )
+{
+ return m_pControlHelper->getLabel(nControlId);
+}
+
+#pragma mark XInitialization
+
+void SAL_CALL SalAquaFilePicker::initialize( const uno::Sequence<uno::Any>& aArguments )
+{
+ SolarMutexGuard aGuard;
+
+ // parameter checking
+ uno::Any aAny;
+ if( 0 == aArguments.getLength() )
+ throw lang::IllegalArgumentException("no arguments",
+ static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ), 1 );
+
+ aAny = aArguments[0];
+
+ if( ( aAny.getValueType() != ::cppu::UnoType<sal_Int16>::get() ) &&
+ (aAny.getValueType() != ::cppu::UnoType<sal_Int8>::get() ) )
+ throw lang::IllegalArgumentException("invalid argument type",
+ static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ), 1 );
+
+ sal_Int16 templateId = -1;
+ aAny >>= templateId;
+
+ switch( templateId )
+ {
+ case FILEOPEN_SIMPLE:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILESAVE_SIMPLE:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILESAVE_AUTOEXTENSION_PASSWORD:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILESAVE_AUTOEXTENSION_SELECTION:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILESAVE_AUTOEXTENSION_TEMPLATE:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILEOPEN_PLAY:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILEOPEN_LINK_PLAY:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILEOPEN_READONLY_VERSION:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILEOPEN_LINK_PREVIEW:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ case FILESAVE_AUTOEXTENSION:
+ m_nDialogType = NAVIGATIONSERVICES_SAVE;
+ break;
+ case FILEOPEN_PREVIEW:
+ m_nDialogType = NAVIGATIONSERVICES_OPEN;
+ break;
+ default:
+ throw lang::IllegalArgumentException("Unknown template",
+ static_cast<XFilePicker*>( static_cast<XFilePicker3*>(this) ),
+ 1 );
+ }
+
+ m_pControlHelper->initialize(templateId);
+
+ implInitialize();
+}
+
+#pragma mark XCancellable
+
+void SAL_CALL SalAquaFilePicker::cancel()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pDialog != nil) {
+ [m_pDialog cancel:nil];
+ }
+}
+
+#pragma mark XEventListener
+
+void SalAquaFilePicker::disposing( const lang::EventObject& aEvent )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<XFilePickerListener> xFilePickerListener( aEvent.Source, css::uno::UNO_QUERY );
+
+ if( xFilePickerListener.is() )
+ removeFilePickerListener( xFilePickerListener );
+}
+
+#pragma mark XServiceInfo
+
+OUString SAL_CALL SalAquaFilePicker::getImplementationName()
+{
+ return "com.sun.star.ui.dialogs.SalAquaFilePicker";
+}
+
+sal_Bool SAL_CALL SalAquaFilePicker::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL SalAquaFilePicker::getSupportedServiceNames()
+{
+ return FilePicker_getSupportedServiceNames();
+}
+
+#pragma mark Misc/Private
+
+void SalAquaFilePicker::fileSelectionChanged( FilePickerEvent aEvent )
+{
+ if (m_xListener.is())
+ m_xListener->fileSelectionChanged( aEvent );
+}
+
+void SalAquaFilePicker::directoryChanged( FilePickerEvent aEvent )
+{
+ if (m_xListener.is())
+ m_xListener->directoryChanged( aEvent );
+}
+
+void SalAquaFilePicker::controlStateChanged( FilePickerEvent aEvent )
+{
+ if (m_xListener.is())
+ m_xListener->controlStateChanged( aEvent );
+}
+
+void SalAquaFilePicker::dialogSizeChanged()
+{
+ if (m_xListener.is())
+ m_xListener->dialogSizeChanged();
+}
+
+
+// Misc
+
+void SalAquaFilePicker::ensureFilterHelper()
+{
+ SolarMutexGuard aGuard;
+
+ if (nullptr == m_pFilterHelper) {
+ m_pFilterHelper = new FilterHelper;
+ m_pControlHelper->setFilterHelper(m_pFilterHelper);
+ [m_pDelegate setFilterHelper:m_pFilterHelper];
+ }
+}
+
+void SalAquaFilePicker::updateFilterUI()
+{
+ m_pControlHelper->updateFilterUI();
+}
+
+void SalAquaFilePicker::updateSaveFileNameExtension()
+{
+ if (m_nDialogType != NAVIGATIONSERVICES_SAVE) {
+ return;
+ }
+
+ // we need to set this here again because initial setting does
+ //[m_pDialog setExtensionHidden:YES];
+
+ SolarMutexGuard aGuard;
+
+ if (!m_pControlHelper->isAutoExtensionEnabled()) {
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH // setAllowedFileTypes (12.0)
+ [m_pDialog setAllowedFileTypes:nil];
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+ [m_pDialog setAllowsOtherFileTypes:YES];
+ } else {
+ ensureFilterHelper();
+
+ OUStringList aStringList = m_pFilterHelper->getCurrentFilterSuffixList();
+ if( aStringList.empty()) // #i9328#
+ return;
+
+ OUString suffix = (*(aStringList.begin())).copy(1);
+ NSString *requiredFileType = [NSString stringWithOUString:suffix];
+
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH // setAllowedFileTypes (12.0)
+ [m_pDialog setAllowedFileTypes:[NSArray arrayWithObjects:requiredFileType, nil]];
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+
+ [m_pDialog setAllowsOtherFileTypes:NO];
+ }
+}
+
+void SalAquaFilePicker::filterControlChanged()
+{
+ if (m_pDialog == nil) {
+ return;
+ }
+
+ SolarMutexGuard aGuard;
+
+ updateSaveFileNameExtension();
+
+ [m_pDialog validateVisibleColumns];
+
+ FilePickerEvent evt;
+ evt.ElementId = LISTBOX_FILTER;
+ controlStateChanged( evt );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_SalAquaFilePicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SalAquaFilePicker());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaFolderPicker.hxx b/fpicker/source/aqua/SalAquaFolderPicker.hxx
new file mode 100644
index 000000000..f563b1983
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaFolderPicker.hxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/implbase.hxx>
+#include <com/sun/star/util/XCancellable.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
+
+#include "SalAquaPicker.hxx"
+
+#include <rtl/ustring.hxx>
+
+
+
+
+class SalAquaFolderPicker :
+ public SalAquaPicker,
+ public cppu::WeakImplHelper<
+ css::ui::dialogs::XFolderPicker2,
+ css::lang::XServiceInfo,
+ css::lang::XEventListener >
+{
+public:
+
+ // constructor
+ SalAquaFolderPicker();
+
+
+ // 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;
+
+
+ // XServiceInfo
+
+
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+
+ // XCancellable
+
+
+ virtual void SAL_CALL cancel( ) override;
+
+
+ // XEventListener
+
+
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
+
+private:
+ SalAquaFolderPicker( const SalAquaFolderPicker& ) = delete;
+ SalAquaFolderPicker& operator=( const SalAquaFolderPicker& ) = delete;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaFolderPicker.mm b/fpicker/source/aqua/SalAquaFolderPicker.mm
new file mode 100644
index 000000000..3dabf488a
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaFolderPicker.mm
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <cppuhelper/interfacecontainer.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include "SalAquaFolderPicker.hxx"
+
+#include <iostream>
+
+#include "resourceprovider.hxx"
+
+#include <osl/file.hxx>
+#include "NSString_OOoAdditions.hxx"
+#include "NSURL_OOoAdditions.hxx"
+
+#pragma mark DEFINES
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+SalAquaFolderPicker::SalAquaFolderPicker()
+{
+ m_nDialogType = NAVIGATIONSERVICES_DIRECTORY;
+}
+
+// XExecutableDialog
+
+void SAL_CALL SalAquaFolderPicker::setTitle( const OUString& aTitle )
+{
+ SolarMutexGuard aGuard;
+
+ implsetTitle(aTitle);
+}
+
+sal_Int16 SAL_CALL SalAquaFolderPicker::execute()
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int16 retVal = 0;
+
+ int nResult = runandwaitforresult();
+
+ switch( nResult )
+ {
+ case NSModalResponseOK:
+ retVal = ExecutableDialogResults::OK;
+ break;
+
+ case NSModalResponseCancel:
+ retVal = ExecutableDialogResults::CANCEL;
+ break;
+
+ default:
+ throw uno::RuntimeException("The dialog returned with an unknown result!", static_cast< cppu::OWeakObject * >( this ));
+ break;
+ }
+
+ return retVal;
+}
+
+// XFolderPicker
+
+void SAL_CALL SalAquaFolderPicker::setDisplayDirectory( const OUString& aDirectory )
+{
+ SolarMutexGuard aGuard;
+
+ implsetDisplayDirectory(aDirectory);
+}
+
+OUString SAL_CALL SalAquaFolderPicker::getDisplayDirectory()
+{
+ SolarMutexGuard aGuard;
+
+ OUString aDirectory = implgetDisplayDirectory();
+
+ return aDirectory;
+}
+
+OUString SAL_CALL SalAquaFolderPicker::getDirectory()
+{
+ SolarMutexGuard aGuard;
+
+ NSArray *files = nil;
+ if (m_nDialogType == NAVIGATIONSERVICES_DIRECTORY) {
+ files = [static_cast<NSOpenPanel*>(m_pDialog) URLs];
+ }
+
+ NSUInteger nFiles = [files count];
+ SAL_INFO("fpicker.aqua", "# of items: " << nFiles);
+
+ if (nFiles < 1) {
+ throw uno::RuntimeException("no directory selected", static_cast< cppu::OWeakObject * >( this ));
+ }
+
+ OUString aDirectory;
+
+ NSURL *url = [files objectAtIndex:0];
+
+ aDirectory = [url OUString];
+
+ implsetDisplayDirectory(aDirectory);
+
+ return aDirectory;
+}
+
+void SAL_CALL SalAquaFolderPicker::setDescription( const OUString& rDescription )
+{
+ [m_pDialog setMessage:[NSString stringWithOUString:rDescription]];
+}
+
+// XServiceInfo
+
+OUString SAL_CALL SalAquaFolderPicker::getImplementationName()
+{
+ return "com.sun.star.ui.dialogs.SalAquaFolderPicker";
+}
+
+sal_Bool SAL_CALL SalAquaFolderPicker::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL SalAquaFolderPicker::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.dialogs.SystemFolderPicker", "com.sun.star.ui.dialogs.AquaFolderPicker" };
+}
+
+// XCancellable
+
+void SAL_CALL SalAquaFolderPicker::cancel()
+{
+ SolarMutexGuard aGuard;
+
+ [m_pDialog cancel:nil];
+}
+
+// XEventListener
+
+void SAL_CALL SalAquaFolderPicker::disposing( const lang::EventObject& )
+{
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_SalAquaFolderPicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SalAquaFolderPicker());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaPicker.hxx b/fpicker/source/aqua/SalAquaPicker.hxx
new file mode 100644
index 000000000..4eab78c52
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaPicker.hxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <osl/mutex.hxx>
+
+#include <rtl/ustring.hxx>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include "ControlHelper.hxx"
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+class SalAquaPicker
+{
+public:
+ // constructor
+ SalAquaPicker();
+ virtual ~SalAquaPicker();
+
+ int run();
+ int runandwaitforresult();
+
+ OUString const& getDisplayDirectory() { return m_sDisplayDirectory; }
+
+ ControlHelper* getControlHelper() const { return m_pControlHelper; }
+
+protected:
+ OUString m_sDisplayDirectory;
+
+ NSSavePanel* m_pDialog;
+
+ ControlHelper* m_pControlHelper;
+
+ osl::Mutex m_rbHelperMtx;
+
+ // The type of dialog
+ enum NavigationServices_DialogType
+ {
+ NAVIGATIONSERVICES_OPEN,
+ NAVIGATIONSERVICES_SAVE,
+ NAVIGATIONSERVICES_DIRECTORY
+ };
+
+ NavigationServices_DialogType m_nDialogType;
+
+ /// @throws css::uno::RuntimeException
+ void implsetTitle(const OUString& aTitle);
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void implsetDisplayDirectory(const OUString& rDirectory);
+
+ /// @throws css::uno::RuntimeException
+ OUString const& implgetDisplayDirectory();
+
+ void implInitialize();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/SalAquaPicker.mm b/fpicker/source/aqua/SalAquaPicker.mm
new file mode 100644
index 000000000..c2cf497f5
--- /dev/null
+++ b/fpicker/source/aqua/SalAquaPicker.mm
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <cppuhelper/interfacecontainer.h>
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+#include "SalAquaPicker.hxx"
+#include <osl/file.hxx>
+#include "NSString_OOoAdditions.hxx"
+
+#include "NSURL_OOoAdditions.hxx"
+
+#include "SalAquaFilePicker.hxx"
+
+#include <stdio.h>
+
+#pragma mark DEFINES
+#define kSetHideExtensionStateKey @"NSNavLastUserSetHideExtensionButtonState"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+SalAquaPicker::SalAquaPicker()
+: m_pDialog(nullptr)
+, m_pControlHelper(new ControlHelper())
+{
+}
+
+SalAquaPicker::~SalAquaPicker()
+{
+ SolarMutexGuard aGuard;
+
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ if (nullptr != m_pControlHelper)
+ delete m_pControlHelper;
+
+ if (nullptr != m_pDialog)
+ [m_pDialog release];
+
+ [pool release];
+}
+
+void SalAquaPicker::implInitialize()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pDialog != nil) {
+ return;
+ }
+
+ switch (m_nDialogType)
+ {
+ case NAVIGATIONSERVICES_OPEN:
+ m_pDialog = [NSOpenPanel openPanel];
+ [static_cast<NSOpenPanel*>(m_pDialog) setCanChooseDirectories:NO];
+ [static_cast<NSOpenPanel*>(m_pDialog) setCanChooseFiles:YES];
+ break;
+
+ case NAVIGATIONSERVICES_SAVE:
+ m_pDialog = [NSSavePanel savePanel];
+ [m_pDialog setCanSelectHiddenExtension:NO]; //changed for issue #102102
+ /* I would have loved to use
+ * [(NSSavePanel*)m_pDialog setExtensionHidden:YES];
+ * here but unfortunately this
+ * a) only works when the dialog is already displayed because it seems to act on the corresponding checkbox (that we don't show but that doesn't matter)
+ * b) macOS saves this setting on an application-based level which means that the last state is always being restored again when the app runs for the next time
+ *
+ * So the only reliable way seems to be using the NSUserDefaults object because that is where that value is stored and
+ * to just overwrite it if it has the wrong value.
+ */
+ {
+ NSUserDefaults *pDefaults = [NSUserDefaults standardUserDefaults];
+ NSNumber *pExtn = [pDefaults objectForKey:kSetHideExtensionStateKey];
+ if(pExtn == nil || [pExtn boolValue] == NO) {
+ [pDefaults setBool:YES forKey:kSetHideExtensionStateKey];
+ }
+ }
+ break;
+
+ case NAVIGATIONSERVICES_DIRECTORY:
+ m_pDialog = [NSOpenPanel openPanel];
+ [static_cast<NSOpenPanel*>(m_pDialog) setCanChooseDirectories:YES];
+ [static_cast<NSOpenPanel*>(m_pDialog) setCanChooseFiles:NO];
+ break;
+
+ default:
+ break;
+ }
+
+ if (m_pDialog != nil) {
+ [static_cast<NSOpenPanel*>(m_pDialog) setCanCreateDirectories:YES];
+ //Retain the dialog instance or it will go away immediately
+ [m_pDialog retain];
+ }
+}
+
+int SalAquaPicker::run()
+{
+ SolarMutexGuard aGuard;
+
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ if (m_pDialog == nullptr) {
+ //this is the case e.g. for the folder picker at this stage
+ implInitialize();
+ }
+
+ NSView *userPane = m_pControlHelper->getUserPane();
+ if (userPane != nullptr) {
+ [m_pDialog setAccessoryView:userPane];
+ }
+
+ int retVal = 0;
+
+ NSURL *startDirectory;
+ if (m_sDisplayDirectory.getLength() > 0) {
+ NSString *temp = [NSString stringWithOUString:m_sDisplayDirectory];
+ startDirectory = [NSURL URLWithString:temp];
+
+ SAL_INFO("fpicker.aqua", "start dir: " << [startDirectory path]);
+ }
+ else {
+ startDirectory = [NSURL fileURLWithPath:NSHomeDirectory() isDirectory:YES];
+ }
+
+ switch(m_nDialogType) {
+ case NAVIGATIONSERVICES_DIRECTORY:
+ case NAVIGATIONSERVICES_OPEN:
+ [m_pDialog setDirectoryURL:startDirectory];
+ retVal = [static_cast<NSOpenPanel*>(m_pDialog) runModal];
+ break;
+ case NAVIGATIONSERVICES_SAVE:
+ [m_pDialog setDirectoryURL:startDirectory];
+ [m_pDialog setNameFieldStringValue:[NSString stringWithOUString:static_cast<SalAquaFilePicker*>(this)->getSaveFileName()]];
+ retVal = [m_pDialog runModal];
+ break;
+ default:
+ break;
+ }
+
+ if (retVal == NSModalResponseOK) {
+ NSURL* pDir = [m_pDialog directoryURL];
+ if (pDir) {
+ implsetDisplayDirectory([pDir OUString]);
+ }
+ }
+
+ [pool release];
+
+ return retVal;
+}
+
+int SalAquaPicker::runandwaitforresult()
+{
+ SolarMutexGuard aGuard;
+
+ int status = run();
+
+ return status;
+}
+
+void SalAquaPicker::implsetDisplayDirectory( const OUString& aDirectory )
+{
+ SolarMutexGuard aGuard;
+
+ if (aDirectory != m_sDisplayDirectory) {
+ m_sDisplayDirectory = aDirectory;
+ }
+}
+
+OUString const & SalAquaPicker::implgetDisplayDirectory()
+{
+ return m_sDisplayDirectory;
+}
+
+void SalAquaPicker::implsetTitle( const OUString& aTitle )
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pDialog != nil) {
+ [m_pDialog setTitle:[NSString stringWithOUString:aTitle]];
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/fps_aqua.component b/fpicker/source/aqua/fps_aqua.component
new file mode 100644
index 000000000..bf143911a
--- /dev/null
+++ b/fpicker/source/aqua/fps_aqua.component
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.ui.dialogs.SalAquaFilePicker"
+ constructor="fpicker_SalAquaFilePicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.AquaFilePicker"/>
+ </implementation>
+ <implementation name="com.sun.star.ui.dialogs.SalAquaFolderPicker"
+ constructor="fpicker_SalAquaFolderPicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.AquaFolderPicker"/>
+ </implementation>
+</component>
diff --git a/fpicker/source/aqua/resourceprovider.hxx b/fpicker/source/aqua/resourceprovider.hxx
new file mode 100644
index 000000000..ba581389f
--- /dev/null
+++ b/fpicker/source/aqua/resourceprovider.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <sal/types.h>
+
+#include <premac.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+#define FOLDERPICKER_TITLE 500
+#define FOLDER_PICKER_DEF_DESCRIPTION 501
+#define FILE_PICKER_TITLE_OPEN 502
+#define FILE_PICKER_TITLE_SAVE 503
+#define FILE_PICKER_FILE_TYPE 504
+#define FILE_PICKER_OVERWRITE 505
+
+namespace CResourceProvider {
+
+NSString* getResString( sal_Int32 aId );
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/aqua/resourceprovider.mm b/fpicker/source/aqua/resourceprovider.mm
new file mode 100644
index 000000000..951833ae2
--- /dev/null
+++ b/fpicker/source/aqua/resourceprovider.mm
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <osl/mutex.hxx>
+#include <fpicker/strings.hrc>
+#include <vcl/svapp.hxx>
+#include <unotools/resmgr.hxx>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+
+#include "NSString_OOoAdditions.hxx"
+#include <fpicker/fpsofficeResMgr.hxx>
+#include "resourceprovider.hxx"
+
+using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
+using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
+
+// we have to translate control ids to resource ids
+
+namespace {
+
+struct Entry
+{
+ sal_Int32 ctrlId;
+ TranslateId resId;
+};
+
+}
+
+Entry const CtrlIdToResIdTable[] = {
+ { CHECKBOX_AUTOEXTENSION, STR_SVT_FILEPICKER_AUTO_EXTENSION },
+ { CHECKBOX_PASSWORD, STR_SVT_FILEPICKER_PASSWORD },
+ { CHECKBOX_FILTEROPTIONS, STR_SVT_FILEPICKER_FILTER_OPTIONS },
+ { CHECKBOX_READONLY, STR_SVT_FILEPICKER_READONLY },
+ { CHECKBOX_LINK, STR_SVT_FILEPICKER_INSERT_AS_LINK },
+ { CHECKBOX_PREVIEW, STR_SVT_FILEPICKER_SHOW_PREVIEW },
+ { PUSHBUTTON_PLAY, STR_SVT_FILEPICKER_PLAY },
+ { LISTBOX_VERSION_LABEL, STR_SVT_FILEPICKER_VERSION },
+ { LISTBOX_TEMPLATE_LABEL, STR_SVT_FILEPICKER_TEMPLATES },
+ { LISTBOX_IMAGE_TEMPLATE_LABEL, STR_SVT_FILEPICKER_IMAGE_TEMPLATE },
+ { LISTBOX_IMAGE_ANCHOR_LABEL, STR_SVT_FILEPICKER_IMAGE_ANCHOR },
+ { CHECKBOX_SELECTION, STR_SVT_FILEPICKER_SELECTION },
+ { FOLDERPICKER_TITLE, STR_SVT_FOLDERPICKER_DEFAULT_TITLE },
+ { FOLDER_PICKER_DEF_DESCRIPTION, STR_SVT_FOLDERPICKER_DEFAULT_DESCRIPTION },
+ { FILE_PICKER_OVERWRITE, STR_SVT_ALREADYEXISTOVERWRITE },
+ { LISTBOX_FILTER_LABEL, STR_SVT_FILEPICKER_FILTER_TITLE},
+ { FILE_PICKER_TITLE_OPEN, STR_FILEDLG_OPEN },
+ { FILE_PICKER_TITLE_SAVE, STR_FILEDLG_SAVE },
+ { FILE_PICKER_FILE_TYPE, STR_FILEDLG_TYPE }
+};
+
+const sal_Int32 SIZE_TABLE = SAL_N_ELEMENTS( CtrlIdToResIdTable );
+
+static TranslateId CtrlIdToResId(sal_Int32 aControlId)
+{
+ TranslateId pResId;
+
+ for ( sal_Int32 i = 0; i < SIZE_TABLE; i++ )
+ {
+ if ( CtrlIdToResIdTable[i].ctrlId == aControlId )
+ {
+ pResId = CtrlIdToResIdTable[i].resId;
+ break;
+ }
+ }
+
+ return pResId;
+}
+
+namespace CResourceProvider_Impl
+{
+ static NSString* getResString(sal_Int16 aId)
+ {
+ OUString aResString;
+
+ // translate the control id to a resource id
+ TranslateId pResId = CtrlIdToResId(aId);
+ if (pResId)
+ aResString = FpsResId(pResId);
+
+ return [NSString stringWithOUString:aResString];
+ }
+};
+
+NSString* CResourceProvider::getResString( sal_Int32 aId )
+{
+ NSString* sImmutable = CResourceProvider_Impl::getResString(aId);
+ NSMutableString *sMutableString = [NSMutableString stringWithString:sImmutable];
+ [sMutableString replaceOccurrencesOfString:@"~" withString:@"" options:0 range:NSMakeRange(0, [sMutableString length])];
+
+ NSString *result = [NSString stringWithString:sMutableString];
+
+ return result;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeControlAccess.cxx b/fpicker/source/office/OfficeControlAccess.cxx
new file mode 100644
index 000000000..3d06627da
--- /dev/null
+++ b/fpicker/source/office/OfficeControlAccess.cxx
@@ -0,0 +1,770 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/macros.h>
+#include "OfficeControlAccess.hxx"
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ControlActions.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+
+#include <algorithm>
+#include <utility>
+
+
+namespace svt
+{
+
+
+ // helper -------------------------------------------------------------
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::ui::dialogs;
+
+ using namespace ExtendedFilePickerElementIds;
+ using namespace CommonFilePickerElementIds;
+ using namespace InternalFilePickerElementIds;
+
+
+ namespace
+ {
+
+ struct ControlDescription
+ {
+ const char* pControlName;
+ sal_Int16 nControlId;
+ PropFlags nPropertyFlags;
+ };
+
+
+ typedef const ControlDescription* ControlDescIterator;
+
+
+ #define PROPERTY_FLAGS_COMMON ( PropFlags::Enabled | PropFlags::Visible | PropFlags::HelpUrl )
+ #define PROPERTY_FLAGS_LISTBOX ( PropFlags::ListItems | PropFlags::SelectedItem | PropFlags::SelectedItemIndex )
+ #define PROPERTY_FLAGS_CHECKBOX ( PropFlags::Checked | PropFlags::Text )
+
+ // Note: this array MUST be sorted by name!
+ const ControlDescription aDescriptions[] = {
+ { "AutoExtensionBox", CHECKBOX_AUTOEXTENSION, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "CancelButton", PUSHBUTTON_CANCEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "CurrentFolderText", FIXEDTEXT_CURRENTFOLDER, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "FileURLEdit", EDIT_FILEURL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "FileURLEditLabel", EDIT_FILEURL_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "FileView", CONTROL_FILEVIEW, PROPERTY_FLAGS_COMMON },
+ { "FilterList", LISTBOX_FILTER, PROPERTY_FLAGS_COMMON },
+ { "FilterListLabel", LISTBOX_FILTER_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "FilterOptionsBox", CHECKBOX_FILTEROPTIONS, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "GpgPassword", CHECKBOX_GPGENCRYPTION, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "HelpButton", PUSHBUTTON_HELP, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "ImageAnchorList", LISTBOX_IMAGE_ANCHOR, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_LISTBOX },
+ { "ImageAnchorListLabel", LISTBOX_IMAGE_ANCHOR_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "ImageTemplateList", LISTBOX_IMAGE_TEMPLATE, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_LISTBOX },
+ { "ImageTemplateListLabel", LISTBOX_IMAGE_TEMPLATE_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "LevelUpButton", TOOLBOXBUTTON_LEVEL_UP, PROPERTY_FLAGS_COMMON },
+ { "LinkBox", CHECKBOX_LINK, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "NewFolderButton", TOOLBOXBUTTON_NEW_FOLDER, PROPERTY_FLAGS_COMMON },
+ { "OkButton", PUSHBUTTON_OK , PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "PasswordBox", CHECKBOX_PASSWORD, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "PlayButton", PUSHBUTTON_PLAY, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "PreviewBox", CHECKBOX_PREVIEW, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "ReadOnlyBox", CHECKBOX_READONLY, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "SelectionBox", CHECKBOX_SELECTION, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_CHECKBOX },
+ { "TemplateList", LISTBOX_TEMPLATE, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_LISTBOX },
+ { "TemplateListLabel", LISTBOX_TEMPLATE_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text },
+ { "VersionList", LISTBOX_VERSION, PROPERTY_FLAGS_COMMON | PROPERTY_FLAGS_LISTBOX },
+ { "VersionListLabel", LISTBOX_VERSION_LABEL, PROPERTY_FLAGS_COMMON | PropFlags::Text }
+ };
+
+ const sal_Int32 s_nControlCount = SAL_N_ELEMENTS( aDescriptions );
+
+ ControlDescIterator s_pControls = aDescriptions;
+ ControlDescIterator s_pControlsEnd = aDescriptions + s_nControlCount;
+
+ struct ControlDescriptionLookup
+ {
+ bool operator()( const ControlDescription& rDesc1, const ControlDescription& rDesc2 )
+ {
+ return strcmp(rDesc1.pControlName, rDesc2.pControlName) < 0;
+ }
+ };
+
+ struct ControlProperty
+ {
+ const char* pPropertyName;
+ PropFlags nPropertyId;
+ };
+
+ typedef const ControlProperty* ControlPropertyIterator;
+
+ const ControlProperty aProperties[] = {
+ { "Text", PropFlags::Text },
+ { "Enabled", PropFlags::Enabled },
+ { "Visible", PropFlags::Visible },
+ { "HelpURL", PropFlags::HelpUrl },
+ { "ListItems", PropFlags::ListItems },
+ { "SelectedItem", PropFlags::SelectedItem },
+ { "SelectedItemIndex", PropFlags::SelectedItemIndex },
+ { "Checked", PropFlags::Checked }
+ };
+
+ const int s_nPropertyCount = SAL_N_ELEMENTS( aProperties );
+
+ ControlPropertyIterator s_pProperties = aProperties;
+ ControlPropertyIterator s_pPropertiesEnd = aProperties + s_nPropertyCount;
+
+
+ struct ControlPropertyLookup
+ {
+ OUString m_sLookup;
+ explicit ControlPropertyLookup(OUString aLookup)
+ : m_sLookup(std::move(aLookup))
+ {
+ }
+
+ bool operator()(const ControlProperty& rProp)
+ {
+ return m_sLookup.equalsAscii(rProp.pPropertyName);
+ }
+ };
+
+
+ void lcl_throwIllegalArgumentException( )
+ {
+ throw IllegalArgumentException();
+ // TODO: error message in the exception
+ }
+ }
+
+ OControlAccess::OControlAccess(IFilePickerController* pController, SvtFileView* pFileView)
+ : m_pFilePickerController(pController)
+ , m_pFileView(pFileView)
+ {
+ DBG_ASSERT( m_pFilePickerController, "OControlAccess::OControlAccess: invalid control locator!" );
+ }
+
+ bool OControlAccess::IsFileViewWidget(weld::Widget const * pControl) const
+ {
+ if (!pControl)
+ return false;
+ if (!m_pFileView)
+ return false;
+ return pControl == m_pFileView->identifier();
+ }
+
+ void OControlAccess::setHelpURL(weld::Widget* pControl, const OUString& sHelpURL)
+ {
+ OUString sHelpID( sHelpURL );
+ INetURLObject aHID( sHelpURL );
+ if (aHID.GetProtocol() == INetProtocol::Hid)
+ sHelpID = aHID.GetURLPath();
+
+ // URLs should always be UTF8 encoded and escaped
+ OString sID( OUStringToOString( sHelpID, RTL_TEXTENCODING_UTF8 ) );
+ if (IsFileViewWidget(pControl))
+ {
+ // the file view "overrides" the SetHelpId
+ m_pFileView->set_help_id(sID);
+ }
+ else
+ pControl->set_help_id(sID);
+ }
+
+ OUString OControlAccess::getHelpURL(weld::Widget const * pControl) const
+ {
+ OString aHelpId = pControl->get_help_id();
+ if (IsFileViewWidget(pControl))
+ {
+ // the file view "overrides" the SetHelpId
+ aHelpId = m_pFileView->get_help_id();
+ }
+
+ OUString sHelpURL;
+ OUString aTmp( OStringToOUString( aHelpId, RTL_TEXTENCODING_UTF8 ) );
+ INetURLObject aHID( aTmp );
+ if ( aHID.GetProtocol() == INetProtocol::NotValid )
+ sHelpURL = INET_HID_SCHEME;
+ sHelpURL += aTmp;
+ return sHelpURL;
+ }
+
+ Any OControlAccess::getControlProperty( std::u16string_view rControlName, const OUString& rControlProperty )
+ {
+ // look up the control
+ sal_Int16 nControlId = -1;
+ PropFlags nPropertyMask = PropFlags::NONE;
+ weld::Widget* pControl = implGetControl( rControlName, &nControlId, &nPropertyMask );
+ // will throw an IllegalArgumentException if the name is not valid
+
+ // look up the property
+ ControlPropertyIterator aPropDesc = ::std::find_if( s_pProperties, s_pPropertiesEnd, ControlPropertyLookup( rControlProperty ) );
+ if ( aPropDesc == s_pPropertiesEnd )
+ // it's a completely unknown property
+ lcl_throwIllegalArgumentException();
+
+ if ( !( nPropertyMask & aPropDesc->nPropertyId ) )
+ // it's a property which is known, but not allowed for this control
+ lcl_throwIllegalArgumentException();
+
+ return implGetControlProperty( pControl, aPropDesc->nPropertyId );
+ }
+
+ weld::Widget* OControlAccess::implGetControl( std::u16string_view rControlName, sal_Int16* _pId, PropFlags* _pPropertyMask ) const
+ {
+ weld::Widget* pControl = nullptr;
+ ControlDescription tmpDesc;
+ OString aControlName = OUStringToOString( rControlName, RTL_TEXTENCODING_UTF8 );
+ tmpDesc.pControlName = aControlName.getStr();
+
+ // translate the name into an id
+ auto aFoundRange = ::std::equal_range( s_pControls, s_pControlsEnd, tmpDesc, ControlDescriptionLookup() );
+ if ( aFoundRange.first != aFoundRange.second )
+ {
+ // get the VCL control determined by this id
+ pControl = m_pFilePickerController->getControl( aFoundRange.first->nControlId );
+ }
+
+ // if not found 'til here, the name is invalid, or we do not have the control in the current mode
+ if ( !pControl )
+ lcl_throwIllegalArgumentException();
+
+ // out parameters and outta here
+ if ( _pId )
+ *_pId = aFoundRange.first->nControlId;
+ if ( _pPropertyMask )
+ *_pPropertyMask = aFoundRange.first->nPropertyFlags;
+
+ return pControl;
+ }
+
+ void OControlAccess::setControlProperty( std::u16string_view rControlName, const OUString& rControlProperty, const css::uno::Any& rValue )
+ {
+ // look up the control
+ sal_Int16 nControlId = -1;
+ weld::Widget* pControl = implGetControl( rControlName, &nControlId );
+ // will throw an IllegalArgumentException if the name is not valid
+
+ // look up the property
+ ControlPropertyIterator aPropDesc = ::std::find_if( s_pProperties, s_pPropertiesEnd, ControlPropertyLookup( rControlProperty ) );
+ if ( aPropDesc == s_pPropertiesEnd )
+ lcl_throwIllegalArgumentException();
+
+ // set the property
+ implSetControlProperty( nControlId, pControl, aPropDesc->nPropertyId, rValue, false );
+ }
+
+ Sequence< OUString > OControlAccess::getSupportedControls( ) const
+ {
+ Sequence< OUString > aControls( s_nControlCount );
+ OUString* pControls = aControls.getArray();
+
+ // collect the names of all _actually_existent_ controls
+ for ( ControlDescIterator aControl = s_pControls; aControl != s_pControlsEnd; ++aControl )
+ {
+ if ( m_pFilePickerController->getControl( aControl->nControlId ) )
+ *pControls++ = OUString::createFromAscii( aControl->pControlName );
+ }
+
+ aControls.realloc( pControls - aControls.getArray() );
+ return aControls;
+ }
+
+ Sequence< OUString > OControlAccess::getSupportedControlProperties( std::u16string_view rControlName )
+ {
+ sal_Int16 nControlId = -1;
+ PropFlags nPropertyMask = PropFlags::NONE;
+ implGetControl( rControlName, &nControlId, &nPropertyMask );
+ // will throw an IllegalArgumentException if the name is not valid
+
+ // fill in the property names
+ Sequence< OUString > aProps( s_nPropertyCount );
+ OUString* pProperty = aProps.getArray();
+
+ for ( ControlPropertyIterator aProp = s_pProperties; aProp != s_pPropertiesEnd; ++aProp )
+ if ( nPropertyMask & aProp->nPropertyId )
+ *pProperty++ = OUString::createFromAscii( aProp->pPropertyName );
+
+ aProps.realloc( pProperty - aProps.getArray() );
+ return aProps;
+ }
+
+ bool OControlAccess::isControlSupported( std::u16string_view rControlName )
+ {
+ ControlDescription tmpDesc;
+ OString aControlName = OUStringToOString(rControlName, RTL_TEXTENCODING_UTF8);
+ tmpDesc.pControlName = aControlName.getStr();
+ return ::std::binary_search( s_pControls, s_pControlsEnd, tmpDesc, ControlDescriptionLookup() );
+ }
+
+ bool OControlAccess::isControlPropertySupported( std::u16string_view rControlName, const OUString& rControlProperty )
+ {
+ // look up the control
+ sal_Int16 nControlId = -1;
+ PropFlags nPropertyMask = PropFlags::NONE;
+ implGetControl( rControlName, &nControlId, &nPropertyMask );
+ // will throw an IllegalArgumentException if the name is not valid
+
+ // look up the property
+ ControlPropertyIterator aPropDesc = ::std::find_if( s_pProperties, s_pPropertiesEnd, ControlPropertyLookup( rControlProperty ) );
+ if ( aPropDesc == s_pPropertiesEnd )
+ // it's a property which is completely unknown
+ return false;
+
+ return bool( aPropDesc->nPropertyId & nPropertyMask );
+ }
+
+ void OControlAccess::setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const Any& rValue )
+ {
+ weld::Widget* pControl = m_pFilePickerController->getControl( nControlId );
+ DBG_ASSERT( pControl, "OControlAccess::SetValue: don't have this control in the current mode!" );
+ if ( !pControl )
+ return;
+
+ PropFlags nPropertyId = PropFlags::Unknown;
+ if ( ControlActions::SET_HELP_URL == nControlAction )
+ {
+ nPropertyId = PropFlags::HelpUrl;
+ }
+ else
+ {
+ switch ( nControlId )
+ {
+ case CHECKBOX_AUTOEXTENSION:
+ case CHECKBOX_PASSWORD:
+ case CHECKBOX_FILTEROPTIONS:
+ case CHECKBOX_READONLY:
+ case CHECKBOX_LINK:
+ case CHECKBOX_PREVIEW:
+ case CHECKBOX_SELECTION:
+ nPropertyId = PropFlags::Checked;
+ break;
+
+ case LISTBOX_FILTER:
+ SAL_WARN( "fpicker.office", "Use the XFilterManager to access the filter listbox" );
+ break;
+
+ case LISTBOX_VERSION:
+ case LISTBOX_TEMPLATE:
+ case LISTBOX_IMAGE_TEMPLATE:
+ case LISTBOX_IMAGE_ANCHOR:
+ if ( ControlActions::SET_SELECT_ITEM == nControlAction )
+ {
+ nPropertyId = PropFlags::SelectedItemIndex;
+ }
+ else
+ {
+ weld::ComboBox* pComboBox = dynamic_cast<weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::SetValue: implGetControl returned nonsense!");
+ implDoListboxAction(pComboBox, nControlAction, rValue);
+ }
+ break;
+ }
+ }
+
+ if ( PropFlags::Unknown != nPropertyId )
+ implSetControlProperty( nControlId, pControl, nPropertyId, rValue );
+ }
+
+ Any OControlAccess::getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) const
+ {
+ Any aRet;
+
+ weld::Widget* pControl = m_pFilePickerController->getControl( nControlId );
+ DBG_ASSERT( pControl, "OControlAccess::GetValue: don't have this control in the current mode!" );
+ if ( pControl )
+ {
+ PropFlags nPropertyId = PropFlags::Unknown;
+ if ( ControlActions::SET_HELP_URL == nControlAction )
+ {
+ nPropertyId = PropFlags::HelpUrl;
+ }
+ else
+ {
+ switch ( nControlId )
+ {
+ case CHECKBOX_AUTOEXTENSION:
+ case CHECKBOX_PASSWORD:
+ case CHECKBOX_GPGENCRYPTION:
+ case CHECKBOX_FILTEROPTIONS:
+ case CHECKBOX_READONLY:
+ case CHECKBOX_LINK:
+ case CHECKBOX_PREVIEW:
+ case CHECKBOX_SELECTION:
+ nPropertyId = PropFlags::Checked;
+ break;
+
+ case LISTBOX_FILTER:
+ if ( ControlActions::GET_SELECTED_ITEM == nControlAction )
+ {
+ aRet <<= m_pFilePickerController->getCurFilter();
+ }
+ else
+ {
+ SAL_WARN( "fpicker.office", "Use the XFilterManager to access the filter listbox" );
+ }
+ break;
+
+ case LISTBOX_VERSION:
+ case LISTBOX_TEMPLATE:
+ case LISTBOX_IMAGE_TEMPLATE:
+ case LISTBOX_IMAGE_ANCHOR:
+ switch ( nControlAction )
+ {
+ case ControlActions::GET_SELECTED_ITEM:
+ nPropertyId = PropFlags::SelectedItem;
+ break;
+ case ControlActions::GET_SELECTED_ITEM_INDEX:
+ nPropertyId = PropFlags::SelectedItemIndex;
+ break;
+ case ControlActions::GET_ITEMS:
+ nPropertyId = PropFlags::ListItems;
+ break;
+ default:
+ SAL_WARN( "fpicker.office", "OControlAccess::GetValue: invalid control action for the listbox!" );
+ break;
+ }
+ break;
+ }
+ }
+
+ if ( PropFlags::Unknown != nPropertyId )
+ aRet = implGetControlProperty( pControl, nPropertyId );
+ }
+
+ return aRet;
+ }
+
+ void OControlAccess::setLabel( sal_Int16 nId, const OUString &rLabel )
+ {
+ weld::Widget* pControl = m_pFilePickerController->getControl(nId, true);
+ if (weld::Label* pLabel = dynamic_cast<weld::Label*>(pControl))
+ {
+ pLabel->set_label(rLabel);
+ return;
+ }
+ if (weld::Button* pButton = dynamic_cast<weld::Button*>(pControl))
+ {
+ pButton->set_label(rLabel);
+ return;
+ }
+ assert(false && "OControlAccess::GetValue: don't have this control in the current mode!");
+ }
+
+ OUString OControlAccess::getLabel( sal_Int16 nId ) const
+ {
+ weld::Widget* pControl = m_pFilePickerController->getControl(nId, true);
+ if (weld::Label* pLabel = dynamic_cast<weld::Label*>(pControl))
+ return pLabel->get_label();
+ if (weld::Button* pButton = dynamic_cast<weld::Button*>(pControl))
+ return pButton->get_label();
+ assert(false && "OControlAccess::GetValue: don't have this control in the current mode!");
+ return OUString();
+ }
+
+ void OControlAccess::enableControl(sal_Int16 nId, bool bEnable)
+ {
+ m_pFilePickerController->enableControl(nId, bEnable);
+ }
+
+ void OControlAccess::implDoListboxAction(weld::ComboBox* pListbox, sal_Int16 nControlAction, const Any& rValue)
+ {
+ switch ( nControlAction )
+ {
+ case ControlActions::ADD_ITEM:
+ {
+ OUString aEntry;
+ rValue >>= aEntry;
+ if ( !aEntry.isEmpty() )
+ pListbox->append_text( aEntry );
+ }
+ break;
+
+ case ControlActions::ADD_ITEMS:
+ {
+ Sequence < OUString > aTemplateList;
+ rValue >>= aTemplateList;
+
+ if ( aTemplateList.hasElements() )
+ {
+ for ( const OUString& s : std::as_const(aTemplateList) )
+ pListbox->append_text( s );
+ }
+ }
+ break;
+
+ case ControlActions::DELETE_ITEM:
+ {
+ sal_Int32 nPos = 0;
+ if ( rValue >>= nPos )
+ pListbox->remove( nPos );
+ }
+ break;
+
+ case ControlActions::DELETE_ITEMS:
+ pListbox->clear();
+ break;
+
+ default:
+ SAL_WARN( "fpicker.office", "Wrong ControlAction for implDoListboxAction()" );
+ }
+ }
+
+ void OControlAccess::implSetControlProperty( sal_Int16 nControlId, weld::Widget* pControl, PropFlags _nProperty, const Any& rValue, bool _bIgnoreIllegalArgument )
+ {
+ if ( !pControl )
+ pControl = m_pFilePickerController->getControl( nControlId );
+ DBG_ASSERT( pControl, "OControlAccess::implSetControlProperty: invalid argument, this will crash!" );
+ if ( !pControl )
+ return;
+
+ DBG_ASSERT( pControl == m_pFilePickerController->getControl( nControlId ),
+ "OControlAccess::implSetControlProperty: inconsistent parameters!" );
+
+ switch ( _nProperty )
+ {
+ case PropFlags::Text:
+ {
+ OUString sText;
+ if (rValue >>= sText)
+ {
+ weld::Label* pLabel = dynamic_cast<weld::Label*>(pControl);
+ assert(pLabel);
+ pLabel->set_label(sText);
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ lcl_throwIllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::Enabled:
+ {
+ bool bEnabled = false;
+ if ( rValue >>= bEnabled )
+ {
+ m_pFilePickerController->enableControl( nControlId, bEnabled );
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ lcl_throwIllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::Visible:
+ {
+ bool bVisible = false;
+ if ( rValue >>= bVisible )
+ {
+ pControl->set_visible( bVisible );
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ lcl_throwIllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::HelpUrl:
+ {
+ OUString sHelpURL;
+ if ( rValue >>= sHelpURL )
+ {
+ setHelpURL(pControl, sHelpURL);
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ lcl_throwIllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::ListItems:
+ {
+ weld::ComboBox* pComboBox = dynamic_cast<weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implSetControlProperty: invalid control/property combination!");
+
+ Sequence< OUString > aItems;
+ if ( rValue >>= aItems )
+ {
+ // remove all previous items
+ pComboBox->clear();
+
+ // add the new ones
+ for (auto const & item : std::as_const(aItems))
+ {
+ pComboBox->append_text(item);
+ }
+
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ lcl_throwIllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::SelectedItem:
+ {
+ weld::ComboBox* pComboBox = dynamic_cast<weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implSetControlProperty: invalid control/property combination!");
+
+ OUString sSelected;
+ if ( rValue >>= sSelected )
+ {
+ pComboBox->set_active_text(sSelected);
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ lcl_throwIllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::SelectedItemIndex:
+ {
+ weld::ComboBox* pComboBox = dynamic_cast<weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implSetControlProperty: invalid control/property combination!");
+
+ sal_Int32 nPos = 0;
+ if ( rValue >>= nPos )
+ {
+ pComboBox->set_active(nPos);
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ lcl_throwIllegalArgumentException();
+ }
+ }
+ break;
+
+ case PropFlags::Checked:
+ {
+ weld::Toggleable* pToggleButton = dynamic_cast<weld::Toggleable*>(pControl);
+ assert(pToggleButton && "OControlAccess::implSetControlProperty: invalid control/property combination!");
+
+ bool bChecked = false;
+ if ( rValue >>= bChecked )
+ {
+ pToggleButton->set_active(bChecked);
+ }
+ else if ( !_bIgnoreIllegalArgument )
+ {
+ lcl_throwIllegalArgumentException();
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "OControlAccess::implSetControlProperty: invalid property id!" );
+ }
+ }
+
+ Any OControlAccess::implGetControlProperty( weld::Widget const * pControl, PropFlags _nProperty ) const
+ {
+ assert(pControl && "OControlAccess::implGetControlProperty: invalid argument, this will crash!");
+
+ Any aReturn;
+ switch ( _nProperty )
+ {
+ case PropFlags::Text:
+ {
+ const weld::Label* pLabel = dynamic_cast<const weld::Label*>(pControl);
+ assert(pLabel);
+ aReturn <<= pLabel->get_label();
+ break;
+ }
+ case PropFlags::Enabled:
+ aReturn <<= pControl->get_sensitive();
+ break;
+
+ case PropFlags::Visible:
+ aReturn <<= pControl->get_visible();
+ break;
+
+ case PropFlags::HelpUrl:
+ aReturn <<= getHelpURL(pControl);
+ break;
+
+ case PropFlags::ListItems:
+ {
+ const weld::ComboBox* pComboBox = dynamic_cast<const weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implGetControlProperty: invalid control/property combination!");
+
+ Sequence< OUString > aItems(pComboBox->get_count());
+ OUString* pItems = aItems.getArray();
+ for (sal_Int32 i = 0; i < pComboBox->get_count(); ++i)
+ *pItems++ = pComboBox->get_text(i);
+
+ aReturn <<= aItems;
+ break;
+ }
+
+ case PropFlags::SelectedItem:
+ {
+ const weld::ComboBox* pComboBox = dynamic_cast<const weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implGetControlProperty: invalid control/property combination!");
+
+ sal_Int32 nSelected = pComboBox->get_active();
+ OUString sSelected;
+ if (nSelected != -1)
+ sSelected = pComboBox->get_active_text();
+ aReturn <<= sSelected;
+ break;
+ }
+
+ case PropFlags::SelectedItemIndex:
+ {
+ const weld::ComboBox* pComboBox = dynamic_cast<const weld::ComboBox*>(pControl);
+ assert(pComboBox && "OControlAccess::implGetControlProperty: invalid control/property combination!");
+
+ sal_Int32 nSelected = pComboBox->get_active();
+ if (nSelected != -1)
+ aReturn <<= nSelected;
+ else
+ aReturn <<= sal_Int32(-1);
+ break;
+ }
+
+ case PropFlags::Checked:
+ {
+ const weld::Toggleable* pToggleButton = dynamic_cast<const weld::Toggleable*>(pControl);
+ assert(pToggleButton && "OControlAccess::implGetControlProperty: invalid control/property combination!");
+
+ aReturn <<= pToggleButton->get_active();
+ break;
+ }
+
+ default:
+ OSL_FAIL( "OControlAccess::implGetControlProperty: invalid property id!" );
+ }
+ return aReturn;
+ }
+
+} // namespace svt
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeControlAccess.hxx b/fpicker/source/office/OfficeControlAccess.hxx
new file mode 100644
index 000000000..859ff15f2
--- /dev/null
+++ b/fpicker/source/office/OfficeControlAccess.hxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "fileview.hxx"
+#include "pickercallbacks.hxx"
+#include <o3tl/typed_flags_set.hxx>
+
+enum class PropFlags {
+ Unknown = -1, // used as an error sentinel
+ NONE = 0x0000,
+ Text = 0x0001,
+ Enabled = 0x0002,
+ Visible = 0x0004,
+ HelpUrl = 0x0008,
+ ListItems = 0x0010,
+ SelectedItem = 0x0020,
+ SelectedItemIndex = 0x0040,
+ Checked = 0x0080,
+};
+namespace o3tl {
+ template<> struct typed_flags<PropFlags> : is_typed_flags<PropFlags, 0x00ff> {};
+}
+
+
+namespace svt
+{
+
+
+ namespace InternalFilePickerElementIds
+ {
+ const sal_Int16 PUSHBUTTON_HELP = sal_Int16(0x1000);
+ const sal_Int16 TOOLBOXBUTTON_LEVEL_UP = sal_Int16(0x1002);
+ const sal_Int16 TOOLBOXBUTTON_NEW_FOLDER = sal_Int16(0x1003);
+ const sal_Int16 FIXEDTEXT_CURRENTFOLDER = sal_Int16(0x1004);
+ }
+
+
+ /** implements the XControlAccess, XControlInformation and XFilePickerControlAccess for the file picker
+ */
+ class OControlAccess
+ {
+ IFilePickerController* m_pFilePickerController;
+ SvtFileView* m_pFileView;
+
+ public:
+ OControlAccess( IFilePickerController* pController, SvtFileView* pFileView );
+
+ // XControlAccess implementation
+ void setControlProperty( std::u16string_view rControlName, const OUString& rControlProperty, const css::uno::Any& rValue );
+ css::uno::Any getControlProperty( std::u16string_view rControlName, const OUString& rControlProperty );
+
+ // XControlInformation implementation
+ css::uno::Sequence< OUString > getSupportedControls( ) const;
+ css::uno::Sequence< OUString > getSupportedControlProperties( std::u16string_view rControlName );
+ static bool isControlSupported( std::u16string_view rControlName );
+ bool isControlPropertySupported( std::u16string_view rControlName, const OUString& rControlProperty );
+
+ // XFilePickerControlAccess
+ void setValue( sal_Int16 nId, sal_Int16 nCtrlAction, const css::uno::Any& rValue );
+ css::uno::Any getValue( sal_Int16 nId, sal_Int16 nCtrlAction ) const;
+ void setLabel( sal_Int16 nId, const OUString& rValue );
+ OUString getLabel( sal_Int16 nId ) const;
+ void enableControl( sal_Int16 nId, bool bEnable );
+
+ void setHelpURL(weld::Widget* pControl, const OUString& rURL);
+ OUString getHelpURL(weld::Widget const* pControl) const;
+
+ private:
+ /** implements the various methods for setting properties on controls
+
+ @param nControlId
+ the id of the control
+ @param pControl
+ the affected control. Must be the same as referred by <arg>nControlId</arg>, or NULL.
+ @param nProperty
+ the property to set
+ See PropFlags::*
+ @param rValue
+ the value to set
+ @param bIgnoreIllegalArgument
+ if <FALSE/>, an exception will be thrown if the given value is of improper type
+ */
+ void implSetControlProperty(
+ sal_Int16 nControlId,
+ weld::Widget* pControl, PropFlags nProperty, const css::uno::Any& rValue,
+ bool bIgnoreIllegalArgument = true );
+
+ weld::Widget* implGetControl( std::u16string_view rControlName, sal_Int16* pId, PropFlags* pPropertyMask = nullptr ) const;
+
+ /** implements the various methods for retrieving properties from controls
+
+ @param pControl
+ the affected control
+ @PRECOND not <NULL/>
+ @param nProperty
+ the property to retrieve
+ See PropFlags::*
+ @return
+ */
+ css::uno::Any implGetControlProperty( weld::Widget const * pControl, PropFlags nProperty ) const;
+
+ bool IsFileViewWidget(weld::Widget const * pControl) const;
+
+ static void implDoListboxAction(weld::ComboBox* pListbox, sal_Int16 nCtrlAction, const css::uno::Any& rValue);
+
+ };
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeFilePicker.cxx b/fpicker/source/office/OfficeFilePicker.cxx
new file mode 100644
index 000000000..7410e0c96
--- /dev/null
+++ b/fpicker/source/office/OfficeFilePicker.cxx
@@ -0,0 +1,1108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "OfficeControlAccess.hxx"
+#include "OfficeFilePicker.hxx"
+#include "iodlg.hxx"
+#include "RemoteFilesDialog.hxx"
+
+#include <utility>
+#include <vector>
+#include <algorithm>
+#include <sal/log.hxx>
+#include <tools/urlobj.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/ui/dialogs/FilePickerEvent.hpp>
+#include <com/sun/star/ui/dialogs/FilePreviewImageFormats.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <unotools/pathoptions.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/make_shared.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::awt;
+using namespace ::utl;
+
+
+struct FilterEntry
+{
+protected:
+ OUString m_sTitle;
+ OUString m_sFilter;
+
+ UnoFilterList m_aSubFilters;
+
+public:
+ FilterEntry( OUString _aTitle, OUString _aFilter )
+ :m_sTitle(std::move( _aTitle ))
+ ,m_sFilter(std::move( _aFilter ))
+ {
+ }
+
+ FilterEntry( OUString _aTitle, const UnoFilterList& _rSubFilters );
+
+ const OUString& getTitle() const { return m_sTitle; }
+ const OUString& getFilter() const { return m_sFilter; }
+
+ /// determines if the filter has sub filter (i.e., the filter is a filter group in real)
+ bool hasSubFilters( ) const;
+
+ /** retrieves the filters belonging to the entry
+ */
+ void getSubFilters( UnoFilterList& _rSubFilterList );
+
+ // helpers for iterating the sub filters
+ const UnoFilterEntry* beginSubFilters() const { return m_aSubFilters.getConstArray(); }
+ const UnoFilterEntry* endSubFilters() const { return m_aSubFilters.getConstArray() + m_aSubFilters.getLength(); }
+};
+
+
+FilterEntry::FilterEntry( OUString _aTitle, const UnoFilterList& _rSubFilters )
+ :m_sTitle(std::move( _aTitle ))
+ ,m_aSubFilters( _rSubFilters )
+{
+}
+
+
+bool FilterEntry::hasSubFilters( ) const
+{
+ return m_aSubFilters.hasElements();
+}
+
+
+void FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
+{
+ _rSubFilterList = m_aSubFilters;
+}
+
+struct ElementEntry_Impl
+{
+ sal_Int16 m_nElementID;
+ sal_Int16 m_nControlAction;
+ Any m_aValue;
+ OUString m_aLabel;
+ bool m_bEnabled : 1;
+
+ bool m_bHasValue : 1;
+ bool m_bHasLabel : 1;
+ bool m_bHasEnabled : 1;
+
+ explicit ElementEntry_Impl( sal_Int16 nId );
+
+ void setValue( const Any& rVal ) { m_aValue = rVal; m_bHasValue = true; }
+ void setAction( sal_Int16 nAction ) { m_nControlAction = nAction; }
+ void setLabel( const OUString& rVal ) { m_aLabel = rVal; m_bHasLabel = true; }
+ void setEnabled( bool bEnabled ) { m_bEnabled = bEnabled; m_bHasEnabled = true; }
+};
+
+ElementEntry_Impl::ElementEntry_Impl( sal_Int16 nId )
+ : m_nElementID( nId )
+ , m_nControlAction( 0 )
+ , m_bEnabled( false )
+ , m_bHasValue( false )
+ , m_bHasLabel( false )
+ , m_bHasEnabled( false )
+{}
+
+
+void SvtFilePicker::prepareExecute()
+{
+ // set the default directory
+ // --**-- doesn't match the spec yet
+ if ( !m_aDisplayDirectory.isEmpty() || !m_aDefaultName.isEmpty() )
+ {
+ bool isFileSet = false;
+ if ( !m_aDisplayDirectory.isEmpty() )
+ {
+
+ INetURLObject aPath;
+ INetURLObject givenPath( m_aDisplayDirectory );
+ if (!givenPath.HasError())
+ aPath = givenPath;
+ else
+ {
+ aPath = INetURLObject( SvtPathOptions().GetWorkPath() );
+ }
+ if ( !m_aDefaultName.isEmpty() )
+ {
+ aPath.insertName( m_aDefaultName );
+ m_xDlg->SetHasFilename( true );
+ }
+ m_xDlg->SetPath( aPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ isFileSet = true;
+ }
+ if ( !isFileSet && !m_aDefaultName.isEmpty() )
+ {
+ m_xDlg->SetPath( m_aDefaultName );
+ m_xDlg->SetHasFilename( true );
+ }
+ }
+ else
+ {
+ // set the default standard dir
+ INetURLObject aStdDirObj( SvtPathOptions().GetWorkPath() );
+ m_xDlg->SetPath( aStdDirObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+
+ // set the control values and set the control labels, too
+ if ( m_pElemList && !m_pElemList->empty() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+
+ for (auto const& elem : *m_pElemList)
+ {
+ if ( elem.m_bHasValue )
+ aAccess.setValue( elem.m_nElementID, elem.m_nControlAction, elem.m_aValue );
+ if ( elem.m_bHasLabel )
+ aAccess.setLabel( elem.m_nElementID, elem.m_aLabel );
+ if ( elem.m_bHasEnabled )
+ aAccess.enableControl( elem.m_nElementID, elem.m_bEnabled );
+ }
+
+ }
+
+ if ( m_pFilterList )
+ {
+ for (auto & elem : *m_pFilterList)
+ {
+ if ( elem.hasSubFilters() )
+ { // it's a filter group
+ UnoFilterList aSubFilters;
+ elem.getSubFilters( aSubFilters );
+
+ m_xDlg->AddFilterGroup( elem.getTitle(), aSubFilters );
+ }
+ else
+ {
+ // it's a single filter
+ m_xDlg->AddFilter( elem.getTitle(), elem.getFilter() );
+ }
+ }
+ }
+
+ // set the default filter
+ if ( !m_aCurrentFilter.isEmpty() )
+ m_xDlg->SetCurFilter( m_aCurrentFilter );
+
+}
+
+void SvtFilePicker::DialogClosedHdl(sal_Int32 nResult)
+{
+ if ( m_xDlgClosedListener.is() )
+ {
+ sal_Int16 nRet = static_cast< sal_Int16 >(nResult);
+ css::ui::dialogs::DialogClosedEvent aEvent( *this, nRet );
+ m_xDlgClosedListener->dialogClosed( aEvent );
+ m_xDlgClosedListener.clear();
+ }
+}
+
+// SvtFilePicker
+PickerFlags SvtFilePicker::getPickerFlags() const
+{
+ // set the winbits for creating the filedialog
+ PickerFlags nBits = PickerFlags::NONE;
+
+ // set the standard bits according to the service name
+ if ( m_nServiceType == TemplateDescription::FILEOPEN_SIMPLE )
+ {
+ nBits = PickerFlags::Open;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_SIMPLE )
+ {
+ nBits = PickerFlags::SaveAs;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_AUTOEXTENSION )
+ {
+ nBits = PickerFlags::SaveAs | PickerFlags::AutoExtension;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD )
+ {
+ nBits = PickerFlags::SaveAs | PickerFlags::Password | PickerFlags::AutoExtension;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS )
+ {
+ nBits = PickerFlags::SaveAs | PickerFlags::Password | PickerFlags::AutoExtension | PickerFlags::FilterOptions;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_AUTOEXTENSION_TEMPLATE )
+ {
+ nBits = PickerFlags::SaveAs | PickerFlags::AutoExtension | PickerFlags::Templates;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILESAVE_AUTOEXTENSION_SELECTION )
+ {
+ nBits = PickerFlags::SaveAs | PickerFlags::AutoExtension | PickerFlags::Selection;
+ }
+
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE )
+ {
+ nBits = PickerFlags::Open | PickerFlags::InsertAsLink | PickerFlags::ShowPreview | PickerFlags::ImageTemplate;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_PLAY )
+ {
+ nBits = PickerFlags::Open | PickerFlags::PlayButton;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_LINK_PLAY )
+ {
+ nBits = PickerFlags::Open | PickerFlags::InsertAsLink | PickerFlags::PlayButton;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_READONLY_VERSION )
+ {
+ nBits = PickerFlags::Open | PickerFlags::ReadOnly | PickerFlags::ShowVersions;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_LINK_PREVIEW )
+ {
+ nBits = PickerFlags::Open | PickerFlags::InsertAsLink | PickerFlags::ShowPreview;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR )
+ {
+ nBits = PickerFlags::Open | PickerFlags::InsertAsLink | PickerFlags::ShowPreview | PickerFlags::ImageAnchor;
+ }
+ else if ( m_nServiceType == TemplateDescription::FILEOPEN_PREVIEW )
+ {
+ nBits = PickerFlags::Open | PickerFlags::ShowPreview;
+ }
+ if ( m_bMultiSelection && ( nBits & PickerFlags::Open ) )
+ nBits |= PickerFlags::MultiSelection;
+
+ return nBits;
+}
+
+
+void SvtFilePicker::notify( sal_Int16 _nEventId, sal_Int16 _nControlId )
+{
+ if ( !m_xListener.is() )
+ return;
+
+ FilePickerEvent aEvent( *this, _nControlId );
+
+ switch ( _nEventId )
+ {
+ case FILE_SELECTION_CHANGED:
+ m_xListener->fileSelectionChanged( aEvent );
+ break;
+ case DIRECTORY_CHANGED:
+ m_xListener->directoryChanged( aEvent );
+ break;
+ case CTRL_STATE_CHANGED:
+ m_xListener->controlStateChanged( aEvent );
+ break;
+ case DIALOG_SIZE_CHANGED:
+ m_xListener->dialogSizeChanged();
+ break;
+ default:
+ SAL_WARN( "fpicker.office", "SvtFilePicker::notify(): Unknown event id!" );
+ break;
+ }
+}
+
+
+namespace {
+
+ struct FilterTitleMatch
+ {
+ protected:
+ const OUString& rTitle;
+
+ public:
+ explicit FilterTitleMatch( const OUString& _rTitle ) : rTitle( _rTitle ) { }
+
+
+ bool operator () ( const FilterEntry& _rEntry )
+ {
+ bool bMatch;
+ if ( !_rEntry.hasSubFilters() )
+ // a real filter
+ bMatch = ( _rEntry.getTitle() == rTitle );
+ else
+ // a filter group -> search the sub filters
+ bMatch =
+ ::std::any_of(
+ _rEntry.beginSubFilters(),
+ _rEntry.endSubFilters(),
+ *this
+ );
+
+ return bMatch;
+ }
+ bool operator () ( const UnoFilterEntry& _rEntry )
+ {
+ return _rEntry.First == rTitle;
+ }
+ };
+}
+
+
+bool SvtFilePicker::FilterNameExists( const OUString& rTitle )
+{
+ bool bRet = false;
+
+ if ( m_pFilterList )
+ bRet =
+ ::std::any_of(
+ m_pFilterList->begin(),
+ m_pFilterList->end(),
+ FilterTitleMatch( rTitle )
+ );
+
+ return bRet;
+}
+
+
+bool SvtFilePicker::FilterNameExists( const UnoFilterList& _rGroupedFilters )
+{
+ bool bRet = false;
+
+ if ( m_pFilterList )
+ {
+ const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
+ const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
+ for ( ; pStart != pEnd; ++pStart )
+ if ( ::std::any_of( m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch( pStart->First ) ) )
+ break;
+
+ bRet = pStart != pEnd;
+ }
+
+ return bRet;
+}
+
+
+void SvtFilePicker::ensureFilterList( const OUString& _rInitialCurrentFilter )
+{
+ if ( !m_pFilterList )
+ {
+ m_pFilterList.reset( new FilterList );
+
+ // set the first filter to the current filter
+ if ( m_aCurrentFilter.isEmpty() )
+ m_aCurrentFilter = _rInitialCurrentFilter;
+ }
+}
+
+
+
+SvtFilePicker::SvtFilePicker()
+ :m_bMultiSelection ( false )
+ ,m_nServiceType ( TemplateDescription::FILEOPEN_SIMPLE )
+{
+}
+
+SvtFilePicker::~SvtFilePicker()
+{
+}
+
+sal_Int16 SvtFilePicker::implExecutePicker( )
+{
+ m_xDlg->SetFileCallback( this );
+
+ prepareExecute();
+
+ m_xDlg->EnableAutocompletion();
+ // now we are ready to execute the dialog
+ sal_Int16 nRet = m_xDlg->run();
+
+ // the execution of the dialog yields, so it is possible the at this point the window or the dialog is closed
+ if (m_xDlg)
+ m_xDlg->SetFileCallback( nullptr );
+
+ return nRet;
+}
+
+std::shared_ptr<SvtFileDialog_Base> SvtFilePicker::implCreateDialog( weld::Window* pParent )
+{
+ PickerFlags nBits = getPickerFlags();
+
+ auto dialog = o3tl::make_shared<SvtFileDialog>(pParent, nBits);
+
+ // Set StandardDir if present
+ if ( !m_aStandardDir.isEmpty())
+ {
+ OUString sStandardDir = m_aStandardDir;
+ dialog->SetStandardDir( sStandardDir );
+ dialog->SetDenyList( m_aDenyList );
+ }
+
+ return dialog;
+}
+
+
+// disambiguate XInterface
+
+IMPLEMENT_FORWARD_XINTERFACE2( SvtFilePicker, OCommonPicker, SvtFilePicker_Base )
+
+
+// disambiguate XTypeProvider
+
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvtFilePicker, OCommonPicker, SvtFilePicker_Base )
+
+IMPLEMENT_FORWARD_XINTERFACE3( SvtRemoteFilePicker, SvtFilePicker, OCommonPicker, SvtFilePicker_Base )
+
+
+// disambiguate XTypeProvider
+
+css::uno::Sequence< css::uno::Type > SAL_CALL SvtRemoteFilePicker::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ SvtFilePicker::getTypes(),
+ OCommonPicker::getTypes(),
+ SvtFilePicker_Base::getTypes()
+ );
+}
+IMPLEMENT_GET_IMPLEMENTATION_ID( SvtRemoteFilePicker )
+
+
+// XExecutableDialog functions
+
+
+void SAL_CALL SvtFilePicker::setTitle( const OUString& _rTitle )
+{
+ OCommonPicker::setTitle( _rTitle );
+}
+
+sal_Int16 SAL_CALL SvtFilePicker::execute( )
+{
+ return OCommonPicker::execute();
+}
+
+// XAsynchronousExecutableDialog functions
+void SAL_CALL SvtFilePicker::setDialogTitle( const OUString& _rTitle )
+{
+ setTitle( _rTitle );
+}
+
+void SAL_CALL SvtFilePicker::startExecuteModal( const Reference< css::ui::dialogs::XDialogClosedListener >& xListener )
+{
+ m_xDlgClosedListener = xListener;
+ prepareDialog();
+ prepareExecute();
+ m_xDlg->EnableAutocompletion();
+ if (!m_xDlg->PrepareExecute())
+ return;
+ weld::DialogController::runAsync(m_xDlg, [this](sal_Int32 nResult){
+ DialogClosedHdl(nResult);
+ });
+}
+
+// XFilePicker functions
+void SAL_CALL SvtFilePicker::setMultiSelectionMode( sal_Bool bMode )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ m_bMultiSelection = bMode;
+}
+
+void SAL_CALL SvtFilePicker::setDefaultName( const OUString& aName )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ m_aDefaultName = aName;
+}
+
+void SAL_CALL SvtFilePicker::setDisplayDirectory( const OUString& aDirectory )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ m_aDisplayDirectory = aDirectory;
+}
+
+OUString SAL_CALL SvtFilePicker::getDisplayDirectory()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (m_xDlg)
+ {
+ OUString aPath = m_xDlg->GetPath();
+
+ if( m_aOldHideDirectory == aPath )
+ return m_aOldDisplayDirectory;
+ m_aOldHideDirectory = aPath;
+
+ if( !m_xDlg->ContentIsFolder( aPath ) )
+ {
+ INetURLObject aFolder( aPath );
+ aFolder.CutLastName();
+ aPath = aFolder.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ m_aOldDisplayDirectory = aPath;
+ return aPath;
+ }
+ else
+ return m_aDisplayDirectory;
+}
+
+Sequence< OUString > SAL_CALL SvtFilePicker::getSelectedFiles()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (!m_xDlg)
+ {
+ Sequence< OUString > aEmpty;
+ return aEmpty;
+ }
+
+ return comphelper::containerToSequence(m_xDlg->GetPathList());
+}
+
+Sequence< OUString > SAL_CALL SvtFilePicker::getFiles()
+{
+ Sequence< OUString > aFiles = getSelectedFiles();
+ if (aFiles.getLength() > 1)
+ aFiles.realloc(1);
+ return aFiles;
+}
+
+
+// XFilePickerControlAccess functions
+
+
+void SAL_CALL SvtFilePicker::setValue( sal_Int16 nElementID,
+ sal_Int16 nControlAction,
+ const Any& rValue )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (m_xDlg)
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ aAccess.setValue( nElementID, nControlAction, rValue );
+ }
+ else
+ {
+ if ( !m_pElemList )
+ m_pElemList.reset( new ElementList );
+
+ bool bFound = false;
+
+ for (auto & elem : *m_pElemList)
+ {
+ if ( ( elem.m_nElementID == nElementID ) &&
+ ( !elem.m_bHasValue || ( elem.m_nControlAction == nControlAction ) ) )
+ {
+ elem.setAction( nControlAction );
+ elem.setValue( rValue );
+ bFound = true;
+ }
+ }
+
+ if ( !bFound )
+ {
+ ElementEntry_Impl aNew( nElementID );
+ aNew.setAction( nControlAction );
+ aNew.setValue( rValue );
+ m_pElemList->insert( m_pElemList->end(), aNew );
+ }
+ }
+}
+
+
+Any SAL_CALL SvtFilePicker::getValue( sal_Int16 nElementID, sal_Int16 nControlAction )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ Any aAny;
+
+ // execute() called?
+ if (m_xDlg)
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ aAny = aAccess.getValue( nElementID, nControlAction );
+ }
+ else if ( m_pElemList )
+ {
+ for (auto const& elem : *m_pElemList)
+ {
+ if ( ( elem.m_nElementID == nElementID ) &&
+ ( elem.m_bHasValue ) &&
+ ( elem.m_nControlAction == nControlAction ) )
+ {
+ aAny = elem.m_aValue;
+ break;
+ }
+ }
+ }
+
+ return aAny;
+}
+
+
+void SAL_CALL SvtFilePicker::setLabel( sal_Int16 nLabelID, const OUString& rValue )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (m_xDlg)
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ aAccess.setLabel( nLabelID, rValue );
+ }
+ else
+ {
+ if ( !m_pElemList )
+ m_pElemList.reset( new ElementList );
+
+ bool bFound = false;
+
+ for (auto & elem : *m_pElemList)
+ {
+ if ( elem.m_nElementID == nLabelID )
+ {
+ elem.setLabel( rValue );
+ bFound = true;
+ }
+ }
+
+ if ( !bFound )
+ {
+ ElementEntry_Impl aNew( nLabelID );
+ aNew.setLabel( rValue );
+ m_pElemList->insert( m_pElemList->end(), aNew );
+ }
+ }
+}
+
+
+OUString SAL_CALL SvtFilePicker::getLabel( sal_Int16 nLabelID )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ OUString aLabel;
+
+ if (m_xDlg)
+ {
+ ::svt::OControlAccess aAccess(m_xDlg.get(), m_xDlg->GetView());
+ aLabel = aAccess.getLabel( nLabelID );
+ }
+ else if ( m_pElemList )
+ {
+ for (auto const& elem : *m_pElemList)
+ {
+ if ( elem.m_nElementID == nLabelID )
+ {
+ if ( elem.m_bHasLabel )
+ aLabel = elem.m_aLabel;
+ break;
+ }
+ }
+ }
+
+ return aLabel;
+}
+
+
+void SAL_CALL SvtFilePicker::enableControl( sal_Int16 nElementID, sal_Bool bEnable )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (m_xDlg)
+ {
+ ::svt::OControlAccess aAccess(m_xDlg.get(), m_xDlg->GetView());
+ aAccess.enableControl( nElementID, bEnable );
+ }
+ else
+ {
+ if ( !m_pElemList )
+ m_pElemList.reset( new ElementList );
+
+ bool bFound = false;
+
+ for (auto & elem : *m_pElemList)
+ {
+ if ( elem.m_nElementID == nElementID )
+ {
+ elem.setEnabled( bEnable );
+ bFound = true;
+ }
+ }
+
+ if ( !bFound )
+ {
+ ElementEntry_Impl aNew( nElementID );
+ aNew.setEnabled( bEnable );
+ m_pElemList->insert( m_pElemList->end(), aNew );
+ }
+ }
+}
+
+
+// XFilePickerNotifier functions
+
+
+void SAL_CALL SvtFilePicker::addFilePickerListener( const Reference< XFilePickerListener >& xListener )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ m_xListener = xListener;
+}
+
+
+void SAL_CALL SvtFilePicker::removeFilePickerListener( const Reference< XFilePickerListener >& )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ m_xListener.clear();
+}
+
+// XFilePreview functions
+Sequence< sal_Int16 > SAL_CALL SvtFilePicker::getSupportedImageFormats()
+{
+ checkAlive();
+
+ return { FilePreviewImageFormats::BITMAP };
+}
+
+sal_Int32 SAL_CALL SvtFilePicker::getTargetColorDepth()
+{
+ return 0;
+}
+
+sal_Int32 SAL_CALL SvtFilePicker::getAvailableWidth()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ sal_Int32 nWidth = 0;
+
+ if (m_xDlg)
+ nWidth = m_xDlg->getAvailableWidth();
+
+ return nWidth;
+}
+
+sal_Int32 SAL_CALL SvtFilePicker::getAvailableHeight()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ sal_Int32 nHeight = 0;
+
+ if (m_xDlg)
+ nHeight = m_xDlg->getAvailableHeight();
+
+ return nHeight;
+}
+
+void SAL_CALL SvtFilePicker::setImage(sal_Int16 /*aImageFormat*/, const Any& rImage)
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if (m_xDlg)
+ m_xDlg->setImage(rImage);
+}
+
+sal_Bool SAL_CALL SvtFilePicker::setShowState( sal_Bool )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ bool bRet = false;
+
+ if (m_xDlg)
+ {
+ // #97633 for the system filedialog it's
+ // useful to make the preview switchable
+ // because the preview occupies
+ // half of the size of the file listbox
+ // which is not the case here,
+ // so we (TRA/FS) decided not to make
+ // the preview window switchable because
+ // else we would have to change the layout
+ // of the file dialog dynamically
+ // support for set/getShowState is optionally
+ // see css::ui::dialogs::XFilePreview
+
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+
+sal_Bool SAL_CALL SvtFilePicker::getShowState()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ bool bRet = false;
+
+ if (m_xDlg)
+ bRet = m_xDlg->getShowState();
+
+ return bRet;
+}
+
+
+// XFilterGroupManager functions
+
+
+void SAL_CALL SvtFilePicker::appendFilterGroup( const OUString& sGroupTitle,
+ const Sequence< StringPair >& aFilters )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+
+ // check the names
+ if ( FilterNameExists( aFilters ) )
+ throw IllegalArgumentException(
+ "filter name exists",
+ static_cast< OWeakObject * >(this), 1);
+
+ // ensure that we have a filter list
+ OUString sInitialCurrentFilter;
+ if ( aFilters.hasElements() )
+ sInitialCurrentFilter = aFilters[0].First;
+ ensureFilterList( sInitialCurrentFilter );
+
+ // append the filter
+ m_pFilterList->insert( m_pFilterList->end(), FilterEntry( sGroupTitle, aFilters ) );
+}
+
+
+// XFilterManager functions
+
+
+void SAL_CALL SvtFilePicker::appendFilter( const OUString& aTitle,
+ const OUString& aFilter )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ // check the name
+ if ( FilterNameExists( aTitle ) )
+ // TODO: a more precise exception message
+ throw IllegalArgumentException();
+
+ // ensure that we have a filter list
+ ensureFilterList( aTitle );
+
+ // append the filter
+ m_pFilterList->insert( m_pFilterList->end(), FilterEntry( aTitle, aFilter ) );
+}
+
+
+void SAL_CALL SvtFilePicker::setCurrentFilter( const OUString& aTitle )
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( ! FilterNameExists( aTitle ) )
+ throw IllegalArgumentException();
+
+ m_aCurrentFilter = aTitle;
+
+ if (m_xDlg)
+ m_xDlg->SetCurFilter( aTitle );
+}
+
+
+OUString SAL_CALL SvtFilePicker::getCurrentFilter()
+{
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ OUString aFilter = m_xDlg ? m_xDlg->GetCurFilter() :
+ m_aCurrentFilter;
+ return aFilter;
+}
+
+
+// XInitialization functions
+
+
+void SAL_CALL SvtFilePicker::initialize( const Sequence< Any >& _rArguments )
+{
+ checkAlive();
+
+ Sequence< Any > aArguments( _rArguments.getLength());
+
+ m_nServiceType = TemplateDescription::FILEOPEN_SIMPLE;
+
+ if ( _rArguments.hasElements() )
+ {
+ // compatibility: one argument, type sal_Int16 , specifies the service type
+ int index = 0;
+ auto pArguments = aArguments.getArray();
+ if (_rArguments[0] >>= m_nServiceType)
+ {
+ // skip the first entry if it was the ServiceType, because it's not needed in OCommonPicker::initialize and it's not a NamedValue
+ NamedValue emptyNamedValue;
+ pArguments[0] <<= emptyNamedValue;
+ index = 1;
+
+ }
+ for ( int i = index; i < _rArguments.getLength(); i++)
+ {
+ NamedValue namedValue;
+ pArguments[i] = _rArguments[i];
+
+ if (aArguments[i] >>= namedValue )
+ {
+
+ if ( namedValue.Name == "StandardDir" )
+ {
+ OUString sStandardDir;
+
+ namedValue.Value >>= sStandardDir;
+
+ // Set the directory for the "back to the default dir" button
+ if ( !sStandardDir.isEmpty() )
+ {
+ m_aStandardDir = sStandardDir;
+ }
+ }
+ else if ( namedValue.Name == "DenyList" )
+ {
+ namedValue.Value >>= m_aDenyList;
+ }
+ }
+ }
+ }
+
+ // let the base class analyze the sequence (will call into implHandleInitializationArgument)
+ OCommonPicker::initialize( aArguments );
+}
+
+
+bool SvtFilePicker::implHandleInitializationArgument( const OUString& _rName, const Any& _rValue )
+{
+ if ( _rName == "TemplateDescription" )
+ {
+ m_nServiceType = TemplateDescription::FILEOPEN_SIMPLE;
+ OSL_VERIFY( _rValue >>= m_nServiceType );
+ return true;
+ }
+ if ( _rName == "StandardDir" )
+ {
+ OSL_VERIFY( _rValue >>= m_aStandardDir );
+ return true;
+ }
+
+ if ( _rName == "DenyList" )
+ {
+ OSL_VERIFY( _rValue >>= m_aDenyList );
+ return true;
+ }
+
+
+ return OCommonPicker::implHandleInitializationArgument( _rName, _rValue );
+}
+
+
+// XServiceInfo
+
+
+/* XServiceInfo */
+OUString SAL_CALL SvtFilePicker::getImplementationName()
+{
+ return "com.sun.star.svtools.OfficeFilePicker";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SvtFilePicker::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SvtFilePicker::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.dialogs.OfficeFilePicker" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_SvtFilePicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SvtFilePicker());
+}
+
+
+// SvtRemoteFilePicker
+
+SvtRemoteFilePicker::SvtRemoteFilePicker()
+{
+}
+
+std::shared_ptr<SvtFileDialog_Base> SvtRemoteFilePicker::implCreateDialog(weld::Window* pParent)
+{
+ PickerFlags nBits = getPickerFlags();
+
+ auto dialog = std::make_shared<RemoteFilesDialog>(pParent, nBits);
+
+ // Set StandardDir if present
+ if ( !m_aStandardDir.isEmpty())
+ {
+ OUString sStandardDir = m_aStandardDir;
+ dialog->SetStandardDir( sStandardDir );
+ dialog->SetDenyList( m_aDenyList );
+ }
+
+ return dialog;
+}
+
+// XServiceInfo
+
+
+/* XServiceInfo */
+OUString SAL_CALL SvtRemoteFilePicker::getImplementationName()
+{
+ return "com.sun.star.svtools.RemoteFilePicker";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SvtRemoteFilePicker::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SvtRemoteFilePicker::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.dialogs.RemoteFilePicker" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_SvtRemoteFilePicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SvtRemoteFilePicker());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeFilePicker.hxx b/fpicker/source/office/OfficeFilePicker.hxx
new file mode 100644
index 000000000..b75a3f636
--- /dev/null
+++ b/fpicker/source/office/OfficeFilePicker.hxx
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/implbase5.hxx>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/ui/dialogs/XFilePreview.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerListener.hpp>
+#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+
+
+#include "commonpicker.hxx"
+#include "pickercallbacks.hxx"
+
+#include <vector>
+
+class Dialog;
+struct FilterEntry;
+struct ElementEntry_Impl;
+enum class PickerFlags;
+
+typedef ::std::vector< FilterEntry > FilterList; // can be maintained more effectively
+typedef ::std::vector< ElementEntry_Impl > ElementList;
+
+typedef css::beans::StringPair UnoFilterEntry;
+typedef css::uno::Sequence< UnoFilterEntry > UnoFilterList; // can be transported more effectively
+
+// class SvtFilePicker ---------------------------------------------------
+
+typedef ::cppu::ImplHelper5 < css::ui::dialogs::XFilePicker3
+ , css::ui::dialogs::XFilePickerControlAccess
+ , css::ui::dialogs::XFilePreview
+ , css::lang::XServiceInfo
+ , css::ui::dialogs::XAsynchronousExecutableDialog
+ > SvtFilePicker_Base;
+
+class SvtFilePicker :public SvtFilePicker_Base
+ ,public ::svt::OCommonPicker
+ ,public ::svt::IFilePickerListener
+{
+protected:
+ std::unique_ptr<FilterList>
+ m_pFilterList;
+ std::unique_ptr<ElementList>
+ m_pElemList;
+
+ bool m_bMultiSelection;
+ sal_Int16 m_nServiceType;
+ OUString m_aDefaultName;
+ OUString m_aCurrentFilter;
+
+ OUString m_aOldDisplayDirectory;
+ OUString m_aOldHideDirectory;
+
+ OUString m_aStandardDir;
+ css::uno::Sequence< OUString >
+ m_aDenyList;
+
+ css::uno::Reference< css::ui::dialogs::XFilePickerListener >
+ m_xListener;
+ css::uno::Reference< css::ui::dialogs::XDialogClosedListener >
+ m_xDlgClosedListener;
+
+public:
+ SvtFilePicker();
+ virtual ~SvtFilePicker() override;
+
+
+ // disambiguate XInterface
+
+ DECLARE_XINTERFACE( )
+
+
+ // disambiguate XTypeProvider
+
+ DECLARE_XTYPEPROVIDER( )
+
+
+ // XExecutableDialog functions
+
+ virtual void SAL_CALL setTitle( const OUString& _rTitle ) override;
+ virtual sal_Int16 SAL_CALL execute( ) override;
+
+
+ // XAsynchronousExecutableDialog functions
+
+ virtual void SAL_CALL setDialogTitle( const OUString& _rTitle ) override;
+ virtual void SAL_CALL startExecuteModal( const css::uno::Reference< css::ui::dialogs::XDialogClosedListener >& xListener ) override;
+
+
+ // XFilePicker functions
+
+
+ virtual void SAL_CALL setMultiSelectionMode( sal_Bool bMode ) override;
+ virtual void SAL_CALL setDefaultName( const OUString& aName ) override;
+ virtual void SAL_CALL setDisplayDirectory( const OUString& aDirectory ) override;
+ virtual OUString SAL_CALL getDisplayDirectory() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getFiles() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSelectedFiles() override;
+
+
+ // XFilePickerControlAccess functions
+
+
+ virtual void SAL_CALL setValue( sal_Int16 ElementID, sal_Int16 ControlAction, const css::uno::Any& value ) override;
+ virtual css::uno::Any SAL_CALL getValue( sal_Int16 ElementID, sal_Int16 ControlAction ) override;
+ virtual void SAL_CALL setLabel( sal_Int16 ElementID, const OUString& aValue ) override;
+ virtual OUString SAL_CALL getLabel( sal_Int16 ElementID ) override;
+ virtual void SAL_CALL enableControl( sal_Int16 ElementID, sal_Bool bEnable ) override;
+
+
+ // XFilePickerNotifier functions
+
+
+ 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;
+
+
+ // XFilePreview functions
+
+
+ virtual css::uno::Sequence< sal_Int16 > SAL_CALL getSupportedImageFormats() override;
+ virtual sal_Int32 SAL_CALL getTargetColorDepth() override;
+ virtual sal_Int32 SAL_CALL getAvailableWidth() override;
+ virtual sal_Int32 SAL_CALL getAvailableHeight() override;
+ virtual void SAL_CALL setImage( sal_Int16 aImageFormat, const css::uno::Any& aImage ) override;
+ virtual sal_Bool SAL_CALL setShowState( sal_Bool bShowState ) override;
+ virtual sal_Bool SAL_CALL getShowState() override;
+
+
+ // XFilterManager functions
+
+
+ virtual void SAL_CALL appendFilter( const OUString& aTitle, const OUString& aFilter ) override;
+ virtual void SAL_CALL setCurrentFilter( const OUString& aTitle ) override;
+ virtual OUString SAL_CALL getCurrentFilter() override;
+
+
+ // XFilterGroupManager functions
+
+ virtual void SAL_CALL appendFilterGroup( const OUString& sGroupTitle, const css::uno::Sequence< css::beans::StringPair >& aFilters ) override;
+
+
+ // these methods are here because they're ambiguous
+
+ virtual void SAL_CALL cancel() override
+ { ::svt::OCommonPicker::cancel(); }
+ virtual void SAL_CALL dispose() override
+ { ::svt::OCommonPicker::dispose(); }
+ virtual void SAL_CALL addEventListener(const css::uno::Reference<css::lang::XEventListener>& l) override
+ { ::svt::OCommonPicker::addEventListener(l); }
+ virtual void SAL_CALL removeEventListener(const css::uno::Reference<css::lang::XEventListener>& l) override
+ { ::svt::OCommonPicker::removeEventListener(l); }
+
+
+ // XInitialization functions
+
+
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+
+ // XServiceInfo functions
+
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+protected:
+
+ // OCommonPicker overridables
+
+ virtual std::shared_ptr<SvtFileDialog_Base> implCreateDialog( weld::Window* pParent ) override;
+ virtual sal_Int16 implExecutePicker( ) override;
+ virtual bool implHandleInitializationArgument(
+ const OUString& _rName,
+ const css::uno::Any& _rValue
+ ) override;
+
+protected:
+ PickerFlags getPickerFlags() const;
+ virtual void notify( sal_Int16 _nEventId, sal_Int16 _nControlId ) override;
+
+ bool FilterNameExists( const OUString& rTitle );
+ bool FilterNameExists( const UnoFilterList& _rGroupedFilters );
+
+ void ensureFilterList( const OUString& _rInitialCurrentFilter );
+
+ void prepareExecute( );
+
+ void DialogClosedHdl(sal_Int32 nResult);
+};
+
+// SvtRemoteFilePicker
+
+class SvtRemoteFilePicker : public SvtFilePicker
+{
+public:
+ SvtRemoteFilePicker();
+
+ virtual std::shared_ptr<SvtFileDialog_Base> implCreateDialog( weld::Window* pParent ) override;
+
+ // disambiguate XInterface
+
+ DECLARE_XINTERFACE( )
+
+ // disambiguate XTypeProvider
+
+ DECLARE_XTYPEPROVIDER( )
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeFolderPicker.cxx b/fpicker/source/office/OfficeFolderPicker.cxx
new file mode 100644
index 000000000..c941d6cb6
--- /dev/null
+++ b/fpicker/source/office/OfficeFolderPicker.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "OfficeFolderPicker.hxx"
+
+#include "iodlg.hxx"
+
+#include <vector>
+#include <tools/urlobj.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/make_shared.hxx>
+#include <unotools/pathoptions.hxx>
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+SvtFolderPicker::SvtFolderPicker()
+{
+}
+
+SvtFolderPicker::~SvtFolderPicker()
+{
+}
+
+void SAL_CALL SvtFolderPicker::setTitle( const OUString& _rTitle )
+{
+ OCommonPicker::setTitle( _rTitle );
+}
+
+sal_Int16 SAL_CALL SvtFolderPicker::execute( )
+{
+ return OCommonPicker::execute();
+}
+
+void SAL_CALL SvtFolderPicker::setDialogTitle( const OUString& _rTitle)
+{
+ setTitle( _rTitle );
+}
+
+void SAL_CALL SvtFolderPicker::startExecuteModal( const Reference< css::ui::dialogs::XDialogClosedListener >& xListener )
+{
+ m_xListener = xListener;
+ prepareDialog();
+ prepareExecute();
+
+ m_xDlg->EnableAutocompletion();
+ if (!m_xDlg->PrepareExecute())
+ return;
+ weld::DialogController::runAsync(m_xDlg, [this](sal_Int32 nResult){
+ DialogClosedHdl(nResult);
+ });
+}
+
+std::shared_ptr<SvtFileDialog_Base> SvtFolderPicker::implCreateDialog( weld::Window* pParent )
+{
+ return o3tl::make_shared<SvtFileDialog>(pParent, PickerFlags::PathDialog);
+}
+
+sal_Int16 SvtFolderPicker::implExecutePicker( )
+{
+ prepareExecute();
+
+ // now we are ready to execute the dialog
+ m_xDlg->EnableAutocompletion( false );
+ return m_xDlg->run();
+}
+
+void SvtFolderPicker::prepareExecute()
+{
+ // set the default directory
+ if ( !m_aDisplayDirectory.isEmpty() )
+ m_xDlg->SetPath( m_aDisplayDirectory );
+ else
+ {
+ // set the default standard dir
+ INetURLObject aStdDirObj( SvtPathOptions().GetWorkPath() );
+ m_xDlg->SetPath( aStdDirObj.GetMainURL( INetURLObject::DecodeMechanism::NONE) );
+ }
+}
+
+void SvtFolderPicker::DialogClosedHdl(sal_Int32 nResult)
+{
+ if ( m_xListener.is() )
+ {
+ sal_Int16 nRet = static_cast<sal_Int16>(nResult);
+ css::ui::dialogs::DialogClosedEvent aEvent( *this, nRet );
+ m_xListener->dialogClosed( aEvent );
+ m_xListener.clear();
+ }
+}
+
+void SAL_CALL SvtFolderPicker::setDisplayDirectory( const OUString& aDirectory )
+{
+ m_aDisplayDirectory = aDirectory;
+}
+
+OUString SAL_CALL SvtFolderPicker::getDisplayDirectory()
+{
+ if (!m_xDlg)
+ return m_aDisplayDirectory;
+
+ std::vector<OUString> aPathList(m_xDlg->GetPathList());
+
+ if(!aPathList.empty())
+ return aPathList[0];
+
+ return OUString();
+}
+
+OUString SAL_CALL SvtFolderPicker::getDirectory()
+{
+ if (!m_xDlg)
+ return m_aDisplayDirectory;
+
+ std::vector<OUString> aPathList(m_xDlg->GetPathList());
+
+ if(!aPathList.empty())
+ return aPathList[0];
+
+ return OUString();
+}
+
+void SAL_CALL SvtFolderPicker::setDescription( const OUString& )
+{
+}
+
+void SvtFolderPicker::cancel()
+{
+ OCommonPicker::cancel();
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SvtFolderPicker::getImplementationName()
+{
+ return "com.sun.star.svtools.OfficeFolderPicker";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SvtFolderPicker::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SvtFolderPicker::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.dialogs.OfficeFolderPicker" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_SvtFolderPicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SvtFolderPicker());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/OfficeFolderPicker.hxx b/fpicker/source/office/OfficeFolderPicker.hxx
new file mode 100644
index 000000000..7b98fb5bf
--- /dev/null
+++ b/fpicker/source/office/OfficeFolderPicker.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
+#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include "commonpicker.hxx"
+
+class Dialog;
+
+typedef
+ cppu::ImplInheritanceHelper<
+ svt::OCommonPicker, css::ui::dialogs::XFolderPicker2,
+ css::ui::dialogs::XAsynchronousExecutableDialog,
+ css::lang::XServiceInfo >
+ SvtFolderPicker_Base;
+
+class SvtFolderPicker: public SvtFolderPicker_Base
+{
+private:
+ css::uno::Reference< css::ui::dialogs::XDialogClosedListener >
+ m_xListener;
+
+ void prepareExecute();
+ void DialogClosedHdl(sal_Int32 nResult);
+
+public:
+ SvtFolderPicker();
+ virtual ~SvtFolderPicker() override;
+
+
+ // XFolderPicker2 functions
+
+ virtual void SAL_CALL setDisplayDirectory( const OUString& aDirectory ) override;
+ virtual OUString SAL_CALL getDisplayDirectory() override;
+ virtual OUString SAL_CALL getDirectory() override;
+ virtual void SAL_CALL setDescription( const OUString& aDescription ) override;
+
+ virtual void SAL_CALL cancel() override;
+
+
+ // XExecutableDialog functions
+
+ virtual void SAL_CALL setTitle( const OUString& _rTitle ) override;
+ virtual sal_Int16 SAL_CALL execute( ) override;
+
+
+ // XAsynchronousExecutableDialog functions
+
+ virtual void SAL_CALL setDialogTitle( const OUString& _rTitle ) override;
+ virtual void SAL_CALL startExecuteModal( const css::uno::Reference< css::ui::dialogs::XDialogClosedListener >& xListener ) override;
+
+
+ // XServiceInfo functions
+
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+protected:
+
+ // OCommonPicker overridables
+
+ virtual std::shared_ptr<SvtFileDialog_Base> implCreateDialog( weld::Window* pParent ) override;
+ virtual sal_Int16 implExecutePicker( ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/PlacesListBox.cxx b/fpicker/source/office/PlacesListBox.cxx
new file mode 100644
index 000000000..86bd50517
--- /dev/null
+++ b/fpicker/source/office/PlacesListBox.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "PlacesListBox.hxx"
+#include <svtools/PlaceEditDialog.hxx>
+
+#include <bitmaps.hlst>
+
+PlacesListBox::PlacesListBox(std::unique_ptr<weld::TreeView> xControl,
+ std::unique_ptr<weld::Button> xAdd,
+ std::unique_ptr<weld::Button> xDel,
+ SvtFileDialog* pFileDlg)
+ : mpDlg(pFileDlg)
+ , mxImpl(std::move(xControl))
+ , mxAddBtn(std::move(xAdd))
+ , mxDelBtn(std::move(xDel))
+ , mnNbEditables(0)
+ , mbUpdated( false )
+{
+ Size aSize(mxImpl->get_approximate_digit_width() * 18,
+ mxImpl->get_height_rows(9));
+ mxImpl->set_size_request(aSize.Width(), aSize.Height());
+
+ mxImpl->connect_changed( LINK( this, PlacesListBox, Selection ) );
+ mxImpl->connect_row_activated( LINK( this, PlacesListBox, DoubleClick ) ) ;
+ mxImpl->connect_query_tooltip(LINK(this, PlacesListBox, QueryTooltipHdl));
+}
+
+PlacesListBox::~PlacesListBox( )
+{
+}
+
+void PlacesListBox::AppendPlace( const PlacePtr& pPlace )
+{
+ maPlaces.push_back( pPlace );
+ mxImpl->append_text(pPlace->GetName());
+ mxImpl->set_image(maPlaces.size() - 1, getEntryIcon(pPlace));
+
+ if(pPlace->IsEditable()) {
+ ++mnNbEditables;
+ mbUpdated = true;
+ }
+}
+
+bool PlacesListBox::IsUpdated() {
+ if(mbUpdated) {
+ mbUpdated = false;
+ return true;
+ }
+ return false;
+}
+
+void PlacesListBox::RemovePlace( sal_uInt16 nPos )
+{
+ if ( nPos < maPlaces.size() )
+ {
+ if(maPlaces[nPos]->IsEditable()) {
+ --mnNbEditables;
+ mbUpdated = true;
+ }
+ maPlaces.erase( maPlaces.begin() + nPos );
+ mxImpl->remove(nPos);
+ }
+}
+
+void PlacesListBox::RemoveSelectedPlace() {
+ RemovePlace(mxImpl->get_cursor_index());
+}
+
+void PlacesListBox::SetAddHdl( const Link<weld::Button&,void>& rHdl )
+{
+ mxAddBtn->connect_clicked( rHdl );
+}
+
+void PlacesListBox::SetDelHdl( const Link<weld::Button&,void>& rHdl )
+{
+ mxDelBtn->connect_clicked( rHdl );
+}
+
+void PlacesListBox::SetDelEnabled( bool enabled )
+{
+ mxDelBtn->set_sensitive( enabled );
+}
+
+OUString PlacesListBox::getEntryIcon( const PlacePtr& pPlace )
+{
+ OUString theImage = BMP_FILEDLG_PLACE_LOCAL;
+ if ( !pPlace->IsLocal( ) )
+ theImage = BMP_FILEDLG_PLACE_REMOTE;
+ return theImage;
+}
+
+IMPL_LINK_NOARG( PlacesListBox, Selection, weld::TreeView&, void )
+{
+ sal_uInt32 nSelected = mxImpl->get_cursor_index();
+ PlacePtr pPlace = maPlaces[nSelected];
+
+ if (pPlace->IsEditable())
+ mpDlg->RemovablePlaceSelected();
+ else
+ mpDlg->RemovablePlaceSelected(false);
+
+ updateView();
+}
+
+IMPL_LINK_NOARG( PlacesListBox, DoubleClick, weld::TreeView&, bool )
+{
+ sal_uInt16 nSelected = mxImpl->get_cursor_index();
+ PlacePtr pPlace = maPlaces[nSelected];
+ if ( !pPlace->IsEditable() || pPlace->IsLocal( ) )
+ return true;
+ PlaceEditDialog aDlg(mpDlg->getDialog(), pPlace);
+ short aRetCode = aDlg.run();
+ switch (aRetCode)
+ {
+ case RET_OK :
+ {
+ pPlace->SetName ( aDlg.GetServerName() );
+ pPlace->SetUrl( aDlg.GetServerUrl() );
+ mbUpdated = true;
+ break;
+ }
+ case RET_NO :
+ {
+ RemovePlace(nSelected);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+}
+
+IMPL_LINK(PlacesListBox, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString)
+{
+ const OUString sText = mxImpl->get_text(rIter);
+ for (const auto& pPlace : maPlaces)
+ {
+ if (pPlace->GetName() == sText)
+ return pPlace->GetUrlObject().GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);
+ }
+ return OUString();
+}
+
+void PlacesListBox::updateView( )
+{
+ sal_uInt32 nSelected = mxImpl->get_cursor_index();
+ PlacePtr pPlace = maPlaces[nSelected];
+ mpDlg->OpenURL_Impl( pPlace->GetUrl( ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/PlacesListBox.hxx b/fpicker/source/office/PlacesListBox.hxx
new file mode 100644
index 000000000..ed3a0798e
--- /dev/null
+++ b/fpicker/source/office/PlacesListBox.hxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include "iodlg.hxx"
+
+#include <svtools/place.hxx>
+#include <vcl/weld.hxx>
+
+#include <memory>
+#include <vector>
+
+typedef std::shared_ptr<Place> PlacePtr;
+
+class PlacesListBox;
+
+/** ListBox to handle Places.
+ */
+class PlacesListBox
+{
+private:
+ std::vector<PlacePtr> maPlaces;
+ SvtFileDialog* mpDlg;
+ std::unique_ptr<weld::TreeView> mxImpl;
+ std::unique_ptr<weld::Button> mxAddBtn;
+ std::unique_ptr<weld::Button> mxDelBtn;
+ sal_Int32 mnNbEditables;
+ bool mbUpdated;
+
+public:
+ PlacesListBox(std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::Button> xAddBtn,
+ std::unique_ptr<weld::Button> xDelBtn,
+ SvtFileDialog* pFileDlg);
+ ~PlacesListBox();
+
+ void AppendPlace( const PlacePtr& pPlace );
+ void RemovePlace( sal_uInt16 nPos );
+ void RemoveSelectedPlace();
+ sal_Int32 GetNbEditablePlaces() const { return mnNbEditables;}
+ bool IsUpdated();
+ const std::vector<PlacePtr>& GetPlaces() const { return maPlaces;}
+
+ void SetAddHdl( const Link<weld::Button&,void>& rHdl );
+ void SetDelHdl( const Link<weld::Button&,void>& rHdl );
+ void SetDelEnabled( bool enabled );
+ void updateView( );
+
+ void set_help_id(const OString& rHelpId) { mxImpl->set_help_id(rHelpId); }
+
+private:
+
+ static OUString getEntryIcon(const PlacePtr& pPlace);
+
+ DECL_LINK( Selection, weld::TreeView&, void );
+ DECL_LINK( DoubleClick, weld::TreeView&, bool );
+ DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/QueryFolderName.hxx b/fpicker/source/office/QueryFolderName.hxx
new file mode 100644
index 000000000..81bc4ad10
--- /dev/null
+++ b/fpicker/source/office/QueryFolderName.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class QueryFolderNameDialog : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::Entry> m_xNameEdit;
+ std::unique_ptr<weld::Button> m_xOKBtn;
+
+ DECL_LINK(OKHdl, weld::Button&, void);
+ DECL_LINK(NameHdl, weld::Entry&, void);
+
+public:
+ QueryFolderNameDialog(weld::Window* _pParent, const OUString& rTitle,
+ const OUString& rDefaultText);
+ virtual ~QueryFolderNameDialog() override;
+ OUString GetName() const { return m_xNameEdit->get_text(); }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/RemoteFilesDialog.cxx b/fpicker/source/office/RemoteFilesDialog.cxx
new file mode 100644
index 000000000..4536690f9
--- /dev/null
+++ b/fpicker/source/office/RemoteFilesDialog.cxx
@@ -0,0 +1,1201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <config_oauth2.h>
+
+#include "fpsmartcontent.hxx"
+#include "QueryFolderName.hxx"
+#include "RemoteFilesDialog.hxx"
+#include <fpicker/fpsofficeResMgr.hxx>
+#include <fpicker/strings.hrc>
+#include <strings.hrc>
+#include <comphelper/docpasswordrequest.hxx>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/PasswordContainer.hpp>
+#include <svtools/PlaceEditDialog.hxx>
+#include <tools/debug.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <vcl/errinf.hxx>
+#include <officecfg/Office/Common.hxx>
+
+using namespace ::svt;
+
+RemoteFilesDialog::RemoteFilesDialog( weld::Window* pParent, PickerFlags nBits )
+ : SvtFileDialog_Base( pParent, "fps/ui/remotefilesdialog.ui", "RemoteFilesDialog" )
+ , m_xContext( comphelper::getProcessComponentContext() )
+ , m_xMasterPasswd( PasswordContainer::create( m_xContext ) )
+ , m_bIsInExecute( false )
+ , m_xOk_btn(m_xBuilder->weld_button("ok"))
+ , m_xCancel_btn(m_xBuilder->weld_button("cancel"))
+ , m_xManageServices(m_xBuilder->weld_menu_button("add_service_btn"))
+ , m_xServices_lb(m_xBuilder->weld_combo_box("services_lb"))
+ , m_xPathContainer(m_xBuilder->weld_container("breadcrumb_container"))
+ , m_xNewFolder(m_xBuilder->weld_button("new_folder"))
+ , m_xListView_btn(m_xBuilder->weld_toggle_button("list_view"))
+ , m_xIconView_btn(m_xBuilder->weld_toggle_button("icon_view"))
+ , m_xFilter_lb(m_xBuilder->weld_combo_box("filter_lb"))
+ , m_xName_ed(new AutocompleteEdit(m_xBuilder->weld_entry("filename")))
+{
+ m_eMode = ( nBits & PickerFlags::SaveAs ) ? REMOTEDLG_MODE_SAVE : REMOTEDLG_MODE_OPEN;
+ m_eType = ( nBits & PickerFlags::PathDialog ) ? REMOTEDLG_TYPE_PATHDLG : REMOTEDLG_TYPE_FILEDLG;
+ bool bMultiselection = bool( nBits & PickerFlags::MultiSelection );
+ m_bIsUpdated = false;
+ m_bIsConnected = false;
+ m_bServiceChanged = false;
+ m_nCurrentFilter = -1;
+
+ m_xName_ed->show();
+
+ // limit width due to super wide strings that may end up here
+ m_xFilter_lb->set_size_request(m_xFilter_lb->get_approximate_digit_width() * 60, -1);
+
+ m_xFilter_lb->set_sensitive(false);
+ m_xName_ed->set_sensitive(false);
+ m_xNewFolder->set_sensitive(false);
+
+ if( m_eMode == REMOTEDLG_MODE_OPEN )
+ {
+ m_xNewFolder->hide();
+ }
+ else
+ {
+ m_xOk_btn->set_label(FpsResId(STR_EXPLORERFILE_BUTTONSAVE));
+ m_xNewFolder->connect_clicked( LINK( this, RemoteFilesDialog, NewFolderHdl ) );
+ }
+
+ m_xListView_btn->set_active(true);
+ m_xIconView_btn->connect_clicked( LINK( this, RemoteFilesDialog, IconViewHdl ) );
+ m_xListView_btn->connect_clicked( LINK( this, RemoteFilesDialog, ListViewHdl ) );
+
+ m_xOk_btn->set_sensitive(false);
+
+ m_xOk_btn->connect_clicked( LINK( this, RemoteFilesDialog, OkHdl ) );
+ m_xCancel_btn->connect_clicked( LINK( this, RemoteFilesDialog, CancelHdl ) );
+
+ m_sRootLabel = FpsResId( STR_SVT_ROOTLABEL );
+ m_xPath.reset(new Breadcrumb(m_xPathContainer.get()));
+ m_xPath->connect_clicked( LINK( this, RemoteFilesDialog, SelectBreadcrumbHdl ) );
+ m_xPath->SetMode( SvtBreadcrumbMode::ALL_VISITED );
+
+ m_xContainer = m_xBuilder->weld_container("container");
+ m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * 82, -1);
+
+ m_xFileView.reset(new SvtFileView(m_xDialog.get(),
+ m_xBuilder->weld_tree_view("fileview"),
+ m_xBuilder->weld_icon_view("iconview"),
+ REMOTEDLG_TYPE_PATHDLG == m_eType,
+ bMultiselection, false));
+
+ m_xFileView->SetDoubleClickHdl( LINK( this, RemoteFilesDialog, DoubleClickHdl ) );
+ m_xFileView->SetSelectHdl( LINK( this, RemoteFilesDialog, SelectHdl ) );
+ m_xFileView->EnableDelete( true );
+
+ m_xTreeView.reset(new FolderTree(m_xBuilder->weld_tree_view("foldertree"), m_xDialog.get()));
+ m_xTreeView->connect_changed(LINK(this, RemoteFilesDialog, TreeSelectHdl));
+
+ m_xContainer->set_sensitive(false);
+
+ m_sIniKey = "RemoteFilesDialog";
+ InitSize();
+
+ m_xName_ed->connect_focus_in(LINK(this, RemoteFilesDialog, FileNameGetFocusHdl));
+ m_xName_ed->connect_changed(LINK(this, RemoteFilesDialog, FileNameModifyHdl));
+
+ m_xManageServices->connect_selected(LINK(this, RemoteFilesDialog, EditServiceMenuHdl));
+
+ FillServicesListbox();
+
+ m_xServices_lb->connect_changed( LINK( this, RemoteFilesDialog, SelectServiceHdl ) );
+
+ m_xFilter_lb->connect_changed( LINK( this, RemoteFilesDialog, SelectFilterHdl ) );
+}
+
+RemoteFilesDialog::~RemoteFilesDialog()
+{
+ m_xFileView->SetSelectHdl(Link<SvtFileView*,void>());
+
+ // save window state
+ if( !m_sIniKey.isEmpty() )
+ {
+ SvtViewOptions aDlgOpt( EViewType::Dialog, m_sIniKey );
+ aDlgOpt.SetWindowState(OStringToOUString(m_xDialog->get_window_state(WindowStateMask::All), RTL_TEXTENCODING_UTF8));
+
+ Size aSize(m_xDialog->get_size());
+
+ OUString sSize = OUString::number( aSize.Width() ) + "|";
+ sSize = sSize + OUString::number( aSize.Height() ) + "|";
+
+ OUString sUserData = m_xFileView->GetConfigString();
+ aDlgOpt.SetUserItem( "UserData",
+ Any( sSize + sUserData ) );
+ }
+
+ // save services
+ std::shared_ptr< comphelper::ConfigurationChanges > batch( comphelper::ConfigurationChanges::create() );
+
+ officecfg::Office::Common::Misc::FilePickerLastService::set( m_sLastServiceUrl, batch );
+
+ if( m_bIsUpdated )
+ {
+ Sequence< OUString > placesUrlsList( m_aServices.size() );
+ auto placesUrlsListRange = asNonConstRange(placesUrlsList);
+ Sequence< OUString > placesNamesList( m_aServices.size() );
+ auto placesNamesListRange = asNonConstRange(placesNamesList);
+
+ int i = 0;
+ for (auto const& service : m_aServices)
+ {
+ placesUrlsListRange[i] = service->GetUrl();
+ placesNamesListRange[i] = service->GetName();
+ ++i;
+ }
+
+ officecfg::Office::Common::Misc::FilePickerPlacesUrls::set( placesUrlsList, batch );
+ officecfg::Office::Common::Misc::FilePickerPlacesNames::set( placesNamesList, batch );
+ }
+
+ batch->commit();
+}
+
+void RemoteFilesDialog::EnableExtraMenuItems(bool bEnable)
+{
+ m_xManageServices->set_item_visible("change_password", bEnable);
+ m_xManageServices->set_item_visible("edit_service", bEnable);
+ m_xManageServices->set_item_visible("delete_service", bEnable);
+ m_xManageServices->set_item_visible("change_password", bEnable);
+}
+
+short RemoteFilesDialog::run()
+{
+ if (m_xServices_lb->get_count() > 0)
+ {
+ m_xDialog->show();
+ SelectServiceHdl(*m_xServices_lb);
+ }
+ if (!m_bIsConnected)
+ {
+ m_xServices_lb->set_active(-1);
+ EnableExtraMenuItems(false);
+ }
+
+ m_bIsInExecute = true;
+ short nRet = SvtFileDialog_Base::run();
+ m_bIsInExecute = false;
+ return nRet;
+}
+
+static OUString lcl_GetServiceType( const ServicePtr& pService )
+{
+ INetProtocol aProtocol = pService->GetUrlObject().GetProtocol();
+ switch( aProtocol )
+ {
+ case INetProtocol::Ftp:
+ return "FTP";
+ case INetProtocol::Cmis:
+ {
+ OUString sHost = pService->GetUrlObject().GetHost( INetURLObject::DecodeMechanism::WithCharset );
+
+ if( sHost.startsWith( GDRIVE_BASE_URL ) )
+ return "Google Drive";
+ else if( sHost.startsWith( ALFRESCO_CLOUD_BASE_URL ) )
+ return "Alfresco Cloud";
+ else if( sHost.startsWith( ONEDRIVE_BASE_URL ) )
+ return "OneDrive";
+
+ return "CMIS";
+ }
+ case INetProtocol::Smb:
+ return "Windows Share";
+ case INetProtocol::File:
+ return "SSH";
+ case INetProtocol::Http:
+ return "WebDAV";
+ case INetProtocol::Https:
+ return "WebDAV";
+ case INetProtocol::Generic:
+ return "SSH";
+ default:
+ return OUString();
+ }
+}
+
+void RemoteFilesDialog::InitSize()
+{
+ if( m_sIniKey.isEmpty() )
+ return;
+
+ // initialize from config
+ SvtViewOptions aDlgOpt( EViewType::Dialog, m_sIniKey );
+
+ if( !aDlgOpt.Exists() )
+ return;
+
+ m_xDialog->set_window_state(OUStringToOString(aDlgOpt.GetWindowState(), RTL_TEXTENCODING_UTF8));
+
+ Any aUserData = aDlgOpt.GetUserItem( "UserData" );
+ OUString sCfgStr;
+ if( aUserData >>= sCfgStr )
+ {
+ sal_Int32 nPos1{ sCfgStr.indexOf('|') };
+ if (nPos1<0)
+ return;
+ sal_Int32 nPos2{ sCfgStr.indexOf('|', nPos1+1 ) };
+ if (nPos2<0)
+ return;
+ m_xFileView->SetConfigString( sCfgStr.subView(nPos2+1) );
+ }
+}
+
+void RemoteFilesDialog::FillServicesListbox()
+{
+ m_xServices_lb->clear();
+ m_aServices.clear();
+
+ // Load from user settings
+ Sequence< OUString > placesUrlsList( officecfg::Office::Common::Misc::FilePickerPlacesUrls::get() );
+ Sequence< OUString > placesNamesList( officecfg::Office::Common::Misc::FilePickerPlacesNames::get() );
+
+ unsigned int nPos = 0;
+ unsigned int i = 0;
+
+ m_sLastServiceUrl = officecfg::Office::Common::Misc::FilePickerLastService::get();
+
+ for( sal_Int32 nPlace = 0; nPlace < placesUrlsList.getLength() && nPlace < placesNamesList.getLength(); ++nPlace )
+ {
+ ServicePtr pService = std::make_shared<Place>( placesNamesList[nPlace], placesUrlsList[nPlace], true );
+ m_aServices.push_back( pService );
+
+ // Add to the listbox only remote services, not local bookmarks
+ if( !pService->IsLocal() )
+ {
+ OUString sPrefix = lcl_GetServiceType( pService );
+
+ if( !sPrefix.isEmpty() )
+ sPrefix += ": ";
+
+ if( placesUrlsList[nPlace] == m_sLastServiceUrl )
+ nPos = i;
+
+ m_xServices_lb->append_text(sPrefix + placesNamesList[nPlace]);
+
+ i++;
+ }
+ }
+
+ if (m_xServices_lb->get_count() > 0)
+ {
+ m_xServices_lb->set_active(nPos);
+ EnableExtraMenuItems(true);
+ }
+ else
+ EnableExtraMenuItems(false);
+
+ EnableControls();
+}
+
+int RemoteFilesDialog::GetSelectedServicePos()
+{
+ if( m_aServices.empty() )
+ return -1;
+
+ int nPos = 0;
+ int i = -1;
+
+ int nSelected = m_xServices_lb->get_active();
+
+ int nServices = static_cast<int>(m_aServices.size());
+ while( nPos < nServices )
+ {
+ while( (nPos < nServices) && m_aServices[nPos]->IsLocal() )
+ nPos++;
+ i++;
+ if( i == nSelected )
+ break;
+ nPos++;
+ }
+
+ return nPos;
+}
+
+void RemoteFilesDialog::AddFilter( const OUString& rFilter, const OUString& rType )
+{
+ OUString sName = rFilter;
+
+ m_aFilters.emplace_back( rFilter, rType );
+ if (rType.isEmpty())
+ m_xFilter_lb->append_separator("");
+ else
+ m_xFilter_lb->append_text(sName);
+
+ if (m_xFilter_lb->get_active() == -1)
+ m_xFilter_lb->set_active(0);
+}
+
+void RemoteFilesDialog::OpenURL( OUString const & sURL )
+{
+ if( !m_xFileView )
+ return;
+
+ DisableControls();
+
+ auto xWait = std::make_unique<weld::WaitObject>(m_xDialog.get());
+
+ if( !sURL.isEmpty() )
+ {
+ OUString sFilter = FILEDIALOG_FILTER_ALL;
+
+ if( m_nCurrentFilter != -1)
+ {
+ sFilter = m_aFilters[m_nCurrentFilter].second;
+ }
+
+ m_xFileView->EndInplaceEditing();
+
+ DBG_ASSERT( !m_pCurrentAsyncAction.is(), "SvtFileDialog::executeAsync: previous async action not yet finished!" );
+
+ m_pCurrentAsyncAction = new AsyncPickerAction( this, m_xFileView.get(), AsyncPickerAction::Action::eOpenURL );
+
+ // -1 timeout - sync
+ m_pCurrentAsyncAction->execute( sURL, sFilter, -1, -1, GetDenyList() );
+
+ if( m_eMode != REMOTEDLG_MODE_SAVE )
+ m_xName_ed->set_text( "" );
+
+ m_xFileView->grab_focus();
+ }
+ else
+ {
+ xWait.reset();
+
+ // content doesn't exist
+ ErrorHandler::HandleError( ERRCODE_IO_NOTEXISTS );
+
+ EnableControls();
+ }
+}
+
+OUString RemoteFilesDialog::AddFileExtension(const OUString& rFileName)
+{
+ if (m_nCurrentFilter == -1)
+ return rFileName;
+
+ OUString sExt = m_aFilters[m_nCurrentFilter].second;
+ sal_Int32 nDotPos = rFileName.lastIndexOf( '.' );
+
+ if (nDotPos == -1)
+ return rFileName + sExt.subView( 1 ); // without '*'
+
+ return rFileName;
+}
+
+void RemoteFilesDialog::EnableControls()
+{
+ if (m_xServices_lb->get_count() > 0)
+ {
+ m_xServices_lb->set_sensitive(true);
+
+ if (m_xServices_lb->get_active() != -1)
+ {
+ m_xManageServices->set_item_sensitive("change_password", false);
+
+ try
+ {
+ if( m_xMasterPasswd->isPersistentStoringAllowed() )
+ {
+ int nPos = GetSelectedServicePos();
+
+ if( nPos >= 0 )
+ {
+ OUString sUrl( m_aServices[nPos]->GetUrl() );
+
+ UrlRecord aURLEntries = m_xMasterPasswd->find( sUrl, Reference< XInteractionHandler>() );
+
+ if( aURLEntries.UserList.hasElements() )
+ {
+ m_xManageServices->set_item_sensitive("change_password", true);
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {}
+ }
+ }
+ else
+ m_xServices_lb->set_sensitive(false);
+
+ if( m_bIsConnected )
+ {
+ m_xFilter_lb->set_sensitive(true);
+ m_xName_ed->set_sensitive(true);
+ m_xContainer->set_sensitive(true);
+ m_xNewFolder->set_sensitive(true);
+
+ if (!m_xName_ed->get_text().isEmpty())
+ m_xOk_btn->set_sensitive(true);
+ else
+ m_xOk_btn->set_sensitive(false);
+ }
+ else
+ {
+ m_xFilter_lb->set_sensitive(false);
+ m_xName_ed->set_sensitive(false);
+ m_xContainer->set_sensitive(false);
+ m_xNewFolder->set_sensitive(false);
+ m_xOk_btn->set_sensitive(false);
+ }
+
+ m_xPath->EnableFields( true );
+ m_xManageServices->set_sensitive(true);
+}
+
+void RemoteFilesDialog::DisableControls()
+{
+ m_xServices_lb->set_sensitive(false);
+ m_xFilter_lb->set_sensitive(false);
+ m_xManageServices->set_sensitive(false);
+ m_xName_ed->set_sensitive(false);
+ m_xContainer->set_sensitive(false);
+ m_xOk_btn->set_sensitive(false);
+ m_xPath->EnableFields( false );
+
+ m_xCancel_btn->set_sensitive(true);
+}
+
+void RemoteFilesDialog::SavePassword(const OUString& rURL, const OUString& rUser,
+ const OUString& rPassword, bool bPersistent)
+{
+ if( rURL.isEmpty() || rUser.isEmpty() || rPassword.isEmpty() )
+ return;
+
+ try
+ {
+ if( !bPersistent ||
+ ( m_xMasterPasswd->isPersistentStoringAllowed()
+ && m_xMasterPasswd->authorizateWithMasterPassword( Reference< XInteractionHandler>() ) )
+ )
+ {
+ Reference< XInteractionHandler > xInteractionHandler =
+ InteractionHandler::createWithParent( m_xContext, nullptr );
+
+ Sequence<OUString> aPasswd { rPassword };
+
+ if( bPersistent )
+ m_xMasterPasswd->addPersistent(
+ rURL, rUser, aPasswd, xInteractionHandler );
+ else
+ m_xMasterPasswd->add( rURL, rUser, aPasswd, xInteractionHandler );
+ }
+ }
+ catch( const Exception& )
+ {}
+}
+
+IMPL_LINK_NOARG ( RemoteFilesDialog, IconViewHdl, weld::Button&, void )
+{
+ m_xListView_btn->set_active(false);
+ m_xFileView->SetViewMode( eIcon );
+}
+
+IMPL_LINK_NOARG ( RemoteFilesDialog, ListViewHdl, weld::Button&, void )
+{
+ m_xIconView_btn->set_active(false);
+ m_xFileView->SetViewMode( eDetailedList );
+}
+
+void RemoteFilesDialog::AddService()
+{
+ PlaceEditDialog aDlg(m_xDialog.get());
+ aDlg.ShowPasswordControl();
+ short aRetCode = aDlg.run();
+
+ switch( aRetCode )
+ {
+ case RET_OK :
+ {
+ ServicePtr newService = aDlg.GetPlace();
+ m_aServices.push_back( newService );
+
+ OUString sPassword = aDlg.GetPassword();
+ OUString sUser = aDlg.GetUser();
+ if( !sUser.isEmpty() && !sPassword.isEmpty() )
+ {
+ bool bPersistent = aDlg.IsRememberChecked();
+ SavePassword( newService->GetUrl(), sUser, sPassword, bPersistent );
+ }
+
+ OUString sPrefix = lcl_GetServiceType( newService );
+
+ if(!sPrefix.isEmpty())
+ sPrefix += ": ";
+
+ m_xServices_lb->append_text( sPrefix + newService->GetName() );
+ m_xServices_lb->set_active( m_xServices_lb->get_count() - 1 );
+ EnableExtraMenuItems(true);
+ SelectServiceHdl( *m_xServices_lb );
+
+ m_bIsUpdated = true;
+
+ EnableControls();
+ break;
+ }
+ case RET_CANCEL :
+ default :
+ // Do Nothing
+ break;
+ }
+}
+
+IMPL_LINK_NOARG( RemoteFilesDialog, SelectServiceHdl, weld::ComboBox&, void )
+{
+ int nPos = GetSelectedServicePos();
+
+ if( nPos >= 0 )
+ {
+ OUString sURL = m_aServices[nPos]->GetUrl();
+ EnableExtraMenuItems(true);
+
+ m_bServiceChanged = true;
+ OpenURL( sURL );
+ }
+}
+
+IMPL_LINK ( RemoteFilesDialog, EditServiceMenuHdl, const OString&, rIdent, void )
+{
+ OString sIdent(rIdent);
+ if( sIdent == "edit_service" && m_xServices_lb->get_count() > 0 )
+ {
+ int nSelected = m_xServices_lb->get_active();
+ int nPos = GetSelectedServicePos();
+
+ if( nPos >= 0 )
+ {
+ PlaceEditDialog aDlg(m_xDialog.get(), m_aServices[nPos]);
+ short aRetCode = aDlg.run();
+
+ switch( aRetCode )
+ {
+ case RET_OK :
+ {
+ ServicePtr pEditedService = aDlg.GetPlace();
+
+ m_aServices[nPos] = pEditedService;
+ m_xServices_lb->remove( nSelected );
+
+ OUString sPrefix = lcl_GetServiceType( pEditedService );
+
+ if(!sPrefix.isEmpty())
+ sPrefix += ": ";
+
+ m_xServices_lb->insert_text(nSelected, sPrefix + pEditedService->GetName());
+ m_xServices_lb->set_active( nSelected );
+
+ m_bIsUpdated = true;
+ break;
+ }
+ case RET_NO:
+ sIdent = "delete_service";
+ break;
+ case RET_CANCEL :
+ default :
+ // Do Nothing
+ break;
+ }
+ }
+ }
+ if( sIdent == "delete_service" && m_xServices_lb->get_count() > 0 )
+ {
+ int nSelected = m_xServices_lb->get_active();
+ int nPos = GetSelectedServicePos();
+
+ if( nPos >= 0 )
+ {
+ OUString sMsg = FpsResId( STR_SVT_DELETESERVICE );
+ sMsg = sMsg.replaceFirst( "$servicename$", m_xServices_lb->get_active_text() );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo, sMsg));
+ if (xBox->run() == RET_YES)
+ {
+ // remove password
+ try
+ {
+ if( m_xMasterPasswd->isPersistentStoringAllowed() )
+ {
+ OUString sUrl( m_aServices[nPos]->GetUrl() );
+
+ Reference< XInteractionHandler > xInteractionHandler =
+ InteractionHandler::createWithParent( m_xContext, nullptr );
+
+ UrlRecord aURLEntries = m_xMasterPasswd->find( sUrl, xInteractionHandler );
+
+ if( aURLEntries.Url == sUrl && aURLEntries.UserList.hasElements() )
+ {
+ OUString sUserName = aURLEntries.UserList[0].UserName;
+
+ m_xMasterPasswd->removePersistent( sUrl, sUserName );
+ }
+ }
+ }
+ catch( const Exception& )
+ {}
+
+ m_aServices.erase( m_aServices.begin() + nPos );
+ m_xServices_lb->remove( nSelected );
+
+ m_xServices_lb->set_active(-1);
+ EnableExtraMenuItems(false);
+
+ m_bIsUpdated = true;
+
+ m_bIsConnected = false;
+ EnableControls();
+ }
+ }
+ }
+ else if( sIdent == "change_password" )
+ {
+ try
+ {
+ if( m_xMasterPasswd->isPersistentStoringAllowed() && m_xMasterPasswd->authorizateWithMasterPassword( Reference< XInteractionHandler>() ) )
+ {
+ int nPos = GetSelectedServicePos();
+
+ if( nPos >= 0 )
+ {
+ OUString sUrl( m_aServices[nPos]->GetUrl() );
+
+ Reference< XInteractionHandler > xInteractionHandler =
+ InteractionHandler::createWithParent( m_xContext, nullptr );
+
+ UrlRecord aURLEntries = m_xMasterPasswd->find( sUrl, xInteractionHandler );
+
+ if( aURLEntries.Url == sUrl && aURLEntries.UserList.hasElements() )
+ {
+ OUString sUserName = aURLEntries.UserList[0].UserName;
+
+ rtl::Reference<::comphelper::SimplePasswordRequest> pPasswordRequest
+ = new ::comphelper::SimplePasswordRequest;
+
+ xInteractionHandler->handle( pPasswordRequest );
+
+ if ( pPasswordRequest->isPassword() )
+ {
+ OUString aNewPass = pPasswordRequest->getPassword();
+ Sequence<OUString> aPasswd { aNewPass };
+
+ m_xMasterPasswd->addPersistent(
+ sUrl, sUserName, aPasswd, xInteractionHandler );
+ }
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {}
+ }
+ else if( sIdent == "add_service" )
+ AddService();
+
+ EnableControls();
+}
+
+IMPL_LINK_NOARG( RemoteFilesDialog, DoubleClickHdl, SvtFileView*, bool )
+{
+ SvtContentEntry* pData = m_xFileView->FirstSelected();
+ if (pData)
+ {
+ if (!pData->mbIsFolder)
+ OkHdl(*m_xOk_btn);
+ else
+ OpenURL(pData->maURL);
+ }
+ return true;
+}
+
+IMPL_LINK_NOARG( RemoteFilesDialog, SelectHdl, SvtFileView*, void )
+{
+ SvtContentEntry* pData = m_xFileView->FirstSelected();
+ if (!pData)
+ return;
+
+ if( ( pData->mbIsFolder && ( m_eType == REMOTEDLG_TYPE_PATHDLG ) )
+ || ( !pData->mbIsFolder && ( m_eType == REMOTEDLG_TYPE_FILEDLG ) ) )
+ {
+ // url must contain user info, because we need this info in recent files entry
+ // (to fill user field in login box by default)
+ INetURLObject aURL( pData->maURL );
+ INetURLObject aCurrentURL( m_sLastServiceUrl );
+ aURL.SetUser( aCurrentURL.GetUser() );
+
+ m_sPath = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ m_xName_ed->set_text( aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset) );
+ }
+ else
+ {
+ if( m_eMode == REMOTEDLG_MODE_OPEN )
+ {
+ m_sPath.clear();
+ m_xName_ed->set_text( "" );
+ }
+ }
+
+ EnableControls();
+}
+
+IMPL_LINK_NOARG(RemoteFilesDialog, FileNameGetFocusHdl, weld::Widget&, void)
+{
+ m_xFileView->SetNoSelection();
+}
+
+IMPL_LINK_NOARG(RemoteFilesDialog, FileNameModifyHdl, weld::Entry&, void)
+{
+ m_xFileView->SetNoSelection();
+ if (!m_xOk_btn->get_sensitive())
+ EnableControls();
+}
+
+IMPL_LINK_NOARG( RemoteFilesDialog, SelectFilterHdl, weld::ComboBox&, void )
+{
+ int nPos = m_xFilter_lb->get_active();
+
+ if( nPos != -1 && !m_aFilters[nPos].second.isEmpty() )
+ {
+ m_nCurrentFilter = nPos;
+
+ OUString sCurrentURL = m_xFileView->GetViewURL();
+
+ if( !sCurrentURL.isEmpty() && m_bIsConnected )
+ OpenURL( sCurrentURL );
+ }
+}
+
+IMPL_LINK(RemoteFilesDialog, TreeSelectHdl, weld::TreeView&, rBox, void)
+{
+ OpenURL(rBox.get_selected_id());
+ m_xFileView->grab_focus();
+}
+
+IMPL_LINK(RemoteFilesDialog, SelectBreadcrumbHdl, Breadcrumb*, pPtr, bool)
+{
+ OpenURL( pPtr->GetHdlURL() );
+ return true;
+}
+
+IMPL_LINK_NOARG ( RemoteFilesDialog, NewFolderHdl, weld::Button&, void )
+{
+ m_xFileView->EndInplaceEditing();
+
+ // will be bound after InteractionHandler is enabled
+ SmartContent aContent;
+ aContent.enableDefaultInteractionHandler();
+ // now it can be bound
+ aContent.bindTo( m_xFileView->GetViewURL() );
+ if( !aContent.canCreateFolder() )
+ return;
+
+ OUString aTitle;
+ aContent.getTitle( aTitle );
+ QueryFolderNameDialog aDlg(m_xDialog.get(), aTitle, FpsResId(STR_SVT_NEW_FOLDER));
+ bool bHandled = false;
+
+ while( !bHandled )
+ {
+ if (aDlg.run() == RET_OK)
+ {
+ OUString aUrl = aContent.createFolder(aDlg.GetName());
+ if( !aUrl.isEmpty() )
+ {
+ m_xFileView->CreatedFolder(aUrl, aDlg.GetName());
+ bHandled = true;
+ }
+ }
+ else
+ bHandled = true;
+ }
+}
+
+IMPL_LINK_NOARG ( RemoteFilesDialog, OkHdl, weld::Button&, void )
+{
+ OUString sUserSelectedPath;
+
+ // check if file/path exists
+ OUString sCurrentPath = m_xFileView->GetViewURL();
+ OUString sSelectedItem = m_xFileView->GetCurrentURL();
+ OUString sUserTypedName = m_xName_ed->get_text();
+ OUString sFileName;
+ // auto extension
+ if( m_eMode == REMOTEDLG_MODE_SAVE )
+ sFileName = AddFileExtension(sUserTypedName);
+ else
+ sFileName = sUserTypedName;
+
+ bool bFileDlg = ( m_eType == REMOTEDLG_TYPE_FILEDLG );
+ bool bSelected = ( m_xFileView->GetSelectionCount() > 0 );
+
+ if( !sCurrentPath.endsWith("/") )
+ sCurrentPath += "/";
+
+ if( !bSelected )
+ {
+ m_sPath = sCurrentPath + INetURLObject::encode(sFileName, INetURLObject::PART_FPATH, INetURLObject::EncodeMechanism::All);
+ sUserSelectedPath = sCurrentPath + INetURLObject::encode(sUserTypedName, INetURLObject::PART_FPATH, INetURLObject::EncodeMechanism::All);
+ }
+ else
+ {
+ if( m_eType == REMOTEDLG_TYPE_PATHDLG )
+ m_sPath = sCurrentPath;
+ else
+ m_sPath = sSelectedItem;
+
+ // url must contain user info, because we need this info in recent files entry
+ // (to fill user field in login box by default)
+ INetURLObject aURL( m_sPath );
+ INetURLObject aCurrentURL( m_sLastServiceUrl );
+ aURL.SetUser( aCurrentURL.GetUser() );
+
+ m_sPath = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ sUserSelectedPath = m_sPath;
+ }
+
+ bool bExists = false;
+
+ if( bFileDlg )
+ bExists = ContentIsDocument( m_sPath );
+ else
+ bExists = ContentIsFolder( m_sPath );
+
+ if( bExists )
+ {
+ if( m_eMode == REMOTEDLG_MODE_SAVE )
+ {
+ OUString sMsg = FpsResId( STR_SVT_ALREADYEXISTOVERWRITE );
+ sMsg = sMsg.replaceFirst("$filename$", sFileName);
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo, sMsg));
+ if (xBox->run() != RET_YES)
+ return;
+ }
+ }
+ else
+ {
+ if (ContentIsFolder(sUserSelectedPath))
+ {
+ OpenURL(sUserSelectedPath);
+
+ if (!bSelected)
+ m_xName_ed->grab_focus();
+
+ return;
+ }
+
+ if( m_eMode == REMOTEDLG_MODE_OPEN )
+ return;
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG ( RemoteFilesDialog, CancelHdl, weld::Button&, void )
+{
+ if( m_pCurrentAsyncAction.is() )
+ {
+ m_pCurrentAsyncAction->cancel();
+ onAsyncOperationFinished();
+ }
+ else
+ {
+ m_xDialog->response(RET_CANCEL);
+ }
+}
+
+// SvtFileDialog_Base
+SvtFileView* RemoteFilesDialog::GetView()
+{
+ return m_xFileView.get();
+}
+
+void RemoteFilesDialog::SetHasFilename( bool )
+{
+}
+
+void RemoteFilesDialog::SetDenyList( const css::uno::Sequence< OUString >& rDenyList )
+{
+ m_aDenyList = rDenyList;
+ m_xTreeView->SetDenyList( rDenyList );
+}
+
+const css::uno::Sequence< OUString >& RemoteFilesDialog::GetDenyList() const
+{
+ return m_aDenyList;
+}
+
+void RemoteFilesDialog::SetStandardDir( const OUString& rStdDir )
+{
+ m_sStdDir = rStdDir;
+}
+
+const OUString& RemoteFilesDialog::GetStandardDir() const
+{
+ return m_sStdDir;
+}
+
+void RemoteFilesDialog::SetPath( const OUString& rNewURL )
+{
+ m_sPath = rNewURL;
+
+ if( m_eMode == REMOTEDLG_MODE_SAVE )
+ {
+ INetURLObject aUrl( m_sPath );
+ OUString sFileName = aUrl.GetLastName( INetURLObject::DecodeMechanism::WithCharset );
+
+ m_xName_ed->set_text( sFileName );
+ }
+}
+
+OUString RemoteFilesDialog::getCurrentFileText() const
+{
+ OUString sReturn;
+ if( m_xName_ed )
+ sReturn = m_xName_ed->get_text();
+ return sReturn;
+}
+
+void RemoteFilesDialog::setCurrentFileText( const OUString& rText, bool bSelectAll )
+{
+ if (m_xName_ed)
+ {
+ m_xName_ed->set_text(rText);
+ if( bSelectAll )
+ m_xName_ed->select_region(0, -1);
+ }
+}
+
+void RemoteFilesDialog::AddFilterGroup(
+ const OUString& rFilter,
+ const css::uno::Sequence< css::beans::StringPair >& rFilters )
+{
+ AddFilter( rFilter, OUString() );
+ const StringPair* pSubFilters = rFilters.getConstArray();
+ const StringPair* pSubFiltersEnd = pSubFilters + rFilters.getLength();
+ for ( ; pSubFilters != pSubFiltersEnd; ++pSubFilters )
+ AddFilter( pSubFilters->First, pSubFilters->Second );
+}
+
+OUString RemoteFilesDialog::GetCurFilter() const
+{
+ OUString sFilter;
+
+ if (m_nCurrentFilter != -1)
+ {
+ sFilter = m_aFilters[m_nCurrentFilter].first;
+ }
+
+ return sFilter;
+}
+
+OUString RemoteFilesDialog::getCurFilter( ) const
+{
+ return GetCurFilter();
+}
+
+void RemoteFilesDialog::SetCurFilter( const OUString& rFilter )
+{
+ DBG_ASSERT( !m_bIsInExecute, "SvtFileDialog::SetCurFilter: currently executing!" );
+
+ // look for corresponding filter
+ sal_uInt16 nPos = m_aFilters.size();
+
+ while ( nPos-- )
+ {
+ if ( m_aFilters[nPos].first == rFilter )
+ {
+ m_nCurrentFilter = nPos;
+ m_xFilter_lb->set_active( m_nCurrentFilter );
+ break;
+ }
+ }
+}
+
+void RemoteFilesDialog::FilterSelect()
+{
+}
+
+void RemoteFilesDialog::SetFileCallback( ::svt::IFilePickerListener * )
+{
+}
+
+void RemoteFilesDialog::onAsyncOperationStarted()
+{
+ DisableControls();
+}
+
+void RemoteFilesDialog::onAsyncOperationFinished()
+{
+ m_pCurrentAsyncAction = nullptr;
+ EnableControls();
+}
+
+void RemoteFilesDialog::UpdateControls( const OUString& rURL )
+{
+ int nPos = GetSelectedServicePos();
+
+ if( nPos >= 0 && m_bServiceChanged && rURL == m_aServices[nPos]->GetUrl() )
+ {
+ OUString sURL = m_aServices[nPos]->GetUrl();
+
+ m_xPath->SetRootName( m_sRootLabel );
+ m_xTreeView->clear();
+
+ m_xTreeView->InsertRootEntry(rURL, m_sRootLabel);
+
+ m_xName_ed->grab_focus();
+
+ m_sLastServiceUrl = sURL;
+
+ m_bServiceChanged = false;
+ }
+
+ m_xPath->SetURL( rURL );
+
+ m_xTreeView->connect_changed(Link<weld::TreeView&,void>());
+
+ // read cached data for this url and fill the tree
+ const ::std::vector< SvtContentEntry >& rFolders = m_xFileView->GetContent();
+ ::std::vector< std::pair< OUString, OUString > > aFolders;
+
+ m_xName_ed->ClearEntries();
+
+ for(const auto & rFolder : rFolders)
+ {
+ //WebDAV folders path ends in '/', so strip it
+ OUString aFolderName = rFolder.maURL;
+ if( rFolder.mbIsFolder && ( ( aFolderName.lastIndexOf( '/' ) + 1 ) == aFolderName.getLength() ) )
+ aFolderName = aFolderName.copy( 0, aFolderName.getLength() - 1 );
+
+ int nTitleStart = aFolderName.lastIndexOf( '/' );
+ if( nTitleStart != -1 )
+ {
+ OUString sTitle( INetURLObject::decode(
+ aFolderName.subView( nTitleStart + 1 ),
+ INetURLObject::DecodeMechanism::WithCharset ) );
+
+ if( rFolder.mbIsFolder )
+ {
+ aFolders.emplace_back( sTitle, aFolderName );
+ }
+
+ // add entries to the autocompletion mechanism
+ m_xName_ed->AddEntry( sTitle );
+ }
+ }
+
+ m_xTreeView->FillTreeEntry( rURL, aFolders );
+
+ m_xTreeView->connect_changed( LINK( this, RemoteFilesDialog, TreeSelectHdl ) );
+
+ m_bIsConnected = true;
+ EnableControls();
+}
+
+void RemoteFilesDialog::EnableAutocompletion( bool )
+{
+ // This dialog contains Breadcrumb, not Edit
+}
+
+const OUString& RemoteFilesDialog::GetPath()
+{
+ return m_sPath;
+}
+
+std::vector<OUString> RemoteFilesDialog::GetPathList() const
+{
+ std::vector<OUString> aList;
+
+ m_xFileView->selected_foreach([this, &aList](weld::TreeIter& rCurEntry){
+ // url must contain user info, because we need this info in recent files entry
+ // (to fill user field in login box by default)
+ INetURLObject aURL(m_xFileView->GetURL(rCurEntry));
+ INetURLObject aCurrentURL( m_sLastServiceUrl );
+ aURL.SetUser( aCurrentURL.GetUser() );
+
+ aList.push_back( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ return false;
+ });
+
+ if( aList.empty() && !m_sPath.isEmpty() )
+ aList.push_back( m_sPath );
+
+ return aList;
+}
+
+bool RemoteFilesDialog::ContentIsFolder( const OUString& rURL )
+{
+ try
+ {
+ ::ucbhelper::Content content(rURL,
+ ::utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ m_xContext);
+ return content.isFolder();
+ }
+ catch (css::uno::Exception const&)
+ {
+ return false;
+ }
+}
+
+bool RemoteFilesDialog::ContentIsDocument( const OUString& rURL )
+{
+ try
+ {
+ ::ucbhelper::Content content(rURL,
+ ::utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ m_xContext);
+ return content.isDocument();
+ }
+ catch (css::uno::Exception const&)
+ {
+ return false;
+ }
+}
+
+sal_Int32 RemoteFilesDialog::getAvailableWidth()
+{
+ // This dialog doesn't contain preview
+ return 0;
+}
+
+sal_Int32 RemoteFilesDialog::getAvailableHeight()
+{
+ // This dialog doesn't contain preview
+ return 0;
+}
+
+void RemoteFilesDialog::setImage( const css::uno::Any& )
+{
+ // This dialog doesn't contain preview
+}
+
+bool RemoteFilesDialog::getShowState()
+{
+ // This dialog doesn't contain preview
+ return false;
+}
+
+weld::Widget* RemoteFilesDialog::getControl( sal_Int16, bool) const
+{
+ return nullptr;
+}
+
+void RemoteFilesDialog::enableControl( sal_Int16, bool )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/RemoteFilesDialog.hxx b/fpicker/source/office/RemoteFilesDialog.hxx
new file mode 100644
index 000000000..3dfb4e1dc
--- /dev/null
+++ b/fpicker/source/office/RemoteFilesDialog.hxx
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "autocmpledit.hxx"
+#include <svtools/place.hxx>
+
+#include <unotools/viewoptions.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/task/XPasswordContainer2.hpp>
+
+#include <vector>
+
+#include "asyncfilepicker.hxx"
+#include "fpdialogbase.hxx"
+#include "breadcrumb.hxx"
+#include "fileview.hxx"
+#include "foldertree.hxx"
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ui::dialogs;
+
+enum SvtRemoteDlgMode
+{
+ REMOTEDLG_MODE_OPEN = 0,
+ REMOTEDLG_MODE_SAVE = 1
+};
+
+enum SvtRemoteDlgType
+{
+ REMOTEDLG_TYPE_FILEDLG = 0,
+ REMOTEDLG_TYPE_PATHDLG = 1
+};
+
+typedef std::shared_ptr< Place > ServicePtr;
+
+class RemoteFilesDialog : public SvtFileDialog_Base
+{
+public:
+ RemoteFilesDialog( weld::Window* pParent, PickerFlags nBits );
+ virtual ~RemoteFilesDialog() override;
+
+ virtual short run() override;
+
+ // SvtFileDialog_Base
+
+ virtual SvtFileView* GetView() override;
+
+ virtual void SetHasFilename( bool ) override;
+ virtual void SetDenyList( const css::uno::Sequence< OUString >& rDenyList ) override;
+ virtual const css::uno::Sequence< OUString >& GetDenyList() const override;
+ virtual void SetStandardDir( const OUString& rStdDir ) override;
+ virtual const OUString& GetStandardDir() const override;
+ virtual void SetPath( const OUString& rNewURL ) override;
+ virtual const OUString& GetPath() override;
+ virtual std::vector<OUString> GetPathList() const override;
+ virtual bool ContentIsFolder( const OUString& rURL ) override;
+ bool ContentIsDocument(const OUString& rURL);
+
+ virtual OUString getCurrentFileText() const override;
+ virtual void setCurrentFileText( const OUString& rText, bool bSelectAll = false ) override;
+
+ virtual void AddFilter( const OUString& rFilter, const OUString& rType ) override;
+ virtual void AddFilterGroup( const OUString& _rFilter,
+ const css::uno::Sequence< css::beans::StringPair >& rFilters ) override;
+ virtual OUString GetCurFilter() const override;
+ virtual void SetCurFilter( const OUString& rFilter ) override;
+ virtual void FilterSelect() override;
+
+ virtual void SetFileCallback( ::svt::IFilePickerListener *pNotifier ) override;
+ virtual void onAsyncOperationStarted() override;
+ virtual void onAsyncOperationFinished() override;
+ virtual void UpdateControls( const OUString& rURL ) override;
+
+ virtual void EnableAutocompletion( bool = true) override;
+
+ virtual sal_Int32 getAvailableWidth() override;
+ virtual sal_Int32 getAvailableHeight() override;
+
+ virtual void setImage( const css::uno::Any& rImage ) override;
+
+ virtual bool getShowState() override;
+
+ virtual weld::Widget* getControl( sal_Int16 nControlId, bool bLabelControl = false ) const override;
+ virtual void enableControl( sal_Int16 nControlId, bool bEnable ) override;
+ virtual OUString getCurFilter( ) const override;
+
+private:
+ Reference< XComponentContext > m_xContext;
+ Reference< XPasswordContainer2 > m_xMasterPasswd;
+
+ SvtRemoteDlgMode m_eMode;
+ SvtRemoteDlgType m_eType;
+ bool m_bIsUpdated;
+ bool m_bIsConnected;
+ bool m_bServiceChanged;
+
+ OUString m_sIniKey;
+
+ bool m_bIsInExecute;
+
+ OUString m_sPath;
+ OUString m_sStdDir;
+ OUString m_sRootLabel;
+ OUString m_sLastServiceUrl;
+ int m_nCurrentFilter;
+
+ ::rtl::Reference< ::svt::AsyncPickerAction > m_pCurrentAsyncAction;
+
+ css::uno::Sequence< OUString > m_aDenyList;
+
+ std::unique_ptr<weld::Button> m_xOk_btn;
+ std::unique_ptr<weld::Button> m_xCancel_btn;
+ std::unique_ptr<weld::MenuButton> m_xManageServices;
+ std::unique_ptr<weld::ComboBox> m_xServices_lb;
+ std::unique_ptr<weld::Container> m_xPathContainer;
+ std::unique_ptr<Breadcrumb> m_xPath;
+ std::unique_ptr<weld::Button> m_xNewFolder;
+ std::unique_ptr<weld::ToggleButton> m_xListView_btn;
+ std::unique_ptr<weld::ToggleButton> m_xIconView_btn;
+ std::unique_ptr<FolderTree> m_xTreeView;
+ std::unique_ptr<SvtFileView> m_xFileView;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<weld::ComboBox> m_xFilter_lb;
+ std::unique_ptr<AutocompleteEdit> m_xName_ed;
+
+ std::vector< ServicePtr > m_aServices;
+ std::vector< std::pair< OUString, OUString > > m_aFilters;
+
+ void InitSize();
+
+ void FillServicesListbox();
+
+ /* If failure returns < 0 */
+ int GetSelectedServicePos();
+
+ void OpenURL( OUString const & sURL );
+
+ OUString AddFileExtension(const OUString& rFileName);
+
+ void EnableExtraMenuItems(bool bEnable);
+ void EnableControls();
+ void DisableControls();
+
+ void SavePassword(const OUString& rURL, const OUString& rUser,
+ const OUString& rPassword, bool bPersistent);
+
+ void AddService();
+
+ DECL_LINK ( SelectServiceHdl, weld::ComboBox&, void );
+ DECL_LINK ( EditServiceMenuHdl, const OString&, void );
+
+ DECL_LINK( DoubleClickHdl, SvtFileView*, bool );
+ DECL_LINK( SelectHdl, SvtFileView*, void );
+
+ DECL_LINK( FileNameGetFocusHdl, weld::Widget&, void );
+ DECL_LINK( FileNameModifyHdl, weld::Entry&, void );
+
+ DECL_LINK( SelectFilterHdl, weld::ComboBox&, void );
+
+ DECL_LINK( TreeSelectHdl, weld::TreeView&, void );
+
+ DECL_LINK( SelectBreadcrumbHdl, Breadcrumb*, bool );
+
+ DECL_LINK( NewFolderHdl, weld::Button&, void );
+ DECL_LINK( IconViewHdl, weld::Button&, void );
+ DECL_LINK( ListViewHdl, weld::Button&, void );
+
+ DECL_LINK( OkHdl, weld::Button&, void );
+ DECL_LINK( CancelHdl, weld::Button&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/asyncfilepicker.cxx b/fpicker/source/office/asyncfilepicker.cxx
new file mode 100644
index 000000000..349d586b8
--- /dev/null
+++ b/fpicker/source/office/asyncfilepicker.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "asyncfilepicker.hxx"
+#include "fileview.hxx"
+#include "iodlg.hxx"
+#include <tools/debug.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+
+
+namespace svt
+{
+ AsyncPickerAction::AsyncPickerAction( SvtFileDialog_Base* _pDialog, SvtFileView* _pView, const Action _eAction )
+ :m_eAction ( _eAction )
+ ,m_pView ( _pView )
+ ,m_pDialog ( _pDialog )
+ ,m_bRunning ( false )
+ {
+ assert( m_pDialog && "AsyncPickerAction::AsyncPickerAction: invalid dialog!" );
+ assert( m_pView && "AsyncPickerAction::AsyncPickerAction: invalid view!" );
+ }
+
+
+ AsyncPickerAction::~AsyncPickerAction()
+ {
+ }
+
+
+ void AsyncPickerAction::cancel()
+ {
+ DBG_TESTSOLARMUTEX();
+ // if this asserts, we'd need to have an own mutex per instance
+
+ OSL_ENSURE( m_bRunning, "AsyncPickerAction::cancel: not running" );
+ if ( m_pView )
+ m_pView->CancelRunningAsyncAction();
+ }
+
+
+ void AsyncPickerAction::execute(
+ const OUString& _rURL,
+ const OUString& _rFilter,
+ sal_Int32 _nMinTimeout,
+ sal_Int32 _nMaxTimeout,
+ const css::uno::Sequence< OUString >& rDenyList )
+ {
+ DBG_TESTSOLARMUTEX();
+ // if this asserts, we'd need to have an own mutex per instance
+
+ sal_Int32 nMinTimeout = _nMinTimeout;
+ sal_Int32 nMaxTimeout = _nMaxTimeout;
+ // normalizations
+ if ( nMinTimeout < 0 )
+ // if negative, this is considered as "do it synchronously"
+ nMinTimeout = 0;
+ else if ( nMinTimeout < 1000 )
+ nMinTimeout = 1000;
+ if ( nMaxTimeout <= nMinTimeout )
+ nMaxTimeout = nMinTimeout + 30000;
+
+ std::unique_ptr< FileViewAsyncAction > pActionDescriptor;
+ if ( nMinTimeout )
+ {
+ pActionDescriptor.reset( new FileViewAsyncAction );
+ pActionDescriptor->nMinTimeout = nMinTimeout;
+ pActionDescriptor->nMaxTimeout = nMaxTimeout;
+ pActionDescriptor->aFinishHandler = LINK( this, AsyncPickerAction, OnActionDone );
+ }
+
+ FileViewResult eResult = eFailure;
+ m_sURL = _rURL;
+ switch ( m_eAction )
+ {
+ case ePrevLevel:
+ eResult = m_pView->PreviousLevel( pActionDescriptor.get() );
+ break;
+
+ case eOpenURL:
+ eResult = m_pView->Initialize( _rURL, _rFilter, pActionDescriptor.get(), rDenyList );
+ break;
+
+ case eExecuteFilter:
+ // preserve the filename (FS: why?)
+ m_sFileName = m_pDialog->getCurrentFileText();
+ // execute the new filter
+ eResult = m_pView->ExecuteFilter( _rFilter, pActionDescriptor.get() );
+ break;
+
+ default:
+ OSL_FAIL( "AsyncPickerAction::execute: unknown action!" );
+ break;
+ }
+
+ acquire();
+ if ( ( eResult == eSuccess ) || ( eResult == eFailure ) )
+ {
+ // the handler is only called if the action could not be finished within
+ // the given minimum time period. In case of success, we need to call it
+ // explicitly
+ OnActionDone( reinterpret_cast< void* >( eResult ) );
+ }
+ else if ( eResult == eStillRunning )
+ {
+ m_bRunning = true;
+ m_pDialog->onAsyncOperationStarted();
+ }
+ }
+
+
+ IMPL_LINK( AsyncPickerAction, OnActionDone, void*, pEmptyArg, void )
+ {
+ DBG_TESTSOLARMUTEX();
+ // if this asserts, we'd need to have an own mutex per instance
+
+ FileViewResult eResult = static_cast< FileViewResult >( reinterpret_cast< sal_IntPtr >( pEmptyArg ) );
+ OSL_ENSURE( eStillRunning != eResult, "AsyncPickerAction::OnActionDone: invalid result!" );
+
+ // release once (since we acquired in |execute|), but keep alive until the
+ // end of the method
+ ::rtl::Reference< AsyncPickerAction > xKeepAlive( this );
+ release();
+
+ m_pDialog->onAsyncOperationFinished();
+ m_bRunning = true;
+
+ if ( eFailure == eResult )
+ // TODO: do we need some kind of cleanup here?
+ return;
+
+ if ( eTimeout == eResult )
+ {
+ SvtFileDialog::displayIOException( m_sURL, css::ucb::IOErrorCode_CANT_READ );
+ return;
+ }
+
+ OSL_ENSURE( eSuccess == eResult, "AsyncPickerAction::OnActionDone: what else valid results are there?" );
+
+ switch ( m_eAction )
+ {
+ case ePrevLevel:
+ case eOpenURL:
+ m_pDialog->UpdateControls( m_pView->GetViewURL() );
+ break;
+
+ case eExecuteFilter:
+ // restore the filename
+ m_pView->SetNoSelection();
+ m_pDialog->setCurrentFileText( m_sFileName, true );
+
+ // notify listeners
+ m_pDialog->FilterSelect();
+ break;
+
+ default:
+ OSL_FAIL( "AsyncPickerAction::OnActionDone: unknown action!" );
+ break;
+ }
+ }
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/asyncfilepicker.hxx b/fpicker/source/office/asyncfilepicker.hxx
new file mode 100644
index 000000000..e2bac1208
--- /dev/null
+++ b/fpicker/source/office/asyncfilepicker.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/link.hxx>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <salhelper/simplereferenceobject.hxx>
+
+class SvtFileView;
+class SvtFileDialog_Base;
+
+namespace svt
+{
+
+
+ //= AsyncPickerAction
+
+ class AsyncPickerAction : public salhelper::SimpleReferenceObject
+ {
+ public:
+ enum Action
+ {
+ ePrevLevel,
+ eOpenURL,
+ eExecuteFilter
+ };
+
+ private:
+ Action m_eAction;
+ SvtFileView* m_pView;
+ SvtFileDialog_Base* m_pDialog;
+ OUString m_sURL;
+ OUString m_sFileName;
+ bool m_bRunning;
+
+ public:
+ AsyncPickerAction( SvtFileDialog_Base* _pDialog, SvtFileView* _pView, const Action _eAction );
+
+ /** executes the action
+
+ @param _nMinTimeout
+ the minimum timeout to wait, in milliseconds. If negative, the action will we done
+ synchronously. If between 0 and 999, it will be corrected to 1000, means the
+ smallest valid value is 1000 (which equals one second).
+ @param _nMaxTimeout
+ The maximum time to wait for a result, in milliseconds. If there's no result of
+ the action within the given time frame, the action will be cancelled.
+ If smaller than or equal to <arg>_nMinTimeout</arg>, it will be corrected to
+ <arg>_nMinTimeout</arg> + 30000.
+ */
+ void execute(
+ const OUString& _rURL,
+ const OUString& _rFilter,
+ sal_Int32 _nMinTimeout,
+ sal_Int32 _nMaxTimeout,
+ const css::uno::Sequence< OUString >& rDenyList );
+
+ /// cancels the running action
+ void cancel();
+
+ protected:
+ virtual ~AsyncPickerAction() override;
+
+ private:
+ DECL_LINK( OnActionDone, void*, void );
+
+ AsyncPickerAction( const AsyncPickerAction& ) = delete;
+ AsyncPickerAction& operator=( const AsyncPickerAction& ) = delete;
+ };
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/autocmpledit.cxx b/fpicker/source/office/autocmpledit.cxx
new file mode 100644
index 000000000..89a2d0b0c
--- /dev/null
+++ b/fpicker/source/office/autocmpledit.cxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <vcl/event.hxx>
+#include "autocmpledit.hxx"
+
+AutocompleteEdit::AutocompleteEdit(std::unique_ptr<weld::Entry> xEntry)
+ : m_xEntry(std::move(xEntry))
+ , m_aChangedIdle("fpicker::AutocompleteEdit m_aChangedIdle")
+ , m_nLastCharCode(0)
+{
+ m_xEntry->connect_changed(LINK(this, AutocompleteEdit, ChangedHdl));
+ m_xEntry->connect_key_press(LINK(this, AutocompleteEdit, KeyInputHdl));
+
+ m_aChangedIdle.SetInvokeHandler(LINK(this, AutocompleteEdit, TryAutoComplete));
+}
+
+IMPL_LINK(AutocompleteEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ m_nLastCharCode = rKEvt.GetKeyCode().GetCode();
+ return false;
+}
+
+IMPL_LINK_NOARG(AutocompleteEdit, ChangedHdl, weld::Entry&, void)
+{
+ m_aChangeHdl.Call(*m_xEntry);
+
+ switch (m_nLastCharCode)
+ {
+ case css::awt::Key::DELETE_WORD_BACKWARD:
+ case css::awt::Key::DELETE_WORD_FORWARD:
+ case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
+ case css::awt::Key::DELETE_TO_END_OF_LINE:
+ case KEY_BACKSPACE:
+ case KEY_DELETE:
+ m_aChangedIdle.Stop();
+ break;
+ default:
+ m_aChangedIdle.Start(); //launch this to happen on idle after cursor position will have been set
+ break;
+ }
+}
+
+void AutocompleteEdit::AddEntry( const OUString& rEntry )
+{
+ m_aEntries.push_back( rEntry );
+}
+
+void AutocompleteEdit::ClearEntries()
+{
+ m_aEntries.clear();
+ m_aMatching.clear();
+}
+
+IMPL_LINK_NOARG(AutocompleteEdit, TryAutoComplete, Timer *, void)
+{
+ OUString aCurText = m_xEntry->get_text();
+
+ int nStartPos, nEndPos;
+ m_xEntry->get_selection_bounds(nStartPos, nEndPos);
+ if (std::max(nStartPos, nEndPos) != aCurText.getLength())
+ return;
+
+ auto nLen = std::min(nStartPos, nEndPos);
+ aCurText = aCurText.copy( 0, nLen );
+ if( aCurText.isEmpty() )
+ return;
+
+ if( !m_aEntries.empty() )
+ {
+ if( Match( aCurText ) )
+ {
+ m_xEntry->set_text(m_aMatching[0]);
+ auto nNewLen = m_aMatching[0].getLength();
+ m_xEntry->select_region(nLen, nNewLen);
+ }
+ }
+}
+
+bool AutocompleteEdit::Match( std::u16string_view rText )
+{
+ bool bRet = false;
+
+ m_aMatching.clear();
+
+ for(const OUString & rEntry : m_aEntries)
+ {
+ if( rEntry.startsWithIgnoreAsciiCase( rText ) )
+ {
+ m_aMatching.push_back( rEntry );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/autocmpledit.hxx b/fpicker/source/office/autocmpledit.hxx
new file mode 100644
index 000000000..3eb79eb14
--- /dev/null
+++ b/fpicker/source/office/autocmpledit.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/idle.hxx>
+#include <vcl/weld.hxx>
+#include <vector>
+
+class AutocompleteEdit
+{
+private:
+ std::unique_ptr<weld::Entry> m_xEntry;
+
+ std::vector<OUString> m_aEntries;
+ std::vector<OUString> m_aMatching;
+ Idle m_aChangedIdle;
+ Link<weld::Entry&, void> m_aChangeHdl;
+ sal_uInt16 m_nLastCharCode;
+
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(ChangedHdl, weld::Entry&, void);
+ DECL_LINK(TryAutoComplete, Timer*, void);
+
+ bool Match(std::u16string_view rText);
+
+public:
+ AutocompleteEdit(std::unique_ptr<weld::Entry> xEntry);
+
+ void show() { m_xEntry->show(); }
+ void set_sensitive(bool bSensitive) { m_xEntry->set_sensitive(bSensitive); }
+ OUString get_text() const { return m_xEntry->get_text(); }
+ void set_text(const OUString& rText) { m_xEntry->set_text(rText); }
+ void grab_focus() { m_xEntry->grab_focus(); }
+ void select_region(int nStartPos, int nEndPos) { m_xEntry->select_region(nStartPos, nEndPos); }
+
+ void connect_changed(const Link<weld::Entry&, void>& rLink) { m_aChangeHdl = rLink; }
+ void connect_focus_in(const Link<weld::Widget&, void>& rLink)
+ {
+ m_xEntry->connect_focus_in(rLink);
+ }
+
+ void AddEntry(const OUString& rEntry);
+ void ClearEntries();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/breadcrumb.cxx b/fpicker/source/office/breadcrumb.cxx
new file mode 100644
index 000000000..0b27367d8
--- /dev/null
+++ b/fpicker/source/office/breadcrumb.cxx
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include "breadcrumb.hxx"
+
+Breadcrumb::Breadcrumb(weld::Container* pParent)
+ : m_pParent(pParent)
+ , m_nMaxWidth(m_pParent->get_preferred_size().Width())
+{
+ m_pParent->connect_size_allocate(LINK(this, Breadcrumb, SizeAllocHdl));
+ m_eMode = SvtBreadcrumbMode::ONLY_CURRENT_PATH;
+ appendField(); // root
+}
+
+IMPL_LINK(Breadcrumb, SizeAllocHdl, const Size&, rSize, void)
+{
+ m_nMaxWidth = rSize.Width();
+}
+
+Breadcrumb::~Breadcrumb()
+{
+ m_pParent->connect_size_allocate(Link<const Size&, void>());
+}
+
+void Breadcrumb::EnableFields( bool bEnable )
+{
+ if( bEnable )
+ {
+ INetURLObject aURL( m_aCurrentURL );
+ int nSegments = aURL.getSegmentCount();
+ m_aSegments[nSegments]->m_xLink->set_sensitive(false);
+ }
+}
+
+void Breadcrumb::connect_clicked( const Link<Breadcrumb*,bool>& rLink )
+{
+ m_aClickHdl = rLink;
+}
+
+const OUString& Breadcrumb::GetHdlURL() const
+{
+ return m_sClickedURL;
+}
+
+void Breadcrumb::SetRootName( const OUString& rURL )
+{
+ m_sRootName = rURL;
+
+ // we changed root - clear all fields
+ for (size_t i = 1; i < m_aSegments.size(); ++i)
+ {
+ m_aSegments[i]->m_xLink->set_label("");
+
+ m_aSegments[i]->m_xLink->hide();
+ m_aSegments[i]->m_xSeparator->hide();
+ m_aSegments[i]->m_xLink->set_sensitive(true);
+ }
+}
+
+void Breadcrumb::SetURL( const OUString& rURL )
+{
+ m_aCurrentURL = rURL;
+ INetURLObject aURL(rURL);
+ aURL.setFinalSlash();
+
+ bool bClear = m_eMode == SvtBreadcrumbMode::ONLY_CURRENT_PATH;
+
+ int nSegments = aURL.getSegmentCount();
+
+ size_t nVecSizeRequired = nSegments + 1;
+
+ while (m_aSegments.size() < nVecSizeRequired)
+ appendField();
+
+ // fill the fields under root
+ for (int i = nSegments; i; --i)
+ {
+ OUString sLabel = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset);
+ OUString sLink = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ if (m_eMode == SvtBreadcrumbMode::ALL_VISITED)
+ {
+ if( m_aSegments[i]->m_xLink->get_label() != sLabel )
+ bClear = true;
+ }
+
+ m_aSegments[i]->m_xLink->hide();
+ m_aSegments[i]->m_xLink->set_label(sLabel);
+ m_aSegments[i]->m_xLink->set_sensitive(true);
+ m_aSegments[i]->m_xLink->set_uri(sLink);
+ m_aUris[m_aSegments[i]->m_xLink.get()] = sLink;
+
+ m_aSegments[i]->m_xSeparator->hide();
+
+ aURL.removeSegment();
+ }
+
+ OUString sRootPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::WithCharset);
+
+ // root field
+ m_aSegments[0]->m_xLink->set_label( m_sRootName );
+ m_aSegments[0]->m_xLink->set_sensitive(true);
+ m_aSegments[0]->m_xLink->set_uri(sRootPath);
+ m_aUris[m_aSegments[0]->m_xLink.get()] = sRootPath;
+
+ // clear unused fields
+ for (size_t i = nSegments + 1; i < m_aSegments.size(); i++ )
+ {
+ if( bClear )
+ m_aSegments[i]->m_xLink->set_label( "" );
+
+ m_aSegments[i]->m_xLink->hide();
+ m_aSegments[i]->m_xSeparator->hide();
+ m_aSegments[i]->m_xLink->set_sensitive(true);
+ }
+
+ // show fields
+ unsigned int nSeparatorWidth = m_aSegments[0]->m_xSeparator->get_preferred_size().Width();
+ unsigned int nCurrentWidth = 0;
+ unsigned int nLastVisible = nSegments;
+
+ bool bRight = ( m_eMode == SvtBreadcrumbMode::ALL_VISITED );
+ bool bLeft = true;
+
+ int i = 0;
+
+ while( bLeft || bRight )
+ {
+ if( nSegments - i == -1 )
+ bLeft = false;
+
+ if( bLeft )
+ {
+ unsigned int nIndex = nSegments - i;
+
+ if( showField( nIndex, m_nMaxWidth - nCurrentWidth ) )
+ {
+ nCurrentWidth += m_aSegments[nIndex]->m_xLink->get_preferred_size().Width()
+ + nSeparatorWidth + 2*SPACING;
+ }
+ else
+ {
+ // label is too long
+ if( nSegments != 0 )
+ {
+ m_aSegments[0]->m_xLink->set_label("...");
+ m_aSegments[0]->m_xLink->set_sensitive(false);
+ }
+ bLeft = false;
+ }
+ }
+
+ if( nSegments + i == static_cast<int>(m_aSegments.size()) )
+ bRight = false;
+
+ if( i != 0 && bRight )
+ {
+ unsigned int nIndex = nSegments + i;
+
+ if( m_aSegments[nIndex]->m_xLink->get_label().isEmpty() )
+ {
+ bRight = false;
+ }
+ else if( showField( nIndex, m_nMaxWidth - nCurrentWidth ) )
+ {
+ nCurrentWidth += m_aSegments[nIndex]->m_xLink->get_preferred_size().Width()
+ + nSeparatorWidth + 3*SPACING;
+ nLastVisible = nIndex;
+ }
+ else
+ {
+ bRight = false;
+ }
+ }
+
+ i++;
+ }
+
+ // current dir should be inactive
+ m_aSegments[nSegments]->m_xLink->set_sensitive(false);
+
+ // hide last separator
+ m_aSegments[nLastVisible]->m_xSeparator->hide();
+}
+
+void Breadcrumb::SetMode( SvtBreadcrumbMode eMode )
+{
+ m_eMode = eMode;
+}
+
+void Breadcrumb::appendField()
+{
+ m_aSegments.emplace_back(std::make_unique<BreadcrumbPath>(m_pParent));
+ size_t nIndex = m_aSegments.size() - 1;
+ m_aSegments[nIndex]->m_xLink->hide();
+ m_aSegments[nIndex]->m_xLink->connect_activate_link(LINK(this, Breadcrumb, ClickLinkHdl));
+ m_aSegments[nIndex]->m_xSeparator->set_label( ">" );
+ m_aSegments[nIndex]->m_xSeparator->hide();
+}
+
+bool Breadcrumb::showField( unsigned int nIndex, unsigned int nWidthMax )
+{
+ m_aSegments[nIndex]->m_xLink->show();
+ m_aSegments[nIndex]->m_xSeparator->show();
+
+ unsigned int nSeparatorWidth = m_aSegments[0]->m_xSeparator->get_preferred_size().Width();
+ unsigned int nWidth = m_aSegments[nIndex]->m_xLink->get_preferred_size().Width()
+ + nSeparatorWidth + 3*SPACING;
+
+ if( nWidth > nWidthMax )
+ {
+ if( nIndex != 0 )
+ {
+ m_aSegments[nIndex]->m_xLink->hide();
+ m_aSegments[nIndex]->m_xSeparator->hide();
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+IMPL_LINK(Breadcrumb, ClickLinkHdl, weld::LinkButton&, rLink, bool)
+{
+ m_sClickedURL = m_aUris[&rLink];
+ return m_aClickHdl.Call(this);
+}
+
+BreadcrumbPath::BreadcrumbPath(weld::Container* pContainer)
+ : m_xBuilder(Application::CreateBuilder(pContainer, "fps/ui/breadcrumb.ui"))
+ , m_xContainer(m_xBuilder->weld_container("container"))
+ , m_xLink(m_xBuilder->weld_link_button("link"))
+ , m_xSeparator(m_xBuilder->weld_label("label"))
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/breadcrumb.hxx b/fpicker/source/office/breadcrumb.hxx
new file mode 100644
index 000000000..5f476010c
--- /dev/null
+++ b/fpicker/source/office/breadcrumb.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <map>
+#include <vector>
+
+#define SPACING 6
+
+enum SvtBreadcrumbMode
+{
+ ONLY_CURRENT_PATH = 0,
+ ALL_VISITED = 1
+};
+
+struct BreadcrumbPath
+{
+ BreadcrumbPath(weld::Container* pParent);
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<weld::LinkButton> m_xLink;
+ std::unique_ptr<weld::Label> m_xSeparator;
+};
+
+class Breadcrumb
+{
+private:
+ weld::Container* m_pParent;
+ int m_nMaxWidth;
+
+ std::vector<std::unique_ptr<BreadcrumbPath>> m_aSegments;
+ std::map<weld::LinkButton*, OUString> m_aUris;
+
+ OUString m_sRootName;
+ OUString m_sClickedURL;
+ OUString m_aCurrentURL;
+
+ SvtBreadcrumbMode m_eMode;
+
+ Link<Breadcrumb*, bool> m_aClickHdl;
+
+ void appendField();
+ bool showField(unsigned int nIndex, unsigned int nWidthMax);
+
+ DECL_LINK(SizeAllocHdl, const Size&, void);
+ DECL_LINK(ClickLinkHdl, weld::LinkButton&, bool);
+
+public:
+ Breadcrumb(weld::Container* pParent);
+ ~Breadcrumb();
+
+ void EnableFields(bool bEnable);
+
+ void connect_clicked(const Link<Breadcrumb*, bool>& rLink);
+ const OUString& GetHdlURL() const;
+
+ void SetRootName(const OUString& rURL);
+ void SetURL(const OUString& rURL);
+ void SetMode(SvtBreadcrumbMode eMode);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/commonpicker.cxx b/fpicker/source/office/commonpicker.cxx
new file mode 100644
index 000000000..29ae5d08b
--- /dev/null
+++ b/fpicker/source/office/commonpicker.cxx
@@ -0,0 +1,476 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "commonpicker.hxx"
+#include "fpdialogbase.hxx"
+#include "OfficeControlAccess.hxx"
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/weakeventlistener.hxx>
+#include <comphelper/types.hxx>
+
+
+namespace svt
+{
+
+
+#define PROPERTY_ID_HELPURL 1
+#define PROPERTY_ID_WINDOW 2
+
+ // using --------------------------------------------------------------
+
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::ui::dialogs;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::comphelper;
+
+
+ OCommonPicker::OCommonPicker()
+ :OCommonPicker_Base( m_aMutex )
+ ,OPropertyContainer( GetBroadcastHelper() )
+ ,m_nCancelEvent( nullptr )
+ ,m_bExecuting( false )
+ {
+ // the two properties we have
+ registerProperty(
+ "HelpURL", PROPERTY_ID_HELPURL,
+ PropertyAttribute::TRANSIENT,
+ &m_sHelpURL, cppu::UnoType<decltype(m_sHelpURL)>::get()
+ );
+
+ registerProperty(
+ "Window", PROPERTY_ID_WINDOW,
+ PropertyAttribute::TRANSIENT | PropertyAttribute::READONLY,
+ &m_xWindow, cppu::UnoType<decltype(m_xWindow)>::get()
+ );
+ }
+
+
+ OCommonPicker::~OCommonPicker()
+ {
+ if ( !GetBroadcastHelper().bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ // disambiguate XInterface
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OCommonPicker, OCommonPicker_Base, OPropertyContainer )
+
+
+ // disambiguate XTypeProvider
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCommonPicker, OCommonPicker_Base, OPropertyContainer )
+
+
+ // XComponent related methods
+
+ void OCommonPicker::checkAlive() const
+ {
+ if ( GetBroadcastHelper().bInDispose || GetBroadcastHelper().bDisposed )
+ throw DisposedException();
+ }
+
+ void OCommonPicker::prepareDialog()
+ {
+ if(createPicker())
+ {
+ // set the title
+ if ( !m_aTitle.isEmpty() )
+ m_xDlg->set_title(m_aTitle);
+ }
+ }
+
+
+ void SAL_CALL OCommonPicker::disposing()
+ {
+ SolarMutexGuard aGuard;
+
+ stopWindowListening();
+
+ if ( m_nCancelEvent )
+ Application::RemoveUserEvent( m_nCancelEvent );
+
+ {
+ ::osl::MutexGuard aOwnGuard( m_aMutex );
+ if ( m_bExecuting && m_xDlg )
+ m_xDlg->response(RET_CANCEL);
+ }
+
+ m_xDlg.reset();
+ m_xWindow = nullptr;
+ m_xDialogParent = nullptr;
+ }
+
+
+ void OCommonPicker::stopWindowListening()
+ {
+ disposeComponent( m_xWindowListenerAdapter );
+ disposeComponent( m_xParentListenerAdapter );
+ }
+
+ // XEventListener
+ void SAL_CALL OCommonPicker::disposing( const EventObject& _rSource )
+ {
+ SolarMutexGuard aGuard;
+ bool bDialogDying = _rSource.Source == m_xWindow;
+ bool bParentDying = _rSource.Source == m_xDialogParent;
+
+ if ( bDialogDying || bParentDying )
+ {
+ stopWindowListening();
+
+ SAL_WARN_IF(bDialogDying && m_bExecuting, "fpicker.office", "unexpected disposing before response" );
+
+ // it's the parent which is dying -> delete the dialog
+ {
+ ::osl::MutexGuard aOwnGuard(m_aMutex);
+ if (m_bExecuting && m_xDlg)
+ m_xDlg->response(RET_CANCEL);
+ }
+
+ m_xDlg.reset();
+ m_xWindow = nullptr;
+ m_xDialogParent = nullptr;
+ }
+ else
+ {
+ OSL_FAIL( "OCommonPicker::disposing: where did this come from?" );
+ }
+ }
+
+ // property set related methods
+ ::cppu::IPropertyArrayHelper* OCommonPicker::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps;
+ describeProperties( aProps );
+ return new cppu::OPropertyArrayHelper( aProps );
+ }
+
+ ::cppu::IPropertyArrayHelper& SAL_CALL OCommonPicker::getInfoHelper()
+ {
+ return *getArrayHelper();
+ }
+
+ Reference< XPropertySetInfo > SAL_CALL OCommonPicker::getPropertySetInfo( )
+ {
+ return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
+ }
+
+ void SAL_CALL OCommonPicker::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue)
+ {
+ OPropertyContainer::setFastPropertyValue_NoBroadcast(nHandle, rValue);
+
+ // if the HelpURL changed, forward this to the dialog
+ if (PROPERTY_ID_HELPURL == nHandle && m_xDlg)
+ {
+ ::svt::OControlAccess aAccess(m_xDlg.get(), m_xDlg->GetView());
+ aAccess.setHelpURL(m_xDlg->getDialog(), m_sHelpURL);
+ }
+ }
+
+ bool OCommonPicker::createPicker()
+ {
+ if ( !m_xDlg )
+ {
+ m_xDlg = implCreateDialog(Application::GetFrameWeld(m_xDialogParent));
+ SAL_WARN_IF( !m_xDlg, "fpicker.office", "OCommonPicker::createPicker: invalid dialog returned!" );
+
+ if ( m_xDlg )
+ {
+ weld::Dialog* pDlg = m_xDlg->getDialog();
+
+ ::svt::OControlAccess aAccess(m_xDlg.get(), m_xDlg->GetView());
+ // synchronize the help id of the dialog without help URL property
+ if ( !m_sHelpURL.isEmpty() )
+ { // somebody already set the help URL while we had no dialog yet
+ aAccess.setHelpURL(pDlg, m_sHelpURL);
+ }
+ else
+ {
+ m_sHelpURL = aAccess.getHelpURL(pDlg);
+ }
+
+ m_xWindow = pDlg->GetXWindow();
+
+ // add as event listener to the window
+ OSL_ENSURE( m_xWindow.is(), "OCommonPicker::createFileDialog: invalid window component!" );
+ if ( m_xWindow.is() )
+ {
+ m_xWindowListenerAdapter = new OWeakEventListenerAdapter( this, m_xWindow );
+ // the adapter will add itself as listener, and forward notifications
+ }
+
+ VclPtr<vcl::Window> xVclDialog(VCLUnoHelper::GetWindow(m_xWindow));
+ if (xVclDialog) // this block is quite possibly unnecessary by now
+ {
+ // _and_ add as event listener to the parent - in case the parent is destroyed
+ // before we are disposed, our disposal would access dead VCL windows then...
+ m_xDialogParent = VCLUnoHelper::GetInterface(xVclDialog->GetParent());
+ OSL_ENSURE(m_xDialogParent.is() || !xVclDialog->GetParent(), "OCommonPicker::createFileDialog: invalid window component (the parent this time)!");
+ }
+ if ( m_xDialogParent.is() )
+ {
+ m_xParentListenerAdapter = new OWeakEventListenerAdapter( this, m_xDialogParent );
+ // the adapter will add itself as listener, and forward notifications
+ }
+ }
+ }
+
+ return nullptr != m_xDlg;
+ }
+
+ // XControlAccess functions
+ void SAL_CALL OCommonPicker::setControlProperty( const OUString& aControlName, const OUString& aControlProperty, const Any& aValue )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ aAccess.setControlProperty( aControlName, aControlProperty, aValue );
+ }
+ }
+
+ Any SAL_CALL OCommonPicker::getControlProperty( const OUString& aControlName, const OUString& aControlProperty )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ return aAccess.getControlProperty( aControlName, aControlProperty );
+ }
+
+ return Any();
+ }
+
+ // XControlInformation functions
+ Sequence< OUString > SAL_CALL OCommonPicker::getSupportedControls( )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ return aAccess.getSupportedControls( );
+ }
+
+ return Sequence< OUString >();
+ }
+
+ sal_Bool SAL_CALL OCommonPicker::isControlSupported( const OUString& aControlName )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ return svt::OControlAccess::isControlSupported( aControlName );
+ }
+
+ return false;
+ }
+
+ Sequence< OUString > SAL_CALL OCommonPicker::getSupportedControlProperties( const OUString& aControlName )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ return aAccess.getSupportedControlProperties( aControlName );
+ }
+
+ return Sequence< OUString >();
+ }
+
+ sal_Bool SAL_CALL OCommonPicker::isControlPropertySupported( const OUString& aControlName, const OUString& aControlProperty )
+ {
+ checkAlive();
+
+ SolarMutexGuard aGuard;
+ if ( createPicker() )
+ {
+ ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() );
+ return aAccess.isControlPropertySupported( aControlName, aControlProperty );
+ }
+
+ return false;
+ }
+
+
+ // XExecutableDialog functions
+
+ void OCommonPicker::setTitle( const OUString& _rTitle )
+ {
+ SolarMutexGuard aGuard;
+ m_aTitle = _rTitle;
+ }
+
+
+ sal_Int16 OCommonPicker::execute()
+ {
+ SolarMutexGuard aGuard;
+
+ prepareDialog();
+
+ {
+ ::osl::MutexGuard aOwnGuard( m_aMutex );
+ m_bExecuting = true;
+ }
+ sal_Int16 nResult = implExecutePicker();
+ {
+ ::osl::MutexGuard aOwnGuard( m_aMutex );
+ m_bExecuting = false;
+ }
+
+ return nResult;
+ }
+
+
+ // XCancellable functions
+
+ void SAL_CALL OCommonPicker::cancel( )
+ {
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_nCancelEvent )
+ // nothing to do - the event for cancelling the dialog is already on the way
+ return;
+ }
+
+ // The thread which executes our dialog has locked the solar mutex for
+ // sure. Cancelling the dialog should be done with a locked solar mutex, too.
+ // Thus we post ourself a message for cancelling the dialog. This way, the message
+ // is either handled in the thread which opened the dialog (which may even be
+ // this thread here), or, if no dialog is open, in the thread doing scheduling
+ // currently. Both is okay for us...
+
+ // Note that we could do check if we are really executing the dialog currently.
+ // but the information would be potentially obsolete at the moment our event
+ // arrives, so we need to check it there, anyway...
+ m_nCancelEvent = Application::PostUserEvent( LINK( this, OCommonPicker, OnCancelPicker ) );
+ }
+
+ IMPL_LINK_NOARG(OCommonPicker, OnCancelPicker, void*, void)
+ {
+ // By definition, the solar mutex is locked when we arrive here. Note that this
+ // is important, as for instance the consistency of m_xDlg depends on this mutex.
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nCancelEvent = nullptr;
+
+ if ( !m_bExecuting )
+ // nothing to do. This may be because the dialog was canceled after our cancel method
+ // posted this async event, or because somebody called cancel without the dialog
+ // being executed at this time.
+ return;
+
+ OSL_ENSURE( m_xDlg, "OCommonPicker::OnCancelPicker: executing, but no dialog!" );
+ if (m_xDlg)
+ m_xDlg->response(RET_CANCEL);
+ }
+
+ // XInitialization functions
+ void SAL_CALL OCommonPicker::initialize( const Sequence< Any >& _rArguments )
+ {
+ checkAlive();
+
+ OUString sSettingName;
+ Any aSettingValue;
+
+ PropertyValue aPropArg;
+ NamedValue aPairArg;
+
+
+ const Any* pArguments = _rArguments.getConstArray();
+ const Any* pArgumentsEnd = _rArguments.getConstArray() + _rArguments.getLength();
+ for ( const Any* pArgument = pArguments;
+ pArgument != pArgumentsEnd;
+ ++pArgument
+ )
+ {
+ if ( *pArgument >>= aPropArg )
+ {
+ if ( aPropArg.Name.isEmpty())
+ continue;
+
+ sSettingName = aPropArg.Name;
+ aSettingValue = aPropArg.Value;
+ }
+ else if ( *pArgument >>= aPairArg )
+ {
+ if ( aPairArg.Name.isEmpty())
+ continue;
+
+ sSettingName = aPairArg.Name;
+ aSettingValue = aPairArg.Value;
+
+
+ }
+ else
+ {
+ SAL_WARN( "fpicker", "OCommonPicker::initialize: unknown argument type at position "
+ << (pArguments - _rArguments.getConstArray()));
+ continue;
+ }
+
+ bool bKnownSetting =
+ implHandleInitializationArgument( sSettingName, aSettingValue );
+ DBG_ASSERT( bKnownSetting,
+ OString(
+ "OCommonPicker::initialize: unknown argument \""
+ + OString(sSettingName.getStr(), sSettingName.getLength(), osl_getThreadTextEncoding())
+ + "\"!").getStr() );
+ }
+ }
+
+ bool OCommonPicker::implHandleInitializationArgument( const OUString& _rName, const Any& _rValue )
+ {
+ bool bKnown = true;
+ if ( _rName == "ParentWindow" )
+ {
+ m_xDialogParent.clear();
+ OSL_VERIFY( _rValue >>= m_xDialogParent );
+ OSL_ENSURE( m_xDialogParent.is(), "OCommonPicker::implHandleInitializationArgument: invalid parent window given!" );
+ }
+ else
+ bKnown = false;
+ return bKnown;
+ }
+
+} // namespace svt
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/commonpicker.hxx b/fpicker/source/office/commonpicker.hxx
new file mode 100644
index 000000000..1c24d8eb9
--- /dev/null
+++ b/fpicker/source/office/commonpicker.hxx
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <cppuhelper/basemutex.hxx>
+#include <com/sun/star/ui/dialogs/XControlInformation.hpp>
+#include <com/sun/star/ui/dialogs/XControlAccess.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/util/XCancellable.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <comphelper/propertycontainer.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/uno3.hxx>
+#include <tools/link.hxx>
+
+class SvtFileDialog_Base;
+namespace weld { class Window; }
+struct ImplSVEvent;
+
+namespace svt
+{
+
+
+ typedef ::cppu::WeakComponentImplHelper < css::ui::dialogs::XControlAccess
+ , css::ui::dialogs::XControlInformation
+ , css::lang::XEventListener
+ , css::util::XCancellable
+ , css::lang::XInitialization
+ > OCommonPicker_Base;
+ /** implements common functionality for the 2 UNO picker components
+ */
+ class OCommonPicker
+ :public ::cppu::BaseMutex
+ ,public OCommonPicker_Base
+ ,public ::comphelper::OPropertyContainer
+ ,public ::comphelper::OPropertyArrayUsageHelper< OCommonPicker >
+ {
+ private:
+ // <properties>
+ OUString m_sHelpURL;
+ css::uno::Reference< css::awt::XWindow > m_xWindow;
+ // </properties>
+
+ ImplSVEvent * m_nCancelEvent;
+ bool m_bExecuting;
+
+ css::uno::Reference< css::awt::XWindow > m_xDialogParent;
+
+ css::uno::Reference< css::lang::XComponent > m_xWindowListenerAdapter;
+ css::uno::Reference< css::lang::XComponent > m_xParentListenerAdapter;
+
+ protected:
+ OUString m_aTitle;
+ OUString m_aDisplayDirectory;
+
+ protected:
+ std::shared_ptr<SvtFileDialog_Base> m_xDlg;
+
+ const ::cppu::OBroadcastHelper& GetBroadcastHelper() const { return OCommonPicker_Base::rBHelper; }
+ ::cppu::OBroadcastHelper& GetBroadcastHelper() { return OCommonPicker_Base::rBHelper; }
+
+ public:
+ OCommonPicker();
+
+ protected:
+ virtual ~OCommonPicker() override;
+
+ // overridables
+
+ // will be called with locked SolarMutex
+ virtual std::shared_ptr<SvtFileDialog_Base> implCreateDialog( weld::Window* pParent ) = 0;
+ virtual sal_Int16 implExecutePicker( ) = 0;
+ // do NOT override XExecutableDialog::execute! We need to do some stuff there ourself ...
+
+ protected:
+
+ // disambiguate XInterface
+
+ DECLARE_XINTERFACE( )
+
+
+ // disambiguate XTypeProvider
+
+ DECLARE_XTYPEPROVIDER( )
+
+
+ // ComponentHelper/XComponent
+
+ virtual void SAL_CALL disposing() override;
+
+
+ // XEventListener
+
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+
+ // property set related methods
+
+
+ // XPropertySet pure methods
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ // OPropertySetHelper pure methods
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ // OPropertyArrayUsageHelper pure methods
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ // OPropertySetHelper overridden methods
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(
+ sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+
+
+ // XExecutableDialog functions
+
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setTitle( const OUString& _rTitle );
+ /// @throws css::uno::RuntimeException
+ virtual sal_Int16 SAL_CALL execute();
+
+
+ // XControlAccess functions
+
+ virtual void SAL_CALL setControlProperty( const OUString& aControlName, const OUString& aControlProperty, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getControlProperty( const OUString& aControlName, const OUString& aControlProperty ) override;
+
+
+ // XControlInformation functions
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedControls( ) override;
+ virtual sal_Bool SAL_CALL isControlSupported( const OUString& aControlName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedControlProperties( const OUString& aControlName ) override;
+ virtual sal_Bool SAL_CALL isControlPropertySupported( const OUString& aControlName, const OUString& aControlProperty ) override;
+
+
+ // XCancellable functions
+
+ virtual void SAL_CALL cancel( ) override;
+
+
+ // XInitialization functions
+
+
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+
+ // misc
+
+ void checkAlive() const;
+
+ void prepareDialog();
+
+ protected:
+ bool createPicker();
+
+ /** handle a single argument from the XInitialization::initialize method
+
+ @return <TRUE/> if the argument could be handled
+ */
+ virtual bool implHandleInitializationArgument(
+ const OUString& _rName,
+ const css::uno::Any& _rValue
+ );
+
+ private:
+ void stopWindowListening();
+
+ DECL_LINK( OnCancelPicker, void*, void );
+ };
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/contentenumeration.cxx b/fpicker/source/office/contentenumeration.cxx
new file mode 100644
index 000000000..5b74719a3
--- /dev/null
+++ b/fpicker/source/office/contentenumeration.cxx
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "contentenumeration.hxx"
+#include <svtools/imagemgr.hxx>
+
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/XDynamicResultSet.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/document/DocumentProperties.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+
+namespace svt
+{
+
+
+#define ROW_TITLE 1
+#define ROW_SIZE 2
+#define ROW_DATE_MOD 3
+#define ROW_DATE_CREATE 4
+#define ROW_IS_FOLDER 5
+#define ROW_TARGET_URL 6
+#define ROW_IS_HIDDEN 7
+#define ROW_IS_VOLUME 8
+#define ROW_IS_REMOTE 9
+#define ROW_IS_REMOVABLE 10
+#define ROW_IS_FLOPPY 11
+#define ROW_IS_COMPACTDISC 12
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::util::DateTime;
+ using ::com::sun::star::sdbc::XResultSet;
+ using ::com::sun::star::sdbc::XRow;
+ using ::com::sun::star::ucb::XDynamicResultSet;
+ using ::com::sun::star::ucb::CommandAbortedException;
+ using ::com::sun::star::ucb::XContentAccess;
+ using ::com::sun::star::ucb::XCommandEnvironment;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::document::DocumentProperties;
+ using ::ucbhelper::ResultSetInclude;
+ using ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS;
+
+
+ //= FileViewContentEnumerator
+
+
+ FileViewContentEnumerator::FileViewContentEnumerator(
+ const Reference< XCommandEnvironment >& _rxCommandEnv,
+ ContentData& _rContentToFill, ::osl::Mutex& _rContentMutex )
+ :Thread ( "FileViewContentEnumerator" )
+ ,m_rContent ( _rContentToFill )
+ ,m_rContentMutex ( _rContentMutex )
+ ,m_xCommandEnv ( _rxCommandEnv )
+ ,m_pResultHandler ( nullptr )
+ ,m_bCancelled ( false )
+ {
+ }
+
+
+ FileViewContentEnumerator::~FileViewContentEnumerator()
+ {
+ }
+
+
+ void FileViewContentEnumerator::cancel()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_bCancelled = true;
+ m_pResultHandler = nullptr;
+ m_aFolder.aContent = ::ucbhelper::Content();
+ m_aFolder.sURL.clear();
+ }
+
+
+ EnumerationResult FileViewContentEnumerator::enumerateFolderContentSync(
+ const FolderDescriptor& _rFolder,
+ const css::uno::Sequence< OUString >& rDenyList )
+ {
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_aFolder = _rFolder;
+ m_pResultHandler = nullptr;
+ m_rDenyList = rDenyList;
+ }
+ return enumerateFolderContent();
+ }
+
+
+ void FileViewContentEnumerator::enumerateFolderContent(
+ const FolderDescriptor& _rFolder, IEnumerationResultHandler* _pResultHandler )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_aFolder = _rFolder;
+ m_pResultHandler = _pResultHandler;
+
+ OSL_ENSURE( m_aFolder.aContent.get().is() || !m_aFolder.sURL.isEmpty(),
+ "FileViewContentEnumerator::enumerateFolderContent: invalid folder descriptor!" );
+
+ launch();
+ //TODO: a protocol is missing how to join with the launched thread
+ // before exit(3), to ensure the thread is no longer relying on any
+ // infrastructure while that infrastructure is being shut down in
+ // atexit handlers
+ }
+
+
+ EnumerationResult FileViewContentEnumerator::enumerateFolderContent()
+ {
+ EnumerationResult eResult = EnumerationResult::ERROR;
+ try
+ {
+
+ Reference< XResultSet > xResultSet;
+ Sequence< OUString > aProps{ "Title",
+ "Size",
+ "DateModified",
+ "DateCreated",
+ "IsFolder",
+ "TargetURL",
+ "IsHidden",
+ "IsVolume",
+ "IsRemote",
+ "IsRemoveable",
+ "IsFloppy",
+ "IsCompactDisc" };
+
+ Reference< XCommandEnvironment > xEnvironment;
+ try
+ {
+ FolderDescriptor aFolder;
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ aFolder = m_aFolder;
+ xEnvironment = m_xCommandEnv;
+ }
+ if ( !aFolder.aContent.get().is() )
+ {
+ aFolder.aContent = ::ucbhelper::Content( aFolder.sURL, xEnvironment, comphelper::getProcessComponentContext() );
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_aFolder.aContent = aFolder.aContent;
+ }
+ }
+
+ Reference< XDynamicResultSet > xDynResultSet = aFolder.aContent.createDynamicCursor( aProps, INCLUDE_FOLDERS_AND_DOCUMENTS );
+
+ if ( xDynResultSet.is() )
+ xResultSet = xDynResultSet->getStaticResultSet();
+ }
+ catch( CommandAbortedException& )
+ {
+ TOOLS_WARN_EXCEPTION( "svtools.contnr", "");
+ }
+ catch( Exception& )
+ {
+ }
+
+ if ( xResultSet.is() )
+ {
+ Reference< XRow > xRow( xResultSet, UNO_QUERY );
+ Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
+
+ try
+ {
+ DateTime aDT;
+
+ bool bCancelled = false;
+ while ( !bCancelled && xResultSet->next() )
+ {
+ bool bIsHidden = xRow->getBoolean( ROW_IS_HIDDEN );
+ // don't show hidden files
+ if ( !bIsHidden || xRow->wasNull() )
+ {
+ aDT = xRow->getTimestamp( ROW_DATE_MOD );
+ bool bContainsDate = !xRow->wasNull();
+ if ( !bContainsDate )
+ {
+ aDT = xRow->getTimestamp( ROW_DATE_CREATE );
+ bContainsDate = !xRow->wasNull();
+ }
+
+ OUString aContentURL = xContentAccess->queryContentIdentifierString();
+ OUString aTargetURL = xRow->getString( ROW_TARGET_URL );
+ bool bHasTargetURL = !xRow->wasNull() && !aTargetURL.isEmpty();
+
+ OUString sRealURL = bHasTargetURL ? aTargetURL : aContentURL;
+
+ // check for restrictions
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( /* m_rDenyList.hasElements() && */ URLOnDenyList ( sRealURL ) )
+ continue;
+ }
+
+ std::unique_ptr<SortingData_Impl> pData(new SortingData_Impl);
+ pData->maTargetURL = sRealURL;
+
+ pData->mbIsFolder = xRow->getBoolean( ROW_IS_FOLDER ) && !xRow->wasNull();
+ pData->mbIsVolume = xRow->getBoolean( ROW_IS_VOLUME ) && !xRow->wasNull();
+ pData->mbIsRemote = xRow->getBoolean( ROW_IS_REMOTE ) && !xRow->wasNull();
+ pData->mbIsRemoveable = xRow->getBoolean( ROW_IS_REMOVABLE ) && !xRow->wasNull();
+ pData->mbIsFloppy = xRow->getBoolean( ROW_IS_FLOPPY ) && !xRow->wasNull();
+ pData->mbIsCompactDisc = xRow->getBoolean( ROW_IS_COMPACTDISC ) && !xRow->wasNull();
+ pData->SetNewTitle( xRow->getString( ROW_TITLE ) );
+ pData->maSize = xRow->getLong( ROW_SIZE );
+
+ if ( bHasTargetURL &&
+ INetURLObject( aContentURL ).GetProtocol() == INetProtocol::VndSunStarHier )
+ {
+ ::ucbhelper::Content aCnt( aTargetURL, xEnvironment, comphelper::getProcessComponentContext() );
+ try
+ {
+ aCnt.getPropertyValue("Size") >>= pData->maSize;
+ aCnt.getPropertyValue("DateModified") >>= aDT;
+ }
+ catch (...) {}
+ }
+
+ if ( bContainsDate )
+ {
+ pData->maModDate = ::DateTime( aDT );
+ }
+
+ if ( pData->mbIsFolder )
+ {
+ SolarMutexGuard aGuard;
+ ::svtools::VolumeInfo aVolInfo( pData->mbIsVolume, pData->mbIsRemote,
+ pData->mbIsRemoveable, pData->mbIsFloppy,
+ pData->mbIsCompactDisc );
+ pData->maType = SvFileInformationManager::GetFolderDescription( aVolInfo );
+ }
+ else
+ pData->maType = SvFileInformationManager::GetFileDescription(
+ INetURLObject( pData->maTargetURL ) );
+
+ {
+ ::osl::MutexGuard aGuard( m_rContentMutex );
+ m_rContent.push_back( std::move(pData) );
+ }
+ }
+
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ bCancelled = m_bCancelled;
+ }
+ }
+ eResult = EnumerationResult::SUCCESS;
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svtools.contnr", "FileViewContentEnumerator::enumerateFolderContent: caught an exception while enumerating");
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svtools.contnr", "FileViewContentEnumerator::enumerateFolderContent" );
+ }
+
+ IEnumerationResultHandler* pHandler = nullptr;
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ pHandler = m_pResultHandler;
+ if ( m_bCancelled )
+ return EnumerationResult::ERROR;
+ }
+
+ {
+ ::osl::MutexGuard aGuard( m_rContentMutex );
+ if ( eResult != EnumerationResult::SUCCESS )
+ // clear any "intermediate" and unfinished result
+ m_rContent.clear();
+ }
+
+ if ( pHandler )
+ pHandler->enumerationDone( eResult );
+ return eResult;
+ }
+
+
+ bool FileViewContentEnumerator::URLOnDenyList ( std::u16string_view sRealURL )
+ {
+ std::u16string_view entryName = sRealURL.substr( sRealURL.rfind( '/' ) + 1 );
+
+ return comphelper::findValue(m_rDenyList, entryName) != -1;
+ }
+
+
+ void FileViewContentEnumerator::execute()
+ {
+ enumerateFolderContent();
+ }
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/contentenumeration.hxx b/fpicker/source/office/contentenumeration.hxx
new file mode 100644
index 000000000..b1c07afcf
--- /dev/null
+++ b/fpicker/source/office/contentenumeration.hxx
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <salhelper/thread.hxx>
+#include <ucbhelper/content.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/datetime.hxx>
+#include <utility>
+
+namespace svt
+{
+
+
+ //= SortingData_Impl
+
+ struct SortingData_Impl
+ {
+ private:
+ OUString maFilename; // only filename in upper case - for compare purposes
+ OUString maTitle; // -> be careful when changing maTitle to update maFilename only when new
+ OUString maLowerTitle;
+
+
+ public:
+ OUString maType;
+ OUString maTargetURL;
+ OUString maDisplayName;
+ OUString maDisplaySize;
+ OUString maDisplayDate;
+ DateTime maModDate;
+ OUString maImage;
+ sal_Int64 maSize;
+ bool mbIsFolder;
+ bool mbIsVolume;
+ bool mbIsRemote;
+ bool mbIsRemoveable;
+ bool mbIsFloppy;
+ bool mbIsCompactDisc;
+
+ inline SortingData_Impl();
+ inline const OUString& GetTitle() const;
+ inline const OUString& GetLowerTitle() const;
+ inline const OUString& GetFileName() const;
+ inline void SetNewTitle( const OUString& rNewTitle ); // new maTitle is set -> maFilename is set to same!
+
+ private:
+ inline void SetTitles( const OUString& rNewTitle );
+ };
+
+ inline SortingData_Impl::SortingData_Impl() :
+ maModDate ( DateTime::EMPTY ),
+ maSize ( 0 ),
+ mbIsFolder ( false ),
+ mbIsVolume ( false ),
+ mbIsRemote ( false ),
+ mbIsRemoveable ( false ),
+ mbIsFloppy ( false ),
+ mbIsCompactDisc ( false )
+ {
+ }
+
+ inline const OUString& SortingData_Impl::GetTitle() const
+ {
+ return maTitle;
+ }
+
+ inline const OUString& SortingData_Impl::GetLowerTitle() const
+ {
+ return maLowerTitle;
+ }
+
+ inline const OUString& SortingData_Impl::GetFileName() const
+ {
+ return maFilename;
+ }
+
+ inline void SortingData_Impl::SetNewTitle( const OUString& rNewTitle )
+ {
+ SetTitles( rNewTitle );
+ maFilename = rNewTitle.toAsciiUpperCase();
+ }
+
+ inline void SortingData_Impl::SetTitles( const OUString& rNewTitle )
+ {
+ maTitle = rNewTitle;
+ maLowerTitle = rNewTitle.toAsciiLowerCase();
+ }
+
+
+ //= EnumerationResult
+
+ enum class EnumerationResult
+ {
+ SUCCESS, /// the enumeration was successful
+ ERROR, /// the enumeration was unsuccessful
+ };
+
+
+ //= FolderDescriptor
+
+ struct FolderDescriptor
+ {
+ /** a content object describing the folder. Can be <NULL/>, in this case <member>sURL</member>
+ is relevant.
+ */
+ ::ucbhelper::Content aContent;
+ /** the URL of a folder. Will be ignored if <member>aContent</member> is not <NULL/>.
+ */
+ OUString sURL;
+
+ FolderDescriptor() { }
+
+ explicit FolderDescriptor( OUString _aURL )
+ :sURL(std::move( _aURL ))
+ {
+ }
+ };
+
+
+ //= IEnumerationResultHandler
+
+ class IEnumerationResultHandler
+ {
+ public:
+ virtual void enumerationDone( EnumerationResult _eResult ) = 0;
+
+ protected:
+ ~IEnumerationResultHandler() {}
+ };
+
+
+ //= FileViewContentEnumerator
+
+ class FileViewContentEnumerator: public salhelper::Thread
+ {
+ public:
+ typedef ::std::vector< std::unique_ptr<SortingData_Impl> > ContentData;
+
+ private:
+ ContentData& m_rContent;
+ ::osl::Mutex& m_rContentMutex;
+
+ mutable ::osl::Mutex m_aMutex;
+
+ FolderDescriptor m_aFolder;
+ css::uno::Reference< css::ucb::XCommandEnvironment >
+ m_xCommandEnv;
+ IEnumerationResultHandler* m_pResultHandler;
+ bool m_bCancelled;
+
+ css::uno::Sequence< OUString > m_rDenyList;
+
+ bool URLOnDenyList ( std::u16string_view sRealURL );
+
+ public:
+ /** constructs an enumerator instance
+
+ @param _rContentToFill
+ the structure which is to be filled with the found content
+ @param _rContentMutex
+ the mutex which protects the access to <arg>_rContentToFill</arg>
+ @param _pTranslator
+ an instance which should be used to translate content titles. May be <NULL/>
+ */
+ FileViewContentEnumerator(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& _rxCommandEnv,
+ ContentData& _rContentToFill,
+ ::osl::Mutex& _rContentMutex
+ );
+
+ /** enumerates the content of a given folder
+
+ @param _rFolder
+ the folder whose content is to be enumerated
+ @param _pFilter
+ a filter to apply to the found contents
+ @param _pResultHandler
+ an instance which should handle the results of the enumeration
+ */
+ void enumerateFolderContent(
+ const FolderDescriptor& _rFolder,
+ IEnumerationResultHandler* _pResultHandler
+ );
+
+ /** enumerates the content of a given folder synchronously
+ */
+ EnumerationResult enumerateFolderContentSync(
+ const FolderDescriptor& _rFolder,
+ const css::uno::Sequence< OUString >& rDenyList
+ );
+
+ /** cancels the running operation.
+
+ Note that "cancel" may mean that the operation is running, but its result
+ is simply disregarded later on.
+ */
+ void cancel();
+
+ protected:
+ virtual ~FileViewContentEnumerator() override;
+
+ private:
+ EnumerationResult enumerateFolderContent();
+
+ // Thread overridables
+ virtual void execute() override;
+
+ };
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fileview.cxx b/fpicker/source/office/fileview.cxx
new file mode 100644
index 000000000..7e78f773e
--- /dev/null
+++ b/fpicker/source/office/fileview.cxx
@@ -0,0 +1,1816 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svtools/svtresid.hxx>
+#include <svtools/imagemgr.hxx>
+#include <svtools/querydelete.hxx>
+#include <svtools/strings.hrc>
+#include <bitmaps.hlst>
+#include "contentenumeration.hxx"
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <algorithm>
+#include <string_view>
+#include <vector>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <rtl/math.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/mutex.hxx>
+#include <osl/conditn.hxx>
+#include <salhelper/timer.hxx>
+#include <svtools/urlfilter.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/timer.hxx>
+#include <memory>
+#include "fileview.hxx"
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::beans;
+using namespace ::comphelper;
+using ::svt::SortingData_Impl;
+using ::svt::FolderDescriptor;
+
+constexpr OUStringLiteral ALL_FILES_FILTER = u"*.*";
+
+#define COLUMN_TITLE 1
+#define COLUMN_TYPE 2
+#define COLUMN_SIZE 3
+#define COLUMN_DATE 4
+
+#define QUICK_SEARCH_TIMEOUT 1500 // time in mSec before the quicksearch string will be reset
+
+namespace {
+
+enum class FileViewFlags
+{
+ NONE = 0x00,
+ MULTISELECTION = 0x02,
+ SHOW_TYPE = 0x04,
+ SHOW_NONE = 0x20,
+};
+
+}
+
+namespace o3tl
+{
+ template<> struct typed_flags<FileViewFlags> : is_typed_flags<FileViewFlags, 0x26> {};
+}
+
+namespace
+{
+
+ //= CallbackTimer
+
+ class CallbackTimer : public ::salhelper::Timer
+ {
+ protected:
+ SvtFileView_Impl* const m_pTimeoutHandler;
+
+ public:
+ explicit CallbackTimer( SvtFileView_Impl* _pHandler ) : m_pTimeoutHandler( _pHandler ) { }
+
+ protected:
+ virtual void SAL_CALL onShot() override;
+ };
+
+class ViewTabListBox_Impl
+{
+private:
+ Reference< XCommandEnvironment > mxCmdEnv;
+ std::unique_ptr<weld::TreeView> mxTreeView;
+ std::unique_ptr<weld::TreeIter> mxScratchIter;
+
+ ::osl::Mutex maMutex;
+ SvtFileView_Impl* mpParent;
+ Timer maResetQuickSearch { "fpicker SvtFileView_Impl maResetQuickSearch" };
+ OUString maQuickSearchText;
+ sal_uInt32 mnSearchIndex;
+ bool mbEnableDelete;
+ bool mbEditing;
+ bool const mbShowType;
+
+ void DeleteEntries();
+ void DoQuickSearch( sal_Unicode rChar );
+ bool Kill( const OUString& rURL );
+
+public:
+ ViewTabListBox_Impl(std::unique_ptr<weld::TreeView> xTreeView, weld::Window* pTopLevel, SvtFileView_Impl* pParent, FileViewFlags nFlags);
+
+ std::unique_ptr<weld::TreeIter> make_iterator() const { return mxTreeView->make_iterator(); }
+ void insert(const OUString &rEntry, const OUString& rId, const OUString& rImage, weld::TreeIter& rIter)
+ {
+ mxTreeView->insert(nullptr, -1, &rEntry, &rId, nullptr, nullptr, false, &rIter);
+ mxTreeView->set_image(rIter, rImage);
+ }
+ void append(const OUString& rId, const OUString& rStr, const OUString& rType, const OUString& rSize, const OUString& rDate, const OUString& rImage)
+ {
+ mxTreeView->insert(nullptr, -1, &rStr, &rId, nullptr, nullptr, false, mxScratchIter.get());
+ mxTreeView->set_image(*mxScratchIter, rImage);
+ int nCol = 1;
+ if (mbShowType)
+ mxTreeView->set_text(*mxScratchIter, rType, nCol++);
+ mxTreeView->set_text(*mxScratchIter, rSize, nCol++);
+ mxTreeView->set_text(*mxScratchIter, rDate, nCol++);
+ }
+
+ void scroll_to_row(const weld::TreeIter& rIter) { mxTreeView->scroll_to_row(rIter); }
+ void set_cursor(int nPos) { mxTreeView->set_cursor(nPos); }
+ void set_cursor(const weld::TreeIter& rIter) { mxTreeView->set_cursor(rIter); }
+ bool get_cursor(weld::TreeIter* pIter) const { return mxTreeView->get_cursor(pIter); }
+ bool get_iter_first(weld::TreeIter& rIter) const { return mxTreeView->get_iter_first(rIter); }
+ bool get_selected(weld::TreeIter* pIter) const { return mxTreeView->get_selected(pIter); }
+
+ OUString get_selected_text() const
+ {
+ // tdf#131898 only care about column 0
+ int nIndex = mxTreeView->get_selected_index();
+ return nIndex != -1 ? mxTreeView->get_text(nIndex, 0) : OUString();
+ }
+
+ void unselect_all() { mxTreeView->unselect_all(); }
+
+ OUString get_id(const weld::TreeIter& rIter) { return mxTreeView->get_id(rIter); }
+
+ void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) { mxTreeView->connect_row_activated(rLink); }
+ void connect_changed(const Link<weld::TreeView&, void>& rLink) { mxTreeView->connect_changed(rLink); }
+
+ int n_children() const { return mxTreeView->n_children(); }
+
+ void freeze() { mxTreeView->freeze(); }
+ void thaw() { mxTreeView->thaw(); }
+
+ void show() { mxTreeView->show(); }
+ void hide() { mxTreeView->hide(); }
+ bool get_visible() const { return mxTreeView->get_visible(); }
+
+ int count_selected_rows() const { return mxTreeView->count_selected_rows(); }
+
+ void grab_focus() { mxTreeView->grab_focus(); }
+ bool has_focus() const { return mxTreeView->has_focus(); }
+
+ void set_help_id(const OString& rHelpId) { mxTreeView->set_help_id(rHelpId); }
+ OString get_help_id() const { return mxTreeView->get_help_id(); }
+
+ bool IsEditingActive() const { return mbEditing; }
+
+ void end_editing()
+ {
+ mxTreeView->end_editing();
+ mxTreeView->connect_editing(Link<const weld::TreeIter&, bool>(), Link<const IterString&, bool>());
+ mbEditing = false;
+ }
+
+ void selected_foreach(const std::function<bool(weld::TreeIter&)>& func)
+ {
+ mxTreeView->selected_foreach(func);
+ }
+
+ weld::TreeView* getWidget() const
+ {
+ return mxTreeView.get();
+ }
+
+ void clear() { mxTreeView->clear(); }
+
+ void EnableDelete( bool bEnable ) { mbEnableDelete = bEnable; }
+ bool TypeColumnVisible() const { return mbShowType; }
+
+ const Reference< XCommandEnvironment >& GetCommandEnvironment() const { return mxCmdEnv; }
+
+ DECL_LINK(ResetQuickSearch_Impl, Timer *, void);
+ DECL_LINK(CommandHdl, const CommandEvent&, bool);
+ DECL_LINK(EditingEntryHdl, const weld::TreeIter&, bool);
+ typedef std::pair<const weld::TreeIter&, OUString> IterString;
+ DECL_LINK(EditedEntryHdl, const IterString&, bool);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+
+ void ExecuteContextMenuAction(std::string_view rSelectedPopentry);
+};
+
+}
+
+//= SvtFileView_Impl
+class SvtFileView_Impl :public ::svt::IEnumerationResultHandler
+{
+protected:
+ SvtFileView* m_pAntiImpl;
+ Link<SvtFileView*,void> m_aSelectHandler;
+
+ ::rtl::Reference< ::svt::FileViewContentEnumerator >
+ m_xContentEnumerator;
+ Link<void*,void> m_aCurrentAsyncActionHandler;
+ ::osl::Condition m_aAsyncActionFinished;
+ ::rtl::Reference< ::salhelper::Timer > m_xCancelAsyncTimer;
+ ::svt::EnumerationResult m_eAsyncActionResult;
+ bool m_bRunningAsyncAction;
+ bool m_bAsyncActionCancelled;
+
+public:
+
+ ::std::vector<std::unique_ptr<SortingData_Impl>> maContent;
+ ::std::vector<std::unique_ptr<SvtContentEntry>> maEntries;
+ ::osl::Mutex maMutex;
+
+ weld::Window* m_pTopLevel;
+ std::unique_ptr<ViewTabListBox_Impl> mxView;
+ std::unique_ptr<weld::IconView> mxIconView;
+ sal_uInt16 mnSortColumn;
+ bool mbAscending : 1;
+ bool const mbOnlyFolder : 1;
+ sal_Int16 mnSuspendSelectCallback : 1;
+ bool mbIsFirstResort : 1;
+
+ IntlWrapper const aIntlWrapper;
+
+ OUString maViewURL;
+ OUString maCurrentFilter;
+ OUString maFolderImage;
+ Link<SvtFileView*,void> maOpenDoneLink;
+ Link<SvtFileView*,bool> maDoubleClickHandler;
+
+ Reference< XCommandEnvironment > mxCmdEnv;
+
+ SvtFileView_Impl(SvtFileView* pAntiImpl, weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ Reference < XCommandEnvironment > const & xEnv,
+ FileViewFlags nFlags,
+ bool bOnlyFolder);
+
+ virtual ~SvtFileView_Impl();
+
+ void Clear();
+
+ FileViewResult GetFolderContent_Impl(
+ std::u16string_view rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList );
+
+ FileViewResult GetFolderContent_Impl(
+ const FolderDescriptor& _rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList );
+ void FilterFolderContent_Impl( const OUString &rFilter );
+ void CancelRunningAsyncAction();
+
+ void OpenFolder_Impl();
+ static OUString ReplaceTabWithString(const OUString& rValue);
+ void CreateDisplayText_Impl();
+ void SortFolderContent_Impl();
+
+ void EntryRemoved( std::u16string_view rURL );
+ void EntryRenamed( OUString& rURL,
+ const OUString& rName );
+ const SortingData_Impl& FolderInserted( const OUString& rURL,
+ const OUString& rTitle );
+
+ int GetEntryPos( std::u16string_view rURL );
+
+ void SetViewMode( FileViewMode eMode );
+
+ inline void EnableDelete( bool bEnable );
+
+ void Resort_Impl( sal_Int16 nColumn, bool bAscending );
+ bool SearchNextEntry( sal_uInt32 &nIndex,
+ std::u16string_view rTitle,
+ bool bWrapAround );
+
+ void SetSelectHandler( const Link<SvtFileView*,void>& rHdl );
+ void SetDoubleClickHandler(const Link<SvtFileView*,bool>& rHdl);
+
+ void ResetCursor();
+
+ void EndEditing()
+ {
+ if (mxView->IsEditingActive())
+ mxView->end_editing();
+ }
+
+ void onTimeout();
+
+ void grab_focus()
+ {
+ if (mxView->get_visible())
+ mxView->grab_focus();
+ else
+ mxIconView->grab_focus();
+ }
+
+ bool has_focus() const
+ {
+ return mxView->has_focus() || mxIconView->has_focus();
+ }
+
+ int GetSortColumn() const
+ {
+ sal_uInt16 nOldSortID = mnSortColumn;
+ // skip "TYPE"
+ if (!mxView->TypeColumnVisible() && nOldSortID != COLUMN_TITLE)
+ --nOldSortID;
+ return nOldSortID - 1;
+ }
+
+protected:
+ DECL_LINK(ChangedHdl, weld::TreeView&, void);
+ DECL_LINK(SelectionChangedHdl, weld::IconView&, void);
+ DECL_LINK(RowActivatedHdl, weld::TreeView&, bool);
+ DECL_LINK(ItemActivatedHdl, weld::IconView&, bool);
+
+ // IEnumerationResultHandler overridables
+ virtual void enumerationDone( ::svt::EnumerationResult eResult ) override;
+ void implEnumerationSuccess();
+};
+
+inline void SvtFileView_Impl::EnableDelete( bool bEnable )
+{
+ mxView->EnableDelete( bEnable );
+}
+
+namespace
+{
+ // functions -------------------------------------------------------------
+
+ OUString CreateExactSizeText( sal_Int64 nSize )
+ {
+ double fSize( static_cast<double>(nSize) );
+ int nDec;
+
+ tools::Long nMega = 1024 * 1024;
+ tools::Long nGiga = nMega * 1024;
+
+ OUString aUnitStr(' ');
+
+ if ( nSize < 10000 )
+ {
+ aUnitStr += SvtResId(STR_SVT_BYTES );
+ nDec = 0;
+ }
+ else if ( nSize < nMega )
+ {
+ fSize /= 1024;
+ aUnitStr += SvtResId(STR_SVT_KB);
+ nDec = 1;
+ }
+ else if ( nSize < nGiga )
+ {
+ fSize /= nMega;
+ aUnitStr += SvtResId(STR_SVT_MB);
+ nDec = 2;
+ }
+ else
+ {
+ fSize /= nGiga;
+ aUnitStr += SvtResId(STR_SVT_GB);
+ nDec = 3;
+ }
+
+ OUString aSizeStr( ::rtl::math::doubleToUString( fSize,
+ rtl_math_StringFormat_F, nDec,
+ SvtSysLocale().GetLocaleData().getNumDecimalSep()[0]) );
+ aSizeStr += aUnitStr;
+
+ return aSizeStr;
+ }
+}
+
+ViewTabListBox_Impl::ViewTabListBox_Impl(std::unique_ptr<weld::TreeView> xTreeView,
+ weld::Window* pTopLevel,
+ SvtFileView_Impl* pParent,
+ FileViewFlags nFlags)
+ : mxTreeView(std::move(xTreeView))
+ , mxScratchIter(mxTreeView->make_iterator())
+ , mpParent( pParent )
+ , mnSearchIndex( 0 )
+ , mbEnableDelete( false )
+ , mbEditing( false )
+ , mbShowType(nFlags & FileViewFlags::SHOW_TYPE)
+{
+ std::vector<int> aWidths { 180 };
+ if (nFlags & FileViewFlags::SHOW_TYPE)
+ aWidths.push_back(140);
+ aWidths.push_back(80);
+ mxTreeView->set_column_fixed_widths(aWidths);
+
+ if (nFlags & FileViewFlags::MULTISELECTION)
+ mxTreeView->set_selection_mode(SelectionMode::Multiple);
+
+ maResetQuickSearch.SetTimeout( QUICK_SEARCH_TIMEOUT );
+ maResetQuickSearch.SetInvokeHandler( LINK( this, ViewTabListBox_Impl, ResetQuickSearch_Impl ) );
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xInteractionHandler(
+ InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
+
+ mxCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
+
+ mxTreeView->connect_popup_menu(LINK(this, ViewTabListBox_Impl, CommandHdl));
+ mxTreeView->connect_key_press(LINK(this, ViewTabListBox_Impl, KeyInputHdl));
+}
+
+IMPL_LINK_NOARG(ViewTabListBox_Impl, EditingEntryHdl, const weld::TreeIter&, bool)
+{
+ return mbEditing;
+}
+
+IMPL_LINK_NOARG(ViewTabListBox_Impl, ResetQuickSearch_Impl, Timer *, void)
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maQuickSearchText.clear();
+ mnSearchIndex = 0;
+}
+
+IMPL_LINK(ViewTabListBox_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ if (mbEditing)
+ return false;
+
+ bool bHandled = false;
+
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ if ( 0 == rKeyCode.GetModifier() )
+ {
+ if ( ( rKeyCode.GetCode() == KEY_DELETE ) &&
+ mbEnableDelete )
+ {
+ ResetQuickSearch_Impl( nullptr );
+ DeleteEntries();
+ bHandled = true;
+ }
+ else if ( ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM ) ||
+ ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA ) )
+ {
+ DoQuickSearch( rKEvt.GetCharCode() );
+ bHandled = true;
+ }
+ }
+
+ if (!bHandled)
+ ResetQuickSearch_Impl( nullptr );
+ return bHandled;
+}
+
+IMPL_LINK(ViewTabListBox_Impl, CommandHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ bool bEnableDelete = mbEnableDelete;
+ bool bEnableRename = true;
+
+ int nCount = 0;
+ mxTreeView->selected_foreach([this, &nCount, &bEnableDelete, &bEnableRename](weld::TreeIter& rEntry){
+ ++nCount;
+
+ ::ucbhelper::Content aCnt;
+ try
+ {
+ OUString aURL(weld::fromId<SvtContentEntry*>(
+ mxTreeView->get_id(rEntry))->maURL);
+ aCnt = ::ucbhelper::Content( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
+ }
+ catch( Exception const & )
+ {
+ bEnableDelete = bEnableRename = false;
+ }
+
+ if ( bEnableDelete )
+ {
+ try
+ {
+ Reference< XCommandInfo > aCommands = aCnt.getCommands();
+ if ( aCommands.is() )
+ bEnableDelete = aCommands->hasCommandByName( "delete" );
+ else
+ bEnableDelete = false;
+ }
+ catch( Exception const & )
+ {
+ bEnableDelete = false;
+ }
+ }
+
+ if ( bEnableRename )
+ {
+ try
+ {
+ Reference< XPropertySetInfo > aProps = aCnt.getProperties();
+ if ( aProps.is() )
+ {
+ Property aProp = aProps->getPropertyByName("Title");
+ bEnableRename
+ = !( aProp.Attributes & PropertyAttribute::READONLY );
+ }
+ else
+ bEnableRename = false;
+ }
+ catch( Exception const & )
+ {
+ bEnableRename = false;
+ }
+ }
+
+ bool bStop = !bEnableDelete && !bEnableRename;
+ return bStop;
+ });
+
+ if (nCount == 0)
+ bEnableDelete = false;
+ if (nCount != 1)
+ bEnableRename = false;
+
+ if (bEnableDelete || bEnableRename)
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(mxTreeView.get(), "svt/ui/fileviewmenu.ui"));
+ auto xContextMenu = xBuilder->weld_menu("menu");
+ xContextMenu->set_visible("delete", bEnableDelete);
+ xContextMenu->set_visible("rename", bEnableRename);
+ OString sCommand(xContextMenu->popup_at_rect(mxTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
+ ExecuteContextMenuAction(sCommand);
+ }
+
+ return true;
+}
+
+void ViewTabListBox_Impl::ExecuteContextMenuAction(std::string_view rSelectedPopupEntry)
+{
+ if (rSelectedPopupEntry == "delete")
+ DeleteEntries();
+ else if (rSelectedPopupEntry == "rename")
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator();
+ if (mxTreeView->get_selected(xEntry.get()))
+ {
+ mbEditing = true;
+
+ mxTreeView->connect_editing(LINK(this, ViewTabListBox_Impl, EditingEntryHdl),
+ LINK(this, ViewTabListBox_Impl, EditedEntryHdl));
+
+ mxTreeView->start_editing(*xEntry);
+ }
+ }
+}
+
+void ViewTabListBox_Impl::DeleteEntries()
+{
+ short eResult = svtools::QUERYDELETE_YES;
+
+ mxTreeView->selected_foreach([this, &eResult](weld::TreeIter& rCurEntry){
+ OUString aURL;
+ if (!mxTreeView->get_id(rCurEntry).isEmpty())
+ aURL = weld::fromId<SvtContentEntry*>(mxTreeView->get_id(rCurEntry))->maURL;
+ if (aURL.isEmpty())
+ {
+ mxTreeView->unselect(rCurEntry);
+ return false;
+ }
+
+ bool canDelete = true;
+ try
+ {
+ ::ucbhelper::Content aCnt( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
+ Reference< XCommandInfo > aCommands = aCnt.getCommands();
+ if ( aCommands.is() )
+ canDelete = aCommands->hasCommandByName( "delete" );
+ else
+ canDelete = false;
+ }
+ catch( Exception const & )
+ {
+ canDelete = false;
+ }
+
+ if (!canDelete)
+ {
+ mxTreeView->unselect(rCurEntry);
+ return false; // process next entry
+ }
+
+ if ( eResult != svtools::QUERYDELETE_ALL )
+ {
+ INetURLObject aObj( aURL );
+ svtools::QueryDeleteDlg_Impl aDlg(
+ mxTreeView.get(), aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset));
+
+ if (mxTreeView->count_selected_rows() > 1)
+ aDlg.EnableAllButton();
+
+ eResult = aDlg.run();
+ }
+
+ bool bDeleted = false;
+
+ if (eResult == svtools::QUERYDELETE_ALL || eResult == svtools::QUERYDELETE_YES)
+ {
+ if ( Kill( aURL ) )
+ {
+ mpParent->EntryRemoved( aURL );
+ bDeleted = true;
+ }
+ }
+
+ if (!bDeleted)
+ mxTreeView->unselect(rCurEntry);
+
+ return false;
+ });
+
+ mxTreeView->remove_selection();
+}
+
+IMPL_LINK(ViewTabListBox_Impl, EditedEntryHdl, const IterString&, rIterString, bool)
+{
+ mbEditing = false;
+
+ mxTreeView->connect_editing(Link<const weld::TreeIter&, bool>(), Link<const IterString&, bool>());
+
+ const weld::TreeIter& rEntry = rIterString.first;
+ OUString sNewText = rIterString.second;
+
+ if (sNewText.isEmpty())
+ return false;
+
+ bool bRet = false;
+
+ OUString aURL;
+ SvtContentEntry* pData = weld::fromId<SvtContentEntry*>(mxTreeView->get_id(rEntry));
+
+ if ( pData )
+ aURL = pData->maURL;
+
+ if ( aURL.isEmpty() )
+ return bRet;
+
+ try
+ {
+ OUString aPropName( "Title" );
+ bool canRename = true;
+ ::ucbhelper::Content aContent( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
+
+ try
+ {
+ Reference< XPropertySetInfo > aProps = aContent.getProperties();
+ if ( aProps.is() )
+ {
+ Property aProp = aProps->getPropertyByName( aPropName );
+ canRename = !( aProp.Attributes & PropertyAttribute::READONLY );
+ }
+ else
+ {
+ canRename = false;
+ }
+ }
+ catch ( Exception const & )
+ {
+ canRename = false;
+ }
+
+ if ( canRename )
+ {
+ Any aValue;
+ aValue <<= sNewText;
+ aContent.setPropertyValue( aPropName, aValue );
+ mpParent->EntryRenamed(aURL, sNewText);
+
+ if (pData)
+ pData->maURL = aURL;
+
+ mxTreeView->set_id(rEntry, weld::toId(pData));
+
+ bRet = true;
+ }
+ }
+ catch( Exception const & )
+ {
+ }
+
+ return bRet;
+}
+
+void ViewTabListBox_Impl::DoQuickSearch( sal_Unicode rChar )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maResetQuickSearch.Stop();
+
+ OUString aLastText = maQuickSearchText;
+ sal_uInt32 aLastPos = mnSearchIndex;
+
+ maQuickSearchText += OUString(rChar).toAsciiLowerCase();
+
+ bool bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, false );
+
+ if ( !bFound && ( aLastText.getLength() == 1 ) &&
+ ( aLastText == OUStringChar(rChar) ) )
+ {
+ mnSearchIndex = aLastPos + 1;
+ maQuickSearchText = aLastText;
+ bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, true );
+ }
+
+ if (bFound)
+ {
+ mxTreeView->unselect_all();
+ mxTreeView->select(mnSearchIndex);
+ mxTreeView->set_cursor(mnSearchIndex);
+ mxTreeView->scroll_to_row(mnSearchIndex);
+ }
+
+ maResetQuickSearch.Start();
+}
+
+bool ViewTabListBox_Impl::Kill( const OUString& rContent )
+{
+ bool bRet = true;
+
+ try
+ {
+ ::ucbhelper::Content aCnt( rContent, mxCmdEnv, comphelper::getProcessComponentContext() );
+ aCnt.executeCommand( "delete", Any( true ) );
+ }
+ catch( css::ucb::CommandAbortedException const & )
+ {
+ SAL_INFO( "svtools.contnr", "CommandAbortedException" );
+ bRet = false;
+ }
+ catch( Exception const & )
+ {
+ SAL_INFO( "svtools.contnr", "Any other exception" );
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+SvtFileView::SvtFileView(weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ bool bOnlyFolder, bool bMultiSelection, bool bShowType )
+{
+ FileViewFlags nFlags = FileViewFlags::NONE;
+ if ( bMultiSelection )
+ nFlags |= FileViewFlags::MULTISELECTION;
+ if ( bShowType )
+ nFlags |= FileViewFlags::SHOW_TYPE;
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xInteractionHandler(
+ InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
+ Reference < XCommandEnvironment > xCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
+
+ mpImpl.reset(new SvtFileView_Impl(this, pTopLevel, std::move(xTreeView), std::move(xIconView), xCmdEnv, nFlags, bOnlyFolder));
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ pView->connect_column_clicked(LINK(this, SvtFileView, HeaderSelect_Impl));
+}
+
+void SvtFileView::grab_focus()
+{
+ mpImpl->grab_focus();
+}
+
+bool SvtFileView::has_focus() const
+{
+ return mpImpl->has_focus();
+}
+
+SvtFileView::~SvtFileView()
+{
+}
+
+void SvtFileView::SetViewMode( FileViewMode eMode )
+{
+ mpImpl->SetViewMode( eMode );
+}
+
+OUString SvtFileView::GetURL(const weld::TreeIter& rEntry) const
+{
+ SvtContentEntry* pEntry;
+ if (mpImpl->mxView->get_visible())
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(rEntry));
+ else
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(rEntry));
+ if (pEntry)
+ return pEntry->maURL;
+ return OUString();
+}
+
+OUString SvtFileView::GetCurrentURL() const
+{
+ SvtContentEntry* pEntry = nullptr;
+ OUString aURL;
+ if (mpImpl->mxView->get_visible())
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
+ if (mpImpl->mxView->get_selected(xEntry.get()))
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(*xEntry));
+ }
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxIconView->make_iterator();
+ if (mpImpl->mxIconView->get_selected(xEntry.get()))
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(*xEntry));
+ }
+ if (pEntry)
+ aURL = pEntry->maURL;
+ return aURL;
+}
+
+void SvtFileView::CreatedFolder( const OUString& rUrl, const OUString& rNewFolder )
+{
+ const SortingData_Impl& rEntry = mpImpl->FolderInserted( rUrl, rNewFolder );
+
+ mpImpl->maEntries.emplace_back(std::make_unique<SvtContentEntry>(rUrl, true));
+ OUString sId(weld::toId(mpImpl->maEntries.back().get()));
+
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
+ mpImpl->mxView->insert(rEntry.maDisplayName, sId, mpImpl->maFolderImage, *xEntry);
+ mpImpl->mxView->scroll_to_row(*xEntry);
+
+ std::unique_ptr<weld::TreeIter> xIconEntry = mpImpl->mxIconView->make_iterator();
+ mpImpl->mxIconView->insert(-1, &rEntry.maDisplayName, &sId, &mpImpl->maFolderImage, xIconEntry.get());
+ mpImpl->mxIconView->scroll_to_item(*xIconEntry);
+}
+
+FileViewResult SvtFileView::PreviousLevel( const FileViewAsyncAction* pAsyncDescriptor )
+{
+ FileViewResult eResult = eFailure;
+
+ OUString sParentURL;
+ if ( GetParentURL( sParentURL ) )
+ eResult = Initialize( sParentURL, mpImpl->maCurrentFilter, pAsyncDescriptor, maDenyList );
+
+ return eResult;
+}
+
+bool SvtFileView::GetParentURL( OUString& rParentURL ) const
+{
+ bool bRet = false;
+ try
+ {
+ ::ucbhelper::Content aCnt( mpImpl->maViewURL, mpImpl->mxCmdEnv, comphelper::getProcessComponentContext() );
+ Reference< XContent > xContent( aCnt.get() );
+ Reference< css::container::XChild > xChild( xContent, UNO_QUERY );
+ if ( xChild.is() )
+ {
+ Reference< XContent > xParent( xChild->getParent(), UNO_QUERY );
+ if ( xParent.is() )
+ {
+ rParentURL = xParent->getIdentifier()->getContentIdentifier();
+ bRet = !rParentURL.isEmpty() && rParentURL != mpImpl->maViewURL;
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ // perhaps an unknown url protocol (e.g. "private:newdoc")
+ }
+
+ return bRet;
+}
+
+OString SvtFileView::get_help_id() const
+{
+ return mpImpl->mxView->get_help_id();
+}
+
+void SvtFileView::set_help_id(const OString& rHelpId)
+{
+ mpImpl->mxView->set_help_id(rHelpId);
+}
+
+OUString SvtFileView::get_selected_text() const
+{
+ if (mpImpl->mxView->get_visible())
+ return mpImpl->mxView->get_selected_text();
+ return mpImpl->mxIconView->get_selected_text();
+}
+
+FileViewResult SvtFileView::Initialize(
+ const OUString& rURL,
+ const OUString& rFilter,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList )
+{
+ weld::WaitObject aWaitCursor(mpImpl->m_pTopLevel);
+ maDenyList = rDenyList;
+
+ OUString sPushURL( mpImpl->maViewURL );
+
+ mpImpl->maViewURL = rURL;
+ FileViewResult eResult = ExecuteFilter( rFilter, pAsyncDescriptor );
+ switch ( eResult )
+ {
+ case eFailure:
+ case eTimeout:
+ mpImpl->maViewURL = sPushURL;
+ return eResult;
+
+ case eStillRunning:
+ OSL_ENSURE( pAsyncDescriptor, "SvtFileView::Initialize: we told it to read synchronously!" );
+ [[fallthrough]];
+ case eSuccess:
+ return eResult;
+ }
+
+ OSL_FAIL( "SvtFileView::Initialize: unreachable!" );
+ return eFailure;
+}
+
+FileViewResult SvtFileView::ExecuteFilter( const OUString& rFilter, const FileViewAsyncAction* pAsyncDescriptor )
+{
+ mpImpl->maCurrentFilter = rFilter.toAsciiLowerCase();
+
+ mpImpl->Clear();
+ FileViewResult eResult = mpImpl->GetFolderContent_Impl(mpImpl->maViewURL, pAsyncDescriptor, maDenyList);
+ OSL_ENSURE( ( eResult != eStillRunning ) || pAsyncDescriptor, "SvtFileView::ExecuteFilter: we told it to read synchronously!" );
+ return eResult;
+}
+
+void SvtFileView::CancelRunningAsyncAction()
+{
+ mpImpl->CancelRunningAsyncAction();
+}
+
+void SvtFileView::SetNoSelection()
+{
+ mpImpl->mxView->unselect_all();
+ mpImpl->mxIconView->unselect_all();
+}
+
+void SvtFileView::SetSelectHdl(const Link<SvtFileView*,void>& rHdl)
+{
+ mpImpl->SetSelectHandler(rHdl);
+}
+
+void SvtFileView::SetDoubleClickHdl(const Link<SvtFileView*,bool>& rHdl)
+{
+ mpImpl->SetDoubleClickHandler(rHdl);
+}
+
+sal_uInt32 SvtFileView::GetSelectionCount() const
+{
+ if (mpImpl->mxView->get_visible())
+ return mpImpl->mxView->count_selected_rows();
+ return mpImpl->mxIconView->count_selected_items();
+}
+
+SvtContentEntry* SvtFileView::FirstSelected() const
+{
+ if (mpImpl->mxView->get_visible())
+ {
+ SvtContentEntry* pRet = nullptr;
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
+ if (mpImpl->mxView->get_selected(xEntry.get()))
+ pRet = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(*xEntry));
+ return pRet;
+ }
+
+ SvtContentEntry* pRet = nullptr;
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxIconView->make_iterator();
+ if (mpImpl->mxIconView->get_selected(xEntry.get()))
+ pRet = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(*xEntry));
+ return pRet;
+}
+
+const OUString& SvtFileView::GetViewURL() const
+{
+ return mpImpl->maViewURL;
+}
+
+void SvtFileView::SetOpenDoneHdl( const Link<SvtFileView*,void>& rHdl )
+{
+ mpImpl->maOpenDoneLink = rHdl;
+}
+
+void SvtFileView::EnableDelete( bool bEnable )
+{
+ mpImpl->EnableDelete( bEnable );
+}
+
+void SvtFileView::EndInplaceEditing()
+{
+ return mpImpl->EndEditing();
+}
+
+IMPL_LINK(SvtFileView, HeaderSelect_Impl, int, nColumn, void)
+{
+ sal_uInt16 nItemID = nColumn + 1;
+ // skip "TYPE"
+ if (!mpImpl->mxView->TypeColumnVisible() && nItemID != COLUMN_TITLE)
+ ++nItemID;
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ bool bSortAtoZ = mpImpl->mbAscending;
+
+ //set new arrow positions in headerbar
+ if (nItemID != mpImpl->mnSortColumn)
+ {
+ // remove old indicator, new will be created in OpenFolder_Impl
+ pView->set_sort_indicator(TRISTATE_INDET, mpImpl->GetSortColumn());
+ }
+ else
+ bSortAtoZ = !bSortAtoZ;
+
+ mpImpl->Resort_Impl(nItemID, bSortAtoZ);
+}
+
+OUString SvtFileView::GetConfigString() const
+{
+ // sort order
+ OUString sRet = OUString::number( mpImpl->mnSortColumn ) + ";";
+
+ bool bUp = mpImpl->mbAscending;
+ sRet += OUString::Concat(bUp ? std::u16string_view(u"1") : std::u16string_view(u"0")) + ";";
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ sal_uInt16 nCount = mpImpl->mxView->TypeColumnVisible() ? 4 : 3;
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ sal_uInt16 nId = i + 1;
+ // skip "TYPE"
+ if (!mpImpl->mxView->TypeColumnVisible() && nId != COLUMN_TITLE)
+ ++nId;
+
+ sRet += OUString::number( nId )
+ + ";"
+ + OUString::number(pView->get_column_width(i))
+ + ";";
+ }
+
+ return comphelper::string::stripEnd(sRet, ';');
+}
+
+::std::vector< SvtContentEntry > SvtFileView::GetContent()
+{
+ ::std::vector< SvtContentEntry > aContent;
+
+ for(auto const& elem : mpImpl->maContent)
+ {
+ SvtContentEntry aEntry( elem->maTargetURL, elem->mbIsFolder );
+ aContent.push_back( aEntry );
+ }
+
+ return aContent;
+}
+
+void SvtFileView::SetConfigString(std::u16string_view rCfgStr)
+{
+ sal_Int32 nIdx = 0;
+ sal_uInt16 nSortColumn = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx )));
+ bool bAscending = static_cast<bool>(static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ))));
+
+ std::vector<int> aWidths(mpImpl->mxView->TypeColumnVisible() ? 4 : 3, -1);
+
+ while ( nIdx != -1 )
+ {
+ sal_uInt16 nItemId = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx )));
+
+ int nWidth = o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ));
+
+ // skip "TYPE"
+ if (!mpImpl->mxView->TypeColumnVisible() && nItemId != COLUMN_TITLE)
+ --nItemId;
+ int nColumn = nItemId - 1;
+
+ if (nColumn >= 0 && o3tl::make_unsigned(nColumn) < aWidths.size())
+ aWidths[nColumn] = nWidth;
+ }
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ pView->set_column_fixed_widths(aWidths);
+ if (mpImpl->mnSortColumn != nSortColumn)
+ pView->set_sort_indicator(TRISTATE_INDET, mpImpl->GetSortColumn());
+ mpImpl->Resort_Impl(nSortColumn, bAscending);
+}
+
+SvtFileView_Impl::SvtFileView_Impl(SvtFileView* pAntiImpl, weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ Reference < XCommandEnvironment > const & xEnv,
+ FileViewFlags nFlags, bool bOnlyFolder)
+ : m_pAntiImpl ( pAntiImpl )
+ , m_eAsyncActionResult ( ::svt::EnumerationResult::ERROR )
+ , m_bRunningAsyncAction ( false )
+ , m_bAsyncActionCancelled ( false )
+ , m_pTopLevel ( pTopLevel )
+ , mxView(new ViewTabListBox_Impl(std::move(xTreeView), pTopLevel, this, nFlags))
+ , mxIconView(std::move(xIconView))
+ , mnSortColumn ( COLUMN_TITLE )
+ , mbAscending ( true )
+ , mbOnlyFolder ( bOnlyFolder )
+ , mnSuspendSelectCallback ( 0 )
+ , mbIsFirstResort ( true )
+ , aIntlWrapper ( Application::GetSettings().GetLanguageTag() )
+ , maFolderImage (RID_BMP_FOLDER)
+ , mxCmdEnv ( xEnv )
+{
+ weld::TreeView* pWidget = mxView->getWidget();
+
+ // set the width to something small so it's the parent that decides the final
+ // width
+ Size aSize(42, pWidget->get_height_rows(7));
+ pWidget->set_size_request(aSize.Width(), aSize.Height());
+ mxIconView->set_size_request(aSize.Width(), aSize.Height());
+}
+
+SvtFileView_Impl::~SvtFileView_Impl()
+{
+ Clear();
+}
+
+void SvtFileView_Impl::Clear()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maContent.clear();
+}
+
+FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
+ std::u16string_view rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList )
+{
+ ::osl::ClearableMutexGuard aGuard( maMutex );
+ INetURLObject aFolderObj( rFolder );
+ DBG_ASSERT( aFolderObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
+
+ FolderDescriptor aFolder( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ aGuard.clear();
+ return GetFolderContent_Impl( aFolder, pAsyncDescriptor, rDenyList );
+}
+
+FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
+ const FolderDescriptor& _rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList )
+{
+ DBG_TESTSOLARMUTEX();
+ ::osl::ClearableMutexGuard aGuard( maMutex );
+
+ OSL_ENSURE( !m_xContentEnumerator.is(), "SvtFileView_Impl::GetFolderContent_Impl: still running another enumeration!" );
+ m_xContentEnumerator.set(new ::svt::FileViewContentEnumerator(
+ mxView->GetCommandEnvironment(), maContent, maMutex));
+ // TODO: should we cache and re-use this thread?
+
+ if ( !pAsyncDescriptor )
+ {
+ ::svt::EnumerationResult eResult = m_xContentEnumerator->enumerateFolderContentSync( _rFolder, rDenyList );
+ if ( ::svt::EnumerationResult::SUCCESS == eResult )
+ {
+ implEnumerationSuccess();
+ m_xContentEnumerator.clear();
+ return eSuccess;
+ }
+ m_xContentEnumerator.clear();
+ return eFailure;
+ }
+
+ m_bRunningAsyncAction = true;
+ m_bAsyncActionCancelled = false;
+ m_eAsyncActionResult = ::svt::EnumerationResult::ERROR;
+ m_aAsyncActionFinished.reset();
+
+ // don't (yet) set m_aCurrentAsyncActionHandler to pTimeout->aFinishHandler.
+ // By definition, this handler *only* gets called when the result cannot be obtained
+ // during the minimum wait time, so it is only set below, when needed.
+ m_aCurrentAsyncActionHandler = Link<void*,void>();
+
+ // minimum time to wait
+ TimeValue aTimeout;
+ sal_Int32 nMinTimeout = pAsyncDescriptor->nMinTimeout;
+ OSL_ENSURE( nMinTimeout > 0, "SvtFileView_Impl::GetFolderContent_Impl: invalid minimum timeout!" );
+ if ( nMinTimeout <= 0 )
+ nMinTimeout = sal_Int32( 1000 );
+ aTimeout.Seconds = nMinTimeout / 1000;
+ aTimeout.Nanosec = ( nMinTimeout % 1000 ) * 1000000;
+
+ m_xContentEnumerator->enumerateFolderContent( _rFolder, this );
+
+ // wait until the enumeration is finished
+ // for this, release our own mutex (which is used by the enumerator thread)
+ aGuard.clear();
+
+ ::osl::Condition::Result eResult = ::osl::Condition::result_ok;
+ {
+ // also release the SolarMutex. Not all code which is needed during the enumeration
+ // is Solar-Thread-Safe, in particular there is some code which needs to access
+ // string resources (and our resource system relies on the SolarMutex :()
+ SolarMutexReleaser aSolarRelease;
+
+ // now wait. Note that if we didn't get a pAsyncDescriptor, then this is an infinite wait.
+ eResult = m_aAsyncActionFinished.wait( &aTimeout );
+ }
+
+ ::osl::MutexGuard aGuard2( maMutex );
+ if ( ::osl::Condition::result_timeout == eResult )
+ {
+ // maximum time to wait
+ OSL_ENSURE(!m_xCancelAsyncTimer,
+ "SvtFileView_Impl::GetFolderContent_Impl: there's still a previous timer!");
+ m_xCancelAsyncTimer.set(new CallbackTimer(this));
+ sal_Int32 nMaxTimeout = pAsyncDescriptor->nMaxTimeout;
+ OSL_ENSURE( nMaxTimeout > nMinTimeout,
+ "SvtFileView_Impl::GetFolderContent_Impl: invalid maximum timeout!" );
+ if ( nMaxTimeout <= nMinTimeout )
+ nMaxTimeout = nMinTimeout + 5000;
+ m_xCancelAsyncTimer->setRemainingTime( salhelper::TTimeValue( nMaxTimeout - nMinTimeout ) );
+ // we already waited for nMinTimeout milliseconds, so take this into account
+ m_xCancelAsyncTimer->start();
+
+ m_aCurrentAsyncActionHandler = pAsyncDescriptor->aFinishHandler;
+ DBG_ASSERT( m_aCurrentAsyncActionHandler.IsSet(), "SvtFileView_Impl::GetFolderContent_Impl: nobody interested when it's finished?" );
+ maEntries.clear();
+ mxView->clear();
+ mxIconView->clear();
+ return eStillRunning;
+ }
+
+ m_bRunningAsyncAction = false;
+ switch ( m_eAsyncActionResult )
+ {
+ case ::svt::EnumerationResult::SUCCESS:
+ return eSuccess;
+
+ case ::svt::EnumerationResult::ERROR:
+ return eFailure;
+ }
+
+ SAL_WARN( "svtools.contnr", "SvtFileView_Impl::GetFolderContent_Impl: unreachable!" );
+ return eFailure;
+}
+
+void SvtFileView_Impl::FilterFolderContent_Impl( const OUString &rFilter )
+{
+ if ( rFilter.isEmpty() || ( rFilter == ALL_FILES_FILTER ) )
+ // when replacing names, there is always something to filter (no view of ".nametranslation.table")
+ return;
+
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if ( maContent.empty() )
+ return;
+
+ // collect the filter tokens
+ ::std::vector< WildCard > aFilters;
+ FilterMatch::createWildCardFilterList(rFilter,aFilters);
+
+
+ // do the filtering
+ maContent.erase(std::remove_if(maContent.begin(), maContent.end(),
+ [&aFilters](const std::unique_ptr<SortingData_Impl>& rxContent) {
+ if (rxContent->mbIsFolder)
+ return false;
+ // normalize the content title (we always match case-insensitive)
+ // 91872 - 11.09.2001 - frank.schoenheit@sun.com
+ OUString sCompareString = rxContent->GetFileName(); // filter works on file name, not on title!
+ return std::none_of(aFilters.begin(), aFilters.end(), FilterMatch(sCompareString));
+ }),
+ maContent.end());
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, ChangedHdl, weld::TreeView&, void)
+{
+ if (!mnSuspendSelectCallback)
+ m_aSelectHandler.Call(m_pAntiImpl);
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, SelectionChangedHdl, weld::IconView&, void)
+{
+ if (!mnSuspendSelectCallback)
+ m_aSelectHandler.Call(m_pAntiImpl);
+}
+
+void SvtFileView_Impl::SetSelectHandler(const Link<SvtFileView*,void>& rHdl)
+{
+ m_aSelectHandler = rHdl;
+
+ mxView->connect_changed(LINK(this, SvtFileView_Impl, ChangedHdl));
+ mxIconView->connect_selection_changed(LINK(this, SvtFileView_Impl, SelectionChangedHdl));
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, RowActivatedHdl, weld::TreeView&, bool)
+{
+ return maDoubleClickHandler.Call(m_pAntiImpl);
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, ItemActivatedHdl, weld::IconView&, bool)
+{
+ return maDoubleClickHandler.Call(m_pAntiImpl);
+}
+
+void SvtFileView_Impl::SetDoubleClickHandler(const Link<SvtFileView*,bool>& rHdl)
+{
+ maDoubleClickHandler = rHdl;
+
+ mxView->connect_row_activated(LINK(this, SvtFileView_Impl, RowActivatedHdl));
+ mxIconView->connect_item_activated(LINK(this, SvtFileView_Impl, ItemActivatedHdl));
+}
+
+void SvtFileView_Impl::OpenFolder_Impl()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ mxView->freeze();
+ mxIconView->freeze();
+ maEntries.clear();
+ mxView->clear();
+ mxIconView->clear();
+
+ for (auto const& elem : maContent)
+ {
+ if (mbOnlyFolder && !elem->mbIsFolder)
+ continue;
+
+ // insert entry and set user data
+ maEntries.emplace_back(std::make_unique<SvtContentEntry>(elem->maTargetURL, elem->mbIsFolder));
+ OUString sId(weld::toId(maEntries.back().get()));
+ mxView->append(sId, elem->maDisplayName, elem->maType, elem->maDisplaySize, elem->maDisplayDate, elem->maImage);
+ mxIconView->append(sId, elem->maDisplayName, elem->maImage);
+ }
+
+ ++mnSuspendSelectCallback;
+ mxView->thaw();
+
+ //set sort indicator
+ weld::TreeView* pView = mxView->getWidget();
+ pView->set_sort_indicator(mbAscending ? TRISTATE_TRUE : TRISTATE_FALSE, GetSortColumn());
+
+ mxIconView->thaw();
+ --mnSuspendSelectCallback;
+
+ ResetCursor();
+}
+
+void SvtFileView_Impl::ResetCursor()
+{
+ if (mxView->get_visible())
+ {
+ std::unique_ptr<weld::TreeIter> xFirst = mxView->make_iterator();
+ if (mxView->get_iter_first(*xFirst))
+ {
+ // set cursor to the first entry
+ mxView->set_cursor(*xFirst);
+ }
+ // deselect
+ mxView->unselect_all();
+ }
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xFirst = mxIconView->make_iterator();
+ if (mxIconView->get_iter_first(*xFirst))
+ {
+ // set cursor to the first entry
+ mxIconView->set_cursor(*xFirst);
+ }
+ // deselect
+ mxIconView->unselect_all();
+ }
+}
+
+void SvtFileView_Impl::CancelRunningAsyncAction()
+{
+ DBG_TESTSOLARMUTEX();
+ ::osl::MutexGuard aGuard( maMutex );
+ if ( !m_xContentEnumerator.is() )
+ return;
+
+ m_bAsyncActionCancelled = true;
+ m_xContentEnumerator->cancel();
+ m_bRunningAsyncAction = false;
+
+ m_xContentEnumerator.clear();
+ if ( m_xCancelAsyncTimer.is() && m_xCancelAsyncTimer->isTicking() )
+ m_xCancelAsyncTimer->stop();
+ m_xCancelAsyncTimer.clear();
+}
+
+
+void SvtFileView_Impl::onTimeout()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( maMutex );
+ if ( !m_bRunningAsyncAction )
+ // there might have been a race condition while we waited for the mutex
+ return;
+
+ CancelRunningAsyncAction();
+
+ if ( m_aCurrentAsyncActionHandler.IsSet() )
+ {
+ Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( eTimeout ) );
+ m_aCurrentAsyncActionHandler = Link<void*,void>();
+ }
+}
+
+
+void SvtFileView_Impl::enumerationDone( ::svt::EnumerationResult eResult )
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( maMutex );
+
+ m_xContentEnumerator.clear();
+ if ( m_xCancelAsyncTimer.is() && m_xCancelAsyncTimer->isTicking() )
+ m_xCancelAsyncTimer->stop();
+ m_xCancelAsyncTimer.clear();
+
+ if ( m_bAsyncActionCancelled )
+ // this is to prevent race conditions
+ return;
+
+ m_eAsyncActionResult = eResult;
+ m_bRunningAsyncAction = false;
+
+ m_aAsyncActionFinished.set();
+
+ if ( svt::EnumerationResult::SUCCESS == eResult )
+ implEnumerationSuccess();
+
+ if ( m_aCurrentAsyncActionHandler.IsSet() )
+ {
+ Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( m_eAsyncActionResult ) );
+ m_aCurrentAsyncActionHandler = Link<void*,void>();
+ }
+}
+
+
+void SvtFileView_Impl::implEnumerationSuccess()
+{
+ FilterFolderContent_Impl( maCurrentFilter );
+ SortFolderContent_Impl();
+ CreateDisplayText_Impl();
+ OpenFolder_Impl();
+ maOpenDoneLink.Call( m_pAntiImpl );
+}
+
+OUString SvtFileView_Impl::ReplaceTabWithString(const OUString& rValue)
+{
+ OUString const aTab( "\t" );
+ OUString const aTabString( "%09" );
+
+ sal_Int32 iPos;
+ OUString aValue(rValue);
+ while ( ( iPos = aValue.indexOf( aTab ) ) >= 0 )
+ aValue = aValue.replaceAt( iPos, 1, aTabString );
+ return aValue;
+}
+
+void SvtFileView_Impl::CreateDisplayText_Impl()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ OUString const aDateSep( ", " );
+
+ for (auto const& elem : maContent)
+ {
+ // title, type, size, date
+ elem->maDisplayName = ReplaceTabWithString(elem->GetTitle());
+ // folders don't have a size
+ if ( ! elem->mbIsFolder )
+ elem->maDisplaySize = CreateExactSizeText( elem->maSize );
+ // set the date, but volumes have no date
+ if ( ! elem->mbIsFolder || ! elem->mbIsVolume )
+ {
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
+ elem->maDisplayDate = rLocaleData.getDate( elem->maModDate )
+ + aDateSep
+ + rLocaleData.getTime( elem->maModDate, false );
+ }
+
+ // detect image
+ if ( elem->mbIsFolder )
+ {
+ ::svtools::VolumeInfo aVolInfo( elem->mbIsVolume, elem->mbIsRemote,
+ elem->mbIsRemoveable, elem->mbIsFloppy,
+ elem->mbIsCompactDisc );
+ elem->maImage = SvFileInformationManager::GetFolderImageId(aVolInfo);
+ }
+ else
+ elem->maImage = SvFileInformationManager::GetFileImageId(INetURLObject(elem->maTargetURL));
+ }
+}
+
+void SvtFileView_Impl::Resort_Impl( sal_Int16 nColumn, bool bAscending )
+{
+ // TODO: IconView ()
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if ( ( nColumn == mnSortColumn ) &&
+ ( bAscending == mbAscending ) )
+ return;
+
+ // reset the quick search index
+ mxView->ResetQuickSearch_Impl( nullptr );
+
+ std::unique_ptr<weld::TreeIter> xEntry(mxView->make_iterator());
+ bool bEntry = mxView->get_cursor(xEntry.get());
+
+ OUString aEntryURL;
+ if (bEntry && !mxView->get_id(*xEntry).isEmpty())
+ aEntryURL = weld::fromId<SvtContentEntry*>(mxView->get_id(*xEntry))->maURL;
+
+ mnSortColumn = nColumn;
+ mbAscending = bAscending;
+
+ SortFolderContent_Impl();
+ OpenFolder_Impl();
+
+ if ( !mbIsFirstResort )
+ {
+ int nPos = GetEntryPos( aEntryURL );
+ if (nPos != -1 && nPos < mxView->n_children())
+ {
+ ++mnSuspendSelectCallback; // #i15668#
+ mxView->set_cursor(nPos);
+ --mnSuspendSelectCallback;
+ }
+ }
+ else
+ mbIsFirstResort = false;
+}
+
+static bool gbAscending = true;
+static sal_Int16 gnColumn = COLUMN_TITLE;
+static const CollatorWrapper* pCollatorWrapper = nullptr;
+
+/* this function returns true, if aOne is less than aTwo
+*/
+static bool CompareSortingData_Impl( std::unique_ptr<SortingData_Impl> const & aOne, std::unique_ptr<SortingData_Impl> const & aTwo )
+{
+ DBG_ASSERT( pCollatorWrapper, "*CompareSortingData_Impl(): Can't work this way!" );
+
+ sal_Int32 nComp;
+ bool bRet = false;
+ bool bEqual = false;
+
+ if ( aOne->mbIsFolder != aTwo->mbIsFolder )
+ {
+ bRet = aOne->mbIsFolder;
+
+ // !!! pb: #100376# folder always on top
+ if ( !gbAscending )
+ bRet = !bRet;
+ }
+ else
+ {
+ switch ( gnColumn )
+ {
+ case COLUMN_TITLE:
+ // compare case insensitive first
+ nComp = pCollatorWrapper->compareString( aOne->GetLowerTitle(), aTwo->GetLowerTitle() );
+
+ if ( nComp == 0 )
+ nComp = pCollatorWrapper->compareString( aOne->GetTitle(), aTwo->GetTitle() );
+
+ if ( nComp < 0 )
+ bRet = true;
+ else if ( nComp > 0 )
+ bRet = false;
+ else
+ bEqual = true;
+ break;
+ case COLUMN_TYPE:
+ nComp = pCollatorWrapper->compareString( aOne->maType, aTwo->maType );
+ if ( nComp < 0 )
+ bRet = true;
+ else if ( nComp > 0 )
+ bRet = false;
+ else
+ bEqual = true;
+ break;
+ case COLUMN_SIZE:
+ if ( aOne->maSize < aTwo->maSize )
+ bRet = true;
+ else if ( aOne->maSize > aTwo->maSize )
+ bRet = false;
+ else
+ bEqual = true;
+ break;
+ case COLUMN_DATE:
+ if ( aOne->maModDate < aTwo->maModDate )
+ bRet = true;
+ else if ( aOne->maModDate > aTwo->maModDate )
+ bRet = false;
+ else
+ bEqual = true;
+ break;
+ default:
+ SAL_INFO( "svtools.contnr", "CompareSortingData_Impl: Compare unknown type!" );
+ bRet = false;
+ }
+ }
+
+ // when the two elements are equal, we must not return sal_True (which would
+ // happen if we just return ! ( a < b ) when not sorting ascending )
+ if ( bEqual )
+ return false;
+
+ return gbAscending ? bRet : !bRet;
+}
+
+
+void SvtFileView_Impl::SortFolderContent_Impl()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if ( maContent.size() > 1 )
+ {
+ gbAscending = mbAscending;
+ gnColumn = mnSortColumn;
+ pCollatorWrapper = aIntlWrapper.getCaseCollator();
+
+ std::stable_sort( maContent.begin(), maContent.end(), CompareSortingData_Impl );
+
+ pCollatorWrapper = nullptr;
+ }
+}
+
+
+void SvtFileView_Impl::EntryRemoved( std::u16string_view rURL )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maContent.erase(std::find_if(maContent.begin(), maContent.end(),
+ [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; }));
+}
+
+
+void SvtFileView_Impl::EntryRenamed( OUString& rURL,
+ const OUString& rTitle )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ auto aFoundElem = std::find_if(maContent.begin(), maContent.end(),
+ [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; });
+ if (aFoundElem != maContent.end())
+ {
+ (*aFoundElem)->SetNewTitle( rTitle );
+ (*aFoundElem)->maDisplayName = ReplaceTabWithString(rTitle);
+
+ INetURLObject aURLObj( rURL );
+ aURLObj.setName( rTitle, INetURLObject::EncodeMechanism::All );
+
+ rURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ (*aFoundElem)->maTargetURL = rURL;
+ }
+}
+
+const SortingData_Impl& SvtFileView_Impl::FolderInserted( const OUString& rURL, const OUString& rTitle )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ std::unique_ptr<SortingData_Impl> pData(new SortingData_Impl);
+
+ pData->SetNewTitle( rTitle );
+ pData->maSize = 0;
+ pData->mbIsFolder = true;
+ pData->maTargetURL = rURL;
+
+ ::svtools::VolumeInfo aVolInfo;
+ pData->maType = SvFileInformationManager::GetFolderDescription( aVolInfo );
+ pData->maImage = SvFileInformationManager::GetFolderImageId( aVolInfo );
+
+ // title, type, size, date
+ pData->maDisplayName = ReplaceTabWithString(pData->GetTitle());
+ // set the date
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
+ pData->maDisplayDate = rLocaleData.getDate( pData->maModDate )
+ + ", "
+ + rLocaleData.getTime( pData->maModDate );
+
+ maContent.push_back( std::move(pData) );
+
+ return *maContent.back();
+}
+
+int SvtFileView_Impl::GetEntryPos(std::u16string_view rURL)
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ auto aFoundElem = std::find_if(maContent.begin(), maContent.end(),
+ [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; });
+ return aFoundElem != maContent.end() ? std::distance(maContent.begin(), aFoundElem) : -1;
+}
+
+void SvtFileView_Impl::SetViewMode( FileViewMode eMode )
+{
+ switch ( eMode )
+ {
+ case eDetailedList:
+ mxView->show();
+ mxIconView->hide();
+ break;
+
+ case eIcon:
+ mxView->hide();
+ mxIconView->show();
+ break;
+
+ default:
+ mxView->show();
+ mxIconView->hide();
+ };
+}
+
+bool SvtFileView_Impl::SearchNextEntry( sal_uInt32& nIndex, std::u16string_view rTitle, bool bWrapAround )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ sal_uInt32 nEnd = maContent.size();
+ sal_uInt32 nStart = nIndex;
+ while ( nIndex < nEnd )
+ {
+ SortingData_Impl* pData = maContent[ nIndex ].get();
+ if ( pData->GetLowerTitle().startsWith( rTitle ) )
+ return true;
+ ++nIndex;
+ }
+
+ if ( bWrapAround )
+ {
+ nIndex = 0;
+ while ( nIndex < nEnd && nIndex <= nStart )
+ {
+ SortingData_Impl* pData = maContent[ nIndex ].get();
+ if ( pData->GetLowerTitle().startsWith( rTitle ) )
+ return true;
+ ++nIndex;
+ }
+ }
+
+ return false;
+}
+
+namespace {
+ void SAL_CALL CallbackTimer::onShot()
+ {
+ OSL_ENSURE( m_pTimeoutHandler, "CallbackTimer::onShot: nobody interested in?" );
+ SvtFileView_Impl* pHandler( m_pTimeoutHandler );
+ if ( pHandler )
+ pHandler->onTimeout();
+ }
+}
+
+void SvtFileView::selected_foreach(const std::function<bool(weld::TreeIter&)>& func)
+{
+ if (mpImpl->mxView->get_visible())
+ mpImpl->mxView->selected_foreach(func);
+ else
+ mpImpl->mxIconView->selected_foreach(func);
+}
+
+weld::Widget* SvtFileView::identifier() const
+{
+ return mpImpl->mxView->getWidget();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fileview.hxx b/fpicker/source/office/fileview.hxx
new file mode 100644
index 000000000..e2f132029
--- /dev/null
+++ b/fpicker/source/office/fileview.hxx
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <memory>
+#include <com/sun/star/uno/Sequence.h>
+#include <utility>
+#include <vcl/weld.hxx>
+#include <rtl/ustring.hxx>
+
+namespace com :: sun :: star :: ucb { class XContent; }
+
+// class SvtFileView -----------------------------------------------------
+
+class SvtFileView_Impl;
+struct SvtContentEntry;
+
+/// the result of an action in the FileView
+enum FileViewResult
+{
+ eSuccess,
+ eFailure,
+ eTimeout,
+ eStillRunning
+};
+
+enum FileViewMode
+{
+ eDetailedList,
+ eIcon
+};
+
+/// describes parameters for doing an action on the FileView asynchronously
+struct FileViewAsyncAction
+{
+ sal_uInt32 nMinTimeout; /// minimum time to wait for a result, in milliseconds
+ sal_uInt32 nMaxTimeout; /// maximum time to wait for a result, in milliseconds, until eTimeout is returned
+ Link<void*,void> aFinishHandler; /// the handler to be called when the action is finished. Called in every case, no matter of the result
+
+ FileViewAsyncAction() : nMinTimeout(0), nMaxTimeout (0)
+ {
+ }
+};
+
+class SvtFileView
+{
+private:
+ std::unique_ptr<SvtFileView_Impl> mpImpl;
+ css::uno::Sequence<OUString> maDenyList;
+
+ DECL_LINK(HeaderSelect_Impl, int, void);
+
+public:
+ SvtFileView(weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ bool bOnlyFolder, bool bMultiSelection, bool bShowType = true);
+ ~SvtFileView();
+
+ void SetViewMode( FileViewMode eMode );
+
+ const OUString& GetViewURL() const;
+ OUString GetURL(const weld::TreeIter& rEntry) const;
+ OUString GetCurrentURL() const;
+
+ bool GetParentURL( OUString& _rParentURL ) const;
+ void CreatedFolder( const OUString& rUrl, const OUString& rNewFolder );
+
+ void set_help_id(const OString& rHelpId);
+ OString get_help_id() const;
+
+ void grab_focus();
+ bool has_focus() const;
+
+ OUString get_selected_text() const;
+
+ weld::Widget* identifier() const; // just to uniquely identify this widget
+
+ /** initialize the view with the content of a folder given by URL, and apply an immediate filter
+
+ @param rFolderURL
+ the URL of the folder whose content is to be read
+ @param rFilter
+ the initial filter to be applied
+ @param pAsyncDescriptor
+ If not <NULL/>, this struct describes the parameters for doing the
+ action asynchronously.
+ */
+ FileViewResult Initialize(
+ const OUString& rFolderURL,
+ const OUString& rFilter,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList
+ );
+
+ /** reads the current content of the current folder again, and applies the given filter to it
+
+ Note 1: The folder is really read a second time. This implies that any new elements (which were
+ not present when you called Initialize the last time) are now displayed.
+
+ Note 2: This method must not be called when you previously initialized the view from a sequence
+ of strings, or a UNO content object.
+
+ @param rFilter
+ the filter to be applied
+ @param pAsyncDescriptor
+ If not <NULL/>, this struct describes the parameters for doing the
+ action asynchronously.
+ */
+ FileViewResult ExecuteFilter(
+ const OUString& rFilter,
+ const FileViewAsyncAction* pAsyncDescriptor
+ );
+
+ /** cancels a running async action (if any)
+
+ @seealso Initialize
+ @seealso ExecuteFilter
+ @seealso FileViewAsyncAction
+ */
+ void CancelRunningAsyncAction();
+
+ /** initializes the view with the parent folder of the current folder
+
+ @param rNewURL
+ the URL of the folder which we just navigated to
+ @param pAsyncDescriptor
+ If not <NULL/>, this struct describes the parameters for doing the
+ action asynchronously.
+ */
+ FileViewResult PreviousLevel(
+ const FileViewAsyncAction* pAsyncDescriptor
+ );
+
+ void SetNoSelection();
+
+ void SetSelectHdl( const Link<SvtFileView*,void>& rHdl );
+ void SetDoubleClickHdl( const Link<SvtFileView*,bool>& rHdl );
+ void SetOpenDoneHdl( const Link<SvtFileView*,void>& rHdl );
+
+ sal_uInt32 GetSelectionCount() const;
+ SvtContentEntry* FirstSelected() const;
+
+ void selected_foreach(const std::function<bool(weld::TreeIter&)>& func);
+
+ void EnableDelete( bool bEnable );
+
+ // save and load column size and sort order
+ OUString GetConfigString() const;
+ void SetConfigString( std::u16string_view rCfgStr );
+
+ void EndInplaceEditing();
+
+ ::std::vector< SvtContentEntry > GetContent();
+};
+
+// struct SvtContentEntry ------------------------------------------------
+
+struct SvtContentEntry
+{
+ bool mbIsFolder;
+ OUString maURL;
+
+ SvtContentEntry( OUString aURL, bool bIsFolder ) :
+ mbIsFolder( bIsFolder ), maURL(std::move( aURL )) {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/foldertree.cxx b/fpicker/source/office/foldertree.cxx
new file mode 100644
index 000000000..9b704d6c4
--- /dev/null
+++ b/fpicker/source/office/foldertree.cxx
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <comphelper/processfactory.hxx>
+#include <tools/urlobj.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include "contentenumeration.hxx"
+#include "foldertree.hxx"
+#include <bitmaps.hlst>
+
+using namespace ::com::sun::star::task;
+
+using namespace ::svt;
+
+FolderTree::FolderTree(std::unique_ptr<weld::TreeView> xTreeView, weld::Window* pTopLevel)
+ : m_xTreeView(std::move(xTreeView))
+ , m_xScratchIter(m_xTreeView->make_iterator())
+ , m_pTopLevel(pTopLevel)
+{
+ m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 24,
+ m_xTreeView->get_height_rows(7));
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xInteractionHandler(
+ InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
+ m_xEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
+
+ m_xTreeView->connect_expanding(LINK(this, FolderTree, RequestingChildrenHdl));
+}
+
+IMPL_LINK(FolderTree, RequestingChildrenHdl, const weld::TreeIter&, rEntry, bool)
+{
+ weld::WaitObject aWait(m_pTopLevel);
+
+ FillTreeEntry(rEntry);
+
+ return true;
+}
+
+void FolderTree::InsertRootEntry(const OUString& rId, const OUString& rRootLabel)
+{
+ m_xTreeView->insert(nullptr, -1, &rRootLabel, &rId, nullptr, nullptr,
+ true, m_xScratchIter.get());
+ m_xTreeView->set_image(*m_xScratchIter, RID_BMP_FOLDER);
+ m_xTreeView->set_cursor(*m_xScratchIter);
+}
+
+void FolderTree::FillTreeEntry(const weld::TreeIter& rEntry)
+{
+ OUString sURL = m_xTreeView->get_id(rEntry);
+ OUString sFolderImage(RID_BMP_FOLDER);
+
+ if (m_sLastUpdatedDir != sURL)
+ {
+ while (m_xTreeView->iter_has_child(rEntry))
+ {
+ std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rEntry));
+ (void)m_xTreeView->iter_children(*xChild);
+ m_xTreeView->remove(*xChild);
+ }
+
+ ::std::vector< std::unique_ptr<SortingData_Impl> > aContent;
+
+ ::rtl::Reference< ::svt::FileViewContentEnumerator >
+ xContentEnumerator(new FileViewContentEnumerator(
+ m_xEnv, aContent, m_aMutex));
+
+ FolderDescriptor aFolder(sURL);
+
+ EnumerationResult eResult =
+ xContentEnumerator->enumerateFolderContentSync( aFolder, m_aDenyList );
+
+ if (EnumerationResult::SUCCESS == eResult)
+ {
+ for(const auto & i : aContent)
+ {
+ if (!i->mbIsFolder)
+ continue;
+ m_xTreeView->insert(&rEntry, -1, &i->GetTitle(), &i->maTargetURL,
+ nullptr, nullptr, true, m_xScratchIter.get());
+ m_xTreeView->set_image(*m_xScratchIter, sFolderImage);
+ }
+ }
+ }
+ else
+ {
+ // this dir was updated recently
+ // next time read this remote folder
+ m_sLastUpdatedDir.clear();
+ }
+}
+
+void FolderTree::FillTreeEntry( const OUString & rUrl, const ::std::vector< std::pair< OUString, OUString > >& rFolders )
+{
+ SetTreePath(rUrl);
+
+ std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator());
+ bool bParent = m_xTreeView->get_cursor(xParent.get());
+
+ if (!bParent || m_xTreeView->get_row_expanded(*xParent))
+ return;
+
+ OUString sFolderImage(RID_BMP_FOLDER);
+ while (m_xTreeView->iter_has_child(*xParent))
+ {
+ std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xParent.get()));
+ (void)m_xTreeView->iter_children(*xChild);
+ m_xTreeView->remove(*xChild);
+ }
+
+ for (auto const& folder : rFolders)
+ {
+ m_xTreeView->insert(xParent.get(), -1, &folder.first, &folder.second,
+ nullptr, nullptr, true, m_xScratchIter.get());
+ m_xTreeView->set_image(*m_xScratchIter, sFolderImage);
+ }
+
+ m_sLastUpdatedDir = rUrl;
+ m_xTreeView->expand_row(*xParent);
+}
+
+void FolderTree::SetTreePath( std::u16string_view sUrl )
+{
+ INetURLObject aUrl( sUrl );
+ aUrl.setFinalSlash();
+
+ OUString sPath = aUrl.GetURLPath( INetURLObject::DecodeMechanism::WithCharset );
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
+ bool bEntry = m_xTreeView->get_iter_first(*xEntry);
+ bool bEnd = false;
+
+ while (bEntry && !bEnd)
+ {
+ if (!m_xTreeView->get_id(*xEntry).isEmpty())
+ {
+ OUString sNodeUrl = m_xTreeView->get_id(*xEntry);
+
+ INetURLObject aUrlObj( sNodeUrl );
+ aUrlObj.setFinalSlash();
+
+ sNodeUrl = aUrlObj.GetURLPath( INetURLObject::DecodeMechanism::WithCharset );
+
+ if( sPath == sNodeUrl )
+ {
+ m_xTreeView->select(*xEntry);
+ bEnd = true;
+ }
+ else if( sPath.startsWith( sNodeUrl ) )
+ {
+ if (!m_xTreeView->get_row_expanded(*xEntry))
+ m_xTreeView->expand_row(*xEntry);
+
+ bEntry = m_xTreeView->iter_children(*xEntry);
+ }
+ else
+ {
+ bEntry = m_xTreeView->iter_next_sibling(*xEntry);
+ }
+ }
+ else
+ break;
+ }
+}
+
+void FolderTree::SetDenyList( const css::uno::Sequence< OUString >& rDenyList )
+{
+ m_aDenyList = rDenyList;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/foldertree.hxx b/fpicker/source/office/foldertree.hxx
new file mode 100644
index 000000000..e1ba25699
--- /dev/null
+++ b/fpicker/source/office/foldertree.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <vcl/weld.hxx>
+
+namespace com :: sun :: star :: ucb { class XCommandEnvironment; }
+
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::uno;
+
+class FolderTree
+{
+private:
+ std::unique_ptr<weld::TreeView> m_xTreeView;
+ std::unique_ptr<weld::TreeIter> m_xScratchIter;
+ weld::Window* m_pTopLevel;
+ Reference< XCommandEnvironment > m_xEnv;
+ ::osl::Mutex m_aMutex;
+ Sequence< OUString > m_aDenyList;
+
+ OUString m_sLastUpdatedDir;
+
+ DECL_LINK(RequestingChildrenHdl, const weld::TreeIter&, bool);
+
+public:
+ FolderTree(std::unique_ptr<weld::TreeView> xTreeView, weld::Window* pTopLevel);
+
+ void clear() { m_xTreeView->clear(); }
+
+ void connect_changed(const Link<weld::TreeView&, void>& rLink) { m_xTreeView->connect_changed(rLink); }
+
+ void InsertRootEntry(const OUString& rId, const OUString& rRootLabel);
+ void FillTreeEntry(const weld::TreeIter& rEntry);
+ void FillTreeEntry(const OUString & rUrl, const ::std::vector< std::pair< OUString, OUString > >& rFolders);
+ void SetTreePath(std::u16string_view sUrl);
+ void SetDenyList(const css::uno::Sequence< OUString >& rDenyList);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fpdialogbase.hxx b/fpicker/source/office/fpdialogbase.hxx
new file mode 100644
index 000000000..20ab36297
--- /dev/null
+++ b/fpicker/source/office/fpdialogbase.hxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include "pickercallbacks.hxx"
+
+class SvTabListBox;
+class SvtFileView;
+class SvtFileDialogFilter_Impl;
+
+
+enum class PickerFlags {
+ NONE = 0x000000,
+ AutoExtension = 0x000001,
+ FilterOptions = 0x000002,
+ ShowVersions = 0x000004,
+ InsertAsLink = 0x000008,
+ ShowPreview = 0x000010,
+ Templates = 0x000020,
+ PlayButton = 0x000040,
+ Selection = 0x000080,
+ ImageTemplate = 0x000100,
+ PathDialog = 0x000200,
+ Open = 0x000400,
+ SaveAs = 0x000800,
+ Password = 0x001000,
+ ReadOnly = 0x002000,
+ MultiSelection = 0x004000,
+ ImageAnchor = 0x008000,
+};
+namespace o3tl {
+ template<> struct typed_flags<PickerFlags> : is_typed_flags<PickerFlags, 0x00ffff> {};
+}
+
+inline constexpr OUStringLiteral FILEDIALOG_FILTER_ALL = u"*.*";
+
+// SvtFileDialog_Base
+
+class SvtFileDialog_Base : public weld::GenericDialogController, public ::svt::IFilePickerController
+{
+public:
+ SvtFileDialog_Base(weld::Window* pParent, const OUString& rUIXMLDescription, const OString& rID)
+ : weld::GenericDialogController(pParent, rUIXMLDescription, rID)
+ {
+ }
+
+ virtual bool PrepareExecute() { return true ; }
+
+ virtual SvtFileView* GetView() = 0;
+
+ virtual void SetHasFilename( bool bHasFilename ) = 0;
+ virtual void SetDenyList( const css::uno::Sequence< OUString >& rDenyList ) = 0;
+ virtual const css::uno::Sequence< OUString >& GetDenyList() const = 0;
+ virtual void SetStandardDir( const OUString& rStdDir ) = 0;
+ virtual const OUString& GetStandardDir() const = 0;
+ virtual void SetPath( const OUString& rNewURL ) = 0;
+ virtual const OUString& GetPath() = 0;
+ virtual std::vector<OUString> GetPathList() const = 0;
+ virtual bool ContentIsFolder( const OUString& rURL ) = 0;
+
+ virtual OUString getCurrentFileText() const = 0;
+ virtual void setCurrentFileText( const OUString& rText, bool bSelectAll = false ) = 0;
+
+ virtual void AddFilter( const OUString& rFilter, const OUString& rType ) = 0;
+ virtual void AddFilterGroup( const OUString& _rFilter,
+ const css::uno::Sequence< css::beans::StringPair >& rFilters ) = 0;
+ virtual OUString GetCurFilter() const = 0;
+ virtual void SetCurFilter( const OUString& rFilter ) = 0;
+ virtual void FilterSelect() = 0;
+
+ virtual void SetFileCallback( ::svt::IFilePickerListener *pNotifier ) = 0;
+ virtual void onAsyncOperationStarted() = 0;
+ virtual void onAsyncOperationFinished() = 0;
+ virtual void UpdateControls( const OUString& rURL ) = 0;
+
+ virtual void EnableAutocompletion( bool _bEnable = true ) = 0;
+
+ virtual sal_Int32 getAvailableWidth() = 0;
+ virtual sal_Int32 getAvailableHeight() = 0;
+
+ virtual void setImage( const css::uno::Any& rImage ) = 0;
+
+ virtual bool getShowState() = 0;
+};
+
+#define FILE_SELECTION_CHANGED 1
+#define DIRECTORY_CHANGED 2
+#define CTRL_STATE_CHANGED 4
+#define DIALOG_SIZE_CHANGED 5
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fpinteraction.cxx b/fpicker/source/office/fpinteraction.cxx
new file mode 100644
index 000000000..9d3591d75
--- /dev/null
+++ b/fpicker/source/office/fpinteraction.cxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "fpinteraction.hxx"
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/task/XInteractionDisapprove.hpp>
+#include <com/sun/star/task/XInteractionRetry.hpp>
+
+#include <sal/log.hxx>
+#include <utility>
+
+
+namespace svt
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::task;
+ using namespace ::com::sun::star::ucb;
+
+ OFilePickerInteractionHandler::OFilePickerInteractionHandler( css::uno::Reference< css::task::XInteractionHandler > _xMaster )
+ :m_xMaster(std::move( _xMaster ))
+ ,m_bUsed( false )
+ ,m_eInterceptions( OFilePickerInteractionHandler::E_NOINTERCEPTION )
+ {
+ SAL_WARN_IF( !m_xMaster.is(), "fpicker.office", "OFilePickerInteractionHandler::OFilePickerInteractionHandler: invalid master handler!" );
+ }
+
+
+ OFilePickerInteractionHandler::~OFilePickerInteractionHandler( )
+ {
+ }
+
+
+ void SAL_CALL OFilePickerInteractionHandler::handle( const Reference< XInteractionRequest >& _rxRequest )
+ {
+ if (!_rxRequest.is())
+ return;
+
+ m_bUsed = true;
+
+ // extract some generic continuations ... might we need it later
+ // if something goes wrong.
+ Reference< XInteractionAbort > xAbort;
+ Reference< XInteractionApprove > xApprove;
+ Reference< XInteractionDisapprove > xDisapprove;
+ Reference< XInteractionRetry > xRetry;
+
+ const Sequence< Reference< XInteractionContinuation > > lConts = _rxRequest->getContinuations();
+ for (const Reference< XInteractionContinuation >& rCont : lConts)
+ {
+ if (!xAbort.is())
+ xAbort.set(rCont, UNO_QUERY);
+ if (!xApprove.is())
+ xApprove.set(rCont, UNO_QUERY);
+ if (!xDisapprove.is())
+ xDisapprove.set(rCont, UNO_QUERY);
+ if (!xRetry.is())
+ xRetry.set(rCont, UNO_QUERY);
+ }
+
+ // safe the original request for later analyzing!
+ m_aException = _rxRequest->getRequest();
+
+ // intercept some interesting interactions
+
+ // The "does not exist" interaction will be suppressed here completely.
+ if (m_eInterceptions & OFilePickerInteractionHandler::E_DOESNOTEXIST)
+ {
+ InteractiveIOException aIoException;
+ if (
+ (m_aException >>= aIoException ) &&
+ (IOErrorCode_NOT_EXISTING == aIoException.Code)
+ )
+ {
+ if (xAbort.is())
+ xAbort->select();
+ return;
+ }
+ }
+
+ // no master => abort this operation ...
+ if (!m_xMaster.is())
+ {
+ if (xAbort.is())
+ xAbort->select();
+ return;
+ }
+
+ // forward it to our master - so he can handle all
+ // not interesting interactions :-)
+ m_xMaster->handle(_rxRequest);
+ }
+
+
+ void OFilePickerInteractionHandler::enableInterceptions( EInterceptedInteractions eInterceptions )
+ {
+ m_eInterceptions = eInterceptions;
+ }
+
+
+ void OFilePickerInteractionHandler::resetUseState()
+ {
+ m_bUsed = false;
+ }
+
+
+ void OFilePickerInteractionHandler::forgetRequest()
+ {
+ m_aException = Any();
+ }
+
+
+ bool OFilePickerInteractionHandler::wasAccessDenied() const
+ {
+ InteractiveIOException aIoException;
+ return (m_aException >>= aIoException ) &&
+ (IOErrorCode_ACCESS_DENIED == aIoException.Code);
+ }
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fpinteraction.hxx b/fpicker/source/office/fpinteraction.hxx
new file mode 100644
index 000000000..84f4a43d6
--- /dev/null
+++ b/fpicker/source/office/fpinteraction.hxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/implbase.hxx>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+
+
+namespace svt
+{
+
+
+ //= OFilePickerInteractionHandler
+
+ typedef ::cppu::WeakImplHelper < css::task::XInteractionHandler
+ > OFilePickerInteractionHandler_Base;
+
+ /** an InteractionHandler implementation which extends another handler with some customizability
+ */
+ class OFilePickerInteractionHandler final : public OFilePickerInteractionHandler_Base
+ {
+ public:
+ /** flags, which indicates special handled interactions
+ These values will be used combined as flags - so they must
+ in range [2^n]!
+ */
+ enum EInterceptedInteractions
+ {
+ E_NOINTERCEPTION = 0,
+ E_DOESNOTEXIST = 1
+ // next values [2,4,8,16 ...]!
+ };
+
+ private:
+ css::uno::Reference< css::task::XInteractionHandler > m_xMaster; // our master handler
+ css::uno::Any m_aException; // the last handled request
+ bool m_bUsed; // indicates using of this interaction handler instance
+ EInterceptedInteractions m_eInterceptions; // enable/disable interception of some special interactions
+
+ public:
+ explicit OFilePickerInteractionHandler( css::uno::Reference< css::task::XInteractionHandler > _xMaster );
+
+ // some generic functions
+ void enableInterceptions( EInterceptedInteractions eInterceptions );
+ bool wasUsed () const { return m_bUsed; }
+ void resetUseState ();
+ void forgetRequest ();
+
+ // functions to analyze last cached request
+ bool wasAccessDenied() const;
+
+ private:
+ // XInteractionHandler
+ virtual void SAL_CALL handle( const css::uno::Reference< css::task::XInteractionRequest >& _rxRequest ) override;
+
+ virtual ~OFilePickerInteractionHandler() override;
+ };
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fps_office.component b/fpicker/source/office/fps_office.component
new file mode 100644
index 000000000..cdf5f8247
--- /dev/null
+++ b/fpicker/source/office/fps_office.component
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.svtools.OfficeFilePicker"
+ constructor="fpicker_SvtFilePicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.OfficeFilePicker"/>
+ </implementation>
+ <implementation name="com.sun.star.svtools.RemoteFilePicker"
+ constructor="fpicker_SvtRemoteFilePicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.RemoteFilePicker"/>
+ </implementation>
+ <implementation name="com.sun.star.svtools.OfficeFolderPicker"
+ constructor="fpicker_SvtFolderPicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.OfficeFolderPicker"/>
+ </implementation>
+</component>
diff --git a/fpicker/source/office/fpsmartcontent.cxx b/fpicker/source/office/fpsmartcontent.cxx
new file mode 100644
index 000000000..3f9cad855
--- /dev/null
+++ b/fpicker/source/office/fpsmartcontent.cxx
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "fpsmartcontent.hxx"
+
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/ContentInfo.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <tools/diagnose_ex.h>
+
+
+namespace svt
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::task;
+ using namespace ::com::sun::star::ucb;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::container;
+
+
+ //= SmartContent
+
+
+ SmartContent::SmartContent()
+ :m_eState( NOT_BOUND )
+ {
+ }
+
+
+ SmartContent::SmartContent( const OUString& _rInitialURL )
+ :m_eState( NOT_BOUND )
+ {
+ bindTo( _rInitialURL );
+ }
+
+
+ SmartContent::~SmartContent()
+ {
+ /* This destructor originally contained the following blurb: "Do
+ not delete the content. Because the content will be used by
+ the cache." This is just plain silly, because it relies on
+ the provider caching created contents (which is done by
+ ucbhelper::ContentProviderImplHelper, but we do not actually
+ expect all providers to use that, right?) Otherwise we are
+ just leaking memory.
+
+ TODO: If there is real need for caching the content, it must
+ be done here.
+ */
+ }
+
+
+ void SmartContent::enableOwnInteractionHandler(::svt::OFilePickerInteractionHandler::EInterceptedInteractions eInterceptions)
+ {
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xGlobalInteractionHandler(
+ InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW );
+
+ m_xOwnInteraction = new ::svt::OFilePickerInteractionHandler(xGlobalInteractionHandler);
+ m_xOwnInteraction->enableInterceptions(eInterceptions);
+
+ m_xCmdEnv = new ::ucbhelper::CommandEnvironment( m_xOwnInteraction, Reference< XProgressHandler >() );
+ }
+
+
+ void SmartContent::enableDefaultInteractionHandler()
+ {
+ m_xOwnInteraction.clear();
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xGlobalInteractionHandler(
+ InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW );
+ m_xCmdEnv = new ucbhelper::CommandEnvironment( xGlobalInteractionHandler, Reference< XProgressHandler >() );
+ }
+
+
+ ::svt::OFilePickerInteractionHandler* SmartContent::getOwnInteractionHandler() const
+ {
+ return m_xOwnInteraction.get();
+ }
+
+
+ SmartContent::InteractionHandlerType SmartContent::queryCurrentInteractionHandler() const
+ {
+ if (m_xOwnInteraction.is())
+ return IHT_OWN;
+
+ if (!m_xCmdEnv.is())
+ return IHT_NONE;
+
+ return IHT_DEFAULT;
+ }
+
+
+ void SmartContent::disableInteractionHandler()
+ {
+ m_xOwnInteraction.clear();
+ m_xCmdEnv.clear();
+ }
+
+
+ void SmartContent::bindTo( const OUString& _rURL )
+ {
+ if ( getURL() == _rURL )
+ // nothing to do, regardless of the state
+ return;
+
+ m_pContent.reset();
+ m_eState = INVALID; // default to INVALID
+ m_sURL = _rURL;
+
+ if ( !m_sURL.isEmpty() )
+ {
+ try
+ {
+ m_pContent.reset( new ::ucbhelper::Content( _rURL, m_xCmdEnv, comphelper::getProcessComponentContext() ) );
+ m_eState = UNKNOWN;
+ // from now on, the state is unknown -> we cannot know for sure if the content
+ // is really valid (some UCP's only tell this when asking for properties, not upon
+ // creation)
+ }
+ catch( const ContentCreationException& )
+ {
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "fpicker", "SmartContent::bindTo: unexpected exception caught!" );
+ }
+ }
+ else
+ {
+ m_eState = NOT_BOUND;
+ }
+
+
+ // don't forget to reset the may internal used interaction handler ...
+ // But do it only for our own specialized interaction helper!
+ ::svt::OFilePickerInteractionHandler* pHandler = getOwnInteractionHandler();
+ if (pHandler)
+ {
+ pHandler->resetUseState();
+ pHandler->forgetRequest();
+ }
+ }
+
+
+ bool SmartContent::implIs( const OUString& _rURL, Type _eType )
+ {
+ // bind to this content
+ bindTo( _rURL );
+
+ // did we survive this?
+ if ( isInvalid() || !isBound() )
+ return false;
+
+ assert( m_pContent && "SmartContent::implIs: inconsistence!" );
+ // if, after a bindTo, we don't have a content, then we should be INVALID, or at least
+ // NOT_BOUND (the latter happens, for example, if somebody tries to ask for an empty URL)
+
+ bool bIs = false;
+ try
+ {
+ if ( Folder == _eType )
+ bIs = m_pContent->isFolder();
+ else
+ bIs = m_pContent->isDocument();
+
+ // from here on, we definitely know that the content is valid
+ m_eState = VALID;
+ }
+ catch( const Exception& )
+ {
+ // now we're definitely invalid
+ m_eState = INVALID;
+ }
+ return bIs;
+ }
+
+
+ void SmartContent::getTitle( OUString& /* [out] */ _rTitle )
+ {
+ if ( !isBound() || isInvalid() )
+ return;
+
+ try
+ {
+ OUString sTitle;
+ m_pContent->getPropertyValue("Title") >>= sTitle;
+ _rTitle = sTitle;
+
+ // from here on, we definitely know that the content is valid
+ m_eState = VALID;
+ }
+ catch( const css::uno::Exception& )
+ {
+ // now we're definitely invalid
+ m_eState = INVALID;
+ }
+ }
+
+
+ bool SmartContent::hasParentFolder( )
+ {
+ if ( !isBound() || isInvalid() )
+ return false;
+
+ bool bRet = false;
+ try
+ {
+ Reference< XChild > xChild( m_pContent->get(), UNO_QUERY );
+ if ( xChild.is() )
+ {
+ Reference< XContent > xParent( xChild->getParent(), UNO_QUERY );
+ if ( xParent.is() )
+ {
+ const OUString aParentURL( xParent->getIdentifier()->getContentIdentifier() );
+ bRet = ( !aParentURL.isEmpty() && aParentURL != m_pContent->getURL() );
+
+ // now we're definitely valid
+ m_eState = VALID;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ // now we're definitely invalid
+ m_eState = INVALID;
+ }
+ return bRet;
+ }
+
+
+ bool SmartContent::canCreateFolder( )
+ {
+ if ( !isBound() || isInvalid() )
+ return false;
+
+ bool bRet = false;
+ try
+ {
+ const css::uno::Sequence<css::ucb::ContentInfo> aContentsInfo = m_pContent->queryCreatableContentsInfo();
+ for ( auto const& rInfo : aContentsInfo )
+ {
+ // Simply look for the first KIND_FOLDER...
+ if ( rInfo.Attributes & ContentInfoAttribute::KIND_FOLDER )
+ {
+ bRet = true;
+ break;
+ }
+ }
+
+ // now we're definitely valid
+ m_eState = VALID;
+ }
+ catch( const Exception& )
+ {
+ // now we're definitely invalid
+ m_eState = INVALID;
+ }
+ return bRet;
+ }
+
+ OUString SmartContent::createFolder( const OUString& _rTitle )
+ {
+ OUString aCreatedUrl;
+ try
+ {
+ OUString sFolderType;
+
+ const css::uno::Sequence<css::ucb::ContentInfo> aContentsInfo = m_pContent->queryCreatableContentsInfo();
+ for ( auto const& rInfo : aContentsInfo )
+ {
+ // Simply look for the first KIND_FOLDER...
+ if ( rInfo.Attributes & ContentInfoAttribute::KIND_FOLDER )
+ {
+ sFolderType = rInfo.Type;
+ break;
+ }
+ }
+
+ if ( !sFolderType.isEmpty() )
+ {
+ ucbhelper::Content aCreated;
+ Sequence< OUString > aNames { "Title" };
+ Sequence< Any > aValues { Any(_rTitle) };
+ m_pContent->insertNewContent( sFolderType, aNames, aValues, aCreated );
+
+ aCreatedUrl = aCreated.getURL();
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+ return aCreatedUrl;
+ }
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/fpsmartcontent.hxx b/fpicker/source/office/fpsmartcontent.hxx
new file mode 100644
index 000000000..5f9d4e32f
--- /dev/null
+++ b/fpicker/source/office/fpsmartcontent.hxx
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "fpinteraction.hxx"
+
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <ucbhelper/content.hxx>
+#include <rtl/ref.hxx>
+#include <memory>
+
+
+namespace svt
+{
+
+
+ //= SmartContent
+
+ /** a "smart content" which basically wraps a UCB content, but caches some information
+ so that repeatedly recreating it may be faster
+ */
+ class SmartContent
+ {
+ public:
+ enum State
+ {
+ NOT_BOUND, // never bound
+ UNKNOWN, // bound, but validity is unknown
+ VALID, // bound to a URL, and valid
+ INVALID // bound to a URL, and invalid
+ };
+
+ private:
+ OUString m_sURL;
+ std::unique_ptr<::ucbhelper::Content> m_pContent;
+ State m_eState;
+ css::uno::Reference < css::ucb::XCommandEnvironment > m_xCmdEnv;
+ rtl::Reference<::svt::OFilePickerInteractionHandler> m_xOwnInteraction;
+
+ private:
+ enum Type { Folder, Document };
+ /// checks if the currently bound content is a folder or document
+ bool implIs( const OUString& _rURL, Type _eType );
+
+ SmartContent( const SmartContent& _rSource ) = delete;
+ SmartContent& operator=( const SmartContent& _rSource ) = delete;
+
+ public:
+ SmartContent();
+ explicit SmartContent( const OUString& _rInitialURL );
+ ~SmartContent();
+
+ public:
+
+ /** create and set a specialized interaction handler at the internal used command environment.
+
+ @param eInterceptions
+ will be directly forwarded to OFilePickerInteractionHandler::enableInterceptions()
+ */
+ void enableOwnInteractionHandler(::svt::OFilePickerInteractionHandler::EInterceptedInteractions eInterceptions);
+
+ /** disable the specialized interaction handler and use the global UI interaction handler only.
+ */
+ void enableDefaultInteractionHandler();
+
+ /** return the internal used interaction handler object ...
+ Because this pointer will be valid only, if the uno object is hold
+ alive by its uno reference (and this reference is set on the
+ command environment) we must return NULL, in case this environment does
+ not exist!
+ */
+ ::svt::OFilePickerInteractionHandler* getOwnInteractionHandler() const;
+
+ /** describes different types of interaction handlers
+ */
+ enum InteractionHandlerType
+ {
+ IHT_NONE,
+ IHT_OWN,
+ IHT_DEFAULT
+ };
+
+ /** return the type of the internal used interaction handler object ...
+
+ @seealso InteractionHandlerType
+ */
+ InteractionHandlerType queryCurrentInteractionHandler() const;
+
+ /** disable internal used interaction handler object ...
+ */
+ void disableInteractionHandler();
+
+ /** returns the current state of the content
+
+ @seealso State
+ */
+ State getState( ) const { return m_eState; }
+
+ /** checks if the content is valid
+ <p>Note that "not (is valid)" is not the same as "is invalid"</p>
+ */
+ bool isValid( ) const { return VALID == getState(); }
+
+ /** checks if the content is valid
+ <p>Note that "not (is invalid)" is not the same as "is valid"</p>
+ */
+ bool isInvalid( ) const { return INVALID == getState(); }
+
+ /** checks if the content is bound
+ */
+ bool isBound( ) const { return NOT_BOUND != getState(); }
+
+ /** returns the URL of the content
+ */
+ OUString const & getURL() const { return m_pContent ? m_pContent->getURL() : m_sURL; }
+
+ /** (re)creates the content for the given URL
+
+ <p>Note that getState will return either UNKNOWN or INVALID after the call returns,
+ but never VALID. The reason is that there are content providers which allow to construct
+ content objects, even if the respective contents are not accessible. They tell about this
+ only upon working with the content object (e.g. when asking for the IsFolder).</p>
+
+ @postcond
+ <member>getState</member> does not return NOT_BOUND after the call returns
+ */
+ void bindTo( const OUString& _rURL );
+
+ /** retrieves the title of the content
+ @precond
+ the content is bound and not invalid
+ */
+ void getTitle( OUString& /* [out] */ _rTitle );
+
+ /** checks if the content has a parent folder
+ @precond
+ the content is bound and not invalid
+ */
+ bool hasParentFolder( );
+
+ /** checks if sub folders below the content can be created
+ @precond
+ the content is bound and not invalid
+ */
+ bool canCreateFolder( );
+
+ /** creates a new folder with the given title and return the corresponding URL.
+
+ @return
+ the URL of the created folder or an empty string
+ */
+ OUString createFolder( const OUString& _rTitle );
+
+ /** binds to the given URL, checks whether or not it refers to a folder
+
+ @postcond
+ the content is not in the state UNKNOWN
+ */
+ bool isFolder( const OUString& _rURL )
+ {
+ return implIs( _rURL, Folder );
+ }
+
+ /** checks if the content is existent (it is if and only if it is a document or a folder)
+ */
+ bool is( const OUString& _rURL )
+ {
+ return implIs( _rURL, Folder ) || implIs( _rURL, Document );
+ }
+
+ bool isFolder( ) { return isFolder( getURL() ); }
+ };
+
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/iodlg.cxx b/fpicker/source/office/iodlg.cxx
new file mode 100644
index 000000000..a7addbb10
--- /dev/null
+++ b/fpicker/source/office/iodlg.cxx
@@ -0,0 +1,2331 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/log.hxx>
+#include "fileview.hxx"
+#include "iodlg.hxx"
+#include <svtools/PlaceEditDialog.hxx>
+#include "OfficeControlAccess.hxx"
+#include "PlacesListBox.hxx"
+#include <fpicker/fpsofficeResMgr.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/errinf.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/viewoptions.hxx>
+#include <svtools/sfxecode.hxx>
+
+#include <fpicker/strings.hrc>
+#include <svtools/helpids.h>
+#include <strings.hrc>
+#include "asyncfilepicker.hxx"
+#include "iodlgimp.hxx"
+#include <svtools/inettbc.hxx>
+#include "QueryFolderName.hxx"
+#include <rtl/ustring.hxx>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <comphelper/interaction.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+
+#include <osl/file.hxx>
+#include <vcl/dibtools.hxx>
+
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include "fpinteraction.hxx"
+#include <osl/process.h>
+#include <o3tl/string_view.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::sdbc;
+using namespace ::utl;
+using namespace ::svt;
+
+using namespace ExtendedFilePickerElementIds;
+using namespace CommonFilePickerElementIds;
+using namespace InternalFilePickerElementIds;
+
+// functions -------------------------------------------------------------
+
+namespace
+{
+ OUString getMostCurrentFilter( std::unique_ptr<SvtExpFileDlg_Impl> const & pImpl )
+ {
+ assert( pImpl && "invalid impl pointer" );
+ const SvtFileDialogFilter_Impl* pFilter = pImpl->m_xUserFilter.get();
+
+ if ( !pFilter )
+ pFilter = pImpl->GetCurFilter();
+
+ if ( !pFilter )
+ return OUString();
+
+ return pFilter->GetType();
+ }
+
+ void restoreCurrentFilter( std::unique_ptr<SvtExpFileDlg_Impl> const & pImpl )
+ {
+ SAL_WARN_IF( !pImpl->GetCurFilter(), "fpicker.office", "restoreCurrentFilter: no current filter!" );
+ SAL_WARN_IF( pImpl->GetCurFilterDisplayName().isEmpty(), "fpicker.office", "restoreCurrentFilter: no current filter (no display name)!" );
+
+ pImpl->SelectFilterListEntry( pImpl->GetCurFilterDisplayName() );
+
+#ifdef DBG_UTIL
+ OUString sSelectedDisplayName;
+ DBG_ASSERT( ( pImpl->GetSelectedFilterEntry( sSelectedDisplayName ) == pImpl->GetCurFilter() )
+ && ( sSelectedDisplayName == pImpl->GetCurFilterDisplayName() ),
+ "restoreCurrentFilter: inconsistence!" );
+#endif
+ }
+
+
+ OUString GetFsysExtension_Impl( std::u16string_view rFile, const OUString& rLastFilterExt )
+ {
+ size_t nDotPos = rFile.rfind( '.' );
+ if ( nDotPos != std::u16string_view::npos )
+ {
+ if ( !rLastFilterExt.isEmpty() )
+ {
+ if ( o3tl::equalsIgnoreAsciiCase(rFile.substr( nDotPos + 1 ), rLastFilterExt ) )
+ return rLastFilterExt;
+ }
+ else
+ return OUString(rFile.substr( nDotPos ));
+ }
+ return OUString();
+ }
+
+
+ void SetFsysExtension_Impl( OUString& rFile, std::u16string_view rExtension )
+ {
+ const sal_Int32 nDotPos{ rFile.lastIndexOf('.') };
+ if (nDotPos>=0)
+ {
+ if (!rExtension.empty())
+ rFile = OUString::Concat(rFile.subView(0, nDotPos)) + rExtension; // replace old extension with new (not empty) one
+ else if (nDotPos)
+ rFile = rFile.copy(0, nDotPos-1); // truncate extension (new one is empty)
+ else
+ rFile.clear(); // Filename was just an extension
+ }
+ else if (!rExtension.empty())
+ rFile += OUString::Concat(".") + rExtension;
+ // no extension was present, append new one if not empty
+ }
+
+ void lcl_autoUpdateFileExtension( SvtFileDialog* _pDialog, const OUString& _rLastFilterExt )
+ {
+ // if auto extension is enabled...
+ if ( !_pDialog->isAutoExtensionEnabled() )
+ return;
+
+ // automatically switch to the extension of the (maybe just newly selected) extension
+ OUString aNewFile = _pDialog->getCurrentFileText( );
+ OUString aExt = GetFsysExtension_Impl( aNewFile, _rLastFilterExt );
+
+ // but only if there already is an extension
+ if ( aExt.isEmpty() )
+ return;
+
+ // check if it is a real file extension, and not only the "post-dot" part in
+ // a directory name
+ bool bRealExtensions = true;
+ if ( -1 != aExt.indexOf( '/' ) )
+ bRealExtensions = false;
+ else if ( -1 != aExt.indexOf( '\\' ) )
+ bRealExtensions = false;
+ else
+ {
+ // no easy way to tell, because the part containing the dot already is the last
+ // segment of the complete file name
+ // So we have to check if the file name denotes a folder or a file.
+ // For performance reasons, we do this for file urls only
+ INetURLObject aURL( aNewFile );
+ if ( INetProtocol::NotValid == aURL.GetProtocol() )
+ {
+ OUString sURL;
+ if ( osl::FileBase::getFileURLFromSystemPath( aNewFile, sURL )
+ == osl::FileBase::E_None )
+ aURL = INetURLObject( sURL );
+ }
+ if ( INetProtocol::File == aURL.GetProtocol() )
+ {
+ try
+ {
+ bRealExtensions = !_pDialog->ContentIsFolder( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+ catch( const css::uno::Exception& )
+ {
+ SAL_INFO( "fpicker.office", "Exception in lcl_autoUpdateFileExtension" );
+ }
+ }
+ }
+
+ if ( bRealExtensions )
+ {
+ SetFsysExtension_Impl( aNewFile, _pDialog->GetDefaultExt() );
+ _pDialog->setCurrentFileText( aNewFile );
+ }
+ }
+
+#if defined( UNX )
+ bool lcl_getHomeDirectory( const OUString& _rForURL, OUString& /* [out] */ _rHomeDir )
+ {
+ _rHomeDir.clear();
+
+ // now ask the content broker for a provider for this scheme
+
+ try
+ {
+ // get the provider for the current scheme
+ Reference< XContentProvider > xProvider(
+ UniversalContentBroker::create(
+ comphelper::getProcessComponentContext() )->
+ queryContentProvider( _rForURL ) );
+
+ SAL_WARN_IF( !xProvider.is(), "fpicker.office", "lcl_getHomeDirectory: could not find a (valid) content provider for the current URL!" );
+ Reference< XPropertySet > xProviderProps( xProvider, UNO_QUERY );
+ if ( xProviderProps.is() )
+ {
+ Reference< XPropertySetInfo > xPropInfo = xProviderProps->getPropertySetInfo();
+ static const OUStringLiteral sHomeDirPropertyName( u"HomeDirectory" );
+ if ( !xPropInfo.is() || xPropInfo->hasPropertyByName( sHomeDirPropertyName ) )
+ {
+ OUString sHomeDirectory;
+ xProviderProps->getPropertyValue( sHomeDirPropertyName ) >>= sHomeDirectory;
+ _rHomeDir = sHomeDirectory;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "fpicker", "lcl_getHomeDirectory" );
+ }
+ return !_rHomeDir.isEmpty();
+ }
+#endif
+
+ OUString lcl_ensureFinalSlash( std::u16string_view _rDir )
+ {
+ INetURLObject aWorkPathObj( _rDir, INetProtocol::File );
+ aWorkPathObj.setFinalSlash();
+ return aWorkPathObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+
+
+ /** retrieves the value of an environment variable
+ @return <TRUE/> if and only if the retrieved string value is not empty
+ */
+ bool getEnvironmentValue( const char* _pAsciiEnvName, OUString& _rValue )
+ {
+ _rValue.clear();
+ OUString sEnvName = OUString::createFromAscii( _pAsciiEnvName );
+ osl_getEnvironment( sEnvName.pData, &_rValue.pData );
+ return !_rValue.isEmpty();
+ }
+}
+
+// SvtFileDialog
+SvtFileDialog::SvtFileDialog(weld::Window* pParent, PickerFlags nStyle)
+ : SvtFileDialog_Base(pParent, "fps/ui/explorerfiledialog.ui", "ExplorerFileDialog")
+ , m_xCbReadOnly(m_xBuilder->weld_check_button("readonly"))
+ , m_xCbLinkBox(m_xBuilder->weld_check_button("link"))
+ , m_xCbPreviewBox(m_xBuilder->weld_check_button("cb_preview"))
+ , m_xCbSelection(m_xBuilder->weld_check_button("selection"))
+ , m_xPbPlay(m_xBuilder->weld_button("play"))
+ , m_xPreviewFrame(m_xBuilder->weld_widget("previewframe"))
+ , m_xPrevBmp(m_xBuilder->weld_image("preview"))
+ , m_pFileNotifier(nullptr)
+ , m_xImpl(new SvtExpFileDlg_Impl)
+ , m_nPickerFlags(nStyle)
+ , m_bIsInExecute(false)
+ , m_bInExecuteAsync(false)
+ , m_bHasFilename(false)
+{
+ m_xImpl->m_xCbOptions = m_xBuilder->weld_check_button("options");
+ m_xImpl->m_xFtFileName = m_xBuilder->weld_label("file_name_label");
+ m_xImpl->m_xEdFileName.reset(new SvtURLBox(m_xBuilder->weld_combo_box("file_name")));
+ m_xImpl->m_xFtFileType = m_xBuilder->weld_label("file_type_label");
+ m_xImpl->m_xLbFilter = m_xBuilder->weld_combo_box("file_type");
+ m_xImpl->m_xEdCurrentPath.reset(new SvtURLBox(m_xBuilder->weld_combo_box("current_path")));
+ m_xImpl->m_xBtnFileOpen = m_xBuilder->weld_button("open");
+ m_xImpl->m_xBtnCancel = m_xBuilder->weld_button("cancel");
+ m_xImpl->m_xBtnHelp = m_xBuilder->weld_button("help");
+ m_xImpl->m_xBtnConnectToServer = m_xBuilder->weld_button("connect_to_server");
+ m_xImpl->m_xBtnNewFolder = m_xBuilder->weld_button("new_folder");
+ m_xImpl->m_xCbPassword = m_xBuilder->weld_check_button("password");
+ m_xImpl->m_xCbGPGEncrypt = m_xBuilder->weld_check_button("gpgencrypt");
+ m_xImpl->m_xCbAutoExtension = m_xBuilder->weld_check_button("extension");
+ m_xImpl->m_xSharedLabel = m_xBuilder->weld_label("shared_label");
+ m_xImpl->m_xSharedListBox = m_xBuilder->weld_combo_box("shared");
+
+ // because the "<All Formats> (*.bmp,*...)" entry is too wide,
+ // we need to disable the auto width feature of the filter box
+ int nWidth = m_xImpl->m_xLbFilter->get_approximate_digit_width() * 60;
+ m_xImpl->m_xSharedListBox->set_size_request(nWidth, -1);
+ m_xImpl->m_xLbFilter->set_size_request(nWidth, -1);
+
+ m_xImpl->m_xBtnUp.reset(new SvtUpButton_Impl(m_xBuilder->weld_toolbar("up_bar"),
+ m_xBuilder->weld_menu("up_menu"),
+ this));
+ m_xImpl->m_xBtnUp->set_help_id(HID_FILEOPEN_LEVELUP);
+ m_xImpl->m_xBtnUp->show();
+
+ m_xImpl->m_nStyle = nStyle;
+ m_xImpl->m_eMode = ( nStyle & PickerFlags::SaveAs ) ? FILEDLG_MODE_SAVE : FILEDLG_MODE_OPEN;
+ m_xImpl->m_eDlgType = FILEDLG_TYPE_FILEDLG;
+
+ if (nStyle & PickerFlags::PathDialog)
+ m_xImpl->m_eDlgType = FILEDLG_TYPE_PATHDLG;
+
+ // Set the directory for the "back to the default dir" button
+ INetURLObject aStdDirObj( SvtPathOptions().GetWorkPath() );
+ SetStandardDir( aStdDirObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ // Create control element, the order defines the tab control.
+ m_xImpl->m_xEdFileName->connect_changed( LINK( this, SvtFileDialog, EntrySelectHdl_Impl ) );
+ m_xImpl->m_xEdFileName->connect_entry_activate( LINK( this, SvtFileDialog, OpenUrlHdl_Impl ) );
+
+ // in folder picker mode, only auto-complete directories (no files)
+ bool bIsFolderPicker = m_xImpl->m_eDlgType == FILEDLG_TYPE_PATHDLG;
+ m_xImpl->m_xEdFileName->SetOnlyDirectories( bIsFolderPicker );
+
+ // in save mode, don't use the autocompletion as selection in the edit part
+ bool bSaveMode = FILEDLG_MODE_SAVE == m_xImpl->m_eMode;
+ m_xImpl->m_xEdFileName->SetNoURLSelection( bSaveMode );
+
+ if (nStyle & PickerFlags::MultiSelection)
+ m_xImpl->m_bMultiSelection = true;
+
+ m_xContainer = m_xBuilder->weld_container("container");
+ m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * 95, -1);
+
+ m_xFileView.reset(new SvtFileView(m_xDialog.get(),
+ m_xBuilder->weld_tree_view("fileview"),
+ m_xBuilder->weld_icon_view("iconview"),
+ FILEDLG_TYPE_PATHDLG == m_xImpl->m_eDlgType,
+ m_xImpl->m_bMultiSelection));
+ m_xFileView->set_help_id( HID_FILEDLG_STANDARD );
+
+ if ( nStyle & PickerFlags::ReadOnly )
+ {
+ m_xCbReadOnly->set_help_id( HID_FILEOPEN_READONLY );
+ m_xCbReadOnly->set_label( FpsResId( STR_SVT_FILEPICKER_READONLY ) );
+ m_xCbReadOnly->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xCbReadOnly->show();
+ }
+
+ if ( nStyle & PickerFlags::Password )
+ {
+ m_xImpl->m_xCbPassword->set_label( FpsResId( STR_SVT_FILEPICKER_PASSWORD ) );
+ m_xImpl->m_xCbPassword->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xImpl->m_xCbPassword->show();
+ m_xImpl->m_xCbGPGEncrypt->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xImpl->m_xCbGPGEncrypt->show();
+ }
+
+ // set the ini file for extracting the size
+ m_xImpl->m_aIniKey = "FileDialog";
+
+ AddControls_Impl( );
+
+ // adjust the labels to the mode
+ TranslateId pResId = STR_EXPLORERFILE_OPEN;
+ TranslateId pButtonResId;
+
+ if ( nStyle & PickerFlags::SaveAs )
+ {
+ pResId = STR_EXPLORERFILE_SAVE;
+ pButtonResId = STR_EXPLORERFILE_BUTTONSAVE;
+ }
+
+ if ( nStyle & PickerFlags::PathDialog )
+ {
+ m_xImpl->m_xFtFileName->set_label( FpsResId( STR_PATHNAME ) );
+ pResId = STR_PATHSELECT;
+ pButtonResId = STR_BUTTONSELECT;
+ }
+
+ m_xDialog->set_title(FpsResId(pResId));
+
+ if ( pButtonResId )
+ m_xImpl->m_xBtnFileOpen->set_label( FpsResId( pButtonResId ) );
+
+ if ( FILEDLG_TYPE_FILEDLG != m_xImpl->m_eDlgType )
+ {
+ m_xImpl->m_xFtFileType->hide();
+ m_xImpl->GetFilterListControl()->hide();
+ }
+
+ // Setting preferences of the control elements.
+ m_xImpl->m_xBtnNewFolder->connect_clicked( LINK( this, SvtFileDialog, NewFolderHdl_Impl ) );
+ m_xImpl->m_xBtnFileOpen->connect_clicked( LINK( this, SvtFileDialog, OpenClickHdl_Impl ) );
+ m_xImpl->m_xBtnCancel->connect_clicked( LINK( this, SvtFileDialog, CancelHdl_Impl ) );
+ m_xImpl->SetFilterListSelectHdl( LINK( this, SvtFileDialog, FilterSelectHdl_Impl ) );
+ m_xImpl->m_xEdFileName->connect_focus_in( LINK( this, SvtFileDialog, FileNameGetFocusHdl_Impl ) );
+ m_xImpl->m_xEdFileName->connect_changed( LINK( this, SvtFileDialog, FileNameModifiedHdl_Impl ) );
+ m_xImpl->m_xEdCurrentPath->connect_entry_activate( LINK( this, SvtFileDialog, URLBoxModifiedHdl_Impl ) );
+ m_xImpl->m_xBtnConnectToServer->connect_clicked( LINK ( this, SvtFileDialog, ConnectToServerPressed_Hdl ) );
+
+ m_xFileView->SetSelectHdl( LINK( this, SvtFileDialog, SelectHdl_Impl ) );
+ m_xFileView->SetDoubleClickHdl( LINK( this, SvtFileDialog, DblClickHdl_Impl ) );
+ m_xFileView->SetOpenDoneHdl( LINK( this, SvtFileDialog, OpenDoneHdl_Impl ) );
+
+ // set timer for the filterbox travel
+ m_xImpl->m_aFilterIdle.SetPriority(TaskPriority::LOWEST);
+ m_xImpl->m_aFilterIdle.SetInvokeHandler( LINK( this, SvtFileDialog, FilterSelectTimerHdl_Impl ) );
+
+ if ( PickerFlags::SaveAs & nStyle )
+ {
+ // different help ids if in save-as mode
+ m_xDialog->set_help_id( HID_FILESAVE_DIALOG );
+
+ m_xImpl->m_xEdFileName->set_help_id( HID_FILESAVE_FILEURL );
+ m_xImpl->m_xBtnFileOpen->set_help_id( HID_FILESAVE_DOSAVE );
+ m_xImpl->m_xBtnNewFolder->set_help_id( HID_FILESAVE_CREATEDIRECTORY );
+ m_xImpl->m_xBtnUp->set_help_id( HID_FILESAVE_LEVELUP );
+ m_xImpl->GetFilterListControl()->set_help_id( HID_FILESAVE_FILETYPE );
+ m_xFileView->set_help_id( HID_FILESAVE_FILEVIEW );
+
+ // formerly, there was only _pLbFileVersion, which was used for 3 different
+ // use cases. For reasons of maintainability, I introduced extra members (_pLbTemplates, _pLbImageTemplates)
+ // for the extra use cases, and separated _pLbFileVersion
+ // I did not find out in which cases the help ID is really needed HID_FILESAVE_TEMPLATE - all
+ // tests I made lead to a dialog where _no_ of the three list boxes was present.
+ if (m_xImpl->m_xSharedListBox)
+ m_xImpl->m_xSharedListBox->set_help_id( HID_FILESAVE_TEMPLATE );
+
+ if ( m_xImpl->m_xCbPassword ) m_xImpl->m_xCbPassword->set_help_id( HID_FILESAVE_SAVEWITHPASSWORD );
+ if ( m_xImpl->m_xCbAutoExtension ) m_xImpl->m_xCbAutoExtension->set_help_id( HID_FILESAVE_AUTOEXTENSION );
+ if ( m_xImpl->m_xCbOptions ) m_xImpl->m_xCbOptions->set_help_id( HID_FILESAVE_CUSTOMIZEFILTER );
+ if ( m_xCbSelection ) m_xCbSelection->set_help_id( HID_FILESAVE_SELECTION );
+ }
+
+ /// read our settings from the configuration
+ m_aConfiguration = OConfigurationTreeRoot::createWithComponentContext(
+ ::comphelper::getProcessComponentContext(),
+ "/org.openoffice.Office.UI/FilePicker"
+ );
+
+ m_xDialog->connect_size_allocate(LINK(this, SvtFileDialog, SizeAllocHdl));
+ SizeAllocHdl(Size());
+
+ m_xImpl->m_xEdFileName->grab_focus();
+}
+
+SvtFileDialog::~SvtFileDialog()
+{
+ if (!m_xImpl->m_aIniKey.isEmpty())
+ {
+ // save window state
+ SvtViewOptions aDlgOpt( EViewType::Dialog, m_xImpl->m_aIniKey );
+ aDlgOpt.SetWindowState(OStringToOUString(m_xDialog->get_window_state(WindowStateMask::All), RTL_TEXTENCODING_UTF8));
+ OUString sUserData = m_xFileView->GetConfigString();
+ aDlgOpt.SetUserItem( "UserData",
+ Any( sUserData ) );
+ }
+
+ m_xFileView->SetSelectHdl(Link<SvtFileView*,void>());
+
+ // Save bookmarked places
+ if (!m_xImpl->m_xPlaces->IsUpdated())
+ return;
+
+ const std::vector<PlacePtr> aPlaces = m_xImpl->m_xPlaces->GetPlaces();
+ Sequence< OUString > placesUrlsList(m_xImpl->m_xPlaces->GetNbEditablePlaces());
+ auto placesUrlsListRange = asNonConstRange(placesUrlsList);
+ Sequence< OUString > placesNamesList(m_xImpl->m_xPlaces->GetNbEditablePlaces());
+ auto placesNamesListRange = asNonConstRange(placesNamesList);
+ int i(0);
+ for (auto const& place : aPlaces)
+ {
+ if(place->IsEditable()) {
+ placesUrlsListRange[i] = place->GetUrl();
+ placesNamesListRange[i] = place->GetName();
+ ++i;
+ }
+ }
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Misc::FilePickerPlacesUrls::set(placesUrlsList, batch);
+ officecfg::Office::Common::Misc::FilePickerPlacesNames::set(placesNamesList, batch);
+ batch->commit();
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, NewFolderHdl_Impl, weld::Button&, void)
+{
+ m_xFileView->EndInplaceEditing();
+
+ SmartContent aContent( m_xFileView->GetViewURL( ) );
+ OUString aTitle;
+ aContent.getTitle( aTitle );
+ QueryFolderNameDialog aDlg(m_xDialog.get(), aTitle, FpsResId(STR_SVT_NEW_FOLDER));
+ bool bHandled = false;
+
+ while ( !bHandled )
+ {
+ if (aDlg.run() == RET_OK)
+ {
+ OUString aUrl = aContent.createFolder(aDlg.GetName());
+ if ( !aUrl.isEmpty( ) )
+ {
+ m_xFileView->CreatedFolder(aUrl, aDlg.GetName());
+ bHandled = true;
+ }
+ }
+ else
+ bHandled = true;
+ }
+}
+
+void SvtFileDialog::createNewUserFilter( const OUString& _rNewFilter )
+{
+ // delete the old user filter and create a new one
+ m_xImpl->m_xUserFilter.reset( new SvtFileDialogFilter_Impl( _rNewFilter, _rNewFilter ) );
+
+ // remember the extension
+ bool bIsAllFiles = _rNewFilter == FILEDIALOG_FILTER_ALL;
+ if ( bIsAllFiles )
+ EraseDefaultExt();
+ else
+ SetDefaultExt( _rNewFilter.copy( 2 ) );
+ // TODO: this is nonsense. In the whole file there are a lot of places where we assume that a user filter
+ // is always "*.<something>". But changing this would take some more time than I have now...
+
+ // now, the default extension is set to the one of the user filter (or empty)
+ if ( m_xImpl->GetCurFilter( ) )
+ SetDefaultExt( m_xImpl->GetCurFilter( )->GetExtension() );
+ else
+ EraseDefaultExt();
+}
+
+
+AdjustFilterFlags SvtFileDialog::adjustFilter( const OUString& rFilter )
+{
+ AdjustFilterFlags nReturn = AdjustFilterFlags::NONE;
+
+ const bool bNonEmpty = !rFilter.isEmpty();
+ if ( bNonEmpty )
+ {
+ nReturn |= AdjustFilterFlags::NonEmpty;
+
+ bool bFilterChanged = true;
+
+ // search for a corresponding filter
+ SvtFileDialogFilter_Impl* pFilter = FindFilter_Impl( rFilter, false, bFilterChanged );
+
+ // look for multi-ext filters if necessary
+ if ( !pFilter )
+ pFilter = FindFilter_Impl( rFilter, true, bFilterChanged );
+
+ if ( bFilterChanged )
+ nReturn |= AdjustFilterFlags::Changed;
+
+ if ( !pFilter )
+ {
+ nReturn |= AdjustFilterFlags::UserFilter;
+ // no filter found : use it as user defined filter
+ createNewUserFilter( rFilter );
+ }
+ }
+
+ return nReturn;
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, CancelHdl_Impl, weld::Button&, void)
+{
+ if ( m_pCurrentAsyncAction.is() )
+ {
+ m_pCurrentAsyncAction->cancel();
+ onAsyncOperationFinished();
+ }
+ else
+ {
+ m_xDialog->response(RET_CANCEL);
+ }
+}
+
+IMPL_LINK( SvtFileDialog, OpenClickHdl_Impl, weld::Button&, rVoid, void )
+{
+ OpenHdl_Impl(&rVoid);
+}
+
+IMPL_LINK( SvtFileDialog, OpenUrlHdl_Impl, weld::ComboBox&, rVoid, bool )
+{
+ OpenHdl_Impl(&rVoid);
+ return true;
+}
+
+void SvtFileDialog::OpenHdl_Impl(void const * pVoid)
+{
+ if ( m_xImpl->m_bMultiSelection && m_xFileView->GetSelectionCount() > 1 )
+ {
+ // special open in case of multiselection
+ OpenMultiSelection_Impl();
+ return;
+ }
+
+ OUString aFileName;
+ OUString aOldPath(m_xFileView->GetViewURL());
+ if ( m_xImpl->m_bDoubleClick || m_xFileView->has_focus() )
+ {
+ // Selection done by doubleclicking in the view, get filename from the view
+ aFileName = m_xFileView->GetCurrentURL();
+ }
+
+ if ( aFileName.isEmpty() )
+ {
+ // if an entry is selected in the view...
+ if ( m_xFileView->GetSelectionCount() )
+ { // -> use this one. This will allow us to step down this folder
+ aFileName = m_xFileView->GetCurrentURL();
+ }
+ }
+
+ if ( aFileName.isEmpty() )
+ {
+ // get the URL from the edit field ( if not empty )
+ if ( !m_xImpl->m_xEdFileName->get_active_text().isEmpty() )
+ {
+ OUString aText = m_xImpl->m_xEdFileName->get_active_text();
+
+ // did we reach the root?
+ if ( !INetURLObject( aOldPath ).getSegmentCount() )
+ {
+ if ( ( aText.getLength() == 2 && aText == ".." ) ||
+ ( aText.getLength() == 3 && ( aText == "..\\" || aText == "../" ) ) )
+ // don't go higher than the root
+ return;
+ }
+
+#if defined( UNX )
+ if ( ( 1 == aText.getLength() ) && ( '~' == aText[0] ) )
+ {
+ // go to the home directory
+ if ( lcl_getHomeDirectory( m_xFileView->GetViewURL(), aFileName ) )
+ // in case we got a home dir, reset the text of the edit
+ m_xImpl->m_xEdFileName->set_entry_text( OUString() );
+ }
+ if ( aFileName.isEmpty() )
+#endif
+ {
+ // get url from autocomplete edit
+ aFileName = m_xImpl->m_xEdFileName->GetURL();
+ }
+ }
+ else if ( pVoid == m_xImpl->m_xBtnFileOpen.get() )
+ // OpenHdl was called for the "Open" Button; if edit field is empty, use selected element in the view
+ aFileName = m_xFileView->GetCurrentURL();
+ }
+
+ // MBA->PB: ?!
+ if ( aFileName.isEmpty() && pVoid == m_xImpl->m_xEdFileName.get() && m_xImpl->m_xUserFilter )
+ {
+ m_xImpl->m_xUserFilter.reset();
+ return;
+ }
+
+ sal_Int32 nLen = aFileName.getLength();
+ if ( !nLen )
+ {
+ // if the dialog was opened to select a folder, the last selected folder should be selected
+ if( m_xImpl->m_eDlgType == FILEDLG_TYPE_PATHDLG )
+ {
+ aFileName = m_xImpl->m_xEdCurrentPath->get_active_text();
+ nLen = aFileName.getLength();
+ }
+ else
+ // no file selected !
+ return;
+ }
+
+ // mark input as selected
+ m_xImpl->m_xEdFileName->select_entry_region(0, nLen);
+
+ // if a path with wildcards is given, divide the string into path and wildcards
+ OUString aFilter;
+ if ( !SvtFileDialog::IsolateFilterFromPath_Impl( aFileName, aFilter ) )
+ return;
+
+ // if a filter was retrieved, there were wildcards !
+ AdjustFilterFlags nNewFilterFlags = adjustFilter( aFilter );
+ if ( nNewFilterFlags & AdjustFilterFlags::Changed )
+ {
+ // cut off all text before wildcard in edit and select wildcard
+ m_xImpl->m_xEdFileName->set_entry_text( aFilter );
+ m_xImpl->m_xEdFileName->select_entry_region(0, -1);
+ }
+
+ {
+ INetURLObject aFileObject( aFileName );
+ if ( ( aFileObject.GetProtocol() == INetProtocol::NotValid ) && !aFileName.isEmpty() )
+ {
+ OUString sCompleted = SvtURLBox::ParseSmart( aFileName, m_xFileView->GetViewURL() );
+ if ( !sCompleted.isEmpty() )
+ aFileName = sCompleted;
+ }
+ }
+
+ // check if it is a folder
+ bool bIsFolder = false;
+
+ // first thing before doing anything with the content: Reset it. When the user presses "open" (or "save" or "export",
+ // for that matter), s/he wants the complete handling, including all possible error messages, even if s/he
+ // does the same thing for the same content twice, s/he wants both fails to be displayed.
+ // Without the reset, it could be that the content cached all relevant information, and will not display any
+ // error messages for the same content a second time...
+ m_aContent.bindTo( OUString( ) );
+
+ if ( !aFileName.isEmpty() )
+ {
+ // Make sure we have own Interaction Handler in place. We do not need
+ // to intercept interactions here, but to record the fact that there
+ // was an interaction.
+ SmartContent::InteractionHandlerType eInterActionHandlerType
+ = m_aContent.queryCurrentInteractionHandler();
+ if ( ( eInterActionHandlerType == SmartContent::IHT_NONE ) ||
+ ( eInterActionHandlerType == SmartContent::IHT_DEFAULT ) )
+ m_aContent.enableOwnInteractionHandler(
+ OFilePickerInteractionHandler::E_NOINTERCEPTION );
+
+ bIsFolder = m_aContent.isFolder( aFileName );
+
+ // access denied to the given resource - and interaction was already
+ // used => break following operations
+ OFilePickerInteractionHandler* pHandler
+ = m_aContent.getOwnInteractionHandler();
+
+ OSL_ENSURE( pHandler, "Got no Interaction Handler!!!" );
+
+ if ( pHandler->wasAccessDenied() )
+ return;
+
+ if ( m_aContent.isInvalid() &&
+ ( m_xImpl->m_eMode == FILEDLG_MODE_OPEN ) )
+ {
+ if ( !pHandler->wasUsed() )
+ ErrorHandler::HandleError( ERRCODE_IO_NOTEXISTS );
+
+ return;
+ }
+
+ // restore previous Interaction Handler
+ if ( eInterActionHandlerType == SmartContent::IHT_NONE )
+ m_aContent.disableInteractionHandler();
+ else if ( eInterActionHandlerType == SmartContent::IHT_DEFAULT )
+ m_aContent.enableDefaultInteractionHandler();
+ }
+
+ if ( !bIsFolder // no existent folder
+ && m_xImpl->m_xCbAutoExtension // auto extension is enabled in general
+ && m_xImpl->m_xCbAutoExtension->get_active()// auto extension is really to be used
+ && !GetDefaultExt().isEmpty() // there is a default extension
+ && GetDefaultExt() != "*" // the default extension is not "all"
+ && !( FILEDLG_MODE_SAVE == m_xImpl->m_eMode // we're saving a file
+ && m_xFileView->GetSelectionCount() // there is a selected file in the file view -> it will later on
+ ) // (in SvtFileDialog::GetPathList) be taken as file to save to
+
+ && FILEDLG_MODE_OPEN != m_xImpl->m_eMode // #i83408# don't append extension on open
+ )
+ {
+ // check extension and append the default extension if necessary
+ appendDefaultExtension(aFileName,
+ GetDefaultExt(),
+ m_xImpl->GetCurFilter()->GetType());
+ }
+
+ bool bOpenFolder = ( FILEDLG_TYPE_PATHDLG == m_xImpl->m_eDlgType ) &&
+ !m_xImpl->m_bDoubleClick && pVoid != m_xImpl->m_xEdFileName.get();
+ if ( bIsFolder )
+ {
+ if ( bOpenFolder )
+ {
+ m_aPath = aFileName;
+ }
+ else
+ {
+ if ( aFileName != m_xFileView->GetViewURL() )
+ {
+ OpenURL_Impl( aFileName );
+ }
+ else
+ {
+ if ( nNewFilterFlags & AdjustFilterFlags::Changed )
+ ExecuteFilter();
+ }
+
+ return;
+ }
+ }
+ else if ( !( nNewFilterFlags & AdjustFilterFlags::NonEmpty ) )
+ {
+ // if applicable save URL
+ m_aPath = aFileName;
+ }
+ else
+ {
+ // if applicable filter again
+ if ( nNewFilterFlags & AdjustFilterFlags::Changed )
+ ExecuteFilter();
+ return;
+ }
+
+ INetURLObject aFileObj( aFileName );
+ if ( aFileObj.HasError() )
+ {
+ ErrorHandler::HandleError( ERRCODE_IO_GENERAL );
+ return;
+ }
+
+ switch (m_xImpl->m_eMode)
+ {
+ case FILEDLG_MODE_SAVE:
+ {
+ if ( ::utl::UCBContentHelper::Exists( aFileObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
+ {
+ OUString aMsg = FpsResId(STR_SVT_ALREADYEXISTOVERWRITE);
+ aMsg = aMsg.replaceFirst(
+ "$filename$",
+ aFileObj.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset)
+ );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo, aMsg));
+ if (xBox->run() != RET_YES)
+ return;
+ }
+ else
+ {
+ OUString aCurPath;
+ if (osl::FileBase::getSystemPathFromFileURL(aFileName, aCurPath) == osl::FileBase::E_None)
+ {
+ // if content does not exist: at least its path must exist
+ INetURLObject aPathObj = aFileObj;
+ aPathObj.removeSegment();
+ bool bFolder = m_aContent.isFolder( aPathObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ if ( !bFolder )
+ {
+ ErrorHandler::HandleError( ERRCODE_IO_NOTEXISTSPATH );
+ return;
+ }
+ }
+ }
+ }
+ break;
+
+ case FILEDLG_MODE_OPEN:
+ {
+ // do an existence check herein, again
+
+ if ( INetProtocol::File == aFileObj.GetProtocol( ) )
+ {
+ bool bExists = m_aContent.is( aFileObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ if ( !bExists )
+ {
+ OUString sError(FpsResId(RID_FILEOPEN_NOTEXISTENTFILE));
+
+ OUString sInvalidFile( aFileObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) );
+ if ( INetProtocol::File == aFileObj.GetProtocol() )
+ { // if it's a file URL, transform the URL into system notation
+ OUString sURL( sInvalidFile );
+ OUString sSystem;
+ osl_getSystemPathFromFileURL( sURL.pData, &sSystem.pData );
+ sInvalidFile = sSystem;
+ }
+ sError = sError.replaceFirst( "$name$", sInvalidFile );
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok, sError));
+ xBox->run();
+ return;
+ }
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL("SvtFileDialog, OpenHdl_Impl: invalid mode!");
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+void SvtFileDialog::EnableAutocompletion(bool bEnable)
+{
+ m_xImpl->m_xEdFileName->EnableAutocomplete(bEnable);
+}
+
+IMPL_LINK_NOARG( SvtFileDialog, FilterSelectHdl_Impl, weld::ComboBox&, void )
+{
+ OUString sSelectedFilterDisplayName;
+ SvtFileDialogFilter_Impl* pSelectedFilter = m_xImpl->GetSelectedFilterEntry( sSelectedFilterDisplayName );
+ if ( !pSelectedFilter )
+ { // there is no current selection. This happens if for instance the user selects a group separator using
+ // the keyboard, and then presses enter: When the selection happens, we immediately deselect the entry,
+ // so in this situation there is no current selection.
+ restoreCurrentFilter( m_xImpl );
+ }
+ else
+ {
+ if ( ( pSelectedFilter != m_xImpl->GetCurFilter() )
+ || m_xImpl->m_xUserFilter
+ )
+ {
+ // Store the old filter for the auto extension handling
+ OUString sLastFilterExt = m_xImpl->GetCurFilter()->GetExtension();
+ m_xImpl->m_xUserFilter.reset();
+
+ // if applicable remove filter of the user
+ m_xImpl->SetCurFilter( pSelectedFilter, sSelectedFilterDisplayName );
+
+ // if applicable show extension
+ SetDefaultExt( pSelectedFilter->GetExtension() );
+ sal_Int32 nSepPos = GetDefaultExt().indexOf( FILEDIALOG_DEF_EXTSEP );
+
+ if ( nSepPos != -1 )
+ EraseDefaultExt( nSepPos );
+
+ // update the extension of the current file if necessary
+ lcl_autoUpdateFileExtension( this, sLastFilterExt );
+
+ // if the user is traveling fast through the filterbox
+ // do not filter instantly
+ // FilterSelectHdl_Impl should be started again at idle
+ m_xImpl->m_aFilterIdle.Start();
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, FilterSelectTimerHdl_Impl, Timer*, void)
+{
+ // filter the view again
+ ExecuteFilter();
+}
+
+IMPL_LINK_NOARG( SvtFileDialog, FileNameGetFocusHdl_Impl, weld::Widget&, void )
+{
+ m_xFileView->SetNoSelection();
+}
+
+IMPL_LINK( SvtFileDialog, FileNameModifiedHdl_Impl, weld::ComboBox&, rComboBox, void )
+{
+ FileNameGetFocusHdl_Impl(rComboBox);
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, URLBoxModifiedHdl_Impl, weld::ComboBox&, bool)
+{
+ OUString aPath = m_xImpl->m_xEdCurrentPath->GetURL();
+ OpenURL_Impl(aPath);
+ return true;
+}
+
+IMPL_LINK_NOARG( SvtFileDialog, ConnectToServerPressed_Hdl, weld::Button&, void )
+{
+ m_xFileView->EndInplaceEditing();
+
+ PlaceEditDialog aDlg(m_xDialog.get());
+ short aRetCode = aDlg.run();
+
+ switch (aRetCode) {
+ case RET_OK :
+ {
+ PlacePtr newPlace = aDlg.GetPlace();
+ m_xImpl->m_xPlaces->AppendPlace(newPlace);
+
+ break;
+ }
+ case RET_CANCEL :
+ default :
+ // Do Nothing
+ break;
+ }
+}
+
+IMPL_LINK_NOARG ( SvtFileDialog, AddPlacePressed_Hdl, weld::Button&, void )
+{
+ // Maybe open the PlacesDialog would have been a better idea
+ // there is an ux choice to make we did not make...
+ INetURLObject aURLObj( m_xFileView->GetViewURL() );
+ PlacePtr newPlace =
+ std::make_shared<Place>( aURLObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset),
+ m_xFileView->GetViewURL(), true);
+ m_xImpl->m_xPlaces->AppendPlace(newPlace);
+}
+
+IMPL_LINK_NOARG ( SvtFileDialog, RemovePlacePressed_Hdl, weld::Button&, void )
+{
+ m_xImpl->m_xPlaces->RemoveSelectedPlace();
+}
+
+SvtFileDialogFilter_Impl* SvtFileDialog::FindFilter_Impl
+(
+ const OUString& rFilter,
+ bool bMultiExt,/* TRUE - regard filter with several extensions
+ FALSE - do not ...
+ */
+ bool& rFilterChanged
+)
+
+/* [Description]
+
+ This method looks for the specified extension in the included filters.
+*/
+
+{
+ SvtFileDialogFilter_Impl* pFoundFilter = nullptr;
+ SvtFileDialogFilterList_Impl& rList = m_xImpl->m_aFilter;
+ sal_uInt16 nFilter = rList.size();
+
+ while ( nFilter-- )
+ {
+ SvtFileDialogFilter_Impl* pFilter = rList[ nFilter ].get();
+ const OUString& rType = pFilter->GetType();
+
+ if ( bMultiExt )
+ {
+ sal_Int32 nIdx = 0;
+ while ( !pFoundFilter && nIdx != -1 )
+ {
+ const OUString aSingleType = rType.getToken( 0, FILEDIALOG_DEF_EXTSEP, nIdx );
+#ifdef UNX
+ if ( aSingleType == rFilter )
+#else
+ if ( aSingleType.equalsIgnoreAsciiCase( rFilter ) )
+#endif
+ pFoundFilter = pFilter;
+ }
+ }
+#ifdef UNX
+ else if ( rType == rFilter )
+#else
+ else if ( rType.equalsIgnoreAsciiCase( rFilter ) )
+#endif
+ pFoundFilter = pFilter;
+
+ if ( pFoundFilter )
+ {
+ // activate filter
+ rFilterChanged = m_xImpl->m_xUserFilter || ( m_xImpl->GetCurFilter() != pFilter );
+
+ createNewUserFilter( rFilter );
+
+ break;
+ }
+ }
+ return pFoundFilter;
+}
+
+
+void SvtFileDialog::ExecuteFilter()
+{
+ executeAsync( AsyncPickerAction::eExecuteFilter, OUString(), getMostCurrentFilter(m_xImpl) );
+}
+
+/* [Description]
+
+ OpenHandler for MultiSelection
+*/
+void SvtFileDialog::OpenMultiSelection_Impl()
+{
+ SvtContentEntry* pEntry = m_xFileView->FirstSelected();
+
+ if (pEntry)
+ m_aPath = pEntry->maURL;
+
+ m_xDialog->response(RET_OK);
+}
+
+void SvtFileDialog::UpdateControls( const OUString& rURL )
+{
+ m_xImpl->m_xEdFileName->SetBaseURL( rURL );
+
+ INetURLObject aObj( rURL );
+
+ {
+ OUString sText;
+ SAL_WARN_IF( INetProtocol::NotValid == aObj.GetProtocol(), "fpicker.office", "SvtFileDialog::UpdateControls: Invalid URL!" );
+
+ if ( aObj.getSegmentCount() )
+ {
+ osl::FileBase::getSystemPathFromFileURL(rURL, sText);
+ if ( !sText.isEmpty() )
+ {
+ // no Fsys path for server file system ( only UCB has mountpoints! )
+ if ( INetProtocol::File != aObj.GetProtocol() )
+ sText = rURL.copy( INetURLObject::GetScheme( aObj.GetProtocol() ).getLength() );
+ }
+
+ if ( sText.isEmpty() && aObj.getSegmentCount() )
+ sText = rURL;
+ }
+
+ // path mode ?
+ if ( FILEDLG_TYPE_PATHDLG == m_xImpl->m_eDlgType )
+ // -> set new path in the edit field
+ m_xImpl->m_xEdFileName->set_entry_text( sText );
+
+ // in the "current path" field, truncate the trailing slash
+ if ( aObj.hasFinalSlash() )
+ {
+ aObj.removeFinalSlash();
+ OUString sURL( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ if (osl::FileBase::getSystemPathFromFileURL(sURL, sText) != osl::FileBase::E_None)
+ sText = sURL;
+ }
+
+ if ( sText.isEmpty() && !rURL.isEmpty() )
+ // happens, for instance, for URLs which the INetURLObject does not know to belong to a hierarchical scheme
+ sText = rURL;
+ m_xImpl->m_xEdCurrentPath->set_entry_text(sText);
+ }
+
+ m_aPath = rURL;
+
+ m_xImpl->m_xBtnUp->FillURLMenu();
+
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify( DIRECTORY_CHANGED, 0 );
+}
+
+IMPL_LINK( SvtFileDialog, SelectHdl_Impl, SvtFileView*, pBox, void )
+{
+ SvtContentEntry* pUserData = pBox->FirstSelected();
+ if (pUserData)
+ {
+ INetURLObject aObj( pUserData->maURL );
+ if ( FILEDLG_TYPE_PATHDLG == m_xImpl->m_eDlgType )
+ {
+ if ( aObj.GetProtocol() == INetProtocol::File )
+ {
+ if ( !pUserData->mbIsFolder )
+ aObj.removeSegment();
+ OUString aName = aObj.getFSysPath( static_cast<FSysStyle>(FSysStyle::Detect & ~FSysStyle::Vos) );
+ m_xImpl->m_xEdFileName->set_entry_text( aName );
+ m_xImpl->m_xEdFileName->select_entry_region(0, -1);
+ m_aPath = pUserData->maURL;
+ }
+ else if ( !pUserData->mbIsFolder )
+ {
+ m_xImpl->m_xEdFileName->set_entry_text( pUserData->maURL );
+ m_xImpl->m_xEdFileName->select_entry_region(0, -1);
+ m_aPath = pUserData->maURL;
+ }
+ else
+ m_xImpl->m_xEdFileName->set_entry_text( OUString() );
+ }
+ else
+ {
+ if ( !pUserData->mbIsFolder )
+ {
+ OUString aName = pBox->get_selected_text();
+ m_xImpl->m_xEdFileName->set_entry_text( aName );
+ m_xImpl->m_xEdFileName->select_entry_region(0, -1);
+ m_aPath = pUserData->maURL;
+ }
+ }
+ }
+
+ if ( m_xImpl->m_bMultiSelection && m_xFileView->GetSelectionCount() > 1 )
+ {
+ // clear the file edit for multiselection
+ m_xImpl->m_xEdFileName->set_entry_text( OUString() );
+ }
+
+ FileSelect();
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, DblClickHdl_Impl, SvtFileView*, bool)
+{
+ m_xImpl->m_bDoubleClick = true;
+ OpenHdl_Impl( nullptr );
+ m_xImpl->m_bDoubleClick = false;
+ return true;
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, EntrySelectHdl_Impl, weld::ComboBox&, void)
+{
+ FileSelect();
+}
+
+IMPL_LINK( SvtFileDialog, OpenDoneHdl_Impl, SvtFileView*, pView, void )
+{
+ const OUString& sCurrentFolder( pView->GetViewURL() );
+ // check if we can create new folders
+ EnableControl( m_xImpl->m_xBtnNewFolder.get(), ContentCanMakeFolder( sCurrentFolder ) );
+
+ // check if we can travel one level up
+ bool bCanTravelUp = ContentHasParentFolder( pView->GetViewURL() );
+ if ( bCanTravelUp )
+ {
+ // additional check: the parent folder should not be prohibited
+ INetURLObject aCurrentFolder( sCurrentFolder );
+ SAL_WARN_IF( INetProtocol::NotValid == aCurrentFolder.GetProtocol(),
+ "fpicker.office", "SvtFileDialog::OpenDoneHdl_Impl: invalid current URL!" );
+
+ aCurrentFolder.removeSegment();
+ }
+ EnableControl( m_xImpl->m_xBtnUp->getWidget(), bCanTravelUp );
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, AutoExtensionHdl_Impl, weld::Toggleable&, void)
+{
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify(CTRL_STATE_CHANGED, CHECKBOX_AUTOEXTENSION);
+
+ // update the extension of the current file if necessary
+ lcl_autoUpdateFileExtension( this, m_xImpl->GetCurFilter()->GetExtension() );
+}
+
+IMPL_LINK( SvtFileDialog, ClickHdl_Impl, weld::Toggleable&, rCheckBox, void )
+{
+ if (!m_pFileNotifier)
+ return;
+
+ sal_Int16 nId = -1;
+
+ if ( &rCheckBox == m_xImpl->m_xCbOptions.get() )
+ nId = CHECKBOX_FILTEROPTIONS;
+ else if ( &rCheckBox == m_xCbSelection.get() )
+ nId = CHECKBOX_SELECTION;
+ else if ( &rCheckBox == m_xCbReadOnly.get() )
+ nId = CHECKBOX_READONLY;
+ else if ( &rCheckBox == m_xImpl->m_xCbPassword.get() )
+ nId = CHECKBOX_PASSWORD;
+ else if ( &rCheckBox == m_xImpl->m_xCbGPGEncrypt.get() )
+ nId = CHECKBOX_GPGENCRYPTION;
+ else if ( &rCheckBox == m_xCbLinkBox.get() )
+ nId = CHECKBOX_LINK;
+ else if ( &rCheckBox == m_xCbPreviewBox.get() )
+ nId = CHECKBOX_PREVIEW;
+
+ if ( nId != -1 )
+ m_pFileNotifier->notify( CTRL_STATE_CHANGED, nId );
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, PlayButtonHdl_Impl, weld::Button&, void)
+{
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify(CTRL_STATE_CHANGED, PUSHBUTTON_PLAY);
+}
+
+namespace
+{
+
+bool implIsInvalid( const OUString & rURL )
+{
+ SmartContent aContent( rURL );
+ aContent.enableOwnInteractionHandler( ::svt::OFilePickerInteractionHandler::E_DOESNOTEXIST );
+ aContent.isFolder(); // do this _before_ asking isInvalid! Otherwise result might be wrong.
+ return aContent.isInvalid();
+}
+
+}
+
+
+OUString SvtFileDialog::implGetInitialURL( const OUString& _rPath, std::u16string_view _rFallback )
+{
+ // a URL parser for the fallback
+ INetURLObject aURLParser;
+
+ // set the path
+ bool bWasAbsolute = false;
+ aURLParser = aURLParser.smartRel2Abs( _rPath, bWasAbsolute );
+
+ // is it a valid folder?
+ m_aContent.bindTo( aURLParser.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ bool bIsFolder = m_aContent.isFolder( ); // do this _before_ asking isInvalid!
+ bool bIsInvalid = m_aContent.isInvalid();
+
+ if ( bIsInvalid && m_bHasFilename && !aURLParser.hasFinalSlash() )
+ { // check if the parent folder exists
+ INetURLObject aParent( aURLParser );
+ aParent.removeSegment( );
+ aParent.setFinalSlash( );
+ bIsInvalid = implIsInvalid( aParent.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+
+ if ( bIsInvalid )
+ {
+ INetURLObject aFallback( _rFallback );
+ bIsInvalid = implIsInvalid( aFallback.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ if ( !bIsInvalid )
+ aURLParser = aFallback;
+ }
+
+ if ( bIsInvalid )
+ {
+ INetURLObject aParent( aURLParser );
+ while ( bIsInvalid && aParent.removeSegment() )
+ {
+ aParent.setFinalSlash( );
+ bIsInvalid = implIsInvalid( aParent.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+
+ if ( !bIsInvalid )
+ aURLParser = aParent;
+ }
+
+ if ( !bIsInvalid && bIsFolder )
+ {
+ aURLParser.setFinalSlash();
+ }
+ return aURLParser.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+}
+
+
+short SvtFileDialog::run()
+{
+ if ( !PrepareExecute() )
+ return 0;
+
+ // start the dialog
+ m_bIsInExecute = true;
+ short nResult = GenericDialogController::run();
+ m_bIsInExecute = false;
+
+ SAL_WARN_IF( m_pCurrentAsyncAction.is(), "fpicker.office", "SvtFilePicker::run: still running an async action!" );
+ // the dialog should not be cancellable while an async action is running - first, the action
+ // needs to be cancelled
+
+ // remember last directory
+ if ( RET_OK == nResult )
+ {
+ INetURLObject aURL( m_aPath );
+ if ( aURL.GetProtocol() == INetProtocol::File )
+ {
+ // remember the selected directory only for file URLs not for virtual folders
+ sal_Int32 nLevel = aURL.getSegmentCount();
+ bool bDir = m_aContent.isFolder( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ if ( nLevel > 1 && ( FILEDLG_TYPE_FILEDLG == m_xImpl->m_eDlgType || !bDir ) )
+ aURL.removeSegment();
+ }
+ }
+
+ return nResult;
+}
+
+void SvtFileDialog::onAsyncOperationStarted()
+{
+ EnableUI( false );
+ // the cancel button must be always enabled
+ m_xImpl->m_xBtnCancel->set_sensitive(true);
+ m_xImpl->m_xBtnCancel->grab_focus();
+}
+
+void SvtFileDialog::onAsyncOperationFinished()
+{
+ EnableUI( true );
+ m_pCurrentAsyncAction = nullptr;
+ if ( !m_bInExecuteAsync )
+ m_xImpl->m_xEdFileName->grab_focus();
+ // (if m_bInExecuteAsync is true, then the operation was finished within the minimum wait time,
+ // and to the user, the operation appears to be synchronous)
+}
+
+void SvtFileDialog::RemovablePlaceSelected(bool enable)
+{
+ m_xImpl->m_xPlaces->SetDelEnabled( enable );
+}
+
+void SvtFileDialog::displayIOException( const OUString& _rURL, IOErrorCode _eCode )
+{
+ try
+ {
+ // create make a human-readable string from the URL
+ OUString sDisplayPath;
+ if (osl::FileBase::getSystemPathFromFileURL(_rURL, sDisplayPath)
+ == osl::FileBase::E_None)
+ {
+ sDisplayPath = _rURL;
+ }
+
+ // build an own exception which tells "access denied"
+ InteractiveAugmentedIOException aException;
+ aException.Arguments =
+ { css::uno::Any(sDisplayPath),
+ css::uno::Any(PropertyValue(
+ "Uri",
+ -1, aException.Arguments[ 0 ], PropertyState_DIRECT_VALUE
+ )) };
+ // (formerly, it was sufficient to put the URL first parameter. Nowadays,
+ // the services expects the URL in a PropertyValue named "Uri" ...)
+ aException.Code = _eCode;
+ aException.Classification = InteractionClassification_ERROR;
+
+ // let and interaction handler handle this exception
+ rtl::Reference<::comphelper::OInteractionRequest> pRequest =
+ new ::comphelper::OInteractionRequest( Any( aException ) );
+ pRequest->addContinuation( new ::comphelper::OInteractionAbort( ) );
+
+ Reference< XInteractionHandler2 > xHandler(
+ InteractionHandler::createWithParent( ::comphelper::getProcessComponentContext(), nullptr ) );
+ xHandler->handle( pRequest );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "fpicker", "iodlg::displayIOException" );
+ }
+}
+
+void SvtFileDialog::EnableUI(bool bEnable)
+{
+ m_xDialog->set_sensitive(bEnable);
+
+ if (bEnable)
+ {
+ for (auto& rxControl : m_aDisabledControls)
+ {
+ rxControl->set_sensitive(false);
+ }
+ }
+}
+
+void SvtFileDialog::EnableControl(weld::Widget* pControl, bool bEnable)
+{
+ if (!pControl)
+ {
+ SAL_WARN( "fpicker.office", "SvtFileDialog::EnableControl: invalid control!" );
+ return;
+ }
+
+ pControl->set_sensitive(bEnable);
+
+ if (bEnable)
+ {
+ auto aPos = m_aDisabledControls.find( pControl );
+ if ( m_aDisabledControls.end() != aPos )
+ m_aDisabledControls.erase( aPos );
+ }
+ else
+ m_aDisabledControls.insert( pControl );
+}
+
+bool SvtFileDialog::PrepareExecute()
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return false;
+
+ OUString aEnvValue;
+ if ( getEnvironmentValue( "WorkDirMustContainRemovableMedia", aEnvValue ) && aEnvValue == "1" )
+ {
+ try
+ {
+ INetURLObject aStdDir( GetStandardDir() );
+ ::ucbhelper::Content aCnt( aStdDir.GetMainURL(
+ INetURLObject::DecodeMechanism::NONE ),
+ Reference< XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ Sequence< OUString > aProps { "IsVolume", "IsRemoveable" };
+
+ Reference< XResultSet > xResultSet
+ = aCnt.createCursor( aProps, ::ucbhelper::INCLUDE_FOLDERS_ONLY );
+ if ( xResultSet.is() && !xResultSet->next() )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ FpsResId(STR_SVT_NOREMOVABLEDEVICE)));
+ xBox->run();
+ return false;
+ }
+ }
+ catch ( ContentCreationException const & )
+ {
+ }
+ catch ( CommandAbortedException const & )
+ {
+ }
+ }
+
+ if ( ( m_xImpl->m_nStyle & PickerFlags::SaveAs ) && m_bHasFilename )
+ // when doing a save-as, we do not want the handler to handle "this file does not exist" messages
+ // - finally we're going to save that file, aren't we?
+ m_aContent.enableOwnInteractionHandler(::svt::OFilePickerInteractionHandler::E_DOESNOTEXIST);
+ else
+ m_aContent.enableDefaultInteractionHandler();
+
+ // possibly just a filename without a path
+ OUString aFileNameOnly;
+ if( !m_aPath.isEmpty() && (m_xImpl->m_eMode == FILEDLG_MODE_SAVE)
+ && (m_aPath.indexOf(':') == -1)
+ && (m_aPath.indexOf('\\') == -1)
+ && (m_aPath.indexOf('/') == -1))
+ {
+ aFileNameOnly = m_aPath;
+ m_aPath.clear();
+ }
+
+ // no starting path specified?
+ if ( m_aPath.isEmpty() )
+ {
+ // then use the standard directory
+ m_aPath = lcl_ensureFinalSlash( m_xImpl->GetStandardDir() );
+
+ // attach given filename to path
+ if ( !aFileNameOnly.isEmpty() )
+ m_aPath += aFileNameOnly;
+ }
+
+
+ m_aPath = implGetInitialURL( m_aPath, GetStandardDir() );
+
+ if ( m_xImpl->m_nStyle & PickerFlags::SaveAs && !m_bHasFilename )
+ // when doing a save-as, we do not want the handler to handle "this file does not exist" messages
+ // - finally we're going to save that file, aren't we?
+ m_aContent.enableOwnInteractionHandler(::svt::OFilePickerInteractionHandler::E_DOESNOTEXIST);
+
+ // if applicable show filter
+ m_xImpl->InitFilterList();
+
+ // set up initial filter
+ sal_uInt16 nFilterCount = GetFilterCount();
+ OUString aAll = FpsResId( STR_FILTERNAME_ALL );
+ bool bHasAll = m_xImpl->HasFilterListEntry( aAll );
+ if ( m_xImpl->GetCurFilter() || nFilterCount == 1 || ( nFilterCount == 2 && bHasAll ) )
+ {
+ // if applicable set the only filter or the only filter that
+ // does not refer to all files, as the current one
+ if ( !m_xImpl->GetCurFilter() )
+ {
+ sal_uInt16 nPos = 0;
+ if ( 2 == nFilterCount && bHasAll )
+ {
+ nPos = nFilterCount;
+ while ( nPos-- )
+ {
+ if ( aAll != GetFilterName( nPos ) )
+ break;
+ }
+ }
+ SvtFileDialogFilter_Impl* pNewCurFilter = m_xImpl->m_aFilter[ nPos ].get();
+ assert( pNewCurFilter && "SvtFileDialog::run: invalid filter pos!" );
+ m_xImpl->SetCurFilter( pNewCurFilter, pNewCurFilter->GetName() );
+ }
+
+ // adjust view
+ m_xImpl->SelectFilterListEntry( m_xImpl->GetCurFilter()->GetName() );
+ SetDefaultExt( m_xImpl->GetCurFilter()->GetExtension() );
+ sal_Int32 nSepPos = GetDefaultExt().indexOf( FILEDIALOG_DEF_EXTSEP );
+ if ( nSepPos != -1 )
+ EraseDefaultExt( nSepPos );
+ }
+ else
+ {
+ // if applicable set respectively create filter for all files
+ if ( !bHasAll )
+ {
+ SvtFileDialogFilter_Impl* pAllFilter = implAddFilter( aAll, FILEDIALOG_FILTER_ALL );
+ m_xImpl->InsertFilterListEntry( pAllFilter );
+ m_xImpl->SetCurFilter( pAllFilter, aAll );
+ }
+ m_xImpl->SelectFilterListEntry( aAll );
+ }
+
+ // if applicable isolate filter
+ OUString aFilter;
+
+ if ( !IsolateFilterFromPath_Impl( m_aPath, aFilter ) )
+ return false;
+
+ AdjustFilterFlags nNewFilterFlags = adjustFilter( aFilter );
+ if ( nNewFilterFlags & ( AdjustFilterFlags::NonEmpty | AdjustFilterFlags::UserFilter ) )
+ {
+ m_xImpl->m_xEdFileName->set_entry_text( aFilter );
+ }
+
+ // create and show instance for set path
+ INetURLObject aFolderURL( m_aPath );
+ OUString aFileName( aFolderURL.getName( INetURLObject::LAST_SEGMENT, false ) );
+ sal_Int32 nFileNameLen = aFileName.getLength();
+ bool bFileToSelect = nFileNameLen != 0;
+ if ( bFileToSelect && aFileName[ nFileNameLen - 1 ] != '/' )
+ {
+ OUString aDecodedName = aFolderURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+ m_xImpl->m_xEdFileName->set_entry_text( aDecodedName );
+ aFolderURL.removeSegment();
+ }
+
+ INetURLObject aObj = aFolderURL;
+ if ( aObj.GetProtocol() == INetProtocol::File )
+ {
+ // set folder as current directory
+ aObj.setFinalSlash();
+ }
+
+ UpdateControls( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ // Somebody might want to enable some controls according to the current filter
+ FilterSelect();
+
+ OpenURL_Impl( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ // if applicable read and set size from ini
+ InitSize();
+
+ return true;
+}
+
+void SvtFileDialog::executeAsync( ::svt::AsyncPickerAction::Action eAction,
+ const OUString& rURL, const OUString& rFilter )
+{
+ SAL_WARN_IF( m_pCurrentAsyncAction.is(), "fpicker.office", "SvtFileDialog::executeAsync: previous async action not yet finished!" );
+
+ m_pCurrentAsyncAction = new AsyncPickerAction( this, m_xFileView.get(), eAction );
+
+ bool bReallyAsync = true;
+ m_aConfiguration.getNodeValue( OUString( "FillAsynchronously" ) ) >>= bReallyAsync;
+
+ sal_Int32 nMinTimeout = 0;
+ m_aConfiguration.getNodeValue( OUString( "Timeout/Min" ) ) >>= nMinTimeout;
+ sal_Int32 nMaxTimeout = 0;
+ m_aConfiguration.getNodeValue( OUString( "Timeout/Max" ) ) >>= nMaxTimeout;
+
+ m_bInExecuteAsync = true;
+ m_pCurrentAsyncAction->execute(rURL, rFilter, bReallyAsync ? nMinTimeout : -1, nMaxTimeout, GetDenyList());
+ m_bInExecuteAsync = false;
+}
+
+
+void SvtFileDialog::FileSelect()
+{
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify( FILE_SELECTION_CHANGED, 0 );
+}
+
+
+void SvtFileDialog::FilterSelect()
+{
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify( CTRL_STATE_CHANGED,
+ LISTBOX_FILTER );
+}
+
+
+/* [Description]
+
+ This method sets the path for the default button.
+*/
+void SvtFileDialog::SetStandardDir( const OUString& rStdDir )
+{
+ INetURLObject aObj( rStdDir );
+ SAL_WARN_IF( aObj.GetProtocol() == INetProtocol::NotValid, "fpicker.office", "Invalid protocol!" );
+ aObj.setFinalSlash();
+ m_xImpl->SetStandardDir( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+}
+
+void SvtFileDialog::SetDenyList( const css::uno::Sequence< OUString >& rDenyList )
+{
+ m_xImpl->SetDenyList( rDenyList );
+}
+
+
+const css::uno::Sequence< OUString >& SvtFileDialog::GetDenyList() const
+{
+ return m_xImpl->GetDenyList();
+}
+
+
+/* [Description]
+
+ This method returns the standard path.
+*/
+const OUString& SvtFileDialog::GetStandardDir() const
+{
+ return m_xImpl->GetStandardDir();
+}
+
+
+void SvtFileDialog::PrevLevel_Impl()
+{
+ m_xFileView->EndInplaceEditing();
+
+ OUString sDummy;
+ executeAsync( AsyncPickerAction::ePrevLevel, sDummy, sDummy );
+}
+
+void SvtFileDialog::OpenURL_Impl( const OUString& _rURL )
+{
+ m_xFileView->EndInplaceEditing();
+
+ executeAsync( AsyncPickerAction::eOpenURL, _rURL, getMostCurrentFilter( m_xImpl ) );
+}
+
+SvtFileDialogFilter_Impl* SvtFileDialog::implAddFilter( const OUString& rFilter, const OUString& _rType )
+{
+ SvtFileDialogFilter_Impl* pNewFilter = new SvtFileDialogFilter_Impl( rFilter, _rType );
+ m_xImpl->m_aFilter.push_front( std::unique_ptr<SvtFileDialogFilter_Impl>( pNewFilter ) );
+
+ if ( !m_xImpl->GetCurFilter() )
+ m_xImpl->SetCurFilter( pNewFilter, rFilter );
+
+ return pNewFilter;
+}
+
+void SvtFileDialog::AddFilter( const OUString& rFilter, const OUString& _rType )
+{
+ SAL_WARN_IF( m_bIsInExecute, "fpicker.office", "SvtFileDialog::AddFilter: currently executing!" );
+ implAddFilter ( rFilter, _rType );
+}
+
+
+void SvtFileDialog::AddFilterGroup( const OUString& rFilter, const Sequence< StringPair >& rFilters )
+{
+ SAL_WARN_IF( m_bIsInExecute, "fpicker.office", "SvtFileDialog::AddFilter: currently executing!" );
+
+ implAddFilter( rFilter, OUString() );
+ const StringPair* pSubFilters = rFilters.getConstArray();
+ const StringPair* pSubFiltersEnd = pSubFilters + rFilters.getLength();
+ for ( ; pSubFilters != pSubFiltersEnd; ++pSubFilters )
+ implAddFilter( pSubFilters->First, pSubFilters->Second );
+}
+
+
+void SvtFileDialog::SetCurFilter( const OUString& rFilter )
+{
+ SAL_WARN_IF( m_bIsInExecute, "fpicker.office", "SvtFileDialog::SetCurFilter: currently executing!" );
+
+ // look for corresponding filter
+ sal_uInt16 nPos = m_xImpl->m_aFilter.size();
+
+ while ( nPos-- )
+ {
+ SvtFileDialogFilter_Impl* pFilter = m_xImpl->m_aFilter[ nPos ].get();
+ if ( pFilter->GetName() == rFilter )
+ {
+ m_xImpl->SetCurFilter( pFilter, rFilter );
+ break;
+ }
+ }
+}
+
+OUString SvtFileDialog::GetCurFilter() const
+{
+ OUString aFilter;
+
+ const SvtFileDialogFilter_Impl* pCurrentFilter = m_xImpl->GetCurFilter();
+ if ( pCurrentFilter )
+ aFilter = pCurrentFilter->GetName();
+
+ return aFilter;
+}
+
+OUString SvtFileDialog::getCurFilter( ) const
+{
+ return GetCurFilter();
+}
+
+sal_uInt16 SvtFileDialog::GetFilterCount() const
+{
+ return m_xImpl->m_aFilter.size();
+}
+
+const OUString& SvtFileDialog::GetFilterName( sal_uInt16 nPos ) const
+{
+ assert( nPos < GetFilterCount() && "invalid index" );
+ return m_xImpl->m_aFilter[ nPos ]->GetName();
+}
+
+void SvtFileDialog::InitSize()
+{
+ if (m_xImpl->m_aIniKey.isEmpty())
+ return;
+
+ // initialize from config
+ SvtViewOptions aDlgOpt( EViewType::Dialog, m_xImpl->m_aIniKey );
+
+ if ( aDlgOpt.Exists() )
+ {
+ m_xDialog->set_window_state(OUStringToOString(aDlgOpt.GetWindowState(), RTL_TEXTENCODING_UTF8));
+
+ Any aUserData = aDlgOpt.GetUserItem( "UserData");
+ OUString sCfgStr;
+ if ( aUserData >>= sCfgStr )
+ m_xFileView->SetConfigString( sCfgStr );
+ }
+}
+
+std::vector<OUString> SvtFileDialog::GetPathList() const
+{
+ std::vector<OUString> aList;
+
+ m_xFileView->selected_foreach([this, &aList](weld::TreeIter& rCurEntry){
+ aList.push_back(m_xFileView->GetURL(rCurEntry));
+ return false;
+ });
+
+ if (aList.empty())
+ {
+ if ( !m_xImpl->m_xEdFileName->get_active_text().isEmpty() && m_bIsInExecute )
+ aList.push_back(m_xImpl->m_xEdFileName->GetURL());
+ else
+ aList.push_back(m_aPath);
+ }
+
+ return aList;
+}
+
+bool SvtFileDialog::IsolateFilterFromPath_Impl( OUString& rPath, OUString& rFilter )
+{
+ OUString aReversePath = comphelper::string::reverseString(rPath);
+ sal_Int32 nQuestionMarkPos = rPath.indexOf( '?' );
+ sal_Int32 nWildCardPos = rPath.indexOf( FILEDIALOG_DEF_WILDCARD );
+
+ if ( nQuestionMarkPos != -1 )
+ {
+ // use question mark as wildcard only for files
+ INetProtocol eProt = INetURLObject::CompareProtocolScheme( rPath );
+
+ if ( INetProtocol::NotValid != eProt && INetProtocol::File != eProt )
+ nQuestionMarkPos = -1;
+
+ nWildCardPos = std::min( nWildCardPos, nQuestionMarkPos );
+ }
+
+ rFilter.clear();
+
+ if ( nWildCardPos == -1 )
+ return true;
+
+ sal_Int32 nPathTokenPos = aReversePath.indexOf( '/' );
+
+ if ( nPathTokenPos == -1 )
+ {
+ OUString aDelim(
+#if defined(_WIN32)
+ '\\'
+#else
+ '/'
+#endif
+ );
+
+ nPathTokenPos = aReversePath.indexOf( aDelim );
+#if !defined( UNX )
+ if ( nPathTokenPos == -1 )
+ {
+ nPathTokenPos = aReversePath.indexOf( ':' );
+ }
+#endif
+ }
+
+ // check syntax
+ if ( nPathTokenPos != -1 )
+ {
+ if ( nPathTokenPos < (rPath.getLength() - nWildCardPos - 1) )
+ {
+ ErrorHandler::HandleError( ERRCODE_SFX_INVALIDSYNTAX );
+ return false;
+ }
+
+ // cut off filter
+ rFilter = aReversePath.copy( 0, nPathTokenPos );
+ rFilter = comphelper::string::reverseString(rFilter);
+
+ // determine folder
+ rPath = aReversePath.copy( nPathTokenPos );
+ rPath = comphelper::string::reverseString(rPath);
+ }
+ else
+ {
+ rFilter = rPath;
+ rPath.clear();
+ }
+
+ return true;
+}
+
+IMPL_LINK_NOARG(SvtFileDialog, SizeAllocHdl, const Size&, void)
+{
+ if (m_pFileNotifier)
+ m_pFileNotifier->notify(DIALOG_SIZE_CHANGED, 0);
+}
+
+weld::Widget* SvtFileDialog::getControl( sal_Int16 nControlId, bool bLabelControl ) const
+{
+ weld::Widget* pReturn = nullptr;
+
+ switch ( nControlId )
+ {
+ case CONTROL_FILEVIEW:
+ pReturn = bLabelControl ? nullptr : m_xFileView->identifier();
+ break;
+
+ case EDIT_FILEURL:
+ pReturn = bLabelControl
+ ? static_cast<weld::Widget*>(m_xImpl->m_xFtFileName.get())
+ : static_cast<weld::Widget*>(m_xImpl->m_xEdFileName->getWidget());
+ break;
+
+ case EDIT_FILEURL_LABEL:
+ pReturn = m_xImpl->m_xFtFileName.get();
+ break;
+
+ case CHECKBOX_AUTOEXTENSION:
+ pReturn = m_xImpl->m_xCbAutoExtension.get();
+ break;
+
+ case CHECKBOX_PASSWORD:
+ pReturn = m_xImpl->m_xCbPassword.get();
+ break;
+
+ case CHECKBOX_GPGENCRYPTION:
+ pReturn = m_xImpl->m_xCbGPGEncrypt.get();
+ break;
+
+ case CHECKBOX_FILTEROPTIONS:
+ pReturn = m_xImpl->m_xCbOptions.get();
+ break;
+
+ case CHECKBOX_READONLY:
+ pReturn = m_xCbReadOnly.get();
+ break;
+
+ case CHECKBOX_LINK:
+ pReturn = m_xCbLinkBox.get();
+ break;
+
+ case CHECKBOX_PREVIEW:
+ pReturn = m_xCbPreviewBox.get();
+ break;
+
+ case CHECKBOX_SELECTION:
+ pReturn = m_xCbSelection.get();
+ break;
+
+ case LISTBOX_FILTER:
+ pReturn = bLabelControl ? m_xImpl->m_xFtFileType.get() : m_xImpl->GetFilterListControl();
+ break;
+
+ case LISTBOX_FILTER_LABEL:
+ pReturn = m_xImpl->m_xFtFileType.get();
+ break;
+
+ case FIXEDTEXT_CURRENTFOLDER:
+ pReturn = m_xImpl->m_xEdCurrentPath->getWidget();
+ break;
+
+ case LISTBOX_VERSION:
+ pReturn = bLabelControl
+ ? static_cast<weld::Widget*>(m_xImpl->m_xSharedLabel.get())
+ : static_cast<weld::Widget*>(m_xImpl->m_xSharedListBox.get());
+ break;
+
+ case LISTBOX_TEMPLATE:
+ pReturn = bLabelControl
+ ? static_cast<weld::Widget*>(m_xImpl->m_xSharedLabel.get())
+ : static_cast<weld::Widget*>(m_xImpl->m_xSharedListBox.get());
+ break;
+
+ case LISTBOX_IMAGE_TEMPLATE:
+ pReturn = bLabelControl
+ ? static_cast<weld::Widget*>(m_xImpl->m_xSharedLabel.get())
+ : static_cast<weld::Widget*>(m_xImpl->m_xSharedListBox.get());
+ break;
+
+ case LISTBOX_IMAGE_ANCHOR:
+ pReturn = bLabelControl
+ ? static_cast<weld::Widget*>(m_xImpl->m_xSharedLabel.get())
+ : static_cast<weld::Widget*>(m_xImpl->m_xSharedListBox.get());
+ break;
+
+ case LISTBOX_VERSION_LABEL:
+ pReturn = m_xImpl->m_xSharedLabel.get();
+ break;
+
+ case LISTBOX_TEMPLATE_LABEL:
+ pReturn = m_xImpl->m_xSharedLabel.get();
+ break;
+
+ case LISTBOX_IMAGE_TEMPLATE_LABEL:
+ pReturn = m_xImpl->m_xSharedLabel.get();
+ break;
+
+ case LISTBOX_IMAGE_ANCHOR_LABEL:
+ pReturn = m_xImpl->m_xSharedLabel.get();
+ break;
+
+ case PUSHBUTTON_OK:
+ pReturn = m_xImpl->m_xBtnFileOpen.get();
+ break;
+
+ case PUSHBUTTON_CANCEL:
+ pReturn = m_xImpl->m_xBtnCancel.get();
+ break;
+
+ case PUSHBUTTON_PLAY:
+ pReturn = m_xPbPlay.get();
+ break;
+
+ case PUSHBUTTON_HELP:
+ pReturn = m_xImpl->m_xBtnHelp.get();
+ break;
+
+ case TOOLBOXBUTTON_LEVEL_UP:
+ pReturn = m_xImpl->m_xBtnUp->getWidget();
+ break;
+
+ case TOOLBOXBUTTON_NEW_FOLDER:
+ pReturn = m_xImpl->m_xBtnNewFolder.get();
+ break;
+
+ case LISTBOX_FILTER_SELECTOR:
+ // only exists on SalGtkFilePicker
+ break;
+
+ default:
+ SAL_WARN( "fpicker.office", "SvtFileDialog::getControl: invalid id!" );
+ }
+ return pReturn;
+}
+
+void SvtFileDialog::enableControl(sal_Int16 nControlId, bool bEnable)
+{
+ weld::Widget* pControl = getControl(nControlId);
+ if (pControl)
+ EnableControl(pControl, bEnable);
+ weld::Widget* pLabel = getControl(nControlId, true);
+ if (pLabel)
+ EnableControl(pLabel, bEnable);
+}
+
+void SvtFileDialog::AddControls_Impl( )
+{
+ // create the "insert as link" checkbox, if needed
+ if ( m_nPickerFlags & PickerFlags::InsertAsLink )
+ {
+ m_xCbLinkBox->set_label( FpsResId( STR_SVT_FILEPICKER_INSERT_AS_LINK ) );
+ m_xCbLinkBox->set_help_id( HID_FILEDLG_LINK_CB );
+ m_xCbLinkBox->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xCbLinkBox->show();
+ }
+
+ // create the "show preview" checkbox ( and the preview window, too ), if needed
+ if ( m_nPickerFlags & PickerFlags::ShowPreview )
+ {
+ m_xImpl->m_aIniKey = "ImportGraphicDialog";
+
+ // "preview"
+ m_xCbPreviewBox->set_label( FpsResId( STR_SVT_FILEPICKER_SHOW_PREVIEW ) );
+ m_xCbPreviewBox->set_help_id( HID_FILEDLG_PREVIEW_CB );
+ m_xCbPreviewBox->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xCbPreviewBox->show();
+
+ // generate preview window just here
+ m_aPreviewSize = Size(200, 300);
+ m_xPrevBmp->set_size_request(m_aPreviewSize.Width(), m_aPreviewSize.Height());
+ m_xPrevBmp->connect_size_allocate(LINK(this, SvtFileDialog, PreviewSizeAllocHdl));
+ m_xPreviewFrame->show();
+ m_xPrevBmp->set_accessible_name(FpsResId(STR_PREVIEW));
+ }
+
+ if ( m_nPickerFlags & PickerFlags::AutoExtension )
+ {
+ m_xImpl->m_xCbAutoExtension->set_label( FpsResId( STR_SVT_FILEPICKER_AUTO_EXTENSION ) );
+ m_xImpl->m_xCbAutoExtension->set_active(true);
+ m_xImpl->m_xCbAutoExtension->connect_toggled( LINK( this, SvtFileDialog, AutoExtensionHdl_Impl ) );
+ m_xImpl->m_xCbAutoExtension->show();
+ }
+
+ if ( m_nPickerFlags & PickerFlags::FilterOptions )
+ {
+ m_xImpl->m_xCbOptions->set_label( FpsResId( STR_SVT_FILEPICKER_FILTER_OPTIONS ) );
+ m_xImpl->m_xCbOptions->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xImpl->m_xCbOptions->show();
+ }
+
+ if ( m_nPickerFlags & PickerFlags::Selection )
+ {
+ m_xCbSelection->set_label( FpsResId( STR_SVT_FILEPICKER_SELECTION ) );
+ m_xCbSelection->connect_toggled( LINK( this, SvtFileDialog, ClickHdl_Impl ) );
+ m_xCbSelection->show();
+ }
+
+ if ( m_nPickerFlags & PickerFlags::PlayButton )
+ {
+ m_xPbPlay->set_label( FpsResId( STR_SVT_FILEPICKER_PLAY ) );
+ m_xPbPlay->set_help_id( HID_FILESAVE_DOPLAY );
+ m_xPbPlay->connect_clicked( LINK( this, SvtFileDialog, PlayButtonHdl_Impl ) );
+ m_xPbPlay->show();
+ }
+
+ if ( m_nPickerFlags & PickerFlags::ShowVersions )
+ {
+ m_xImpl->m_xSharedLabel->set_label( FpsResId( STR_SVT_FILEPICKER_VERSION ) );
+ m_xImpl->m_xSharedLabel->show();
+
+ m_xImpl->m_xSharedListBox->set_help_id( HID_FILEOPEN_VERSION );
+ m_xImpl->m_xSharedListBox->show();
+ }
+ else if ( m_nPickerFlags & PickerFlags::Templates )
+ {
+ m_xImpl->m_xSharedLabel->set_label( FpsResId( STR_SVT_FILEPICKER_TEMPLATES ) );
+ m_xImpl->m_xSharedLabel->show();
+
+ m_xImpl->m_xSharedListBox->set_help_id( HID_FILEOPEN_VERSION );
+ m_xImpl->m_xSharedListBox->show();
+ // This is strange. During the re-factoring during 96930, I discovered that this help id
+ // is set in the "Templates mode". This was hidden in the previous implementation.
+ // Shouldn't this be a more meaningful help id.
+ }
+ else if ( m_nPickerFlags & PickerFlags::ImageTemplate )
+ {
+ m_xImpl->m_xSharedLabel->set_label( FpsResId( STR_SVT_FILEPICKER_IMAGE_TEMPLATE ) );
+ m_xImpl->m_xSharedLabel->show();
+
+ m_xImpl->m_xSharedListBox->set_help_id( HID_FILEOPEN_IMAGE_TEMPLATE );
+ m_xImpl->m_xSharedListBox->show();
+ }
+ else if ( m_nPickerFlags & PickerFlags::ImageAnchor )
+ {
+ m_xImpl->m_xSharedLabel->set_label( FpsResId( STR_SVT_FILEPICKER_IMAGE_ANCHOR ) );
+ m_xImpl->m_xSharedLabel->show();
+
+ m_xImpl->m_xSharedListBox->set_help_id( HID_FILEOPEN_IMAGE_ANCHOR );
+ m_xImpl->m_xSharedListBox->show();
+ }
+
+ m_xImpl->m_xPlaces.reset(new PlacesListBox(m_xBuilder->weld_tree_view("places"),
+ m_xBuilder->weld_button("add"),
+ m_xBuilder->weld_button("del"),
+ this));
+ m_xImpl->m_xPlaces->set_help_id("SVT_HID_FILESAVE_PLACES_LISTBOX");
+ m_xImpl->m_xPlaces->SetAddHdl( LINK ( this, SvtFileDialog, AddPlacePressed_Hdl ) );
+ m_xImpl->m_xPlaces->SetDelHdl( LINK ( this, SvtFileDialog, RemovePlacePressed_Hdl ) );
+
+ initDefaultPlaces();
+}
+
+IMPL_LINK(SvtFileDialog, PreviewSizeAllocHdl, const Size&, rSize, void)
+{
+ m_aPreviewSize = rSize;
+}
+
+sal_Int32 SvtFileDialog::getAvailableWidth()
+{
+ if (m_xPrevBmp)
+ return m_aPreviewSize.Width();
+ else
+ return 0;
+}
+
+sal_Int32 SvtFileDialog::getAvailableHeight()
+{
+ if (m_xPrevBmp)
+ return m_aPreviewSize.Height();
+ else
+ return 0;
+}
+
+void SvtFileDialog::setImage(const Any& rImage)
+{
+ if (!m_xPrevBmp || !m_xPreviewFrame->get_visible())
+ return;
+
+ Sequence < sal_Int8 > aBmpSequence;
+
+ if ( rImage >>= aBmpSequence )
+ {
+ BitmapEx aBmp;
+ SvMemoryStream aData( aBmpSequence.getArray(),
+ aBmpSequence.getLength(),
+ StreamMode::READ );
+ ReadDIBBitmapEx(aBmp, aData);
+
+ m_xPrevBmp->set_image(Graphic(aBmp).GetXGraphic());
+ }
+ else
+ {
+ m_xPrevBmp->set_image(nullptr);
+ }
+}
+
+OUString SvtFileDialog::getCurrentFileText( ) const
+{
+ OUString sReturn;
+ if (m_xImpl && m_xImpl->m_xEdFileName)
+ sReturn = m_xImpl->m_xEdFileName->get_active_text();
+ return sReturn;
+}
+
+void SvtFileDialog::setCurrentFileText( const OUString& _rText, bool m_bSelectAll )
+{
+ if (m_xImpl && m_xImpl->m_xEdFileName)
+ {
+ m_xImpl->m_xEdFileName->set_entry_text( _rText );
+ if ( m_bSelectAll )
+ m_xImpl->m_xEdFileName->select_entry_region(0, -1);
+ }
+}
+
+bool SvtFileDialog::isAutoExtensionEnabled() const
+{
+ return m_xImpl->m_xCbAutoExtension && m_xImpl->m_xCbAutoExtension->get_active();
+}
+
+bool SvtFileDialog::getShowState()
+{
+ if (m_xPreviewFrame)
+ return m_xPreviewFrame->get_visible();
+ else
+ return false;
+}
+
+bool SvtFileDialog::ContentHasParentFolder( const OUString& rURL )
+{
+ m_aContent.bindTo( rURL );
+
+ if ( m_aContent.isInvalid() )
+ return false;
+
+ return m_aContent.hasParentFolder( ) && m_aContent.isValid();
+}
+
+bool SvtFileDialog::ContentCanMakeFolder( const OUString& rURL )
+{
+ m_aContent.bindTo( rURL );
+
+ if ( m_aContent.isInvalid() )
+ return false;
+
+ return m_aContent.canCreateFolder( ) && m_aContent.isValid();
+}
+
+bool SvtFileDialog::ContentGetTitle( const OUString& rURL, OUString& rTitle )
+{
+ m_aContent.bindTo( rURL );
+
+ if ( m_aContent.isInvalid() )
+ return false;
+
+ OUString sTitle;
+ m_aContent.getTitle( sTitle );
+ rTitle = sTitle;
+
+ return m_aContent.isValid();
+}
+
+void SvtFileDialog::appendDefaultExtension(OUString& rFileName,
+ std::u16string_view rFilterDefaultExtension,
+ const OUString& rFilterExtensions)
+{
+ const OUString aType(rFilterExtensions.toAsciiLowerCase());
+
+ if ( aType == FILEDIALOG_FILTER_ALL )
+ return;
+
+ const OUString aTemp(rFileName.toAsciiLowerCase());
+ sal_Int32 nPos = 0;
+
+ do
+ {
+ if (nPos+1<aType.getLength() && aType[nPos]=='*') // take care of a leading *
+ ++nPos;
+ const std::u16string_view aExt(o3tl::getToken(aType, 0, FILEDIALOG_DEF_EXTSEP, nPos ));
+ if (aExt.empty())
+ continue;
+ if (o3tl::ends_with(aTemp, aExt))
+ return;
+ }
+ while (nPos>=0);
+
+ rFileName += OUString::Concat(".") + rFilterDefaultExtension;
+}
+
+void SvtFileDialog::initDefaultPlaces( )
+{
+ PlacePtr pRootPlace = std::make_shared<Place>( FpsResId(STR_DEFAULT_DIRECTORY), GetStandardDir() );
+ m_xImpl->m_xPlaces->AppendPlace( pRootPlace );
+
+ // Load from user settings
+ Sequence< OUString > placesUrlsList(officecfg::Office::Common::Misc::FilePickerPlacesUrls::get());
+ Sequence< OUString > placesNamesList(officecfg::Office::Common::Misc::FilePickerPlacesNames::get());
+
+ for(sal_Int32 nPlace = 0; nPlace < placesUrlsList.getLength() && nPlace < placesNamesList.getLength(); ++nPlace)
+ {
+ PlacePtr pPlace = std::make_shared<Place>(placesNamesList[nPlace], placesUrlsList[nPlace], true);
+ m_xImpl->m_xPlaces->AppendPlace(pPlace);
+ }
+
+ // Reset the placesList "updated" state
+ m_xImpl->m_xPlaces->IsUpdated();
+}
+
+QueryFolderNameDialog::QueryFolderNameDialog(weld::Window* _pParent,
+ const OUString& rTitle, const OUString& rDefaultText)
+ : GenericDialogController(_pParent, "fps/ui/foldernamedialog.ui", "FolderNameDialog")
+ , m_xNameEdit(m_xBuilder->weld_entry("entry"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+{
+ m_xDialog->set_title(rTitle);
+ m_xNameEdit->set_text(rDefaultText);
+ m_xNameEdit->select_region(0, -1);
+ m_xOKBtn->connect_clicked(LINK(this, QueryFolderNameDialog, OKHdl));
+ m_xNameEdit->connect_changed(LINK(this, QueryFolderNameDialog, NameHdl));
+};
+
+QueryFolderNameDialog::~QueryFolderNameDialog()
+{
+}
+
+IMPL_LINK_NOARG(QueryFolderNameDialog, OKHdl, weld::Button&, void)
+{
+ // trim the strings
+ m_xNameEdit->set_text(comphelper::string::strip(m_xNameEdit->get_text(), ' '));
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(QueryFolderNameDialog, NameHdl, weld::Entry&, void)
+{
+ // trim the strings
+ OUString aName = comphelper::string::strip(m_xNameEdit->get_text(), ' ');
+ m_xOKBtn->set_sensitive(!aName.isEmpty());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/iodlg.hxx b/fpicker/source/office/iodlg.hxx
new file mode 100644
index 000000000..9de9261af
--- /dev/null
+++ b/fpicker/source/office/iodlg.hxx
@@ -0,0 +1,302 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <memory>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/ucb/IOErrorCode.hpp>
+#include <unotools/confignode.hxx>
+#include "asyncfilepicker.hxx"
+#include "fpsmartcontent.hxx"
+#include "fpdialogbase.hxx"
+#include <o3tl/typed_flags_set.hxx>
+#include <vcl/timer.hxx>
+
+#include <set>
+#include <string_view>
+
+class SvtFileView;
+class SvtFileDialogFilter_Impl;
+class SvtExpFileDlg_Impl;
+class SvtURLBox;
+
+enum class AdjustFilterFlags {
+ NONE = 0x0000,
+ NonEmpty = 0x0001,
+ Changed = 0x0002,
+ UserFilter = 0x0004,
+};
+namespace o3tl {
+ template<> struct typed_flags<AdjustFilterFlags> : is_typed_flags<AdjustFilterFlags, 0x0007> {};
+}
+
+
+class SvtFileDialog final : public SvtFileDialog_Base
+{
+private:
+ std::unique_ptr<weld::CheckButton> m_xCbReadOnly;
+ std::unique_ptr<weld::CheckButton> m_xCbLinkBox;
+ std::unique_ptr<weld::CheckButton> m_xCbPreviewBox;
+ std::unique_ptr<weld::CheckButton> m_xCbSelection;
+ std::unique_ptr<weld::Button> m_xPbPlay;
+ std::unique_ptr<weld::Widget> m_xPreviewFrame;
+ std::unique_ptr<weld::Image> m_xPrevBmp;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<SvtFileView> m_xFileView;
+ ::svt::IFilePickerListener* m_pFileNotifier;
+ std::unique_ptr<SvtExpFileDlg_Impl> m_xImpl;
+ Size m_aPreviewSize;
+ PickerFlags m_nPickerFlags;
+ bool m_bIsInExecute : 1;
+
+ ::svt::SmartContent m_aContent;
+
+ ::std::set<weld::Widget*> m_aDisabledControls;
+
+ ::utl::OConfigurationNode m_aConfiguration;
+ ::rtl::Reference< ::svt::AsyncPickerAction >
+ m_pCurrentAsyncAction;
+ bool m_bInExecuteAsync;
+ bool m_bHasFilename;
+
+ DECL_LINK( FilterSelectHdl_Impl, weld::ComboBox&, void );
+ DECL_LINK( FilterSelectTimerHdl_Impl, Timer*, void );
+ DECL_LINK( NewFolderHdl_Impl, weld::Button&, void );
+ DECL_LINK( OpenUrlHdl_Impl, weld::ComboBox&, bool );
+ DECL_LINK( OpenClickHdl_Impl, weld::Button&, void );
+ DECL_LINK( CancelHdl_Impl, weld::Button&, void );
+ DECL_LINK( FileNameGetFocusHdl_Impl, weld::Widget&, void );
+ DECL_LINK( FileNameModifiedHdl_Impl, weld::ComboBox&, void );
+
+ DECL_LINK( URLBoxModifiedHdl_Impl, weld::ComboBox&, bool );
+ DECL_LINK( ConnectToServerPressed_Hdl, weld::Button&, void );
+
+ DECL_LINK( AddPlacePressed_Hdl, weld::Button&, void );
+ DECL_LINK( RemovePlacePressed_Hdl, weld::Button&, void );
+ DECL_LINK( PreviewSizeAllocHdl, const Size&, void);
+
+ void OpenHdl_Impl(void const * pVoid);
+
+ /** find a filter with the given wildcard
+ @param _rFilter
+ the wildcard pattern to look for in the filter list
+ @param _bMultiExt
+ allow for filters with more than one extension pattern
+ @param _rFilterChanged
+ set to <TRUE/> if the filter changed
+ @return
+ the filter which has been found
+ */
+ SvtFileDialogFilter_Impl* FindFilter_Impl( const OUString& _rFilter,
+ bool _bMultiExt,
+ bool& _rFilterChanged
+ );
+ void ExecuteFilter();
+ void OpenMultiSelection_Impl();
+ void AddControls_Impl( );
+
+ DECL_LINK(SelectHdl_Impl, SvtFileView*, void);
+ DECL_LINK(DblClickHdl_Impl, SvtFileView*, bool);
+ DECL_LINK(EntrySelectHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK(OpenDoneHdl_Impl, SvtFileView*, void);
+ DECL_LINK(AutoExtensionHdl_Impl, weld::Toggleable&, void);
+ DECL_LINK(ClickHdl_Impl, weld::Toggleable&, void);
+ DECL_LINK(PlayButtonHdl_Impl, weld::Button&, void);
+ DECL_LINK(SizeAllocHdl, const Size&, void);
+
+ // removes a filter with wildcards from the path and returns it
+ static bool IsolateFilterFromPath_Impl( OUString& rPath, OUString& rFilter );
+
+ OUString m_aPath;
+ OUString m_aDefExt;
+
+ /** enables or disables the complete UI of the file picker, with only offering a
+ cancel button
+
+ This method preserves the "enabled" state of its controls in the following sense:
+ If you disable a certain control, then disable the dialog UI, then enable the dialog
+ UI, the control will still be disabled.
+ This is under the assumption that you'll use EnableControl. Direct access to the control
+ (such as pControl->Enable()) will break this.
+ */
+ void EnableUI( bool _bEnable );
+
+ /** enables or disables a control
+
+ You are strongly encouraged to prefer this method over pControl->Enable( bEnable ). See
+ <member>EnableUI</member> for details.
+ */
+ void EnableControl(weld::Widget* pControl, bool bEnable);
+ virtual bool PrepareExecute() override;
+
+public:
+ SvtFileDialog( weld::Window* pParent, PickerFlags nBits );
+ virtual ~SvtFileDialog() override;
+
+ virtual short run() override;
+
+ void FileSelect();
+ void FilterSelect() override;
+
+ void SetDenyList( const css::uno::Sequence< OUString >& rDenyList ) override;
+ const css::uno::Sequence< OUString >& GetDenyList() const override;
+ void SetStandardDir( const OUString& rStdDir ) override;
+ const OUString& GetStandardDir() const override;
+ std::vector<OUString> GetPathList() const override; // for MultiSelection
+
+ void AddFilter( const OUString& rFilter,
+ const OUString& rType ) override;
+
+ void AddFilterGroup(
+ const OUString& _rFilter,
+ const css::uno::Sequence< css::beans::StringPair >& rFilters ) override;
+
+ void SetCurFilter( const OUString& rFilter ) override;
+ OUString GetCurFilter() const override;
+ sal_uInt16 GetFilterCount() const;
+ const OUString& GetFilterName( sal_uInt16 nPos ) const;
+
+ void PrevLevel_Impl();
+ void OpenURL_Impl( const OUString& rURL );
+
+ SvtFileView* GetView() override;
+
+ void InitSize();
+ void UpdateControls( const OUString& rURL ) override;
+ void EnableAutocompletion( bool _bEnable = true ) override;
+
+ void SetFileCallback( ::svt::IFilePickerListener *pNotifier ) override { m_pFileNotifier = pNotifier; }
+
+ sal_Int32 getAvailableWidth() override;
+ sal_Int32 getAvailableHeight() override;
+ void setImage( const css::uno::Any& rImage ) override;
+ bool getShowState() override;
+ bool isAutoExtensionEnabled() const;
+
+ OUString getCurrentFileText( ) const override;
+ void setCurrentFileText( const OUString& _rText, bool _bSelectAll = false ) override;
+
+ void onAsyncOperationStarted() override;
+ void onAsyncOperationFinished() override;
+
+ void RemovablePlaceSelected(bool enable = true);
+
+ static void displayIOException( const OUString& _rURL, css::ucb::IOErrorCode _eCode );
+
+ // inline
+ inline void SetPath( const OUString& rNewURL ) override;
+ inline void SetHasFilename( bool bHasFilename ) override;
+ inline const OUString& GetPath() override;
+ inline void SetDefaultExt( const OUString& rExt );
+ inline void EraseDefaultExt( sal_Int32 _nIndex = 0 );
+ inline const OUString& GetDefaultExt() const;
+
+ bool ContentIsFolder( const OUString& rURL ) override { return m_aContent.isFolder( rURL ) && m_aContent.isValid(); }
+ bool ContentHasParentFolder( const OUString& rURL );
+ bool ContentCanMakeFolder( const OUString& rURL );
+ bool ContentGetTitle( const OUString& rURL, OUString& rTitle );
+
+private:
+ SvtFileDialogFilter_Impl* implAddFilter( const OUString& _rFilter, const OUString& _rType );
+
+ /** updates m_xUserFilter with a new filter
+ <p>No checks for necessity are made.</p>
+ */
+ void createNewUserFilter( const OUString& _rNewFilter );
+
+ AdjustFilterFlags adjustFilter( const OUString& _rFilter );
+
+ // IFilePickerController, needed by OControlAccess
+ virtual weld::Widget* getControl( sal_Int16 nControlId, bool bLabelControl = false ) const override;
+ virtual void enableControl( sal_Int16 _nControlId, bool _bEnable ) override;
+ virtual OUString getCurFilter( ) const override;
+
+ OUString implGetInitialURL( const OUString& _rPath, std::u16string_view _rFallback );
+
+ /// executes a certain FileView action asynchronously
+ void executeAsync(
+ ::svt::AsyncPickerAction::Action _eAction,
+ const OUString& _rURL,
+ const OUString& _rFilter
+ );
+
+ /** helper function to check and append the default filter extension if
+ necessary.
+ The function checks if the specified filename already contains one of
+ the valid extensions of the specified filter. If not the filter default
+ extension is appended to the filename.
+
+ @param _rFileName the filename which is checked and extended if necessary.
+ @param _rFilterDefaultExtension the default extension of the used filter.
+ @param _rFilterExtensions a list of one or more valid filter extensions
+ of the used filter.
+
+ */
+ static void appendDefaultExtension(
+ OUString& _rFileName,
+ std::u16string_view _rFilterDefaultExtension,
+ const OUString& _rFilterExtensions);
+
+ void initDefaultPlaces( );
+};
+
+
+inline void SvtFileDialog::SetPath( const OUString& rNewURL )
+{
+ m_aPath = rNewURL;
+}
+
+
+inline void SvtFileDialog::SetHasFilename( bool bHasFilename )
+{
+ m_bHasFilename = bHasFilename;
+}
+
+
+inline const OUString& SvtFileDialog::GetPath()
+{
+ return m_aPath;
+}
+
+
+inline void SvtFileDialog::SetDefaultExt( const OUString& rExt )
+{
+ m_aDefExt = rExt;
+}
+
+inline void SvtFileDialog::EraseDefaultExt( sal_Int32 _nIndex )
+{
+ m_aDefExt = m_aDefExt.copy( 0, _nIndex );
+}
+
+inline const OUString& SvtFileDialog::GetDefaultExt() const
+{
+ return m_aDefExt;
+}
+
+
+inline SvtFileView* SvtFileDialog::GetView()
+{
+ return m_xFileView.get();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/iodlgimp.cxx b/fpicker/source/office/iodlgimp.cxx
new file mode 100644
index 000000000..e9e8e0049
--- /dev/null
+++ b/fpicker/source/office/iodlgimp.cxx
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "fileview.hxx"
+#include "iodlgimp.hxx"
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <svtools/inettbc.hxx>
+#include "iodlg.hxx"
+#include <svtools/imagemgr.hxx>
+#include <svl/svlresid.hxx>
+#include <svl/svl.hrc>
+#include <utility>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::utl;
+
+SvtFileDialogFilter_Impl::SvtFileDialogFilter_Impl( OUString aName, OUString aType )
+ : m_aName(std::move( aName ))
+ , m_aType(std::move( aType ))
+{
+ m_aType = m_aType.toAsciiLowerCase();
+}
+
+SvtFileDialogFilter_Impl::~SvtFileDialogFilter_Impl()
+{
+}
+
+//= SvtUpButton_Impl
+SvtUpButton_Impl::SvtUpButton_Impl(std::unique_ptr<weld::Toolbar> xToolbar,
+ std::unique_ptr<weld::Menu> xMenu,
+ SvtFileDialog* pDlg)
+ : m_xToolbar(std::move(xToolbar))
+ , m_xMenu(std::move(xMenu))
+ , m_pDlg(pDlg)
+{
+ m_xToolbar->set_item_menu("up_btn", m_xMenu.get());
+ m_xToolbar->connect_clicked(LINK(this, SvtUpButton_Impl, ClickHdl));
+ m_xMenu->connect_activate(LINK(this, SvtUpButton_Impl, SelectHdl));
+}
+
+void SvtUpButton_Impl::FillURLMenu()
+{
+ SvtFileView* pBox = m_pDlg->GetView();
+
+ sal_uInt16 nItemId = 1;
+
+ aURLs.clear();
+ m_xMenu->clear();
+
+ // determine parent levels
+ INetURLObject aObject( pBox->GetViewURL() );
+ sal_Int32 nCount = aObject.getSegmentCount();
+
+ ::svtools::VolumeInfo aVolInfo( true /* volume */, false /* remote */,
+ false /* removable */, false /* floppy */,
+ false /* compact disk */ );
+ OUString aVolumeImage( SvFileInformationManager::GetFolderImageId( aVolInfo ) );
+
+ while ( nCount >= 1 )
+ {
+ aObject.removeSegment();
+ OUString aParentURL(aObject.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+
+ OUString aTitle;
+
+ if (nCount == 1) // adjust the title of the top level entry (the workspace)
+ aTitle = SvlResId(STR_SVT_MIMETYPE_CNT_FSYSBOX);
+ else if (!m_pDlg->ContentGetTitle(aParentURL, aTitle) || aTitle.isEmpty())
+ aTitle = aObject.getName();
+
+ OUString aImage = ( nCount > 1 ) // if nCount == 1 means workplace, which detects the wrong image
+ ? SvFileInformationManager::GetImageId( aObject ) : aVolumeImage;
+
+ m_xMenu->append(OUString::number(nItemId), aTitle, aImage);
+ aURLs.push_back(aParentURL);
+
+ ++nItemId;
+ --nCount;
+ }
+}
+
+IMPL_LINK(SvtUpButton_Impl, SelectHdl, const OString&, rId, void)
+{
+ sal_uInt32 nId = rId.toUInt32();
+ if (nId)
+ {
+ --nId;
+ assert( nId <= aURLs.size() && "SvtUpButton_Impl: wrong index" );
+
+ m_pDlg->OpenURL_Impl(aURLs[nId]);
+ }
+}
+
+IMPL_LINK_NOARG(SvtUpButton_Impl, ClickHdl, const OString&, void)
+{
+ m_pDlg->PrevLevel_Impl();
+}
+
+// SvtExpFileDlg_Impl
+SvtExpFileDlg_Impl::SvtExpFileDlg_Impl()
+ : m_pCurFilter( nullptr )
+ , m_eMode( FILEDLG_MODE_OPEN )
+ , m_eDlgType( FILEDLG_TYPE_FILEDLG )
+ , m_nStyle( PickerFlags::NONE )
+ , m_aFilterIdle("fpicker SvtExpFileDlg_Impl m_aFilterIdle")
+ , m_bDoubleClick( false )
+ , m_bMultiSelection( false )
+{
+}
+
+SvtExpFileDlg_Impl::~SvtExpFileDlg_Impl()
+{
+}
+
+void SvtExpFileDlg_Impl::SetStandardDir( const OUString& _rDir )
+{
+ m_aStdDir = _rDir;
+ if (m_aStdDir.isEmpty())
+ m_aStdDir = "file:///";
+}
+
+namespace {
+ OUString lcl_DecoratedFilter( std::u16string_view _rOriginalFilter )
+ {
+ return "<" + OUString::Concat(_rOriginalFilter) + ">";
+ }
+}
+
+void SvtExpFileDlg_Impl::SetCurFilter( SvtFileDialogFilter_Impl const * pFilter, const OUString& rDisplayName )
+{
+ DBG_ASSERT( pFilter, "SvtExpFileDlg_Impl::SetCurFilter: invalid filter!" );
+ DBG_ASSERT( ( rDisplayName == pFilter->GetName() )
+ || ( rDisplayName == lcl_DecoratedFilter( pFilter->GetName() ) ),
+ "SvtExpFileDlg_Impl::SetCurFilter: arguments are inconsistent!" );
+
+ m_pCurFilter = pFilter;
+ m_sCurrentFilterDisplayName = rDisplayName;
+}
+
+void SvtExpFileDlg_Impl::InsertFilterListEntry(const SvtFileDialogFilter_Impl* pFilterDesc)
+{
+ // insert and set user data
+ OUString sId(weld::toId(pFilterDesc));
+ OUString sName = pFilterDesc->GetName();
+ if (pFilterDesc->isGroupSeparator())
+ m_xLbFilter->append_separator(sId);
+ else
+ m_xLbFilter->append(sId, sName);
+}
+
+void SvtExpFileDlg_Impl::InitFilterList( )
+{
+ // clear the current list
+ m_xLbFilter->clear();
+
+ // reinit it
+ sal_uInt16 nPos = m_aFilter.size();
+
+ // search for the first entry which is no group separator
+ while ( nPos-- && m_aFilter[ nPos ]->isGroupSeparator() )
+ ;
+
+ // add all following entries
+ while ( static_cast<sal_Int16>(nPos) >= 0 )
+ InsertFilterListEntry( m_aFilter[ nPos-- ].get() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/iodlgimp.hxx b/fpicker/source/office/iodlgimp.hxx
new file mode 100644
index 000000000..caac91793
--- /dev/null
+++ b/fpicker/source/office/iodlgimp.hxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "PlacesListBox.hxx"
+
+#include <vcl/idle.hxx>
+
+#include <deque>
+#include <memory>
+#include <vector>
+
+class CheckBox;
+class SvtFileDialog;
+
+#define FILEDIALOG_DEF_EXTSEP ';'
+#define FILEDIALOG_DEF_WILDCARD '*'
+
+
+// SvtFileDialogFilter_Impl
+
+
+/* [Description]
+
+ Instances of this class represent a filter.
+*/
+
+class SvtFileDialogFilter_Impl
+{
+private:
+ OUString m_aName; // name of the entry
+ OUString m_aType; // filter wildcard - if empty, the entry marks a group
+
+public:
+ SvtFileDialogFilter_Impl( OUString aName, OUString aType );
+ ~SvtFileDialogFilter_Impl();
+
+ const OUString& GetName() const { return m_aName; }
+ const OUString& GetType() const { return m_aType; }
+ OUString GetExtension() const { return m_aType.getLength() > 2 ? m_aType.copy( 2 ) : OUString(); }
+
+ bool isGroupSeparator() const { return m_aType.isEmpty(); }
+};
+
+typedef std::deque<std::unique_ptr<SvtFileDialogFilter_Impl>> SvtFileDialogFilterList_Impl;
+
+enum SvtFileDlgMode
+{
+ FILEDLG_MODE_OPEN = 0,
+ FILEDLG_MODE_SAVE = 1
+};
+
+enum SvtFileDlgType
+{
+ FILEDLG_TYPE_FILEDLG = 0,
+ FILEDLG_TYPE_PATHDLG
+};
+
+class SvtUpButton_Impl
+{
+private:
+ std::unique_ptr<weld::Toolbar> m_xToolbar;
+ std::unique_ptr<weld::Menu> m_xMenu;
+ SvtFileDialog* m_pDlg;
+
+ std::vector<OUString> aURLs;
+
+public:
+ SvtUpButton_Impl(std::unique_ptr<weld::Toolbar> xToolbar,
+ std::unique_ptr<weld::Menu> xMenu,
+ SvtFileDialog* pDlg);
+
+ void set_help_id(const OString& rHelpId) { m_xToolbar->set_help_id(rHelpId); }
+ void show() { m_xToolbar->show(); }
+
+ void FillURLMenu();
+
+ weld::Widget* getWidget() { return m_xToolbar.get(); }
+
+private:
+
+ DECL_LINK(SelectHdl, const OString&, void);
+ DECL_LINK(ClickHdl, const OString&, void);
+};
+
+class SvtURLBox;
+class SvtExpFileDlg_Impl
+{
+private:
+ const SvtFileDialogFilter_Impl* m_pCurFilter;
+ OUString m_sCurrentFilterDisplayName; // may differ from m_pCurFilter->GetName in case it is a cached entry
+
+ css::uno::Sequence< OUString > m_aDenyList;
+
+public:
+ SvtFileDialogFilterList_Impl m_aFilter;
+ std::unique_ptr<SvtFileDialogFilter_Impl> m_xUserFilter;
+
+ std::unique_ptr<weld::Label> m_xFtFileName;
+ std::unique_ptr<SvtURLBox> m_xEdFileName;
+
+ std::unique_ptr<weld::Label> m_xSharedLabel;
+ std::unique_ptr<weld::ComboBox> m_xSharedListBox;
+
+ std::unique_ptr<weld::Label> m_xFtFileType;
+ std::unique_ptr<weld::ComboBox> m_xLbFilter;
+ std::unique_ptr<weld::Button> m_xBtnFileOpen;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+ std::unique_ptr<weld::Button> m_xBtnHelp;
+ std::unique_ptr<SvtUpButton_Impl> m_xBtnUp;
+ std::unique_ptr<weld::Button> m_xBtnNewFolder;
+ std::unique_ptr<weld::CheckButton> m_xCbPassword;
+ std::unique_ptr<weld::CheckButton> m_xCbGPGEncrypt;
+ std::unique_ptr<SvtURLBox> m_xEdCurrentPath;
+ std::unique_ptr<weld::CheckButton> m_xCbAutoExtension;
+ std::unique_ptr<weld::CheckButton> m_xCbOptions;
+
+ std::unique_ptr<PlacesListBox> m_xPlaces;
+ std::unique_ptr<weld::Button> m_xBtnConnectToServer;
+
+ SvtFileDlgMode m_eMode;
+ SvtFileDlgType m_eDlgType;
+ PickerFlags m_nStyle;
+
+ OUString m_aStdDir;
+
+ // delay filter when traveling the filterbox
+ Idle m_aFilterIdle;
+
+ // shows OpenHdl_Imp() if the open was triggered by a double click
+ bool m_bDoubleClick;
+
+ // MultiSelection?
+ bool m_bMultiSelection;
+
+ // remember sizes
+ OUString m_aIniKey;
+
+ explicit SvtExpFileDlg_Impl();
+ ~SvtExpFileDlg_Impl();
+
+ void SetDenyList( const css::uno::Sequence< OUString >& rDenyList ) { m_aDenyList = rDenyList; }
+ const css::uno::Sequence< OUString >& GetDenyList() const { return m_aDenyList; }
+ void SetStandardDir( const OUString& rDir );
+ const OUString& GetStandardDir() const { return m_aStdDir; }
+
+ // access to the filter listbox only as weld::Widget* - we want to maintain the entries/userdata ourself
+ weld::Widget* GetFilterListControl() { return m_xLbFilter.get(); }
+ const weld::Widget* GetFilterListControl() const { return m_xLbFilter.get(); }
+ void SetFilterListSelectHdl(const Link<weld::ComboBox&, void>& rHandler)
+ {
+ m_xLbFilter->connect_changed(rHandler);
+ }
+
+ // inits the listbox for the filters from the filter list (_pFilter)
+ void InitFilterList( );
+ bool HasFilterListEntry( const OUString& rFilterName )
+ {
+ return m_xLbFilter->find_text(rFilterName) != -1;
+ }
+
+ void SelectFilterListEntry( const OUString& rFilterName )
+ {
+ m_xLbFilter->set_active_text(rFilterName);
+ }
+
+ void InsertFilterListEntry( const SvtFileDialogFilter_Impl* _pFilterDesc );
+ // _pFilterDesc must already have been added to _pFilter
+ SvtFileDialogFilter_Impl* GetSelectedFilterEntry( OUString& rDisplayName ) const
+ {
+ rDisplayName = m_xLbFilter->get_active_text();
+ return weld::fromId<SvtFileDialogFilter_Impl*>(m_xLbFilter->get_active_id());
+ }
+
+ // access to the current filter via methods only - need to care for consistency between m_pCurFilter and m_sCurrentFilterDisplayName
+ const SvtFileDialogFilter_Impl* GetCurFilter( ) const
+ {
+ return m_pCurFilter;
+ }
+
+ const OUString& GetCurFilterDisplayName() const
+ {
+ return m_sCurrentFilterDisplayName;
+ }
+
+ void SetCurFilter( SvtFileDialogFilter_Impl const * _pFilter, const OUString& rDisplayName );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/office/pickercallbacks.hxx b/fpicker/source/office/pickercallbacks.hxx
new file mode 100644
index 000000000..dcc82c083
--- /dev/null
+++ b/fpicker/source/office/pickercallbacks.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+namespace weld { class Widget; }
+
+namespace svt
+{
+ class IFilePickerController
+ {
+ public:
+ virtual weld::Widget* getControl( sal_Int16 nControlId, bool bLabelControl = false ) const = 0;
+ virtual void enableControl( sal_Int16 nControlId, bool bEnable ) = 0;
+ virtual OUString getCurFilter( ) const = 0;
+
+ protected:
+ ~IFilePickerController() {}
+ };
+
+ class IFilePickerListener
+ {
+ public:
+ virtual void notify( sal_Int16 nEventId, sal_Int16 nControlId ) = 0;
+
+ protected:
+ ~IFilePickerListener() {}
+ };
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/FilterContainer.cxx b/fpicker/source/win32/FilterContainer.cxx
new file mode 100644
index 000000000..67ccd6353
--- /dev/null
+++ b/fpicker/source/win32/FilterContainer.cxx
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <memory>
+#include <stdexcept>
+#include <osl/diagnose.h>
+#include "FilterContainer.hxx"
+
+#include <utility>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+CFilterContainer::CFilterContainer( sal_Int32 initSize ) :
+ m_vFilters( initSize ),
+ m_bIterInitialized( false )
+{
+}
+
+// add a name/filter pair
+
+bool CFilterContainer::addFilter(
+ const OUString& aName, const OUString& aFilter, bool bAllowDuplicates )
+{
+ // check if the filter is already in the container
+ sal_Int32 pos = -1;
+
+ if ( !bAllowDuplicates )
+ {
+ pos = getFilterTagPos( aName );
+ if ( pos < 0 ) // if not there, append
+ {
+ m_vFilters.push_back( std::make_pair( aName, aFilter ) );
+ m_bIterInitialized = false;
+ }
+ }
+ else
+ {
+ m_vFilters.push_back( std::make_pair( aName, aFilter ) );
+ m_bIterInitialized = false;
+ }
+
+ return pos < 0;
+}
+
+// delete a filter
+// Precondition: the container is not empty
+// there is a filter identified by the given name
+
+bool CFilterContainer::delFilter( const OUString& aName )
+{
+ OSL_ASSERT( !m_vFilters.empty() );
+
+ sal_Int32 pos = getFilterTagPos( aName );
+ if ( pos > -1 )
+ {
+ m_vFilters.erase( m_vFilters.begin() + pos );
+ m_bIterInitialized = false;
+ }
+
+ return pos > -1;
+}
+
+// return the number of filters currently in the container
+
+sal_Int32 CFilterContainer::numFilter( )
+{
+ return m_vFilters.size( );
+}
+
+// clear all entries
+
+void CFilterContainer::empty()
+{
+ m_vFilters.clear( );
+}
+
+// get a filter by name
+// Precondition: the container is not empty
+// there is a filter identified by the name
+
+bool CFilterContainer::getFilterByName(const OUString& aName, OUString& theFilter) const
+{
+ OSL_PRECOND( !m_vFilters.empty() , "Empty filter container" );
+ return getFilterByIndex(getFilterTagPos(aName), theFilter);
+}
+
+bool CFilterContainer::getFilterByIndex(sal_Int32 aIndex, OUString& theFilter) const
+{
+ bool bRet = true;
+
+ try
+ {
+ theFilter = m_vFilters.at(aIndex).second;
+ }
+ catch (std::out_of_range&)
+ {
+ OSL_FAIL("Filter index out of range");
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+bool CFilterContainer::getFilterNameByIndex(sal_Int32 aIndex, OUString& theName) const
+{
+ bool bRet = true;
+
+ try
+ {
+ theName = m_vFilters.at(aIndex).first;
+ }
+ catch( std::out_of_range& )
+ {
+ OSL_FAIL( "Filter index out of range" );
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+sal_Int32 CFilterContainer::getFilterPos( const OUString& aName ) const
+{
+ return getFilterTagPos( aName );
+}
+
+// returns the index of the filter identified by name
+
+sal_Int32 CFilterContainer::getFilterTagPos( const OUString& aName ) const
+{
+ if ( !m_vFilters.empty() )
+ {
+ FILTER_VECTOR_T::const_iterator iter = std::find_if(m_vFilters.begin(), m_vFilters.end(),
+ [&aName](const FILTER_ENTRY_T& rFilter) { return rFilter.first.equalsIgnoreAsciiCase(aName); });
+ if (iter != m_vFilters.end())
+ return std::distance(m_vFilters.begin(), iter);
+ }
+
+ return -1;
+}
+
+// starts enumerating the filter in the container
+
+void CFilterContainer::beginEnumFilter( )
+{
+ m_iter = m_vFilters.begin( );
+ m_bIterInitialized = true;
+}
+
+// returns true if another filter has been retrieved
+
+bool CFilterContainer::getNextFilter( FILTER_ENTRY_T& nextFilterEntry )
+{
+ OSL_ASSERT( m_bIterInitialized );
+
+ bool bRet = ( m_iter != m_vFilters.end( ) );
+
+ if ( bRet )
+ nextFilterEntry = *m_iter++;
+ else
+ m_bIterInitialized = false;
+
+ return bRet;
+}
+
+void CFilterContainer::setCurrentFilter( const OUString& aName )
+{
+ m_sCurrentFilter = aName;
+}
+
+OUString CFilterContainer::getCurrentFilter() const
+{
+ return m_sCurrentFilter;
+}
+
+// calculates the length of a '\0' separated filter, that means
+// length of the name + '\0' + length of the filter string +
+// a trailing '\0'
+
+static sal_uInt32 getLengthFilter( CFilterContainer::FILTER_ENTRY_T aFilterEntry )
+{
+ return (
+ aFilterEntry.first.getLength( ) + 1 +
+ aFilterEntry.second.getLength( ) + 1 );
+}
+
+// calculates the length of all filters currently in the container
+
+static sal_uInt32 getTotalFilterLength( CFilterContainer& aFilterContainer )
+{
+ CFilterContainer::FILTER_ENTRY_T nextFilter;
+
+ aFilterContainer.beginEnumFilter( );
+
+ sal_uInt32 totalLength = 0;
+ while( aFilterContainer.getNextFilter( nextFilter ) )
+ totalLength += getLengthFilter( nextFilter );
+
+ return ( totalLength > 0 ) ? totalLength + 1 : totalLength;
+}
+
+static
+void wcsmemcpy( sal_Unicode* pDest, const sal_Unicode* pSrc, sal_uInt32 nLength )
+{
+ memcpy( pDest, pSrc, nLength * sizeof( sal_Unicode ) );
+}
+
+// a helper trivial helper function to create a filter buffer in the
+// format the Win32 API requires,
+// e.g. "Text\0*.txt\0Doc\0*.doc;*xls\0\0"
+
+OUString makeWinFilterBuffer( CFilterContainer& aFilterContainer )
+{
+ // calculate the required buffer size
+ sal_uInt32 reqBuffSize = getTotalFilterLength( aFilterContainer );
+
+ // return if there are no filters
+ if ( !reqBuffSize )
+ return OUString( );
+
+ auto pBuff = std::make_unique<sal_Unicode[]>(reqBuffSize);
+
+ // initialize the buffer with 0
+ ZeroMemory( pBuff.get(), sizeof( sal_Unicode ) * reqBuffSize );
+
+ OUString winFilterBuff;
+ CFilterContainer::FILTER_ENTRY_T nextFilter;
+ sal_uInt32 memPos = 0;
+
+ aFilterContainer.beginEnumFilter( );
+
+ while( aFilterContainer.getNextFilter( nextFilter ) )
+ {
+ wcsmemcpy(
+ pBuff.get() + memPos,
+ nextFilter.first.getStr( ),
+ nextFilter.first.getLength( ) );
+
+ memPos += nextFilter.first.getLength( ) + 1;
+
+ wcsmemcpy(
+ pBuff.get() + memPos,
+ nextFilter.second.getStr( ),
+ nextFilter.second.getLength( ) );
+
+ memPos += nextFilter.second.getLength( ) + 1 ;
+ }
+
+ winFilterBuff = OUString( pBuff.get(), reqBuffSize );
+
+ return winFilterBuff;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/FilterContainer.hxx b/fpicker/source/win32/FilterContainer.hxx
new file mode 100644
index 000000000..a1e498cef
--- /dev/null
+++ b/fpicker/source/win32/FilterContainer.hxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+#include <vector>
+
+
+// helper class, only usable by OFilterContainer
+
+
+class CFilterContainer
+{
+public:
+ // defines a filter entry which is made of a name and a filter value
+ // e.g. 'Text *.txt'
+ typedef std::pair< OUString, OUString > FILTER_ENTRY_T;
+
+public:
+ explicit CFilterContainer( sal_Int32 initSize = 0 );
+
+ // add a new filter
+ // returns true if the filter was successfully added
+ // returns false if duplicates are not allowed and
+ // the filter is already in the container
+ bool addFilter(
+ const OUString& aName,
+ const OUString& aFilter,
+ bool bAllowDuplicates = false );
+
+ // delete the specified filter returns true on
+ // success and false if the filter was not found
+ bool delFilter( const OUString& aName );
+
+ // the number of filter already added
+ sal_Int32 numFilter( );
+
+ // clear all entries
+ void empty( );
+
+ // retrieve a filter from the container. These methods
+ // return true on success and false if the specified
+ // filter was not found
+ bool getFilterByName(const OUString& aName, OUString& theFilter) const;
+ bool getFilterByIndex(sal_Int32 aIndex, OUString& theFilter) const;
+ bool getFilterNameByIndex(sal_Int32 aIndex, OUString& theName) const;
+
+ // returns the position of the specified filter or -1
+ // if the filter was not found
+ sal_Int32 getFilterPos( const OUString& aName ) const;
+
+ // starts enumerating the filter in the container
+ void beginEnumFilter( );
+
+ // returns true if another filter has been retrieved
+ bool getNextFilter( FILTER_ENTRY_T& nextFilterEntry );
+
+ // cache current filter
+ void setCurrentFilter( const OUString& aName );
+
+ // returns cached current filter
+ OUString getCurrentFilter() const;
+
+protected:
+ typedef std::vector< FILTER_ENTRY_T > FILTER_VECTOR_T;
+
+private:
+ // prevent copy and assignment
+ CFilterContainer( const CFilterContainer& );
+ CFilterContainer& SAL_CALL operator=( const CFilterContainer& );
+
+ sal_Int32 getFilterTagPos( const OUString& aName ) const;
+
+private:
+ FILTER_VECTOR_T m_vFilters;
+ FILTER_VECTOR_T::const_iterator m_iter;
+ bool m_bIterInitialized;
+ OUString m_sCurrentFilter;
+};
+
+
+// a helper function to create a filter buffer in the format
+// the Win32 API requires, e.g. "Text\0*.txt\0Doc\0*.doc;*xls\0\0"
+
+
+OUString makeWinFilterBuffer( CFilterContainer& aFilterContainer );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/IVistaFilePickerInternalNotify.hxx b/fpicker/source/win32/IVistaFilePickerInternalNotify.hxx
new file mode 100644
index 000000000..42f00c196
--- /dev/null
+++ b/fpicker/source/win32/IVistaFilePickerInternalNotify.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <systools/win32/uwinapi.h>
+
+namespace fpicker::win32::vista{
+
+
+// types, const etc.
+
+
+/** todo document me
+ */
+class IVistaFilePickerInternalNotify
+{
+ public:
+
+ virtual void onAutoExtensionChanged (bool bChecked) = 0;
+
+ virtual bool onFileTypeChanged( UINT nTypeIndex ) = 0;
+
+ virtual void onDirectoryChanged() = 0;
+
+ protected:
+ ~IVistaFilePickerInternalNotify() {}
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/VistaFilePicker.cxx b/fpicker/source/win32/VistaFilePicker.cxx
new file mode 100644
index 000000000..dbd503073
--- /dev/null
+++ b/fpicker/source/win32/VistaFilePicker.cxx
@@ -0,0 +1,541 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "VistaFilePicker.hxx"
+
+#include "WinImplHelper.hxx"
+#include "shared.hxx"
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerNotifier.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerListener.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+
+#include <cppuhelper/interfacecontainer.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/processfactory.hxx>
+#include <osl/file.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <shlobj.h>
+
+namespace fpicker{
+namespace win32{
+namespace vista{
+
+VistaFilePicker::VistaFilePicker(bool bFolderPicker)
+ : TVistaFilePickerBase (m_aMutex )
+ , m_bInitialized (false )
+ , m_bFolderPicker (bFolderPicker )
+{
+}
+
+VistaFilePicker::~VistaFilePicker()
+{
+}
+
+void SAL_CALL VistaFilePicker::addFilePickerListener(const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener)
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_ADD_PICKER_LISTENER);
+ rRequest.setArgument(PROP_PICKER_LISTENER, xListener);
+
+ m_rDialog.doRequest(rRequest);
+}
+
+void SAL_CALL VistaFilePicker::removeFilePickerListener(const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener )
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_REMOVE_PICKER_LISTENER);
+ rRequest.setArgument(PROP_PICKER_LISTENER, xListener);
+
+ m_rDialog.doRequest(rRequest);
+}
+
+void VistaFilePicker::disposing(const css::lang::EventObject& /*aEvent*/)
+{
+}
+
+void SAL_CALL VistaFilePicker::setMultiSelectionMode(sal_Bool bMode)
+{
+ ensureInit();
+
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_SET_MULTISELECTION_MODE);
+ rRequest.setArgument(PROP_MULTISELECTION_MODE, bMode);
+
+ m_rDialog.doRequest(rRequest);
+}
+
+void SAL_CALL VistaFilePicker::setTitle(const OUString& sTitle)
+{
+ ensureInit();
+
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_SET_TITLE);
+ rRequest.setArgument(PROP_TITLE, sTitle);
+
+ m_rDialog.doRequest(rRequest);
+}
+
+void SAL_CALL VistaFilePicker::appendFilter(const OUString& sTitle ,
+ const OUString& sFilter)
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_APPEND_FILTER);
+ rRequest.setArgument(PROP_FILTER_TITLE, sTitle );
+ rRequest.setArgument(PROP_FILTER_VALUE, sFilter);
+
+ m_rDialog.doRequest(rRequest);
+}
+
+void SAL_CALL VistaFilePicker::setCurrentFilter(const OUString& sTitle)
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_SET_CURRENT_FILTER);
+ rRequest.setArgument(PROP_FILTER_TITLE, sTitle);
+
+ m_rDialog.doRequest(rRequest);
+}
+
+OUString SAL_CALL VistaFilePicker::getCurrentFilter()
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_GET_CURRENT_FILTER);
+
+ m_rDialog.doRequest(rRequest);
+
+ const OUString sTitle = rRequest.getArgumentOrDefault(PROP_FILTER_TITLE, OUString());
+ return sTitle;
+}
+
+void SAL_CALL VistaFilePicker::appendFilterGroup(const OUString& /*sGroupTitle*/,
+ const css::uno::Sequence< css::beans::StringPair >& rFilters )
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_APPEND_FILTERGROUP);
+ rRequest.setArgument(PROP_FILTER_GROUP, rFilters);
+
+ m_rDialog.doRequest(rRequest);
+}
+
+void SAL_CALL VistaFilePicker::setDefaultName(const OUString& sName )
+{
+ ensureInit();
+
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_SET_DEFAULT_NAME);
+ rRequest.setArgument(PROP_FILENAME, sName);
+
+ m_rDialog.doRequest(rRequest);
+}
+
+void SAL_CALL VistaFilePicker::setDisplayDirectory(const OUString& sDirectory)
+{
+ ensureInit();
+
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_SET_DIRECTORY);
+ rRequest.setArgument(PROP_DIRECTORY, sDirectory);
+
+ m_rDialog.doRequest(rRequest);
+}
+
+OUString SAL_CALL VistaFilePicker::getDisplayDirectory()
+{
+ ensureInit();
+
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_GET_DIRECTORY);
+ m_rDialog.doRequest(rRequest);
+ const OUString sDirectory = rRequest.getArgumentOrDefault(PROP_DIRECTORY, OUString());
+
+ return sDirectory;
+}
+
+// @deprecated can't be supported any longer ... see IDL description for further details
+css::uno::Sequence< OUString > SAL_CALL VistaFilePicker::getFiles()
+{
+ css::uno::Sequence< OUString > lFiles = getSelectedFiles();
+ // multiselection doesn't really work
+ // so just retrieve the first url
+ if (lFiles.getLength() > 1)
+ lFiles.realloc(1);
+ m_lLastFiles = lFiles;
+ return lFiles;
+}
+
+css::uno::Sequence< OUString > SAL_CALL VistaFilePicker::getSelectedFiles()
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_GET_SELECTED_FILES);
+
+ m_rDialog.doRequest(rRequest);
+
+ const css::uno::Sequence< OUString > lFiles = rRequest.getArgumentOrDefault(PROP_SELECTED_FILES, css::uno::Sequence< OUString >());
+ m_lLastFiles = lFiles;
+ return lFiles;
+}
+
+void VistaFilePicker::ensureInit()
+{
+ if ( !m_bInitialized )
+ {
+ if (m_bFolderPicker)
+ {
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_CREATE_FOLDER_PICKER);
+ m_rDialog.doRequest(rRequest);
+ m_bInitialized = true;
+ }
+ else
+ {
+ initialize( { css::uno::Any(css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE) });
+ }
+ }
+}
+
+::sal_Int16 SAL_CALL VistaFilePicker::execute()
+{
+ ensureInit();
+
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_SHOW_DIALOG_MODAL);
+
+ // show a modal window
+ m_rDialog.doRequest(rRequest);
+
+ const bool bOK = rRequest.getArgumentOrDefault(PROP_DIALOG_SHOW_RESULT, false );
+ m_lLastFiles = rRequest.getArgumentOrDefault(PROP_SELECTED_FILES , css::uno::Sequence< OUString >());
+
+ ::sal_Int16 nResult = css::ui::dialogs::ExecutableDialogResults::CANCEL;
+ if (bOK)
+ nResult = css::ui::dialogs::ExecutableDialogResults::OK;
+ return nResult;
+}
+
+// XFilePicker
+
+void SAL_CALL VistaFilePicker::setValue( ::sal_Int16 nControlId ,
+ ::sal_Int16 nControlAction,
+ const css::uno::Any& aValue )
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_SET_CONTROL_VALUE);
+ rRequest.setArgument(PROP_CONTROL_ID , nControlId );
+ rRequest.setArgument(PROP_CONTROL_ACTION, nControlAction);
+ rRequest.setArgument(PROP_CONTROL_VALUE , aValue );
+
+ m_rDialog.doRequest(rRequest);
+}
+
+css::uno::Any SAL_CALL VistaFilePicker::getValue(::sal_Int16 nControlId ,
+ ::sal_Int16 nControlAction)
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_GET_CONTROL_VALUE);
+ rRequest.setArgument(PROP_CONTROL_ID , nControlId );
+ rRequest.setArgument(PROP_CONTROL_ACTION, nControlAction);
+
+ m_rDialog.doRequest(rRequest);
+ return rRequest.getValue(PROP_CONTROL_VALUE);
+}
+
+void SAL_CALL VistaFilePicker::enableControl(::sal_Int16 nControlId,
+ sal_Bool bEnable )
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_ENABLE_CONTROL);
+ rRequest.setArgument(PROP_CONTROL_ID , nControlId);
+ rRequest.setArgument(PROP_CONTROL_ENABLE, bEnable );
+
+ m_rDialog.doRequest(rRequest);
+}
+
+void SAL_CALL VistaFilePicker::setLabel( ::sal_Int16 nControlId,
+ const OUString& sLabel )
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_SET_CONTROL_LABEL);
+ rRequest.setArgument(PROP_CONTROL_ID , nControlId);
+ rRequest.setArgument(PROP_CONTROL_LABEL, sLabel );
+
+ m_rDialog.doRequest(rRequest);
+}
+
+OUString SAL_CALL VistaFilePicker::getLabel(::sal_Int16 nControlId)
+{
+ Request rRequest;
+ rRequest.setRequest (VistaFilePickerImpl::E_GET_CONTROL_LABEL);
+ rRequest.setArgument(PROP_CONTROL_ID, nControlId);
+
+ m_rDialog.doRequest(rRequest);
+ const OUString sLabel = rRequest.getArgumentOrDefault(PROP_CONTROL_LABEL, OUString());
+ return sLabel;
+}
+
+css::uno::Sequence< ::sal_Int16 > SAL_CALL VistaFilePicker::getSupportedImageFormats()
+{
+ return css::uno::Sequence< sal_Int16 >();
+}
+
+sal_Int32 SAL_CALL VistaFilePicker::getTargetColorDepth()
+{
+ return 0;
+}
+
+sal_Int32 SAL_CALL VistaFilePicker::getAvailableWidth()
+{
+ return 0;
+}
+
+sal_Int32 SAL_CALL VistaFilePicker::getAvailableHeight()
+{
+ return 0;
+}
+
+void SAL_CALL VistaFilePicker::setImage( sal_Int16 /*nImageFormat*/,
+ const css::uno::Any& /*aImage */)
+{
+}
+
+sal_Bool SAL_CALL VistaFilePicker::setShowState(sal_Bool /*bShowState*/)
+{
+ return false;
+}
+
+sal_Bool SAL_CALL VistaFilePicker::getShowState()
+{
+ return false;
+}
+
+void SAL_CALL VistaFilePicker::initialize(const css::uno::Sequence< css::uno::Any >& lArguments)
+{
+ if (lArguments.getLength() < 1)
+ throw css::lang::IllegalArgumentException(
+ "XInitialization::initialize() called without arguments.",
+ static_cast< css::ui::dialogs::XFilePicker2* >( this ),
+ 1);
+
+ sal_Int32 nTemplate = -1;
+ lArguments[0] >>= nTemplate;
+
+ bool bFileOpenDialog = true;
+ ::sal_Int32 nFeatures = 0;
+
+ switch(nTemplate)
+ {
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE :
+ {
+ bFileOpenDialog = true;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILESAVE_SIMPLE :
+ {
+ bFileOpenDialog = false;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD :
+ {
+ bFileOpenDialog = false;
+ nFeatures |= FEATURE_AUTOEXTENSION;
+ nFeatures |= FEATURE_PASSWORD;
+ nFeatures |= FEATURE_GPGPASSWORD;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS :
+ {
+ bFileOpenDialog = false;
+ nFeatures |= FEATURE_AUTOEXTENSION;
+ nFeatures |= FEATURE_PASSWORD;
+ nFeatures |= FEATURE_FILTEROPTIONS;
+ nFeatures |= FEATURE_GPGPASSWORD;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_SELECTION :
+ {
+ bFileOpenDialog = false;
+ nFeatures |= FEATURE_AUTOEXTENSION;
+ nFeatures |= FEATURE_SELECTION;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_TEMPLATE :
+ {
+ bFileOpenDialog = false;
+ nFeatures |= FEATURE_AUTOEXTENSION;
+ nFeatures |= FEATURE_TEMPLATE;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE :
+ {
+ bFileOpenDialog = true;
+ nFeatures |= FEATURE_LINK;
+ nFeatures |= FEATURE_PREVIEW;
+ nFeatures |= FEATURE_IMAGETEMPLATE;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR :
+ {
+ bFileOpenDialog = true;
+ nFeatures |= FEATURE_LINK;
+ nFeatures |= FEATURE_PREVIEW;
+ nFeatures |= FEATURE_IMAGEANCHOR;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_PLAY :
+ {
+ bFileOpenDialog = true;
+ nFeatures |= FEATURE_PLAY;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PLAY :
+ {
+ bFileOpenDialog = true;
+ nFeatures |= FEATURE_LINK;
+ nFeatures |= FEATURE_PLAY;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION :
+ {
+ bFileOpenDialog = true;
+ nFeatures |= FEATURE_READONLY;
+ nFeatures |= FEATURE_VERSION;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW :
+ {
+ bFileOpenDialog = true;
+ nFeatures |= FEATURE_LINK;
+ nFeatures |= FEATURE_PREVIEW;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION :
+ {
+ bFileOpenDialog = false;
+ nFeatures |= FEATURE_AUTOEXTENSION;
+ }
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_PREVIEW :
+ {
+ bFileOpenDialog = true;
+ nFeatures |= FEATURE_PREVIEW;
+ }
+ break;
+ }
+ css::uno::Reference<css::awt::XWindow> xParentWindow;
+ if(lArguments.getLength() > 1)
+ {
+ lArguments[1] >>= xParentWindow;
+ }
+ Request rRequest;
+ if (bFileOpenDialog)
+ {
+ if (!m_bFolderPicker)
+ rRequest.setRequest(VistaFilePickerImpl::E_CREATE_OPEN_DIALOG);
+ else
+ rRequest.setRequest(VistaFilePickerImpl::E_CREATE_FOLDER_PICKER);
+ }
+ else
+ rRequest.setRequest (VistaFilePickerImpl::E_CREATE_SAVE_DIALOG);
+ rRequest.setArgument(PROP_FEATURES, nFeatures);
+ rRequest.setArgument(PROP_TEMPLATE_DESCR, nTemplate);
+ if(xParentWindow.is())
+ rRequest.setArgument(PROP_PARENT_WINDOW, xParentWindow);
+ m_rDialog.doRequest(rRequest);
+
+ m_bInitialized = true;
+}
+
+void SAL_CALL VistaFilePicker::cancel()
+{
+}
+
+OUString SAL_CALL VistaFilePicker::getDirectory()
+{
+ ensureInit();
+ css::uno::Sequence< OUString > aFileSeq = getSelectedFiles();
+ assert(aFileSeq.getLength() <= 1);
+ return aFileSeq.getLength() ? aFileSeq[0] : OUString();
+}
+
+void SAL_CALL VistaFilePicker::setDescription( const OUString& aDescription )
+{
+ setTitle(aDescription);
+}
+
+// XServiceInfo
+
+OUString SAL_CALL VistaFilePicker::getImplementationName()
+{
+ if (m_bFolderPicker)
+ return "com.sun.star.ui.dialogs.Win32FolderPicker";
+ else
+ return "com.sun.star.ui.dialogs.Win32FilePicker";
+}
+
+sal_Bool SAL_CALL VistaFilePicker::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL VistaFilePicker::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.ui.dialogs.FilePicker",
+ "com.sun.star.ui.dialogs.SystemFilePicker",
+ "com.sun.star.ui.dialogs.SystemFolderPicker" };
+}
+
+} // namespace vista
+} // namespace win32
+} // namespace fpicker
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_win32_FilePicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ::fpicker::win32::vista::VistaFilePicker(false));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+fpicker_win32_FolderPicker_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ::fpicker::win32::vista::VistaFilePicker(true));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/VistaFilePicker.hxx b/fpicker/source/win32/VistaFilePicker.hxx
new file mode 100644
index 000000000..db4235a1d
--- /dev/null
+++ b/fpicker/source/win32/VistaFilePicker.hxx
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "requests.hxx"
+#include "VistaFilePickerImpl.hxx"
+#include "VistaFilePickerEventHandler.hxx"
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/ui/dialogs/XFilePreview.hpp>
+#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <rtl/ustring.hxx>
+
+namespace fpicker{
+namespace win32{
+namespace vista{
+
+
+// types
+
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::ui::dialogs::XFilePicker3,
+ css::ui::dialogs::XFilePickerControlAccess,
+ css::ui::dialogs::XFilePreview,
+ css::ui::dialogs::XFolderPicker2,
+ css::lang::XInitialization,
+ css::lang::XServiceInfo > TVistaFilePickerBase;
+
+
+/** Implements the XFilePicker & friends interface(s)
+ for Windows Vista and upcoming versions.
+
+ Note: This will be a UNO wrapper for the real file picker
+ implementation only. The real implementation is done in class
+ VistaFilePickerImpl.
+ */
+class VistaFilePicker : public ::cppu::BaseMutex
+ , public TVistaFilePickerBase
+{
+public:
+
+
+ // ctor/dtor
+
+
+ explicit VistaFilePicker( bool bFolderPicker );
+ virtual ~VistaFilePicker() override;
+
+
+ // 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& sTitle ) 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& sName ) override;
+
+ virtual void SAL_CALL setDisplayDirectory( const OUString& sDirectory ) override;
+
+ virtual OUString SAL_CALL getDisplayDirectory( ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getFiles( ) override;
+
+ // XFilePicker2 functions
+ virtual css::uno::Sequence< OUString > SAL_CALL getSelectedFiles( ) override;
+
+
+ // XFilterManager functions
+
+
+ virtual void SAL_CALL appendFilter( const OUString& sTitle ,
+ const OUString& sFilter ) override;
+
+ virtual void SAL_CALL setCurrentFilter( const OUString& sTitle ) override;
+
+ virtual OUString SAL_CALL getCurrentFilter( ) override;
+
+
+ // XFilterGroupManager functions
+
+
+ virtual void SAL_CALL appendFilterGroup( const OUString& sGroupTitle,
+ const css::uno::Sequence< css::beans::StringPair >& lFilters ) override;
+
+
+ // XFilePickerControlAccess functions
+
+
+ virtual void SAL_CALL setValue( sal_Int16 nControlId ,
+ sal_Int16 nControlAction,
+ const css::uno::Any& aValue ) 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& sLabel ) override;
+
+ virtual OUString SAL_CALL getLabel( sal_Int16 nControlId ) override;
+
+
+ // XFilePreview
+
+
+ virtual css::uno::Sequence< sal_Int16 > SAL_CALL getSupportedImageFormats( ) override;
+
+ virtual sal_Int32 SAL_CALL getTargetColorDepth( ) override;
+
+ virtual sal_Int32 SAL_CALL getAvailableWidth( ) override;
+
+ virtual sal_Int32 SAL_CALL getAvailableHeight( ) override;
+
+ virtual void SAL_CALL setImage( sal_Int16 nImageFormat,
+ const css::uno::Any& aImage ) override;
+
+ virtual sal_Bool SAL_CALL setShowState( sal_Bool bShowState ) override;
+
+ virtual sal_Bool SAL_CALL getShowState( ) override;
+
+
+ // XInitialization
+
+
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& lArguments ) override;
+
+
+ // XCancellable
+
+
+ virtual void SAL_CALL cancel( ) override;
+
+
+ // XEventListener
+
+ /// @throws css::uno::RuntimeException
+ virtual void disposing( const css::lang::EventObject& aEvent );
+
+
+ // XServiceInfo
+
+
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+
+ // XFolderPicker functions
+
+
+ virtual OUString SAL_CALL getDirectory( ) override;
+
+ virtual void SAL_CALL setDescription( const OUString& aDescription ) override;
+
+
+ private:
+
+ // prevent copy and assignment
+ VistaFilePicker( const VistaFilePicker& );
+ VistaFilePicker& operator=( const VistaFilePicker& );
+
+ using WeakComponentImplHelperBase::disposing;
+
+ void ensureInit();
+
+ private:
+
+ css::uno::Sequence< OUString > m_lLastFiles;
+
+ VistaFilePickerImpl m_rDialog;
+
+ bool m_bInitialized;
+ const bool m_bFolderPicker;
+};
+
+} // namespace vista
+} // namespace win32
+} // namespace fpicker
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/VistaFilePickerEventHandler.cxx b/fpicker/source/win32/VistaFilePickerEventHandler.cxx
new file mode 100644
index 000000000..97c0c9fbd
--- /dev/null
+++ b/fpicker/source/win32/VistaFilePickerEventHandler.cxx
@@ -0,0 +1,313 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "VistaFilePickerEventHandler.hxx"
+
+#include "requests.hxx"
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/document/XDocumentRevisionListPersistence.hpp>
+#include <com/sun/star/util/RevisionTag.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+
+#include <osl/file.hxx>
+
+
+// namespace directives
+
+
+namespace fpicker{
+namespace win32{
+namespace vista{
+
+
+VistaFilePickerEventHandler::VistaFilePickerEventHandler(IVistaFilePickerInternalNotify* pInternalNotify)
+ : m_nRefCount (0 )
+ , m_nListenerHandle (0 )
+ , m_pDialog ( )
+ , m_pInternalNotify (pInternalNotify)
+ , m_lListener (m_aMutex)
+{
+}
+
+
+VistaFilePickerEventHandler::~VistaFilePickerEventHandler()
+{
+}
+
+
+HRESULT STDMETHODCALLTYPE VistaFilePickerEventHandler::QueryInterface(REFIID rIID ,
+ void** ppObject)
+{
+ *ppObject=nullptr;
+
+ if ( rIID == IID_IUnknown )
+ *ppObject = static_cast<IUnknown*>(static_cast<IFileDialogEvents*>(this));
+
+ if ( rIID == IID_IFileDialogEvents )
+ *ppObject = static_cast<IFileDialogEvents*>(this);
+
+ if ( rIID == IID_IFileDialogControlEvents )
+ *ppObject = static_cast<IFileDialogControlEvents*>(this);
+
+ if ( *ppObject != nullptr )
+ {
+ static_cast<IUnknown*>(*ppObject)->AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+
+ULONG STDMETHODCALLTYPE VistaFilePickerEventHandler::AddRef()
+{
+ return osl_atomic_increment(&m_nRefCount);
+}
+
+
+ULONG STDMETHODCALLTYPE VistaFilePickerEventHandler::Release()
+{
+ ULONG nReturn = --m_nRefCount;
+ if ( m_nRefCount == 0 )
+ delete this;
+
+ return nReturn;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnFileOk(IFileDialog* /*pDialog*/)
+{
+ return E_NOTIMPL;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnFolderChanging(IFileDialog* /*pDialog*/,
+ IShellItem* /*pFolder*/)
+{
+ return E_NOTIMPL;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnFolderChange(IFileDialog* /*pDialog*/)
+{
+ impl_sendEvent(E_DIRECTORY_CHANGED, 0);
+ m_pInternalNotify->onDirectoryChanged();
+ return S_OK;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnSelectionChange(IFileDialog* /*pDialog*/)
+{
+ impl_sendEvent(E_FILE_SELECTION_CHANGED, 0);
+ return S_OK;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnShareViolation(IFileDialog* /*pDialog*/ ,
+
+ IShellItem* /*pItem*/ ,
+
+ FDE_SHAREVIOLATION_RESPONSE* /*pResponse*/)
+{
+ impl_sendEvent(E_CONTROL_STATE_CHANGED, css::ui::dialogs::CommonFilePickerElementIds::LISTBOX_FILTER);
+ return S_OK;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnTypeChange(IFileDialog* pDialog)
+{
+ UINT nFileTypeIndex;
+ HRESULT hResult = pDialog->GetFileTypeIndex( &nFileTypeIndex );
+
+ if ( hResult == S_OK )
+ {
+ if ( m_pInternalNotify->onFileTypeChanged( nFileTypeIndex ))
+ impl_sendEvent(E_CONTROL_STATE_CHANGED, css::ui::dialogs::CommonFilePickerElementIds::LISTBOX_FILTER);
+ }
+
+ return S_OK;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnOverwrite(IFileDialog* /*pDialog*/ ,
+ IShellItem* /*pItem*/ ,
+ FDE_OVERWRITE_RESPONSE* /*pResponse*/)
+{
+ return E_NOTIMPL;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnItemSelected(IFileDialogCustomize* /*pCustomize*/,
+
+ DWORD nIDCtl ,
+
+ DWORD /*nIDItem*/ )
+{
+
+ impl_sendEvent(E_CONTROL_STATE_CHANGED, static_cast<sal_Int16>( nIDCtl ));
+ return S_OK;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnButtonClicked(IFileDialogCustomize* /*pCustomize*/,
+ DWORD nIDCtl )
+{
+
+ impl_sendEvent(E_CONTROL_STATE_CHANGED, static_cast<sal_Int16>( nIDCtl));
+ return S_OK;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnCheckButtonToggled(IFileDialogCustomize* /*pCustomize*/,
+ DWORD nIDCtl ,
+ BOOL bChecked )
+{
+ if (nIDCtl == css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION)
+ m_pInternalNotify->onAutoExtensionChanged(bChecked);
+
+ impl_sendEvent(E_CONTROL_STATE_CHANGED, static_cast<sal_Int16>( nIDCtl));
+
+ return S_OK;
+}
+
+
+STDMETHODIMP VistaFilePickerEventHandler::OnControlActivating(IFileDialogCustomize* /*pCustomize*/,
+ DWORD nIDCtl )
+{
+ impl_sendEvent(E_CONTROL_STATE_CHANGED, static_cast<sal_Int16>( nIDCtl));
+ return S_OK;
+}
+
+
+void VistaFilePickerEventHandler::addFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener )
+{
+ m_lListener.addInterface(cppu::UnoType<css::ui::dialogs::XFilePickerListener>::get(), xListener);
+}
+
+
+void VistaFilePickerEventHandler::removeFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener )
+{
+ m_lListener.removeInterface(cppu::UnoType<css::ui::dialogs::XFilePickerListener>::get(), xListener);
+}
+
+
+void VistaFilePickerEventHandler::startListening( const TFileDialog& pBroadcaster )
+{
+ if (m_pDialog.is())
+ return;
+
+ m_pDialog = pBroadcaster;
+ m_pDialog->Advise(this, &m_nListenerHandle);
+}
+
+
+void VistaFilePickerEventHandler::stopListening()
+{
+ if (m_pDialog.is())
+ {
+ m_pDialog->Unadvise(m_nListenerHandle);
+ m_pDialog.clear();
+ }
+}
+
+const OUStringLiteral PROP_CONTROL_ID = u"control_id";
+const OUStringLiteral PROP_PICKER_LISTENER = u"picker_listener";
+
+namespace {
+
+void doRequest(Request& rRequest)
+{
+ const ::sal_Int32 nEventID = rRequest.getRequest();
+ const ::sal_Int16 nControlID = rRequest.getArgumentOrDefault(PROP_CONTROL_ID, ::sal_Int16(0));
+ const css::uno::Reference< css::ui::dialogs::XFilePickerListener > xListener = rRequest.getArgumentOrDefault(PROP_PICKER_LISTENER, css::uno::Reference< css::ui::dialogs::XFilePickerListener >());
+
+ if ( ! xListener.is())
+ return;
+
+ css::ui::dialogs::FilePickerEvent aEvent;
+ aEvent.ElementId = nControlID;
+
+ switch (nEventID)
+ {
+ case VistaFilePickerEventHandler::E_FILE_SELECTION_CHANGED :
+ xListener->fileSelectionChanged(aEvent);
+ break;
+
+ case VistaFilePickerEventHandler::E_DIRECTORY_CHANGED :
+ xListener->directoryChanged(aEvent);
+ break;
+
+ case VistaFilePickerEventHandler::E_HELP_REQUESTED :
+ xListener->helpRequested(aEvent);
+ break;
+
+ case VistaFilePickerEventHandler::E_CONTROL_STATE_CHANGED :
+ xListener->controlStateChanged(aEvent);
+ break;
+
+ case VistaFilePickerEventHandler::E_DIALOG_SIZE_CHANGED :
+ xListener->dialogSizeChanged();
+ break;
+
+ // no default here. Let compiler detect changes on enum set !
+ }
+}
+
+}
+
+void VistaFilePickerEventHandler::impl_sendEvent( EEventType eEventType,
+ ::sal_Int16 nControlID)
+{
+ comphelper::OInterfaceContainerHelper2* pContainer = m_lListener.getContainer( cppu::UnoType<css::ui::dialogs::XFilePickerListener>::get());
+ if ( ! pContainer)
+ return;
+
+ comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
+ while (pIterator.hasMoreElements())
+ {
+ try
+ {
+ css::uno::Reference< css::ui::dialogs::XFilePickerListener > xListener (
+ static_cast< css::ui::dialogs::XFilePickerListener* >(pIterator.next()));
+
+ Request rRequest;
+ rRequest.setRequest (eEventType);
+ rRequest.setArgument(PROP_PICKER_LISTENER, xListener);
+ if ( nControlID )
+ rRequest.setArgument(PROP_CONTROL_ID, nControlID);
+
+ doRequest(rRequest);
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ pIterator.remove();
+ }
+ }
+}
+
+} // namespace vista
+} // namespace win32
+} // namespace fpicker
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/VistaFilePickerEventHandler.hxx b/fpicker/source/win32/VistaFilePickerEventHandler.hxx
new file mode 100644
index 000000000..b27ef7326
--- /dev/null
+++ b/fpicker/source/win32/VistaFilePickerEventHandler.hxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <shobjidl.h>
+
+#include "vistatypes.h"
+#include "IVistaFilePickerInternalNotify.hxx"
+
+#include <com/sun/star/ui/dialogs/XFilePickerListener.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <cppuhelper/basemutex.hxx>
+#include <comphelper/multicontainer2.hxx>
+#include <osl/interlck.h>
+
+namespace fpicker{
+namespace win32{
+namespace vista{
+
+
+// types, const etcpp.
+
+
+/** todo document me
+ */
+class VistaFilePickerEventHandler : public ::cppu::BaseMutex
+ , public IFileDialogEvents
+ , public IFileDialogControlEvents
+{
+ public:
+
+
+ // ctor/dtor
+
+
+ explicit VistaFilePickerEventHandler(IVistaFilePickerInternalNotify* pInternalNotify);
+ virtual ~VistaFilePickerEventHandler();
+
+
+ // IUnknown
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID rIID ,
+ void** ppObject) override;
+ virtual ULONG STDMETHODCALLTYPE AddRef() override;
+ virtual ULONG STDMETHODCALLTYPE Release() override;
+
+
+ // IFileDialogEvents
+
+
+ STDMETHODIMP OnFileOk(IFileDialog* pDialog) override;
+
+ STDMETHODIMP OnFolderChanging(IFileDialog* pDialog,
+ IShellItem* pFolder) override;
+
+ STDMETHODIMP OnFolderChange(IFileDialog* pDialog) override;
+
+ STDMETHODIMP OnSelectionChange(IFileDialog* pDialog) override;
+
+ STDMETHODIMP OnShareViolation(IFileDialog* pDialog ,
+ IShellItem* pItem ,
+ FDE_SHAREVIOLATION_RESPONSE* pResponse) override;
+
+ STDMETHODIMP OnTypeChange(IFileDialog* pDialog) override;
+
+ STDMETHODIMP OnOverwrite(IFileDialog* pDialog ,
+ IShellItem* pItem ,
+ FDE_OVERWRITE_RESPONSE* pResponse) override;
+
+
+ // IFileDialogControlEvents
+
+
+ STDMETHODIMP OnItemSelected(IFileDialogCustomize* pCustomize,
+ DWORD nIDCtl ,
+ DWORD nIDItem ) override;
+
+ STDMETHODIMP OnButtonClicked(IFileDialogCustomize* pCustomize,
+ DWORD nIDCtl ) override;
+
+ STDMETHODIMP OnCheckButtonToggled(IFileDialogCustomize* pCustomize,
+ DWORD nIDCtl ,
+ BOOL bChecked ) override;
+
+ STDMETHODIMP OnControlActivating(IFileDialogCustomize* pCustomize,
+ DWORD nIDCtl ) override;
+
+
+ // XFilePickerNotifier
+
+ /// @throws css::uno::RuntimeException
+ virtual void addFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener );
+
+ /// @throws css::uno::RuntimeException
+ virtual void removeFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener );
+
+
+ // native interface
+
+
+ /** start listening for file picker events on the given file open dialog COM object.
+ *
+ * The broadcaster will be cached internally so deregistration will be easy.
+ * Further all needed information is capsulated within this class (e.g. the listener handler).
+ * Nobody outside must know such information.
+ *
+ * Nothing will happen if an inconsistent state will be detected
+ * (means: double registration will be ignored).
+ *
+ * @param pBroadcaster
+ * reference to the dialog, where we should start listening.
+ */
+ void startListening( const TFileDialog& pBroadcaster );
+
+
+ /** stop listening for file picker events on the internally cached dialog COM object.
+ *
+ * The COM dialog provided on the startListening() call was cached internally.
+ * And now it's used to deregister this listener. Doing so the also internally cached
+ * listener handle is used. If listener was not already registered - nothing will happen.
+ */
+ void stopListening();
+
+ public:
+
+ enum EEventType
+ {
+ E_FILE_SELECTION_CHANGED,
+ E_DIRECTORY_CHANGED,
+ E_HELP_REQUESTED,
+ E_CONTROL_STATE_CHANGED,
+ E_DIALOG_SIZE_CHANGED
+ };
+
+ private:
+
+
+ /// @todo document me
+ void impl_sendEvent( EEventType eEventType,
+ ::sal_Int16 nControlID);
+
+ private:
+
+
+ /// ref count for AddRef/Release()
+ oslInterlockedCount m_nRefCount;
+
+
+ /// unique handle for this listener provided by the broadcaster on registration time
+ DWORD m_nListenerHandle;
+
+
+ /// cached file dialog instance (there we listen for events)
+ TFileDialog m_pDialog;
+
+
+ IVistaFilePickerInternalNotify* m_pInternalNotify;
+
+
+ /** used to inform file picker listener asynchronously.
+ * Those listener must be called asynchronously .. because
+ * every request will block the caller thread. Mostly that will be
+ * the main thread of the office. Further the global SolarMutex will
+ * be locked during this time. If we call our listener back now synchronously ..
+ * we will block on SolarMutex.acquire() forever .-))
+ */
+ comphelper::OMultiTypeInterfaceContainerHelper2 m_lListener;
+};
+
+} // namespace vista
+} // namespace win32
+} // namespace fpicker
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/VistaFilePickerImpl.cxx b/fpicker/source/win32/VistaFilePickerImpl.cxx
new file mode 100644
index 000000000..741fdadb6
--- /dev/null
+++ b/fpicker/source/win32/VistaFilePickerImpl.cxx
@@ -0,0 +1,1249 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include "VistaFilePickerImpl.hxx"
+
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ControlActions.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XSystemDependentWindowPeer.hpp>
+#include <com/sun/star/lang/SystemDependent.hpp>
+#include <comphelper/sequence.hxx>
+#include <fpicker/strings.hrc>
+#include <fpicker/fpsofficeResMgr.hxx>
+#include <osl/file.hxx>
+#include <rtl/process.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/string_view.hxx>
+#include <vcl/svapp.hxx>
+#include "WinImplHelper.hxx"
+
+#include <shlguid.h>
+#include <shlobj.h>
+
+static bool is_current_process_window(HWND hwnd)
+{
+ DWORD pid;
+ GetWindowThreadProcessId(hwnd, &pid);
+ return (pid == GetCurrentProcessId());
+}
+
+static HWND choose_parent_window()
+{
+ HWND hwnd_parent = GetForegroundWindow();
+ if (!is_current_process_window(hwnd_parent))
+ hwnd_parent = GetDesktopWindow();
+ return hwnd_parent;
+}
+
+namespace {
+
+bool createFolderItem(OUString const& url, sal::systools::COMReference<IShellItem>& folder)
+{
+ OUString path;
+ if (osl::FileBase::getSystemPathFromFileURL(url, path)
+ != osl::FileBase::E_None)
+ {
+ return false;
+ }
+ HRESULT res = SHCreateItemFromParsingName(
+ o3tl::toW(path.getStr()), nullptr,
+ IID_PPV_ARGS(&folder));
+ return SUCCEEDED(res);
+}
+
+}
+
+namespace fpicker{
+namespace win32{
+namespace vista{
+
+
+// types, const etcpp.
+
+
+const ::sal_Int16 INVALID_CONTROL_ID = -1;
+const ::sal_Int16 INVALID_CONTROL_ACTION = -1;
+
+// Guids used for IFileDialog::SetClientGuid
+const GUID CLIENTID_FILEDIALOG_SIMPLE = {0xB8628FD3, 0xA3F5, 0x4845, 0x9B, 0x62, 0xD5, 0x1E, 0xDF, 0x97, 0xC4, 0x83};
+const GUID CLIENTID_FILEDIALOG_OPTIONS = {0x93ED486F, 0x0D04, 0x4807, 0x8C, 0x44, 0xAC, 0x26, 0xCB, 0x6C, 0x5D, 0x36};
+const GUID CLIENTID_FILESAVE_PASSWORD = {0xC12D4F4C, 0x4D41, 0x4D4F, 0x97, 0xEF, 0x87, 0xF9, 0x8D, 0xB6, 0x1E, 0xA6};
+const GUID CLIENTID_FILESAVE_SELECTION = {0x5B2482B3, 0x0358, 0x4E09, 0xAA, 0x64, 0x2B, 0x76, 0xB2, 0xA0, 0xDD, 0xFE};
+const GUID CLIENTID_FILESAVE_TEMPLATE = {0x9996D877, 0x20D5, 0x424B, 0x9C, 0x2E, 0xD3, 0xB6, 0x31, 0xEC, 0xF7, 0xCE};
+const GUID CLIENTID_FILEOPEN_LINK_TEMPLATE = {0x32237796, 0x1509, 0x49D1, 0xBB, 0x7E, 0x63, 0xAD, 0x36, 0xAE, 0x86, 0x8C};
+const GUID CLIENTID_FILEOPEN_LINK_ANCHOR = {0xBE3188CB, 0x399A, 0x45AE, 0x8F, 0x78, 0x75, 0x17, 0xAF, 0x26, 0x81, 0xEA};
+const GUID CLIENTID_FILEOPEN_PLAY = {0x32CFB147, 0xF5AE, 0x4F90, 0xA1, 0xF1, 0x81, 0x20, 0x72, 0xBB, 0x2F, 0xC5};
+const GUID CLIENTID_FILEOPEN_LINK = {0x39AC4BAE, 0x7D2D, 0x46BC, 0xBE, 0x2E, 0xF8, 0x8C, 0xB5, 0x65, 0x5E, 0x6A};
+
+
+class TDialogImplBase
+{
+public:
+ TDialogImplBase(IFileDialog* iDialog)
+ : m_iDialog(iDialog)
+ {
+ }
+
+ virtual ~TDialogImplBase() = default;
+
+ TFileDialog getComPtr() { return m_iDialog; }
+ virtual sal::systools::COMReference<IShellItemArray> getResult(bool bInExecute)
+ {
+ sal::systools::COMReference<IShellItem> iItem;
+ if (m_iDialog.is())
+ {
+ if (bInExecute)
+ m_iDialog->GetCurrentSelection(&iItem);
+ else
+ m_iDialog->GetResult(&iItem);
+ }
+ void* iItems = nullptr;
+ if (iItem.is())
+ SHCreateShellItemArrayFromShellItem(iItem.get(), IID_IShellItemArray, &iItems);
+ return static_cast<IShellItemArray*>(iItems);
+ }
+
+private:
+ TFileDialog m_iDialog;
+};
+
+namespace {
+
+template <class ComPtrDialog, REFCLSID CLSID> class TDialogImpl : public TDialogImplBase
+{
+public:
+ TDialogImpl()
+ : TDialogImplBase(ComPtrDialog(CLSID).get())
+ {
+ }
+};
+
+class TOpenDialogImpl : public TDialogImpl<TFileOpenDialog, CLSID_FileOpenDialog>
+{
+public:
+ sal::systools::COMReference<IShellItemArray> getResult(bool bInExecute) override
+ {
+ sal::systools::COMReference<IShellItemArray> iItems;
+ TFileOpenDialog iDialog(getComPtr(), sal::systools::COM_QUERY_THROW);
+ bool bGetResult = false;
+ if (!iDialog.is())
+ bGetResult = true;
+ else if (FAILED(bInExecute ? iDialog->GetSelectedItems(&iItems) : iDialog->GetResults(&iItems)))
+ bGetResult = true;
+
+ if (bGetResult)
+ iItems = TDialogImplBase::getResult(bInExecute);
+
+ return iItems;
+ }
+};
+
+}
+
+using TSaveDialogImpl = TDialogImpl<TFileSaveDialog, CLSID_FileSaveDialog>;
+using TFolderPickerDialogImpl = TDialogImpl<TFileOpenDialog, CLSID_FileOpenDialog>;
+
+
+static OUString lcl_getURLFromShellItem (IShellItem* pItem)
+{
+ LPWSTR pStr = nullptr;
+ OUString sURL;
+ HRESULT hr;
+
+ hr = pItem->GetDisplayName ( SIGDN_FILESYSPATH, &pStr );
+ if (SUCCEEDED(hr))
+ {
+ ::osl::FileBase::getFileURLFromSystemPath( OUString(o3tl::toU(pStr)), sURL );
+ goto cleanup;
+ }
+
+ hr = pItem->GetDisplayName ( SIGDN_URL, &pStr );
+ if (SUCCEEDED(hr))
+ {
+ sURL = o3tl::toU(pStr);
+ goto cleanup;
+ }
+
+ hr = pItem->GetDisplayName ( SIGDN_PARENTRELATIVEPARSING, &pStr );
+ if (SUCCEEDED(hr))
+ {
+ GUID known_folder_id;
+ std::wstring aStr = pStr;
+ CoTaskMemFree (pStr);
+
+ if (0 == aStr.compare(0, 3, L"::{"))
+ aStr = aStr.substr(2);
+ hr = IIDFromString(aStr.c_str(), &known_folder_id);
+ if (SUCCEEDED(hr))
+ {
+ hr = SHGetKnownFolderPath(known_folder_id, 0, nullptr, &pStr);
+ if (SUCCEEDED(hr))
+ {
+ ::osl::FileBase::getFileURLFromSystemPath(OUString(o3tl::toU(pStr)), sURL);
+ goto cleanup;
+ }
+ }
+ }
+
+ // Default fallback
+ hr = SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &pStr);
+ if (SUCCEEDED(hr))
+ ::osl::FileBase::getFileURLFromSystemPath(OUString(o3tl::toU(pStr)), sURL);
+ else // shouldn't happen...
+ goto bailout;
+
+cleanup:
+ CoTaskMemFree (pStr);
+bailout:
+ return sURL;
+}
+
+// Vista file picker shows the filter mask next to filter name in the list; so we need to remove the
+// mask from the filter name to avoid duplicating masks
+static OUString lcl_AdjustFilterName(const OUString& sName)
+{
+ const sal_Int32 idx = sName.indexOf("(.");
+ return (idx > 0) ? OUString(o3tl::trim(sName.subView(0, idx))) : sName;
+}
+
+// rvStrings holds the OUStrings, pointers to which data are stored in returned COMDLG_FILTERSPEC
+static ::std::vector<COMDLG_FILTERSPEC> lcl_buildFilterList(CFilterContainer& rContainer,
+ std::vector<OUString>& rvStrings)
+{
+ ::std::vector< COMDLG_FILTERSPEC > lList ;
+ CFilterContainer::FILTER_ENTRY_T aFilter;
+
+ rContainer.beginEnumFilter( );
+ while( rContainer.getNextFilter(aFilter) )
+ {
+ COMDLG_FILTERSPEC aSpec;
+
+ rvStrings.push_back(lcl_AdjustFilterName(aFilter.first)); // to avoid dangling pointer
+ aSpec.pszName = o3tl::toW(rvStrings.back().getStr());
+ aSpec.pszSpec = o3tl::toW(aFilter.second.getStr());
+
+ lList.push_back(aSpec);
+ }
+
+ return lList;
+}
+
+
+VistaFilePickerImpl::VistaFilePickerImpl()
+ : m_lFilters ()
+ , m_iEventHandler(new VistaFilePickerEventHandler(this))
+ , m_bInExecute (false)
+ , m_bWasExecuted (false)
+ , m_hParentWindow(nullptr)
+ , m_sDirectory ()
+ , m_sFilename ()
+{
+}
+
+
+VistaFilePickerImpl::~VistaFilePickerImpl()
+{
+}
+
+
+void VistaFilePickerImpl::doRequest(Request& rRequest)
+{
+ try
+ {
+ switch(rRequest.getRequest())
+ {
+ case E_ADD_PICKER_LISTENER :
+ impl_sta_addFilePickerListener(rRequest);
+ break;
+
+ case E_REMOVE_PICKER_LISTENER :
+ impl_sta_removeFilePickerListener(rRequest);
+ break;
+
+ case E_APPEND_FILTER :
+ impl_sta_appendFilter(rRequest);
+ break;
+
+ case E_APPEND_FILTERGROUP :
+ impl_sta_appendFilterGroup(rRequest);
+ break;
+
+ case E_SET_CURRENT_FILTER :
+ impl_sta_setCurrentFilter(rRequest);
+ break;
+
+ case E_GET_CURRENT_FILTER :
+ impl_sta_getCurrentFilter(rRequest);
+ break;
+
+ case E_CREATE_OPEN_DIALOG :
+ impl_sta_CreateOpenDialog(rRequest);
+ break;
+
+ case E_CREATE_SAVE_DIALOG :
+ impl_sta_CreateSaveDialog(rRequest);
+ break;
+
+ case E_CREATE_FOLDER_PICKER:
+ impl_sta_CreateFolderPicker(rRequest);
+ break;
+
+ case E_SET_MULTISELECTION_MODE :
+ impl_sta_SetMultiSelectionMode(rRequest);
+ break;
+
+ case E_SET_TITLE :
+ impl_sta_SetTitle(rRequest);
+ break;
+
+ case E_SET_FILENAME:
+ impl_sta_SetFileName(rRequest);
+ break;
+
+ case E_SET_DIRECTORY :
+ impl_sta_SetDirectory(rRequest);
+ break;
+
+ case E_GET_DIRECTORY :
+ impl_sta_GetDirectory(rRequest);
+ break;
+
+ case E_SET_DEFAULT_NAME :
+ impl_sta_SetDefaultName(rRequest);
+ break;
+
+ case E_GET_SELECTED_FILES :
+ impl_sta_getSelectedFiles(rRequest);
+ break;
+
+ case E_SHOW_DIALOG_MODAL :
+ impl_sta_ShowDialogModal(rRequest);
+ break;
+
+ case E_SET_CONTROL_VALUE :
+ impl_sta_SetControlValue(rRequest);
+ break;
+
+ case E_GET_CONTROL_VALUE :
+ impl_sta_GetControlValue(rRequest);
+ break;
+
+ case E_SET_CONTROL_LABEL :
+ impl_sta_SetControlLabel(rRequest);
+ break;
+
+ case E_GET_CONTROL_LABEL :
+ impl_sta_GetControlLabel(rRequest);
+ break;
+
+ case E_ENABLE_CONTROL :
+ impl_sta_EnableControl(rRequest);
+ break;
+
+ // no default: let the compiler detect changes on enum ERequest !
+ }
+ }
+ catch(...)
+ {}
+}
+
+
+void VistaFilePickerImpl::impl_sta_addFilePickerListener(Request& rRequest)
+{
+ const css::uno::Reference< css::ui::dialogs::XFilePickerListener > xListener = rRequest.getArgumentOrDefault(PROP_PICKER_LISTENER, css::uno::Reference< css::ui::dialogs::XFilePickerListener >());
+ if ( ! xListener.is())
+ return;
+
+ if (m_iEventHandler.is())
+ {
+ auto* pHandlerImpl = static_cast<VistaFilePickerEventHandler*>(m_iEventHandler.get());
+ pHandlerImpl->addFilePickerListener(xListener);
+ }
+}
+
+
+void VistaFilePickerImpl::impl_sta_removeFilePickerListener(Request& rRequest)
+{
+ const css::uno::Reference< css::ui::dialogs::XFilePickerListener > xListener = rRequest.getArgumentOrDefault(PROP_PICKER_LISTENER, css::uno::Reference< css::ui::dialogs::XFilePickerListener >());
+ if ( ! xListener.is())
+ return;
+
+ if (m_iEventHandler.is())
+ {
+ auto* pHandlerImpl = static_cast<VistaFilePickerEventHandler*>(m_iEventHandler.get());
+ pHandlerImpl->removeFilePickerListener(xListener);
+ }
+}
+
+
+void VistaFilePickerImpl::impl_sta_appendFilter(Request& rRequest)
+{
+ const OUString sTitle = rRequest.getArgumentOrDefault(PROP_FILTER_TITLE, OUString());
+ const OUString sFilter = rRequest.getArgumentOrDefault(PROP_FILTER_VALUE, OUString());
+
+ m_lFilters.addFilter(sTitle, sFilter);
+}
+
+
+void VistaFilePickerImpl::impl_sta_appendFilterGroup(Request& rRequest)
+{
+ const css::uno::Sequence< css::beans::StringPair > aFilterGroup =
+ rRequest.getArgumentOrDefault(PROP_FILTER_GROUP, css::uno::Sequence< css::beans::StringPair >());
+
+ if ( m_lFilters.numFilter() > 0 && aFilterGroup.getLength() > 0 )
+ m_lFilters.addFilter( STRING_SEPARATOR, "", true );
+
+ ::sal_Int32 c = aFilterGroup.getLength();
+ ::sal_Int32 i = 0;
+ for (i=0; i<c; ++i)
+ {
+ const css::beans::StringPair& rFilter = aFilterGroup[i];
+ m_lFilters.addFilter(rFilter.First, rFilter.Second);
+ }
+}
+
+
+void VistaFilePickerImpl::impl_sta_setCurrentFilter(Request& rRequest)
+{
+ const OUString sTitle = rRequest.getArgumentOrDefault(PROP_FILTER_TITLE, OUString());
+
+ m_lFilters.setCurrentFilter(sTitle);
+}
+
+
+void VistaFilePickerImpl::impl_sta_getCurrentFilter(Request& rRequest)
+{
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+
+ UINT nIndex = UINT_MAX;
+ HRESULT hResult = iDialog->GetFileTypeIndex(&nIndex);
+ if (
+ ( FAILED(hResult) ) ||
+ ( nIndex == UINT_MAX ) // COM dialog sometimes return S_OK for empty filter lists .-(
+ )
+ return;
+
+ OUString sTitle;
+ ::sal_Int32 nRealIndex = nIndex-1; // COM dialog base on 1 ... filter container on 0 .-)
+ if (
+ (nRealIndex >= 0 ) &&
+ (m_lFilters.getFilterNameByIndex(nRealIndex, sTitle))
+ )
+ rRequest.setArgument(PROP_FILTER_TITLE, sTitle);
+ else if ( nRealIndex == -1 ) // Dialog not visible yet
+ {
+ sTitle = m_lFilters.getCurrentFilter();
+ rRequest.setArgument(PROP_FILTER_TITLE, sTitle);
+ }
+}
+
+
+template <class TDialogImplClass> void VistaFilePickerImpl::impl_sta_CreateDialog()
+{
+ m_pDialog = std::make_shared<TDialogImplClass>();
+}
+
+
+void VistaFilePickerImpl::impl_sta_InitDialog(Request& rRequest, DWORD nOrFlags)
+{
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+
+ DWORD nFlags = 0;
+ iDialog->GetOptions ( &nFlags );
+
+ nFlags &= ~FOS_FORCESHOWHIDDEN;
+ nFlags |= FOS_PATHMUSTEXIST;
+ nFlags |= FOS_DONTADDTORECENT;
+ nFlags |= nOrFlags;
+
+ iDialog->SetOptions ( nFlags );
+
+ css::uno::Reference<css::awt::XWindow> xWindow = rRequest.getArgumentOrDefault(PROP_PARENT_WINDOW, css::uno::Reference<css::awt::XWindow>());
+ if(xWindow.is())
+ {
+ css::uno::Reference<css::awt::XSystemDependentWindowPeer> xSysDepWin(xWindow,css::uno::UNO_QUERY);
+ if(xSysDepWin.is()) {
+ css::uno::Sequence<sal_Int8> aProcessIdent(16);
+ rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8*>(aProcessIdent.getArray()));
+ css::uno::Any aAny = xSysDepWin->getWindowHandle(aProcessIdent,css::lang::SystemDependent::SYSTEM_WIN32);
+ sal_Int64 tmp = 0;
+ aAny >>= tmp;
+ if(tmp != 0)
+ {
+ m_hParentWindow = reinterpret_cast<HWND>(tmp);
+ }
+ }
+ }
+
+ ::sal_Int32 nFeatures = rRequest.getArgumentOrDefault(PROP_FEATURES, ::sal_Int32(0));
+ ::sal_Int32 nTemplate = rRequest.getArgumentOrDefault(PROP_TEMPLATE_DESCR, ::sal_Int32(0));
+ impl_sta_enableFeatures(nFeatures, nTemplate);
+
+ if (m_iEventHandler.is())
+ {
+ auto* pHandlerImpl = static_cast<VistaFilePickerEventHandler*>(m_iEventHandler.get());
+ pHandlerImpl->startListening(iDialog);
+ }
+}
+
+
+void VistaFilePickerImpl::impl_sta_CreateOpenDialog(Request& rRequest)
+{
+ impl_sta_CreateDialog<TOpenDialogImpl>();
+ impl_sta_InitDialog(rRequest, FOS_FILEMUSTEXIST | FOS_OVERWRITEPROMPT);
+}
+
+
+void VistaFilePickerImpl::impl_sta_CreateSaveDialog(Request& rRequest)
+{
+ impl_sta_CreateDialog<TSaveDialogImpl>();
+ impl_sta_InitDialog(rRequest, FOS_FILEMUSTEXIST | FOS_OVERWRITEPROMPT);
+}
+
+
+void VistaFilePickerImpl::impl_sta_CreateFolderPicker(Request& rRequest)
+{
+ impl_sta_CreateDialog<TFolderPickerDialogImpl>();
+ impl_sta_InitDialog(rRequest, FOS_PICKFOLDERS);
+}
+
+
+const ::sal_Int32 GROUP_VERSION = 1;
+const ::sal_Int32 GROUP_TEMPLATE = 2;
+const ::sal_Int32 GROUP_IMAGETEMPLATE = 3;
+const ::sal_Int32 GROUP_CHECKBOXES = 4;
+const ::sal_Int32 GROUP_IMAGEANCHOR = 5;
+
+
+static void setLabelToControl(TFileDialogCustomize iCustom, sal_uInt16 nControlId)
+{
+ OUString aLabel = CResourceProvider::getResString(nControlId);
+ aLabel = SOfficeToWindowsLabel(aLabel);
+ iCustom->SetControlLabel(nControlId, o3tl::toW(aLabel.getStr()) );
+}
+
+
+void VistaFilePickerImpl::impl_sta_enableFeatures(::sal_Int32 nFeatures, ::sal_Int32 nTemplate)
+{
+ GUID aGUID = {};
+ switch (nTemplate)
+ {
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE :
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_PREVIEW :
+ case css::ui::dialogs::TemplateDescription::FILESAVE_SIMPLE :
+ aGUID = CLIENTID_FILEDIALOG_SIMPLE;
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION :
+ case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS :
+ aGUID = CLIENTID_FILEDIALOG_OPTIONS;
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD :
+ aGUID = CLIENTID_FILESAVE_PASSWORD;
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION :
+ case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_SELECTION :
+ aGUID = CLIENTID_FILESAVE_SELECTION;
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_TEMPLATE :
+ aGUID = CLIENTID_FILESAVE_TEMPLATE;
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE :
+ aGUID = CLIENTID_FILEOPEN_LINK_TEMPLATE;
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR :
+ aGUID = CLIENTID_FILEOPEN_LINK_ANCHOR;
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_PLAY :
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PLAY :
+ aGUID = CLIENTID_FILEOPEN_PLAY;
+ break;
+
+ case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW :
+ aGUID = CLIENTID_FILEOPEN_LINK;
+ break;
+ }
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (iDialog.is())
+ iDialog->SetClientGuid ( aGUID );
+
+ TFileDialogCustomize iCustom = impl_getCustomizeInterface();
+ if (!iCustom.is())
+ return;
+
+ if ((nFeatures & FEATURE_VERSION) == FEATURE_VERSION)
+ {
+ iCustom->StartVisualGroup (GROUP_VERSION, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_VERSION).replaceFirst("~","").getStr()));
+ iCustom->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION);
+ iCustom->EndVisualGroup ();
+ iCustom->MakeProminent (GROUP_VERSION);
+ }
+
+ if ((nFeatures & FEATURE_TEMPLATE) == FEATURE_TEMPLATE)
+ {
+ iCustom->StartVisualGroup (GROUP_TEMPLATE, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_TEMPLATES).replaceFirst("~","").getStr()));
+ iCustom->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE);
+ iCustom->EndVisualGroup ();
+ iCustom->MakeProminent (GROUP_TEMPLATE);
+ }
+
+ if ((nFeatures & FEATURE_IMAGETEMPLATE) == FEATURE_IMAGETEMPLATE)
+ {
+ iCustom->StartVisualGroup (GROUP_IMAGETEMPLATE, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_IMAGE_TEMPLATE).replaceFirst("~","").getStr()));
+ iCustom->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE);
+ iCustom->EndVisualGroup ();
+ iCustom->MakeProminent (GROUP_IMAGETEMPLATE);
+ }
+
+ if ((nFeatures & FEATURE_IMAGEANCHOR) == FEATURE_IMAGEANCHOR)
+ {
+ iCustom->StartVisualGroup (GROUP_IMAGEANCHOR, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_IMAGE_ANCHOR).replaceFirst("~","").getStr()));
+ iCustom->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR);
+ iCustom->EndVisualGroup ();
+ iCustom->MakeProminent (GROUP_IMAGEANCHOR);
+ }
+
+ iCustom->StartVisualGroup (GROUP_CHECKBOXES, L"");
+
+ sal_uInt16 nControlId(0);
+ if ((nFeatures & FEATURE_AUTOEXTENSION) == FEATURE_AUTOEXTENSION)
+ {
+ nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION;
+ iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_AUTO_EXTENSION).replaceFirst("~","").getStr()), true);
+ setLabelToControl(iCustom, nControlId);
+ }
+
+ if ((nFeatures & FEATURE_PASSWORD) == FEATURE_PASSWORD)
+ {
+ nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD;
+ iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_PASSWORD).replaceFirst("~","").getStr()), false);
+ setLabelToControl(iCustom, nControlId);
+ }
+
+ if ((nFeatures & FEATURE_GPGPASSWORD) == FEATURE_GPGPASSWORD)
+ {
+ nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION;
+ iCustom->AddCheckButton (nControlId, L"GpgPassword", false);
+ setLabelToControl(iCustom, nControlId);
+ }
+
+ if ((nFeatures & FEATURE_READONLY) == FEATURE_READONLY)
+ {
+ nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY;
+ iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_READONLY).replaceFirst("~","").getStr()), false);
+ setLabelToControl(iCustom, nControlId);
+ }
+
+ if ((nFeatures & FEATURE_FILTEROPTIONS) == FEATURE_FILTEROPTIONS)
+ {
+ nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS;
+ iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_FILTER_OPTIONS).replaceFirst("~","").getStr()), false);
+ setLabelToControl(iCustom, nControlId);
+ }
+
+ if ((nFeatures & FEATURE_LINK) == FEATURE_LINK)
+ {
+ nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK;
+ iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_INSERT_AS_LINK).replaceFirst("~","").getStr()), false);
+ setLabelToControl(iCustom, nControlId);
+ }
+
+ if ((nFeatures & FEATURE_SELECTION) == FEATURE_SELECTION)
+ {
+ nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION;
+ iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_SELECTION).replaceFirst("~","").getStr()), false);
+ setLabelToControl(iCustom, nControlId);
+ }
+
+ /* can be ignored ... new COM dialog supports preview native now !
+ if ((nFeatures & FEATURE_PREVIEW) == FEATURE_PREVIEW)
+ iCustom->AddCheckButton (css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, L"Preview", false);
+ */
+
+ iCustom->EndVisualGroup();
+
+ if ((nFeatures & FEATURE_PLAY) == FEATURE_PLAY)
+ iCustom->AddPushButton (css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_PLAY).replaceFirst("~","").getStr()));
+
+}
+
+
+void VistaFilePickerImpl::impl_sta_SetMultiSelectionMode(Request& rRequest)
+{
+ const bool bMultiSelection = rRequest.getArgumentOrDefault(PROP_MULTISELECTION_MODE, true);
+
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+
+ DWORD nFlags = 0;
+ iDialog->GetOptions(&nFlags);
+
+ if (bMultiSelection)
+ nFlags |= FOS_ALLOWMULTISELECT;
+ else
+ nFlags &= ~FOS_ALLOWMULTISELECT;
+
+ iDialog->SetOptions ( nFlags );
+}
+
+
+void VistaFilePickerImpl::impl_sta_SetTitle(Request& rRequest)
+{
+ OUString sTitle = rRequest.getArgumentOrDefault(PROP_TITLE, OUString());
+
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+
+ iDialog->SetTitle(o3tl::toW(sTitle.getStr()));
+}
+
+
+void VistaFilePickerImpl::impl_sta_SetFileName(Request& rRequest)
+{
+ OUString sFileName = rRequest.getArgumentOrDefault(PROP_FILENAME, OUString());
+
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+
+ iDialog->SetFileName(o3tl::toW(sFileName.getStr()));
+}
+
+
+void VistaFilePickerImpl::impl_sta_SetDirectory(Request& rRequest)
+{
+ OUString sDirectory = rRequest.getArgumentOrDefault(PROP_DIRECTORY, OUString());
+
+ if( !m_bInExecute)
+ {
+ // Vista stores last used folders for file dialogs
+ // so we don't want the application to change the folder
+ // in most cases.
+ // Store the requested folder in the meantime and decide later
+ // what to do
+ m_sDirectory = sDirectory;
+ }
+
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+
+ sal::systools::COMReference<IShellItem> pFolder;
+ if ( !createFolderItem(sDirectory, pFolder) )
+ return;
+
+ iDialog->SetFolder(pFolder.get());
+}
+
+OUString VistaFilePickerImpl::GetDirectory()
+{
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return OUString();
+ sal::systools::COMReference<IShellItem> pFolder;
+ HRESULT hResult = iDialog->GetFolder( &pFolder );
+ if ( FAILED(hResult) )
+ return OUString();
+ return lcl_getURLFromShellItem(pFolder.get());
+}
+
+void VistaFilePickerImpl::impl_sta_GetDirectory(Request& rRequest)
+{
+ const OUString sFolder = m_sDirectory.isEmpty() ? GetDirectory() : m_sDirectory;
+ if (!sFolder.isEmpty())
+ rRequest.setArgument(PROP_DIRECTORY, sFolder);
+}
+
+void VistaFilePickerImpl::impl_sta_SetDefaultName(Request& rRequest)
+{
+ OUString sFilename = rRequest.getArgumentOrDefault(PROP_FILENAME, OUString());
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+
+ TFileDialogCustomize iCustom = impl_getCustomizeInterface();
+ if ( ! iCustom.is())
+ return;
+
+ // if we have the autoextension check box set, remove (or change ???) the extension of the filename
+ // so that the autoextension mechanism can do its job
+ BOOL bValue = FALSE;
+ HRESULT hResult = iCustom->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, &bValue);
+ if ( FAILED(hResult) )
+ return;
+ if ( bValue )
+ {
+ sal_Int32 nSepPos = sFilename.lastIndexOf( '.' );
+ if ( -1 != nSepPos )
+ sFilename = sFilename.copy(0, nSepPos);
+ }
+
+ iDialog->SetFileName (o3tl::toW(sFilename.getStr()));
+ m_sFilename = sFilename;
+}
+
+
+void VistaFilePickerImpl::impl_sta_setFiltersOnDialog()
+{
+ std::vector<OUString> vStrings; // to hold the adjusted filter names, pointers to which will be
+ // stored in lFilters
+ ::std::vector< COMDLG_FILTERSPEC > lFilters = lcl_buildFilterList(m_lFilters, vStrings);
+ OUString sCurrentFilter = m_lFilters.getCurrentFilter();
+ sal_Int32 nCurrentFilter = m_lFilters.getFilterPos(sCurrentFilter);
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+ TFileDialogCustomize iCustomize = impl_getCustomizeInterface();
+ if (!iCustomize.is())
+ return;
+
+ if (lFilters.empty())
+ return;
+
+ COMDLG_FILTERSPEC *pFilt = lFilters.data();
+ iDialog->SetFileTypes(lFilters.size(), pFilt/*&lFilters[0]*/);
+ iDialog->SetFileTypeIndex(nCurrentFilter + 1);
+
+ BOOL bValue = FALSE;
+ HRESULT hResult = iCustomize->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, &bValue);
+ if ( FAILED(hResult) )
+ return;
+
+ if ( bValue )
+ {
+ PCWSTR lpFilterExt = lFilters[0].pszSpec;
+
+ lpFilterExt = wcsrchr( lpFilterExt, '.' );
+ if ( lpFilterExt )
+ lpFilterExt++;
+ iDialog->SetDefaultExtension( lpFilterExt );
+ }
+
+}
+
+
+void VistaFilePickerImpl::impl_sta_getSelectedFiles(Request& rRequest)
+{
+ if (m_pDialog == nullptr)
+ return;
+
+ // ask dialog for results
+ // we must react different if dialog is in execute or not .-(
+ sal::systools::COMReference<IShellItemArray> iItems = m_pDialog->getResult(m_bInExecute);
+ if (!iItems.is())
+ return;
+
+ // convert and pack results
+ std::vector< OUString > lFiles;
+ if (DWORD nCount; SUCCEEDED(iItems->GetCount(&nCount)))
+ {
+ for (DWORD i = 0; i < nCount; ++i)
+ {
+ if (sal::systools::COMReference<IShellItem> iItem;
+ SUCCEEDED(iItems->GetItemAt(i, &iItem)))
+ {
+ if (const OUString sURL = lcl_getURLFromShellItem(iItem.get()); !sURL.isEmpty())
+ lFiles.push_back(sURL);
+ }
+ }
+ }
+
+ rRequest.setArgument(PROP_SELECTED_FILES, comphelper::containerToSequence(lFiles));
+}
+
+
+void VistaFilePickerImpl::impl_sta_ShowDialogModal(Request& rRequest)
+{
+ impl_sta_setFiltersOnDialog();
+
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+
+ // it's important to know if we are showing the dialog.
+ // Some dialog interface methods can't be called then or some
+ // tasks must be done differently .-) (e.g. see impl_sta_getSelectedFiles())
+ m_bInExecute = true;
+
+ m_bWasExecuted = true;
+
+ // we set the directory only if we have a save dialog and a filename
+ // for the other cases, the file dialog remembers its last location
+ // according to its client guid.
+ if( m_sDirectory.getLength())
+ {
+ sal::systools::COMReference<IShellItem> pFolder;
+ if ( createFolderItem(m_sDirectory, pFolder) )
+ {
+ if (m_sFilename.getLength())
+ {
+ OUString aFileURL(m_sDirectory);
+ sal_Int32 nIndex = aFileURL.lastIndexOf('/');
+ if (nIndex != aFileURL.getLength()-1)
+ aFileURL += "/";
+ aFileURL += m_sFilename;
+
+ TFileDialogCustomize iCustom = impl_getCustomizeInterface();
+ if (!iCustom.is())
+ return;
+
+ BOOL bValue = FALSE;
+ HRESULT hResult = iCustom->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, &bValue);
+ if ( bValue )
+ {
+ UINT nFileType;
+ hResult = iDialog->GetFileTypeIndex(&nFileType);
+ if ( SUCCEEDED(hResult) && nFileType > 0 )
+ {
+ // COM dialog base on 1 ... filter container on 0 .-)
+ ::size_t nRealIndex = nFileType-1;
+ OUString sFilter;
+ if (m_lFilters.getFilterByIndex(nRealIndex, sFilter))
+ {
+ const sal_Int32 idx = sFilter.indexOf('.');
+ if (idx >= 0)
+ aFileURL += sFilter.subView(idx);
+ }
+ }
+ }
+
+ // Check existence of file. Set folder only for this special case
+ OUString aSystemPath;
+ osl_getSystemPathFromFileURL( aFileURL.pData, &aSystemPath.pData );
+
+ WIN32_FIND_DATAW aFindFileData;
+ HANDLE hFind = FindFirstFileW( o3tl::toW(aSystemPath.getStr()), &aFindFileData );
+ if (hFind != INVALID_HANDLE_VALUE)
+ iDialog->SetFolder(pFolder.get());
+ else
+ hResult = iDialog->AddPlace(pFolder.get(), FDAP_TOP);
+
+ FindClose( hFind );
+ }
+ else
+ iDialog->AddPlace(pFolder.get(), FDAP_TOP);
+ }
+ }
+
+ HRESULT hResult = E_FAIL;
+ try
+ {
+ // tdf#146007: Make sure we don't hold solar mutex: COM may need to forward
+ // the execution to the main thread, and holding solar mutex could deadlock
+ SolarMutexGuard g; // First acquire, to avoid releaser failure
+ SolarMutexReleaser r;
+ // show dialog and wait for user decision
+ hResult = iDialog->Show(m_hParentWindow ? m_hParentWindow
+ : choose_parent_window()); // parent window needed
+ }
+ catch(...)
+ {}
+
+ m_bInExecute = false;
+
+ if (m_iEventHandler.is())
+ {
+ auto* pHandlerImpl = static_cast<VistaFilePickerEventHandler*>(m_iEventHandler.get());
+ pHandlerImpl->stopListening();
+ }
+
+ if ( FAILED(hResult) )
+ return;
+
+ impl_sta_getSelectedFiles(rRequest);
+ rRequest.setArgument(PROP_DIALOG_SHOW_RESULT, true);
+}
+
+
+TFileDialog VistaFilePickerImpl::impl_getBaseDialogInterface()
+{
+ TFileDialog iDialog;
+
+ if (m_pDialog != nullptr)
+ iDialog = m_pDialog->getComPtr();
+
+ return iDialog;
+}
+
+
+TFileDialogCustomize VistaFilePickerImpl::impl_getCustomizeInterface()
+{
+ if (m_pDialog != nullptr)
+ return { m_pDialog->getComPtr(), sal::systools::COM_QUERY_THROW };
+
+ return {};
+}
+
+
+static void lcl_removeControlItemsWorkaround(const TFileDialogCustomize& iCustom ,
+ ::sal_Int16 nControlId)
+{
+ (void)iCustom->SetSelectedControlItem(nControlId, 1000); // Don't care if this fails (useless?)
+ DWORD i = 0;
+ HRESULT hResult = S_OK;
+ while ( SUCCEEDED(hResult) )
+ hResult = iCustom->RemoveControlItem(nControlId, i++);
+}
+
+
+void VistaFilePickerImpl::impl_sta_SetControlValue(Request& rRequest)
+{
+ ::sal_Int16 nId = rRequest.getArgumentOrDefault(PROP_CONTROL_ID , INVALID_CONTROL_ID );
+ ::sal_Int16 nAction = rRequest.getArgumentOrDefault(PROP_CONTROL_ACTION, INVALID_CONTROL_ACTION);
+ css::uno::Any aValue = rRequest.getValue(PROP_CONTROL_VALUE);
+
+ // don't check for right values here ...
+ // most parameters are optional !
+
+ TFileDialogCustomize iCustom = impl_getCustomizeInterface();
+ if ( ! iCustom.is())
+ return;
+
+ switch (nId)
+ {
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK :
+ //case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW : // can be ignored ... preview is supported native now !
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION :
+ {
+ bool bValue = false;
+ aValue >>= bValue;
+ iCustom->SetCheckButtonState(nId, bValue);
+ }
+ break;
+
+ case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR :
+ {
+ HRESULT hResult;
+ switch (nAction)
+ {
+ case css::ui::dialogs::ControlActions::DELETE_ITEMS :
+ {
+ hResult = iCustom->RemoveAllControlItems(nId);
+ if ( FAILED(hResult) )
+ lcl_removeControlItemsWorkaround(iCustom, nId);
+ }
+ break;
+
+ case css::ui::dialogs::ControlActions::ADD_ITEMS :
+ {
+ aValue >>= m_lItems;
+ for (::sal_Int32 i=0; i<m_lItems.getLength(); ++i)
+ {
+ const OUString& sItem = m_lItems[i];
+ hResult = iCustom->AddControlItem(nId, i, o3tl::toW(sItem.getStr()));
+ }
+ }
+ break;
+
+ case css::ui::dialogs::ControlActions::SET_SELECT_ITEM :
+ {
+ ::sal_Int32 nItem = 0;
+ aValue >>= nItem;
+ hResult = iCustom->SetSelectedControlItem(nId, nItem);
+ }
+ break;
+ }
+ }
+ break;
+
+ case css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY :
+ {
+ }
+ break;
+ }
+}
+
+
+void VistaFilePickerImpl::impl_sta_GetControlValue(Request& rRequest)
+{
+ ::sal_Int16 nId = rRequest.getArgumentOrDefault(PROP_CONTROL_ID , INVALID_CONTROL_ID );
+
+ // don't check for right values here ...
+ // most parameters are optional !
+
+ TFileDialogCustomize iCustom = impl_getCustomizeInterface();
+ if ( ! iCustom.is())
+ return;
+
+ css::uno::Any aValue;
+ if( m_bWasExecuted )
+ switch (nId)
+ {
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS :
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK :
+ //case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW : // can be ignored ... preview is supported native now !
+ case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION :
+ {
+ BOOL bValue = FALSE;
+ HRESULT hResult = iCustom->GetCheckButtonState(nId, &bValue);
+ if ( SUCCEEDED(hResult) )
+ aValue <<= bool(bValue);
+ }
+ break;
+ case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION:
+ case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE:
+ case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE:
+ case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR:
+ {
+ DWORD bValue = 0;
+ HRESULT hResult = iCustom->GetSelectedControlItem(nId, &bValue);
+ if ( SUCCEEDED(hResult) )
+ {
+ const OUString& sItem = m_lItems[bValue];
+ aValue <<= OUString(sItem.getStr());
+ }
+ }
+ break;
+ }
+
+ if (aValue.hasValue())
+ rRequest.setArgument(PROP_CONTROL_VALUE, aValue);
+}
+
+
+void VistaFilePickerImpl::impl_sta_SetControlLabel(Request& rRequest)
+{
+ ::sal_Int16 nId = rRequest.getArgumentOrDefault(PROP_CONTROL_ID , INVALID_CONTROL_ID );
+ OUString sLabel = rRequest.getArgumentOrDefault(PROP_CONTROL_LABEL, OUString() );
+
+ // don't check for right values here ...
+ // most parameters are optional !
+
+ TFileDialogCustomize iCustom = impl_getCustomizeInterface();
+ if ( ! iCustom.is())
+ return;
+ iCustom->SetControlLabel (nId, o3tl::toW(sLabel.getStr()));
+}
+
+
+void VistaFilePickerImpl::impl_sta_GetControlLabel(Request& /*rRequest*/)
+{
+}
+
+
+void VistaFilePickerImpl::impl_sta_EnableControl(Request& rRequest)
+{
+ ::sal_Int16 nId = rRequest.getArgumentOrDefault(PROP_CONTROL_ID , INVALID_CONTROL_ID );
+ bool bEnabled = rRequest.getArgumentOrDefault(PROP_CONTROL_ENABLE, true);
+
+ // don't check for right values here ...
+ // most parameters are optional !
+
+ TFileDialogCustomize iCustom = impl_getCustomizeInterface();
+ if ( ! iCustom.is())
+ return;
+
+ CDCONTROLSTATEF eState = CDCS_VISIBLE;
+ if (bEnabled)
+ eState |= CDCS_ENABLED;
+ else
+ eState |= CDCS_INACTIVE;
+
+ iCustom->SetControlState(nId, eState);
+}
+
+void VistaFilePickerImpl::impl_SetDefaultExtension( const OUString& currentFilter )
+{
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+
+ if (currentFilter.getLength())
+ {
+ OUString FilterExt;
+ m_lFilters.getFilterByName(currentFilter, FilterExt);
+
+ sal_Int32 posOfPoint = FilterExt.indexOf(L'.');
+ const sal_Unicode* pFirstExtStart = FilterExt.getStr() + posOfPoint + 1;
+
+ sal_Int32 posOfSemiColon = FilterExt.indexOf(L';') - 1;
+ if (posOfSemiColon < 0)
+ posOfSemiColon = FilterExt.getLength() - 1;
+
+ FilterExt = OUString(pFirstExtStart, posOfSemiColon - posOfPoint);
+ iDialog->SetDefaultExtension ( o3tl::toW(FilterExt.getStr()) );
+ }
+}
+
+void VistaFilePickerImpl::onAutoExtensionChanged (bool bChecked)
+{
+ const OUString sFilter = m_lFilters.getCurrentFilter ();
+ OUString sExt ;
+ if (!m_lFilters.getFilterByName(sFilter, sExt))
+ return;
+
+ TFileDialog iDialog = impl_getBaseDialogInterface();
+ if (!iDialog.is())
+ return;
+
+ PCWSTR pExt = nullptr;
+ if ( bChecked )
+ {
+ pExt = o3tl::toW(sExt.getStr());
+ pExt = wcsrchr( pExt, '.' );
+ if ( pExt )
+ pExt++;
+ }
+ iDialog->SetDefaultExtension( pExt );
+}
+
+bool VistaFilePickerImpl::onFileTypeChanged( UINT /*nTypeIndex*/ )
+{
+ return true;
+}
+
+void VistaFilePickerImpl::onDirectoryChanged()
+{
+ m_sDirectory = GetDirectory();
+}
+
+} // namespace vista
+} // namespace win32
+} // namespace fpicker
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/VistaFilePickerImpl.hxx b/fpicker/source/win32/VistaFilePickerImpl.hxx
new file mode 100644
index 000000000..8e757c9c6
--- /dev/null
+++ b/fpicker/source/win32/VistaFilePickerImpl.hxx
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "platform_vista.h"
+
+#include <shobjidl.h>
+
+#include "requests.hxx"
+#include "vistatypes.h"
+#include "FilterContainer.hxx"
+#include "VistaFilePickerEventHandler.hxx"
+#include "IVistaFilePickerInternalNotify.hxx"
+#include "resourceprovider.hxx"
+
+#include <cppuhelper/interfacecontainer.h>
+#include <osl/thread.hxx>
+#include <osl/conditn.hxx>
+#include <rtl/ustring.hxx>
+
+namespace fpicker{
+namespace win32{
+namespace vista{
+
+
+// types, const etcpp
+
+
+const ::sal_Int32 FEATURE_AUTOEXTENSION = 1;
+const ::sal_Int32 FEATURE_PASSWORD = 2;
+const ::sal_Int32 FEATURE_FILTEROPTIONS = 4;
+const ::sal_Int32 FEATURE_SELECTION = 8;
+const ::sal_Int32 FEATURE_TEMPLATE = 16;
+const ::sal_Int32 FEATURE_LINK = 32;
+const ::sal_Int32 FEATURE_PREVIEW = 64;
+const ::sal_Int32 FEATURE_IMAGETEMPLATE = 128;
+const ::sal_Int32 FEATURE_PLAY = 256;
+const ::sal_Int32 FEATURE_READONLY = 512;
+const ::sal_Int32 FEATURE_VERSION = 1024;
+const ::sal_Int32 FEATURE_GPGPASSWORD = 2048;
+const ::sal_Int32 FEATURE_IMAGEANCHOR = 4096;
+
+inline constexpr OUStringLiteral PROP_PICKER_LISTENER(u"picker_listener" ); // [XFilePickerListenert]
+inline constexpr OUStringLiteral PROP_DIALOG_SHOW_RESULT(u"dialog_show_result" ); // [sal_Bool] true=OK, false=CANCEL
+inline constexpr OUStringLiteral PROP_SELECTED_FILES(u"selected_files" ); // [seq< OUString >] contains all user selected files (can be empty!)
+inline constexpr OUStringLiteral PROP_MULTISELECTION_MODE(u"multiselection_mode"); // [sal_Bool] true=ON, false=OFF
+inline constexpr OUStringLiteral PROP_TITLE(u"title" ); // [OUString]
+inline constexpr OUStringLiteral PROP_FILENAME(u"filename" ); // [OUString]
+inline constexpr OUStringLiteral PROP_DIRECTORY(u"directory" ); // [OUString]
+inline constexpr OUStringLiteral PROP_FEATURES(u"features" ); // [sal_Int32]
+inline constexpr OUStringLiteral PROP_TEMPLATE_DESCR(u"templatedescription"); // [sal_Int32]
+inline constexpr OUStringLiteral PROP_FILTER_TITLE(u"filter_title" ); // [OUString]
+inline constexpr OUStringLiteral PROP_FILTER_VALUE(u"filter_value" ); // [OUString]
+inline constexpr OUStringLiteral PROP_FILTER_GROUP(u"filter-group" ); // [seq< css:beans::StringPair >] contains a group of filters
+
+inline constexpr OUStringLiteral PROP_CONTROL_ID(u"control_id" ); // [sal_Int16]
+inline constexpr OUStringLiteral PROP_CONTROL_ACTION(u"control_action" ); // [sal_Int16]
+inline constexpr OUStringLiteral PROP_CONTROL_VALUE(u"control_value" ); // [Any]
+inline constexpr OUStringLiteral PROP_CONTROL_LABEL(u"control_label" ); // [OUString]
+inline constexpr OUStringLiteral PROP_CONTROL_ENABLE(u"control_enable" ); // [sal_Bool] true=ON, false=OFF
+inline constexpr OUStringLiteral PROP_PARENT_WINDOW(u"ParentWindow"); //[css::awt::XWindow] preferred parent window
+inline constexpr OUStringLiteral STRING_SEPARATOR(u"------------------------------------------" );
+
+class TDialogImplBase;
+
+/** native implementation of the file picker on Vista and upcoming windows versions.
+ * This dialog uses COM internally. Further it marshall every request so it will
+ * be executed on the main thread which is an STA thread !
+ */
+
+class VistaFilePickerImpl : public IVistaFilePickerInternalNotify
+{
+ public:
+
+ // Workaround made to get input in Template Listbox
+ css::uno::Sequence< OUString > m_lItems;
+ /** used for marshalling requests.
+ * Will be used to map requests to the right implementations.
+ */
+ enum ERequest
+ {
+ E_NO_REQUEST,
+ E_ADD_PICKER_LISTENER,
+ E_REMOVE_PICKER_LISTENER,
+ E_APPEND_FILTER,
+ E_SET_CURRENT_FILTER,
+ E_GET_CURRENT_FILTER,
+ E_CREATE_OPEN_DIALOG,
+ E_CREATE_SAVE_DIALOG,
+ E_CREATE_FOLDER_PICKER,
+ E_SET_MULTISELECTION_MODE,
+ E_SET_TITLE,
+ E_SET_FILENAME,
+ E_GET_DIRECTORY,
+ E_SET_DIRECTORY,
+ E_SET_DEFAULT_NAME,
+ E_GET_SELECTED_FILES,
+ E_SHOW_DIALOG_MODAL,
+ E_SET_CONTROL_VALUE,
+ E_GET_CONTROL_VALUE,
+ E_SET_CONTROL_LABEL,
+ E_GET_CONTROL_LABEL,
+ E_ENABLE_CONTROL,
+ E_APPEND_FILTERGROUP
+ };
+
+ public:
+
+
+ // ctor/dtor - nothing special
+
+ VistaFilePickerImpl();
+ virtual ~VistaFilePickerImpl();
+
+
+ // RequestHandler
+
+ void doRequest(Request& rRequest);
+
+
+ // IVistaFilePickerInternalNotify
+
+ virtual void onAutoExtensionChanged (bool bChecked) override;
+ virtual bool onFileTypeChanged( UINT nTypeIndex ) override;
+ virtual void onDirectoryChanged() override;
+
+ private:
+ OUString GetDirectory();
+
+ /// implementation of request E_ADD_FILEPICKER_LISTENER
+ void impl_sta_addFilePickerListener(Request& rRequest);
+
+
+ /// implementation of request E_REMOVE_FILEPICKER_LISTENER
+ void impl_sta_removeFilePickerListener(Request& rRequest);
+
+
+ /// implementation of request E_APPEND_FILTER
+ void impl_sta_appendFilter(Request& rRequest);
+
+
+ /// implementation of request E_APPEND_FILTERGROUP
+ void impl_sta_appendFilterGroup(Request& rRequest);
+
+
+ /// implementation of request E_SET_CURRENT_FILTER
+ void impl_sta_setCurrentFilter(Request& rRequest);
+
+
+ /// implementation of request E_GET_CURRENT_FILTER
+ void impl_sta_getCurrentFilter(Request& rRequest);
+
+
+ /// implementation of request E_CREATE_OPEN_DIALOG
+ void impl_sta_CreateOpenDialog(Request& rRequest);
+
+
+ /// implementation of request E_CREATE_SAVE_DIALOG
+ void impl_sta_CreateSaveDialog(Request& rRequest);
+
+
+ /// implementation of request E_CREATE_FOLDER_PICKER
+ void impl_sta_CreateFolderPicker(Request& rRequest);
+
+
+ /// implementation of request E_SET_MULTISELECTION_MODE
+ void impl_sta_SetMultiSelectionMode(Request& rRequest);
+
+
+ /// implementation of request E_SET_TITLE
+ void impl_sta_SetTitle(Request& rRequest);
+
+
+ /// implementation of request E_SET_FILENAME
+ void impl_sta_SetFileName(Request& rRequest);
+
+
+ /// implementation of request E_SET_DIRECTORY
+ void impl_sta_SetDirectory(Request& rRequest);
+
+
+ /// implementation of request E_GET_DIRECTORY
+ void impl_sta_GetDirectory(Request& rRequest);
+
+
+ /// implementation of request E_SET_DEFAULT_NAME
+ void impl_sta_SetDefaultName(Request& rRequest);
+
+
+ /// implementation of request E_GET_SELECTED_FILES
+ void impl_sta_getSelectedFiles(Request& rRequest);
+
+
+ /// implementation of request E_SHOW_DIALOG_MODAL
+ void impl_sta_ShowDialogModal(Request& rRequest);
+
+
+ /// implementation of request E_SET_CONTROL_VALUE
+ void impl_sta_SetControlValue(Request& rRequest);
+
+
+ /// implementation of request E_GET_CONTROL_VALUE
+ void impl_sta_GetControlValue(Request& rRequest);
+
+
+ /// implementation of request E_SET_CONTROL_LABEL
+ void impl_sta_SetControlLabel(Request& rRequest);
+
+
+ /// implementation of request E_GET_CONTROL_LABEL
+ static void impl_sta_GetControlLabel(Request& rRequest);
+
+
+ /// implementation of request E_ENABLE_CONTROL
+ void impl_sta_EnableControl(Request& rRequest);
+
+ /** create all needed (optional!) UI controls addressed by the field nFeatures.
+ * The given number nFeatures is used as a flag field. Use const values FEATURE_XXX
+ * to address it.
+ *
+ * Internal new controls will be added to the dialog. Every control can be accessed
+ * by its own control id. Those control ID must be one of the const set
+ * css::ui::dialogs::ExtendedFilePickerElementIds.
+ *
+ * @see setControlValue()
+ * @see getControlValue()
+ * @see setControlLabel()
+ * @see getControlLabel()
+ * @see enableControl()
+ *
+ * @param nFeatures
+ * flag field(!) knows all features which must be enabled.
+ */
+ void impl_sta_enableFeatures(::sal_Int32 nFeatures, ::sal_Int32 nTemplate);
+
+
+ /** returns an interface, which can be used to customize the internally used
+ * COM dialog.
+ *
+ * Because we use two member (open/save dialog) internally, this method
+ * ask the current active one for its customization interface.
+ *
+ * @return the customization interface for the current used dialog.
+ * Must not be null.
+ */
+ TFileDialogCustomize impl_getCustomizeInterface();
+ TFileDialog impl_getBaseDialogInterface();
+
+
+ /// fill filter list of internal used dialog.
+ void impl_sta_setFiltersOnDialog();
+
+ void impl_SetDefaultExtension( const OUString& currentFilter );
+
+ private:
+ template <class TDialogImplClass> void impl_sta_CreateDialog();
+ void impl_sta_InitDialog(Request& rRequest, DWORD nOrFlags);
+
+
+ /// object representing a file dialog
+ std::shared_ptr<TDialogImplBase> m_pDialog;
+
+
+ /// @todo document me
+ CFilterContainer m_lFilters;
+
+
+ /** help us to handle dialog events and provide them to interested office
+ * listener.
+ */
+ TFileDialogEvents m_iEventHandler;
+
+
+ /// @todo document me
+ bool m_bInExecute;
+
+ bool m_bWasExecuted;
+
+ // handle to parent window
+ HWND m_hParentWindow;
+
+
+ OUString m_sDirectory;
+
+
+ OUString m_sFilename;
+};
+
+} // namespace vista
+} // namespace win32
+} // namespace fpicker
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/WinImplHelper.cxx b/fpicker/source/win32/WinImplHelper.cxx
new file mode 100644
index 000000000..5d6e20d92
--- /dev/null
+++ b/fpicker/source/win32/WinImplHelper.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "WinImplHelper.hxx"
+
+#include <vector>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+using ::com::sun::star::lang::IllegalArgumentException;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Sequence;
+
+const sal_Unicode TILDE_SIGN = L'~';
+const sal_Unicode AMPERSAND_SIGN = L'&';
+
+// OS NAME Platform Major Minor
+
+// Windows NT 3.51 VER_PLATFORM_WIN32_NT 3 51
+// Windows NT 4.0 VER_PLATFORM_WIN32_NT 4 0
+// Windows 2000 VER_PLATFORM_WIN32_NT 5 0
+// Windows XP VER_PLATFORM_WIN32_NT 5 1
+// Windows Vista VER_PLATFORM_WIN32_NT 6 0
+// Windows 7 VER_PLATFORM_WIN32_NT 6 1
+// Windows 95 VER_PLATFORM_WIN32_WINDOWS 4 0
+// Windows 98 VER_PLATFORM_WIN32_WINDOWS 4 10
+// Windows ME VER_PLATFORM_WIN32_WINDOWS 4 90
+
+
+static void Replace( const OUString& aLabel, sal_Unicode OldChar, sal_Unicode NewChar, OUStringBuffer& aBuffer )
+{
+ OSL_ASSERT( aLabel.getLength( ) );
+ OSL_ASSERT( aBuffer.getCapacity( ) >= (aLabel.getLength( )) );
+
+ sal_Int32 i = 0;
+ const sal_Unicode* pCurrent = aLabel.getStr( );
+ const sal_Unicode* pNext = aLabel.getStr( ) + 1;
+ const sal_Unicode* pEnd = aLabel.getStr( ) + aLabel.getLength( );
+
+ while( pCurrent < pEnd )
+ {
+ OSL_ASSERT( pNext <= pEnd );
+ OSL_ASSERT( (i >= 0) && (i < aBuffer.getCapacity( )) );
+
+ if ( OldChar == *pCurrent )
+ {
+ if ( OldChar == *pNext )
+ {
+ // two OldChars in line will
+ // be replaced by one
+ // e.g. ~~ -> ~
+ aBuffer.insert( i, *pCurrent );
+
+ // skip the next one
+ pCurrent++;
+ pNext++;
+ }
+ else
+ {
+ // one OldChar will be replace
+ // by NexChar
+ aBuffer.insert( i, NewChar );
+ }
+ }
+ else if ( *pCurrent == NewChar )
+ {
+ // a NewChar will be replaced by
+ // two NewChars
+ // e.g. & -> &&
+ aBuffer.insert( i++, *pCurrent );
+ aBuffer.insert( i, *pCurrent );
+ }
+ else
+ {
+ aBuffer.insert( i, *pCurrent );
+ }
+
+ pCurrent++;
+ pNext++;
+ i++;
+ }
+}
+
+// converts a soffice label to a windows label
+// the following rules for character replacements
+// will be done:
+// '~' -> '&'
+// '~~' -> '~'
+// '&' -> '&&'
+
+OUString SOfficeToWindowsLabel( const OUString& aSOLabel )
+{
+ OUString aWinLabel = aSOLabel;
+
+ if ( (aWinLabel.indexOf( TILDE_SIGN ) > -1) || (aWinLabel.indexOf( AMPERSAND_SIGN ) > -1) )
+ {
+ sal_Int32 nStrLen = aWinLabel.getLength( );
+
+ // in the worst case the new string is
+ // doubled in length, maybe some waste
+ // of memory but how long is a label
+ // normally(?)
+ OUStringBuffer aBuffer( nStrLen * 2 );
+
+ Replace( aWinLabel, TILDE_SIGN, AMPERSAND_SIGN, aBuffer );
+
+ aWinLabel = aBuffer.makeStringAndClear( );
+ }
+
+ return aWinLabel;
+}
+
+// converts a windows label to a soffice label
+// the following rules for character replacements
+// will be done:
+// '&' -> '~'
+// '&&' -> '&'
+// '~' -> '~~'
+
+OUString WindowsToSOfficeLabel( const OUString& aWinLabel )
+{
+ OUString aSOLabel = aWinLabel;
+
+ if ( (aSOLabel.indexOf( TILDE_SIGN ) > -1) || (aSOLabel.indexOf( AMPERSAND_SIGN ) > -1) )
+ {
+ sal_Int32 nStrLen = aSOLabel.getLength( );
+
+ // in the worst case the new string is
+ // doubled in length, maybe some waste
+ // of memory but how long is a label
+ // normally(?)
+ OUStringBuffer aBuffer( nStrLen * 2 );
+
+ Replace( aSOLabel, AMPERSAND_SIGN, TILDE_SIGN, aBuffer );
+
+ aSOLabel = aBuffer.makeStringAndClear( );
+ }
+
+ return aSOLabel;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/WinImplHelper.hxx b/fpicker/source/win32/WinImplHelper.hxx
new file mode 100644
index 000000000..9f7e16e94
--- /dev/null
+++ b/fpicker/source/win32/WinImplHelper.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+#if !defined WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+// converts a soffice label to a windows label
+// the following rules for character replacements
+// will be done:
+// '~' -> '&'
+// '~~' -> '~'
+// '&' -> '&&'
+OUString SOfficeToWindowsLabel(const OUString& aSOLabel);
+
+// converts a windows label to a soffice label
+// the following rules for character replacements
+// will be done:
+// '&' -> '~'
+// '&&' -> '&'
+// '~' -> '~~'
+OUString WindowsToSOfficeLabel(const OUString& aWinLabel);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/fps.component b/fpicker/source/win32/fps.component
new file mode 100644
index 000000000..3cc9c8029
--- /dev/null
+++ b/fpicker/source/win32/fps.component
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.ui.dialogs.Win32FilePicker"
+ constructor="fpicker_win32_FilePicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.SystemFilePicker"/>
+ </implementation>
+ <implementation name="com.sun.star.ui.dialogs.Win32FolderPicker"
+ constructor="fpicker_win32_FolderPicker_get_implementation">
+ <service name="com.sun.star.ui.dialogs.SystemFolderPicker"/>
+ </implementation>
+</component>
diff --git a/fpicker/source/win32/platform_vista.h b/fpicker/source/win32/platform_vista.h
new file mode 100644
index 000000000..ab3f7c52c
--- /dev/null
+++ b/fpicker/source/win32/platform_vista.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#undef _WTL_NO_CSTRING
+
+#define _WTL_NO_CSTRING
+
+#if defined _MSC_VER
+#include <comip.h>
+#undef RGB
+#endif
+
+#ifdef _MSC_VER
+#if defined _M_IX86
+ #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#elif defined _M_IA64
+ #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#elif defined _M_X64
+ #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#else
+ #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#endif
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/requests.hxx b/fpicker/source/win32/requests.hxx
new file mode 100644
index 000000000..a12f61ad8
--- /dev/null
+++ b/fpicker/source/win32/requests.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <comphelper/sequenceashashmap.hxx>
+
+namespace fpicker
+{
+namespace win32
+{
+namespace vista
+{
+/** @todo document me
+ */
+class Request
+{
+ // interface
+
+public:
+ explicit Request()
+ : m_nRequest(-1)
+ , m_lArguments()
+ {
+ }
+
+ virtual ~Request(){};
+
+ void setRequest(::sal_Int32 nRequest) { m_nRequest = nRequest; }
+
+ ::sal_Int32 getRequest() { return m_nRequest; }
+
+ void clearArguments() { m_lArguments.clear(); }
+
+ template <class TArgumentType>
+ void setArgument(const OUString& sName, const TArgumentType& aValue)
+ {
+ m_lArguments[sName] = css::uno::toAny(aValue);
+ }
+
+ template <class TArgumentType>
+ TArgumentType getArgumentOrDefault(const OUString& sName, const TArgumentType& aDefault)
+ {
+ return m_lArguments.getUnpackedValueOrDefault(sName, aDefault);
+ }
+
+ css::uno::Any getValue(OUString const& key) const { return m_lArguments.getValue(key); }
+
+ // member
+
+private:
+ ::sal_Int32 m_nRequest;
+ ::comphelper::SequenceAsHashMap m_lArguments;
+};
+
+} // namespace vista
+} // namespace win32
+} // namespace fpicker
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/fpicker/source/win32/resourceprovider.cxx b/fpicker/source/win32/resourceprovider.cxx
new file mode 100644
index 000000000..3515e9432
--- /dev/null
+++ b/fpicker/source/win32/resourceprovider.cxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <rtl/ustrbuf.hxx>
+#include "resourceprovider.hxx"
+#include <osl/mutex.hxx>
+#include <fpicker/strings.hrc>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+#include <unotools/resmgr.hxx>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <fpicker/fpsofficeResMgr.hxx>
+
+using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
+using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
+
+#define FOLDERPICKER_TITLE 500
+#define FOLDER_PICKER_DEF_DESCRIPTION 501
+
+// we have to translate control ids to resource ids
+
+namespace {
+
+struct Entry
+{
+ sal_Int32 ctrlId;
+ TranslateId resId;
+};
+
+}
+
+Entry const CtrlIdToResIdTable[] = {
+ { CHECKBOX_AUTOEXTENSION, STR_SVT_FILEPICKER_AUTO_EXTENSION },
+ { CHECKBOX_PASSWORD, STR_SVT_FILEPICKER_PASSWORD },
+ { CHECKBOX_FILTEROPTIONS, STR_SVT_FILEPICKER_FILTER_OPTIONS },
+ { CHECKBOX_READONLY, STR_SVT_FILEPICKER_READONLY },
+ { CHECKBOX_LINK, STR_SVT_FILEPICKER_INSERT_AS_LINK },
+ { CHECKBOX_PREVIEW, STR_SVT_FILEPICKER_SHOW_PREVIEW },
+ { PUSHBUTTON_PLAY, STR_SVT_FILEPICKER_PLAY },
+ { LISTBOX_VERSION_LABEL, STR_SVT_FILEPICKER_VERSION },
+ { LISTBOX_TEMPLATE_LABEL, STR_SVT_FILEPICKER_TEMPLATES },
+ { LISTBOX_IMAGE_TEMPLATE_LABEL, STR_SVT_FILEPICKER_IMAGE_TEMPLATE },
+ { LISTBOX_IMAGE_ANCHOR_LABEL, STR_SVT_FILEPICKER_IMAGE_ANCHOR },
+ { CHECKBOX_SELECTION, STR_SVT_FILEPICKER_SELECTION },
+ { FOLDERPICKER_TITLE, STR_SVT_FOLDERPICKER_DEFAULT_TITLE },
+ { FOLDER_PICKER_DEF_DESCRIPTION, STR_SVT_FOLDERPICKER_DEFAULT_DESCRIPTION },
+ { CHECKBOX_GPGENCRYPTION, STR_SVT_FILEPICKER_GPGENCRYPT }
+};
+
+const sal_Int32 SIZE_TABLE = SAL_N_ELEMENTS( CtrlIdToResIdTable );
+
+static TranslateId CtrlIdToResId( sal_Int32 aControlId )
+{
+ TranslateId pResId;
+
+ for ( sal_Int32 i = 0; i < SIZE_TABLE; i++ )
+ {
+ if ( CtrlIdToResIdTable[i].ctrlId == aControlId )
+ {
+ pResId = CtrlIdToResIdTable[i].resId;
+ break;
+ }
+ }
+
+ return pResId;
+}
+
+namespace CResourceProvider
+{
+ OUString getResString( sal_Int16 aId )
+ {
+ OUString aResOUString;
+ // translate the control id to a resource id
+ TranslateId pResId = CtrlIdToResId(aId);
+ if (pResId)
+ aResOUString = FpsResId(pResId);
+ return aResOUString;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/resourceprovider.hxx b/fpicker/source/win32/resourceprovider.hxx
new file mode 100644
index 000000000..09ae8339b
--- /dev/null
+++ b/fpicker/source/win32/resourceprovider.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <sal/types.h>
+
+#include <rtl/ustring.hxx>
+
+namespace CResourceProvider
+{
+OUString getResString(sal_Int16 aId);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/shared.hxx b/fpicker/source/win32/shared.hxx
new file mode 100644
index 000000000..c47f6411e
--- /dev/null
+++ b/fpicker/source/win32/shared.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+const OUStringLiteral BACKSLASH(u"\\");
+const OUStringLiteral FILTER_SEPARATOR(u"------------------------------------------");
+const OUStringLiteral ALL_FILES_WILDCARD(u"*.*");
+const bool ALLOW_DUPLICATES = true;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/vistatypes.h b/fpicker/source/win32/vistatypes.h
new file mode 100644
index 000000000..a6a08faa5
--- /dev/null
+++ b/fpicker/source/win32/vistatypes.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <shobjidl.h>
+#include <systools/win32/comtools.hxx>
+
+namespace fpicker{
+namespace win32{
+namespace vista{
+
+
+// types, const etcpp.
+
+
+typedef sal::systools::COMReference<IFileDialog> TFileDialog;
+typedef sal::systools::COMReference<IFileOpenDialog> TFileOpenDialog;
+typedef sal::systools::COMReference<IFileSaveDialog> TFileSaveDialog;
+typedef sal::systools::COMReference<IFileDialogEvents> TFileDialogEvents;
+typedef sal::systools::COMReference<IFileDialogCustomize> TFileDialogCustomize;
+
+} // namespace vista
+} // namespace win32
+} // namespace fpicker
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/workbench/Test_fps.cxx b/fpicker/source/win32/workbench/Test_fps.cxx
new file mode 100644
index 000000000..899f208f1
--- /dev/null
+++ b/fpicker/source/win32/workbench/Test_fps.cxx
@@ -0,0 +1,356 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/registry/XSimpleRegistry.hpp>
+#include <osl/file.hxx>
+
+#include <cppuhelper/servicefactory.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <osl/diagnose.h>
+#include <com/sun/star/ui/dialogs/XFilePicker.hpp>
+#include <com/sun/star/ui/dialogs/XFilterManager.hpp>
+
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/ui/dialogs/XFilePickerListener.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerNotifier.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ListboxControlActions.hpp>
+#include <com/sun/star/ui/dialogs/XFilePreview.hpp>
+
+#include <osl/thread.h>
+
+#include <stdio.h>
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include "..\FPServiceInfo.hxx"
+
+
+// namespaces
+
+
+using namespace ::cppu ;
+using namespace ::com::sun::star::uno ;
+using namespace ::com::sun::star::lang ;
+using namespace ::com::sun::star::ui::dialogs ;
+using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
+
+using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
+using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds;
+using namespace ::com::sun::star::ui::dialogs::ListboxControlActions;
+
+using namespace std ;
+
+// forward
+
+void TestFilterManager( Reference< XFilePicker > xFilePicker );
+
+
+#define RDB_SYSPATH "D:\\Projects\\gsl\\sysui\\wntmsci7\\bin\\applicat.rdb"
+
+
+// global variables
+
+
+Reference< XMultiServiceFactory > g_xFactory;
+
+static const OUStringLiteral BMP_EXTENSION( u"bmp" );
+
+
+// a test client
+
+
+class FilePickerListener : public WeakImplHelper< XFilePickerListener >
+{
+public:
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source )
+ throw(css::uno::RuntimeException);
+
+ // XFilePickerListener
+ virtual void SAL_CALL fileSelectionChanged( const css::ui::dialogs::FilePickerEvent& aEvent )
+ throw(css::uno::RuntimeException);
+
+ virtual void SAL_CALL directoryChanged( const css::ui::dialogs::FilePickerEvent& aEvent )
+ throw(css::uno::RuntimeException);
+
+ virtual OUString SAL_CALL helpRequested( const css::ui::dialogs::FilePickerEvent& aEvent )
+ throw(css::uno::RuntimeException);
+
+ virtual void SAL_CALL controlStateChanged( const css::ui::dialogs::FilePickerEvent& aEvent )
+ throw(css::uno::RuntimeException);
+
+ virtual void SAL_CALL dialogSizeChanged( )
+ throw (css::uno::RuntimeException);
+};
+
+void SAL_CALL FilePickerListener::disposing( const css::lang::EventObject& Source )
+ throw(css::uno::RuntimeException)
+{
+}
+
+void SAL_CALL FilePickerListener::fileSelectionChanged( const css::ui::dialogs::FilePickerEvent& aEvent )
+ throw(css::uno::RuntimeException)
+{
+ try
+ {
+ Reference< XFilePicker > rXFilePicker( aEvent.Source, UNO_QUERY );
+ Reference< XFilePreview > rXFilePreview( rXFilePicker, UNO_QUERY );
+
+ if ( !rXFilePreview.is( ) )
+ return;
+
+ Sequence< OUString > aFileList = rXFilePicker->getFiles( );
+ if ( 1 == aFileList.getLength( ) )
+ {
+ OUString FilePath = aFileList[0];
+
+ // detect file extension
+ sal_Int32 nIndex = FilePath.lastIndexOf( BMP_EXTENSION );
+ if ( (FilePath.getLength( ) - 3) == nIndex )
+ {
+ OUString FileSysPath;
+ ::osl::FileBase::getSystemPathFromFileURL(
+ FilePath, FileSysPath );
+
+ HANDLE hFile = CreateFileW(
+ FileSysPath.getStr( ),
+ GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL) ;
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return;
+
+ DWORD dwHighSize;
+ DWORD dwFileSize = GetFileSize (hFile, &dwHighSize) ;
+
+ if (dwHighSize)
+ {
+ CloseHandle (hFile) ;
+ return;
+ }
+
+ Sequence< sal_Int8 > aDIB( dwFileSize );
+
+ DWORD dwBytesRead;
+ sal_Bool bSuccess = ReadFile (hFile, aDIB.getArray( ), dwFileSize, &dwBytesRead, NULL) ;
+ CloseHandle (hFile);
+
+ BITMAPFILEHEADER* pbmfh = (BITMAPFILEHEADER*)aDIB.getConstArray( );
+ if (!bSuccess || (dwBytesRead != dwFileSize)
+ || (pbmfh->bfType != * (WORD *) "BM")
+ || (pbmfh->bfSize != dwFileSize))
+ {
+ return;
+ }
+
+ Any aAny;
+
+ aAny <<= aDIB;
+ rXFilePreview->setImage( 1, aAny );
+ }
+ }
+ }
+ catch( IllegalArgumentException& )
+ {
+ }
+}
+
+void SAL_CALL FilePickerListener::directoryChanged( const css::ui::dialogs::FilePickerEvent& aEvent )
+ throw(css::uno::RuntimeException)
+{
+ Reference< XFilePickerControlAccess > rFilePickerCtrlAccess( aEvent.Source, UNO_QUERY );
+}
+
+OUString SAL_CALL FilePickerListener::helpRequested( const css::ui::dialogs::FilePickerEvent& aEvent )
+ throw(css::uno::RuntimeException)
+{
+ return OUString( );
+}
+
+void SAL_CALL FilePickerListener::controlStateChanged( const css::ui::dialogs::FilePickerEvent& aEvent )
+ throw(css::uno::RuntimeException)
+{
+ try
+ {
+ Reference< XFilePickerControlAccess > rFPCtrlAccess( aEvent.Source, UNO_QUERY );
+
+ Any aValue;
+
+ OUString lbString( L"Ein Eintrag 1" );
+ aValue <<= lbString;
+ rFPCtrlAccess->setValue( LISTBOX_VERSION, ADD_ITEM, aValue );
+
+ lbString = L"Ein Eintrag 2";
+ aValue <<= lbString;
+ rFPCtrlAccess->setValue( LISTBOX_VERSION, ADD_ITEM, aValue );
+
+ lbString = L"Ein Eintrag 3";
+ aValue <<= lbString;
+ rFPCtrlAccess->setValue( LISTBOX_VERSION, ADD_ITEM, aValue );
+
+ sal_Int16 nSel = 1;
+ aValue <<= nSel;
+ rFPCtrlAccess->setValue( LISTBOX_VERSION, SET_SELECT_ITEM, aValue );
+
+ sal_Int32 nDel = 0;
+ aValue <<= nDel;
+ rFPCtrlAccess->setValue( LISTBOX_VERSION, DELETE_ITEM, aValue );
+ }
+ catch( ... )
+ {
+ }
+}
+
+void SAL_CALL FilePickerListener::dialogSizeChanged( )
+ throw(css::uno::RuntimeException)
+{
+}
+
+
+// main
+
+
+int SAL_CALL main(int nArgc, char* Argv[], char* Env[] )
+{
+ printf("Starting test of FPS-Service\n");
+
+
+ // get the global service-manager
+
+
+ // Get global factory for uno services.
+ Reference< XMultiServiceFactory > g_xFactory( createRegistryServiceFactory( RDB_SYSPATH ) );
+
+ // Print a message if an error occurred.
+ if ( g_xFactory.is() == sal_False )
+ {
+ OSL_FAIL("Can't create RegistryServiceFactory");
+ return(-1);
+ }
+
+
+ // try to get an Interface to a XFilePicker Service
+
+
+ Sequence< Any > arguments(1);
+ arguments[0] = makeAny( FILEOPEN_READONLY_VERSION );
+
+ Reference< XFilePicker > xFilePicker(
+ g_xFactory->createInstanceWithArguments(
+ "com.sun.star.ui.dialogs.SystemFilePicker", arguments ), UNO_QUERY );
+
+ // install a FilePicker notifier
+ Reference< XFilePickerListener > xFPListener(
+ static_cast< XFilePickerListener* >( new FilePickerListener()), UNO_QUERY );
+
+ Reference< XFilePickerNotifier > xFPNotifier( xFilePicker, UNO_QUERY );
+ if ( xFPNotifier.is( ) )
+ xFPNotifier->addFilePickerListener( xFPListener );
+
+ xFilePicker->setTitle( OUString("FileOpen Simple..."));
+ xFilePicker->setMultiSelectionMode( sal_True );
+ xFilePicker->setDefaultName( OUString("d:\\test2.sxw"));
+
+ OUString aDirURL;
+ OUString aSysPath = OStringToOUString( "d:\\ueaeoe", osl_getThreadTextEncoding( ) );
+ ::osl::FileBase::getFileURLFromSystemPath( aSysPath, aDirURL );
+ xFilePicker->setDisplayDirectory( aDirURL );
+
+ Reference< XFilterManager > xFilterMgr( xFilePicker, UNO_QUERY );
+ if ( xFilterMgr.is( ) )
+ {
+ xFilterMgr->appendFilter( L"Alle", L"*.*" );
+ xFilterMgr->appendFilter( L"BMP", L"*.bmp" );
+ xFilterMgr->appendFilter( L"SDW", L"*.sdw;*.sdc;*.sdi" );
+ xFilterMgr->appendFilter( L"SXW", L"*.sxw;*.sxi" );
+ }
+
+ Reference< XFilePickerControlAccess > xFPControlAccess( xFilePicker, UNO_QUERY );
+
+ Any aAny;
+ sal_Bool bChkState = sal_False;
+
+ aAny.setValue( &bChkState, cppu::UnoType<sal_Bool>::get());
+ xFPControlAccess->setValue( CHECKBOX_AUTOEXTENSION, 0, aAny );
+
+ OUString aVersion( L"Version 1" );
+ aAny <<= aVersion;
+ xFPControlAccess->setValue( LISTBOX_VERSION, ADD_ITEM, aAny );
+ xFPControlAccess->setValue( LISTBOX_VERSION, ADD_ITEM, aAny );
+ xFPControlAccess->setValue( LISTBOX_VERSION, ADD_ITEM, aAny );
+
+ xFilePicker->execute( );
+
+ sal_Bool bCheckState;
+ aAny = xFPControlAccess->getValue( CHECKBOX_AUTOEXTENSION, 0 );
+ if ( aAny.hasValue( ) )
+ bCheckState = *reinterpret_cast< const sal_Bool* >( aAny.getValue( ) );
+
+ aAny = xFPControlAccess->getValue( CHECKBOX_READONLY, 0 );
+ if ( aAny.hasValue( ) )
+ bCheckState = *reinterpret_cast< const sal_Bool* >( aAny.getValue( ) );
+
+ aAny = xFPControlAccess->getValue( LISTBOX_VERSION, GET_SELECTED_ITEM );
+ sal_Int32 nSel;
+ if ( aAny.hasValue( ) )
+ aAny >>= nSel;
+
+ aDirURL = xFilePicker->getDisplayDirectory( );
+ Sequence< OUString > aFileList = xFilePicker->getFiles( );
+ for ( int i = 0; i < aFileList.getLength( ); i++ )
+ {
+ OUString nextPath = aFileList[i];
+ }
+
+ if ( xFPNotifier.is( ) )
+ xFPNotifier->removeFilePickerListener( xFPListener );
+
+
+ // shutdown
+
+
+ // Cast factory to XComponent
+ Reference< XComponent > xComponent( g_xFactory, UNO_QUERY );
+
+ // Print a message if an error occurred.
+ if ( xComponent.is() == sal_False )
+ {
+ OSL_FAIL("Error shutting down");
+ }
+
+ // Dispose and clear factory
+ xComponent->dispose();
+ g_xFactory.clear();
+
+ printf("Test successful\n");
+
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/fpicker/source/win32/workbench/makefile.mk b/fpicker/source/win32/workbench/makefile.mk
new file mode 100644
index 000000000..5398300c1
--- /dev/null
+++ b/fpicker/source/win32/workbench/makefile.mk
@@ -0,0 +1,55 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+PRJ=..$/..$/..$/..
+
+PRJNAME=sysui
+TARGET=testfps
+LIBTARGET=NO
+TARGETTYPE=CUI
+
+# --- Settings -----------------------------------------------------
+
+.INCLUDE : settings.mk
+
+.IF "$(COM)" == "MSC"
+CFLAGS+=-GR -GX
+.ENDIF
+
+# --- Files --------------------------------------------------------
+
+
+OBJFILES=$(OBJ)$/test_fps.obj
+
+APP1TARGET=$(TARGET)
+APP1OBJS=$(OBJFILES)
+
+APP1STDLIBS+=\
+ $(CPPULIB)\
+ $(CPPUHELPERLIB)\
+ $(SALLIB)\
+ $(USER32LIB)\
+ $(OLE32LIB)
+
+APP1DEF=$(MISC)$/$(APP1TARGET).def
+
+# --- Targets ------------------------------------------------------
+
+.INCLUDE : target.mk
+
+
diff --git a/fpicker/uiconfig/ui/breadcrumb.ui b/fpicker/uiconfig/ui/breadcrumb.ui
new file mode 100644
index 000000000..5e58b5a76
--- /dev/null
+++ b/fpicker/uiconfig/ui/breadcrumb.ui
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="fps">
+ <requires lib="gtk+" version="3.20"/>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="container">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkLinkButton" id="link">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/fpicker/uiconfig/ui/explorerfiledialog.ui b/fpicker/uiconfig/ui/explorerfiledialog.ui
new file mode 100644
index 000000000..852ae4f27
--- /dev/null
+++ b/fpicker/uiconfig/ui/explorerfiledialog.ui
@@ -0,0 +1,896 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="fps">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon-name">list-add</property>
+ </object>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon-name">list-remove</property>
+ </object>
+ <object class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">fpicker/res/fp014.png</property>
+ </object>
+ <object class="GtkTreeStore" id="liststore1">
+ <columns>
+ <!-- column-name expander -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="liststore2">
+ <columns>
+ <!-- column-name expander -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="liststore3">
+ <columns>
+ <!-- column-name expander -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name text2 -->
+ <column type="gchararray"/>
+ <!-- column-name text3 -->
+ <column type="gchararray"/>
+ <!-- column-name text1 -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="ExplorerFileDialog">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="layout_style">start</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox" id="box4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkComboBoxText" id="current_path">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <property name="has_entry">True</property>
+ <child internal-child="entry">
+ <object class="GtkEntry">
+ <property name="truncate-multiline">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="connect_to_server">
+ <property name="label" translatable="yes" context="explorerfiledialog|connect_to_server">Servers...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes" context="explorerfiledialog|connect_to_server|tooltip_text">Connect To Server</property>
+ <property name="valign">center</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="connect_to_server-atkobject">
+ <property name="AtkObject::accessible-name" translatable="yes" context="explorerfiledialog|connect_to_server-atkobject">Connect To Server</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="valign">center</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <property name="propagate_natural_width">True</property>
+ <property name="propagate_natural_height">True</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkToolbar" id="up_bar">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkMenuToolButton" id="up_btn">
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="icon_name">res/fp010.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="new_folder">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes" context="explorerfiledialog|new_folder|tooltip_text">Create New Folder</property>
+ <property name="valign">center</property>
+ <property name="image">image4</property>
+ <property name="always_show_image">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="new_folder-atkobject">
+ <property name="AtkObject::accessible-name" translatable="yes" context="explorerfiledialog|new_folder-atkobject">Create New Folder</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="container">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkPaned">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="places">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore1</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">1</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn5">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="explorerfiledialog|places">Places</property>
+ <child>
+ <object class="GtkCellRendererPixbuf" id="cellrenderertext6"/>
+ <attributes>
+ <attribute name="pixbuf">0</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer11"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="add">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes" context="explorerfiledialog|add">Add current folder to Places</property>
+ <property name="image">image1</property>
+ <property name="always_show_image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="del">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes" context="explorerfiledialog|add">Remove selected folder from Places</property>
+ <property name="image">image2</property>
+ <property name="always_show_image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="fileview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore3</property>
+ <property name="search_column">1</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn3">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="explorerfiledialog|name">Name</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererPixbuf" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="pixbuf">0</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer1"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn4">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="explorerfiledialog|type">Type</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer5"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="explorerfiledialog|size">Size</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer2"/>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="explorerfiledialog|date">Date modified</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer3"/>
+ <attributes>
+ <attribute name="text">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="can_focus">True</property>
+ <property name="no_show_all">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkIconView" id="iconview">
+ <property name="can_focus">True</property>
+ <property name="no_show_all">True</property>
+ <property name="margin">6</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore2</property>
+ <property name="pixbuf-column">0</property>
+ <property name="text-column">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkButtonBox" id="buttonbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">start</property>
+ <child>
+ <object class="GtkButton" id="open">
+ <property name="label" translatable="yes" context="explorerfiledialog|open">_Open</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="play">
+ <property name="label" translatable="yes" context="explorerfiledialog|play">_Play</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">12</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="file_name_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="explorerfiledialog|file_name_label">File _name:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">file_name</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shared_label">
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">shared</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="file_type_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="explorerfiledialog|file_type_label">File _type:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">file_type</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="shared">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="file_type">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="file_name">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="has_entry">True</property>
+ <child internal-child="entry">
+ <object class="GtkEntry">
+ <property name="can_focus">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="readonly">
+ <property name="label" translatable="yes" context="explorerfiledialog|readonly">_Read-only</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="checkboxes">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkCheckButton" id="password">
+ <property name="label" translatable="yes" context="explorerfiledialog|password">Save with password</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="extension">
+ <property name="label" translatable="yes" context="explorerfiledialog|extension">_Automatic file name extension</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="options">
+ <property name="label" translatable="yes" context="explorerfiledialog|options">Edit _filter settings</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="selection">
+ <property name="label">
+</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="gpgencrypt">
+ <property name="label" translatable="yes" context="explorerfiledialog|gpgencrypt">Encrypt with GPG key</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkCheckButton" id="link">
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="cb_preview">
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkScrolledWindow" id="previewframe">
+ <property name="can_focus">True</property>
+ <property name="no_show_all">True</property>
+ <property name="valign">start</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <property name="propagate_natural_width">True</property>
+ <property name="propagate_natural_height">True</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImage" id="preview">
+ <property name="width_request">200</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="titlebar">
+ <placeholder/>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkMenu" id="up_menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+</interface>
diff --git a/fpicker/uiconfig/ui/foldernamedialog.ui b/fpicker/uiconfig/ui/foldernamedialog.ui
new file mode 100644
index 000000000..5cc933a70
--- /dev/null
+++ b/fpicker/uiconfig/ui/foldernamedialog.ui
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="fps">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="FolderNameDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="foldernamedialog|FolderNameDialog">Folder Name</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">start</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="foldernamedialog|label2">Na_me:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">entry</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="foldernamedialog|label1">Create New Folder</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/fpicker/uiconfig/ui/remotefilesdialog.ui b/fpicker/uiconfig/ui/remotefilesdialog.ui
new file mode 100644
index 000000000..c486bf7eb
--- /dev/null
+++ b/fpicker/uiconfig/ui/remotefilesdialog.ui
@@ -0,0 +1,562 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="fps">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">svx/res/galicon.png</property>
+ </object>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">svx/res/listview.png</property>
+ </object>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">fpicker/res/fp014.png</property>
+ </object>
+ <object class="GtkTreeStore" id="liststore1">
+ <columns>
+ <!-- column-name expander -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="liststore2">
+ <columns>
+ <!-- column-name expander -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="liststore3">
+ <columns>
+ <!-- column-name expander -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name text2 -->
+ <column type="gchararray"/>
+ <!-- column-name text3 -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkMenu" id="service_edit_menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="add_service">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="remotefilesdialog|edit_service">_Add service</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="edit_service">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="remotefilesdialog|edit_service">_Edit service</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_service">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="remotefilesdialog|delete_service">_Delete service</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="change_password">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="remotefilesdialog|change_password">_Change password</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkDialog" id="RemoteFilesDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="remotefilesdialog|RemoteFilesDialog">Remote Files</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="remotefilesdialog|open">_Open</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="remotefilesdialog|label1">Service:</property>
+ <accessibility>
+ <relation type="label-for" target="services_lb"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="services_lb">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <accessibility>
+ <relation type="labelled-by" target="label1"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="add_service_btn">
+ <property name="label" translatable="yes" context="remotefilesdialog|add_service_btn">_Manage services</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="popup">service_edit_menu</property>
+ <property name="use_popover">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox" id="breadcrumb_container">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="list_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes" context="remotefilesdialog|list_view|tooltip_text">List view</property>
+ <property name="image">image2</property>
+ <property name="active">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="icon_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes" context="remotefilesdialog|icon_view|tooltip_text">Icon view</property>
+ <property name="image">image1</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="new_folder">
+ <property name="width_request">25</property>
+ <property name="height_request">25</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes" context="remotefilesdialog|new_folder|tooltip_text">Create New Folder</property>
+ <property name="margin-start">6</property>
+ <property name="image">image3</property>
+ <property name="always_show_image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="container">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkPaned">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="foldertree">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore1</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="Macro Library List-selection1"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn5">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCellRendererPixbuf" id="cellrenderertext6"/>
+ <attributes>
+ <attribute name="pixbuf">0</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer11"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="fileview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore3</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="Macro Library List-selection2"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn3">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="remotefilesdialog|name">Name</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererPixbuf" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="pixbuf">0</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer1"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn4">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="remotefilesdialog|size">Size</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer2"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="remotefilesdialog|date">Date modified</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer3"/>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="can_focus">True</property>
+ <property name="no_show_all">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkIconView" id="iconview">
+ <property name="can_focus">True</property>
+ <property name="no_show_all">True</property>
+ <property name="margin">6</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore2</property>
+ <property name="pixbuf_column">0</property>
+ <property name="text_column">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="filterLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="remotefilesdialog|filterLabel">Filter</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">filter_lb</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="nameLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="remotefilesdialog|nameLabel">File name</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">filename</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="filter_lb">
+ <property name="width_request">200</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="filename">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ </action-widgets>
+ </object>
+</interface>