1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <Qt5FilePicker.hxx>
21 #include <Qt5FilePicker.moc>
23 #include <Qt5Frame.hxx>
24 #include <Qt5Tools.hxx>
25 #include <Qt5Widget.hxx>
26 #include <Qt5Instance.hxx>
28 #include <com/sun/star/awt/SystemDependentXWindow.hpp>
29 #include <com/sun/star/awt/XSystemDependentWindowPeer.hpp>
30 #include <com/sun/star/awt/XWindow.hpp>
31 #include <com/sun/star/frame/Desktop.hpp>
32 #include <com/sun/star/frame/TerminationVetoException.hpp>
33 #include <com/sun/star/frame/XDesktop.hpp>
34 #include <com/sun/star/lang/DisposedException.hpp>
35 #include <com/sun/star/lang/IllegalArgumentException.hpp>
36 #include <com/sun/star/lang/SystemDependent.hpp>
37 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
38 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
39 #include <com/sun/star/ui/dialogs/ControlActions.hpp>
40 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
41 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
42 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
43 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
44 #include <cppuhelper/interfacecontainer.h>
45 #include <cppuhelper/supportsservice.hxx>
46 #include <rtl/process.h>
47 #include <sal/log.hxx>
49 #include <QtCore/QDebug>
50 #include <QtCore/QRegularExpression>
51 #include <QtCore/QThread>
52 #include <QtCore/QUrl>
53 #include <QtGui/QClipboard>
54 #include <QtGui/QWindow>
55 #include <QtWidgets/QApplication>
56 #include <QtWidgets/QCheckBox>
57 #include <QtWidgets/QComboBox>
58 #include <QtWidgets/QGridLayout>
59 #include <QtWidgets/QHBoxLayout>
60 #include <QtWidgets/QLabel>
61 #include <QtWidgets/QMessageBox>
62 #include <QtWidgets/QPushButton>
63 #include <QtWidgets/QWidget>
65 #include <unx/geninst.h>
66 #include <strings.hrc>
68 using namespace ::com::sun::star
;
69 using namespace ::com::sun::star::ui::dialogs
;
70 using namespace ::com::sun::star::ui::dialogs::TemplateDescription
;
71 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds
;
72 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds
;
73 using namespace ::com::sun::star::lang
;
74 using namespace ::com::sun::star::beans
;
75 using namespace ::com::sun::star::uno
;
79 uno::Sequence
<OUString
> FilePicker_getSupportedServiceNames()
81 return { "com.sun.star.ui.dialogs.FilePicker", "com.sun.star.ui.dialogs.SystemFilePicker",
82 "com.sun.star.ui.dialogs.Qt5FilePicker" };
86 Qt5FilePicker::Qt5FilePicker(css::uno::Reference
<css::uno::XComponentContext
> const& context
,
87 QFileDialog::FileMode eMode
, bool bUseNative
)
88 : Qt5FilePicker_Base(m_aHelperMutex
)
90 , m_bIsFolderPicker(eMode
== QFileDialog::Directory
)
91 , m_pParentWidget(nullptr)
92 , m_pFileDialog(new QFileDialog(nullptr, {}, QDir::homePath()))
93 , m_pExtraControls(new QWidget())
95 m_pFileDialog
->setOption(QFileDialog::DontUseNativeDialog
, !bUseNative
);
97 m_pFileDialog
->setFileMode(eMode
);
98 m_pFileDialog
->setWindowModality(Qt::ApplicationModal
);
100 if (m_bIsFolderPicker
)
102 m_pFileDialog
->setOption(QFileDialog::ShowDirsOnly
, true);
103 m_pFileDialog
->setWindowTitle(toQString(VclResId(STR_FPICKER_FOLDER_DEFAULT_TITLE
)));
106 m_pLayout
= dynamic_cast<QGridLayout
*>(m_pFileDialog
->layout());
108 setMultiSelectionMode(false);
110 // XFilePickerListener notifications
111 connect(m_pFileDialog
.get(), SIGNAL(filterSelected(const QString
&)), this,
112 SLOT(filterSelected(const QString
&)));
113 connect(m_pFileDialog
.get(), SIGNAL(currentChanged(const QString
&)), this,
114 SLOT(currentChanged(const QString
&)));
116 // update automatic file extension when filter is changed
117 connect(m_pFileDialog
.get(), SIGNAL(filterSelected(const QString
&)), this,
118 SLOT(updateAutomaticFileExtension()));
121 Qt5FilePicker::~Qt5FilePicker()
124 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
126 pSalInst
->RunInMainThread([this]() {
127 // must delete it in main thread, otherwise
128 // QSocketNotifier::setEnabled() will crash us
129 m_pFileDialog
.reset();
134 Qt5FilePicker::addFilePickerListener(const uno::Reference
<XFilePickerListener
>& xListener
)
136 SolarMutexGuard aGuard
;
137 m_xListener
= xListener
;
140 void SAL_CALL
Qt5FilePicker::removeFilePickerListener(const uno::Reference
<XFilePickerListener
>&)
142 SolarMutexGuard aGuard
;
146 void SAL_CALL
Qt5FilePicker::setTitle(const OUString
& title
)
149 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
151 pSalInst
->RunInMainThread(
152 [this, &title
]() { m_pFileDialog
->setWindowTitle(toQString(title
)); });
155 sal_Int16 SAL_CALL
Qt5FilePicker::execute()
158 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
160 if (!pSalInst
->IsMainThread())
163 pSalInst
->RunInMainThread([&ret
, this]() { ret
= execute(); });
167 QWidget
* pTransientParent
= m_pParentWidget
;
168 if (!pTransientParent
)
170 vcl::Window
* pWindow
= ::Application::GetActiveTopWindow();
173 Qt5Frame
* pFrame
= dynamic_cast<Qt5Frame
*>(pWindow
->ImplGetFrame());
176 pTransientParent
= pFrame
->asChild();
180 if (!m_aNamedFilterList
.isEmpty())
181 m_pFileDialog
->setNameFilters(m_aNamedFilterList
);
182 if (!m_aCurrentFilter
.isEmpty())
183 m_pFileDialog
->selectNameFilter(m_aCurrentFilter
);
185 updateAutomaticFileExtension();
187 uno::Reference
<css::frame::XDesktop
> xDesktop(css::frame::Desktop::create(m_context
),
190 // will hide the window, so do before show
191 m_pFileDialog
->setParent(pTransientParent
, m_pFileDialog
->windowFlags());
192 m_pFileDialog
->show();
193 xDesktop
->addTerminateListener(this);
194 int result
= m_pFileDialog
->exec();
195 xDesktop
->removeTerminateListener(this);
196 m_pFileDialog
->setParent(nullptr, m_pFileDialog
->windowFlags());
198 if (QFileDialog::Rejected
== result
)
199 return ExecutableDialogResults::CANCEL
;
200 return ExecutableDialogResults::OK
;
203 void SAL_CALL
Qt5FilePicker::setMultiSelectionMode(sal_Bool multiSelect
)
206 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
208 pSalInst
->RunInMainThread([this, multiSelect
]() {
209 if (m_bIsFolderPicker
|| m_pFileDialog
->acceptMode() == QFileDialog::AcceptSave
)
212 m_pFileDialog
->setFileMode(multiSelect
? QFileDialog::ExistingFiles
213 : QFileDialog::ExistingFile
);
217 void SAL_CALL
Qt5FilePicker::setDefaultName(const OUString
& name
)
220 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
222 pSalInst
->RunInMainThread([this, &name
]() { m_pFileDialog
->selectFile(toQString(name
)); });
225 void SAL_CALL
Qt5FilePicker::setDisplayDirectory(const OUString
& dir
)
228 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
230 pSalInst
->RunInMainThread([this, &dir
]() {
231 QString
qDir(toQString(dir
));
232 m_pFileDialog
->setDirectoryUrl(QUrl(qDir
));
236 OUString SAL_CALL
Qt5FilePicker::getDisplayDirectory()
240 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
242 pSalInst
->RunInMainThread(
243 [&ret
, this]() { ret
= toOUString(m_pFileDialog
->directoryUrl().toString()); });
247 uno::Sequence
<OUString
> SAL_CALL
Qt5FilePicker::getFiles()
249 uno::Sequence
<OUString
> seq
= getSelectedFiles();
250 if (seq
.getLength() > 1)
255 uno::Sequence
<OUString
> SAL_CALL
Qt5FilePicker::getSelectedFiles()
259 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
261 pSalInst
->RunInMainThread([&urls
, this]() { urls
= m_pFileDialog
->selectedUrls(); });
263 uno::Sequence
<OUString
> seq(urls
.size());
265 auto const trans
= css::uri::ExternalUriReferenceTranslator::create(m_context
);
267 for (const QUrl
& aURL
: urls
)
269 // Unlike LO, QFileDialog (<https://doc.qt.io/qt-5/qfiledialog.html>) apparently always
270 // treats file-system pathnames as UTF-8--encoded, regardless of LANG/LC_CTYPE locale
271 // setting. And pathnames containing byte sequences that are not valid UTF-8 are apparently
272 // filtered out and not even displayed by QFileDialog, so aURL will always have a "payload"
273 // that matches the pathname's byte sequence. So the pathname's byte sequence (which
274 // happens to also be aURL's payload) in the LANG/LC_CTYPE encoding needs to be converted
275 // into LO's internal UTF-8 file URL encoding via
276 // XExternalUriReferenceTranslator::translateToInternal (which looks somewhat paradoxical as
277 // aURL.toEncoded() nominally already has a UTF-8 payload):
278 auto const extUrl
= toOUString(aURL
.toEncoded());
279 auto intUrl
= trans
->translateToInternal(extUrl
);
280 if (intUrl
.isEmpty())
282 // If translation failed, fall back to original URL:
283 SAL_WARN("vcl.qt5", "cannot convert <" << extUrl
<< "> from locale encoding to UTF-8");
292 void SAL_CALL
Qt5FilePicker::appendFilter(const OUString
& title
, const OUString
& filter
)
295 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
297 if (!pSalInst
->IsMainThread())
299 pSalInst
->RunInMainThread([this, &title
, &filter
]() { appendFilter(title
, filter
); });
303 // '/' need to be escaped else they are assumed to be mime types
304 QString sTitle
= toQString(title
).replace("/", "\\/");
306 QString sFilterName
= sTitle
;
307 // the Qt5 non-native file picker adds the extensions to the filter title, so strip them
308 if (m_pFileDialog
->testOption(QFileDialog::DontUseNativeDialog
))
310 int pos
= sFilterName
.indexOf(" (");
312 sFilterName
.truncate(pos
);
315 QString sGlobFilter
= toQString(filter
);
317 // LibreOffice gives us filters separated by ';' qt dialogs just want space separated
318 sGlobFilter
.replace(";", " ");
320 // make sure "*.*" is not used as "all files"
321 sGlobFilter
.replace("*.*", "*");
323 m_aNamedFilterList
<< QStringLiteral("%1 (%2)").arg(sFilterName
, sGlobFilter
);
324 m_aTitleToFilterMap
[sTitle
] = m_aNamedFilterList
.constLast();
325 m_aNamedFilterToExtensionMap
[m_aNamedFilterList
.constLast()] = sGlobFilter
;
328 void SAL_CALL
Qt5FilePicker::setCurrentFilter(const OUString
& title
)
331 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
333 pSalInst
->RunInMainThread([this, &title
]() {
334 m_aCurrentFilter
= m_aTitleToFilterMap
.value(toQString(title
).replace("/", "\\/"));
338 OUString SAL_CALL
Qt5FilePicker::getCurrentFilter()
342 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
344 pSalInst
->RunInMainThread([&filter
, this]() {
345 filter
= m_aTitleToFilterMap
.key(m_pFileDialog
->selectedNameFilter());
348 if (filter
.isEmpty())
349 filter
= "ODF Text Document (.odt)";
350 return toOUString(filter
);
353 void SAL_CALL
Qt5FilePicker::appendFilterGroup(const OUString
& rGroupTitle
,
354 const uno::Sequence
<beans::StringPair
>& filters
)
357 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
359 if (!pSalInst
->IsMainThread())
361 pSalInst
->RunInMainThread(
362 [this, &rGroupTitle
, &filters
]() { appendFilterGroup(rGroupTitle
, filters
); });
366 const sal_uInt16 length
= filters
.getLength();
367 for (sal_uInt16 i
= 0; i
< length
; ++i
)
369 beans::StringPair aPair
= filters
[i
];
370 appendFilter(aPair
.First
, aPair
.Second
);
374 uno::Any
Qt5FilePicker::handleGetListValue(const QComboBox
* pWidget
, sal_Int16 nControlAction
)
377 switch (nControlAction
)
379 case ControlActions::GET_ITEMS
:
381 Sequence
<OUString
> aItemList(pWidget
->count());
382 for (sal_Int32 i
= 0; i
< pWidget
->count(); ++i
)
383 aItemList
[i
] = toOUString(pWidget
->itemText(i
));
387 case ControlActions::GET_SELECTED_ITEM
:
389 if (!pWidget
->currentText().isEmpty())
390 aAny
<<= toOUString(pWidget
->currentText());
393 case ControlActions::GET_SELECTED_ITEM_INDEX
:
395 if (pWidget
->currentIndex() >= 0)
396 aAny
<<= static_cast<sal_Int32
>(pWidget
->currentIndex());
401 "undocumented/unimplemented ControlAction for a list " << nControlAction
);
407 void Qt5FilePicker::handleSetListValue(QComboBox
* pWidget
, sal_Int16 nControlAction
,
408 const uno::Any
& rValue
)
410 switch (nControlAction
)
412 case ControlActions::ADD_ITEM
:
416 pWidget
->addItem(toQString(sItem
));
419 case ControlActions::ADD_ITEMS
:
421 Sequence
<OUString
> aStringList
;
422 rValue
>>= aStringList
;
423 for (auto const& sItem
: std::as_const(aStringList
))
424 pWidget
->addItem(toQString(sItem
));
427 case ControlActions::DELETE_ITEM
:
431 pWidget
->removeItem(nPos
);
434 case ControlActions::SET_SELECT_ITEM
:
438 pWidget
->setCurrentIndex(nPos
);
443 "undocumented/unimplemented ControlAction for a list " << nControlAction
);
447 pWidget
->setEnabled(pWidget
->count() > 0);
450 void SAL_CALL
Qt5FilePicker::setValue(sal_Int16 controlId
, sal_Int16 nControlAction
,
451 const uno::Any
& value
)
454 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
456 if (!pSalInst
->IsMainThread())
458 pSalInst
->RunInMainThread([this, controlId
, nControlAction
, &value
]() {
459 setValue(controlId
, nControlAction
, value
);
464 if (m_aCustomWidgetsMap
.contains(controlId
))
466 QWidget
* widget
= m_aCustomWidgetsMap
.value(controlId
);
467 QCheckBox
* cb
= dynamic_cast<QCheckBox
*>(widget
);
469 cb
->setChecked(value
.get
<bool>());
472 QComboBox
* combo
= dynamic_cast<QComboBox
*>(widget
);
474 handleSetListValue(combo
, nControlAction
, value
);
478 SAL_WARN("vcl.qt5", "set value on unknown control " << controlId
);
481 uno::Any SAL_CALL
Qt5FilePicker::getValue(sal_Int16 controlId
, sal_Int16 nControlAction
)
484 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
486 if (!pSalInst
->IsMainThread())
489 pSalInst
->RunInMainThread([&ret
, this, controlId
, nControlAction
]() {
490 ret
= getValue(controlId
, nControlAction
);
496 if (m_aCustomWidgetsMap
.contains(controlId
))
498 QWidget
* widget
= m_aCustomWidgetsMap
.value(controlId
);
499 QCheckBox
* cb
= dynamic_cast<QCheckBox
*>(widget
);
501 res
<<= cb
->isChecked();
504 QComboBox
* combo
= dynamic_cast<QComboBox
*>(widget
);
506 res
= handleGetListValue(combo
, nControlAction
);
510 SAL_WARN("vcl.qt5", "get value on unknown control " << controlId
);
515 void SAL_CALL
Qt5FilePicker::enableControl(sal_Int16 controlId
, sal_Bool enable
)
518 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
520 pSalInst
->RunInMainThread([this, controlId
, enable
]() {
521 if (m_aCustomWidgetsMap
.contains(controlId
))
522 m_aCustomWidgetsMap
.value(controlId
)->setEnabled(enable
);
524 SAL_WARN("vcl.qt5", "enable unknown control " << controlId
);
528 void SAL_CALL
Qt5FilePicker::setLabel(sal_Int16 controlId
, const OUString
& label
)
531 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
533 if (!pSalInst
->IsMainThread())
535 pSalInst
->RunInMainThread([this, controlId
, label
]() { setLabel(controlId
, label
); });
539 if (m_aCustomWidgetsMap
.contains(controlId
))
541 QCheckBox
* cb
= dynamic_cast<QCheckBox
*>(m_aCustomWidgetsMap
.value(controlId
));
543 cb
->setText(toQString(label
));
546 SAL_WARN("vcl.qt5", "set label on unknown control " << controlId
);
549 OUString SAL_CALL
Qt5FilePicker::getLabel(sal_Int16 controlId
)
552 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
554 if (!pSalInst
->IsMainThread())
557 pSalInst
->RunInMainThread([&ret
, this, controlId
]() { ret
= getLabel(controlId
); });
562 if (m_aCustomWidgetsMap
.contains(controlId
))
564 QCheckBox
* cb
= dynamic_cast<QCheckBox
*>(m_aCustomWidgetsMap
.value(controlId
));
569 SAL_WARN("vcl.qt5", "get label on unknown control " << controlId
);
571 return toOUString(label
);
574 QString
Qt5FilePicker::getResString(const char* pResId
)
578 if (pResId
== nullptr)
581 aResString
= toQString(VclResId(pResId
));
583 return aResString
.replace('~', '&');
586 void Qt5FilePicker::addCustomControl(sal_Int16 controlId
)
588 QWidget
* widget
= nullptr;
589 QLabel
* label
= nullptr;
590 const char* resId
= nullptr;
591 QCheckBox
* pCheckbox
= nullptr;
595 case CHECKBOX_AUTOEXTENSION
:
596 resId
= STR_FPICKER_AUTO_EXTENSION
;
598 case CHECKBOX_PASSWORD
:
599 resId
= STR_FPICKER_PASSWORD
;
601 case CHECKBOX_FILTEROPTIONS
:
602 resId
= STR_FPICKER_FILTER_OPTIONS
;
604 case CHECKBOX_READONLY
:
605 resId
= STR_FPICKER_READONLY
;
608 resId
= STR_FPICKER_INSERT_AS_LINK
;
610 case CHECKBOX_PREVIEW
:
611 resId
= STR_FPICKER_SHOW_PREVIEW
;
613 case CHECKBOX_SELECTION
:
614 resId
= STR_FPICKER_SELECTION
;
616 case CHECKBOX_GPGENCRYPTION
:
617 resId
= STR_FPICKER_GPGENCRYPT
;
619 case PUSHBUTTON_PLAY
:
620 resId
= STR_FPICKER_PLAY
;
622 case LISTBOX_VERSION
:
623 resId
= STR_FPICKER_VERSION
;
625 case LISTBOX_TEMPLATE
:
626 resId
= STR_FPICKER_TEMPLATES
;
628 case LISTBOX_IMAGE_TEMPLATE
:
629 resId
= STR_FPICKER_IMAGE_TEMPLATE
;
631 case LISTBOX_IMAGE_ANCHOR
:
632 resId
= STR_FPICKER_IMAGE_ANCHOR
;
634 case LISTBOX_VERSION_LABEL
:
635 case LISTBOX_TEMPLATE_LABEL
:
636 case LISTBOX_IMAGE_TEMPLATE_LABEL
:
637 case LISTBOX_IMAGE_ANCHOR_LABEL
:
638 case LISTBOX_FILTER_SELECTOR
:
644 case CHECKBOX_AUTOEXTENSION
:
645 pCheckbox
= new QCheckBox(getResString(resId
), m_pExtraControls
);
646 // to add/remove automatic file extension based on checkbox
647 connect(pCheckbox
, SIGNAL(stateChanged(int)), this,
648 SLOT(updateAutomaticFileExtension()));
651 case CHECKBOX_PASSWORD
:
652 case CHECKBOX_FILTEROPTIONS
:
653 case CHECKBOX_READONLY
:
655 case CHECKBOX_PREVIEW
:
656 case CHECKBOX_SELECTION
:
657 case CHECKBOX_GPGENCRYPTION
:
658 widget
= new QCheckBox(getResString(resId
), m_pExtraControls
);
660 case PUSHBUTTON_PLAY
:
662 case LISTBOX_VERSION
:
663 case LISTBOX_TEMPLATE
:
664 case LISTBOX_IMAGE_ANCHOR
:
665 case LISTBOX_IMAGE_TEMPLATE
:
666 case LISTBOX_FILTER_SELECTOR
:
667 label
= new QLabel(getResString(resId
), m_pExtraControls
);
668 widget
= new QComboBox(m_pExtraControls
);
669 label
->setBuddy(widget
);
671 case LISTBOX_VERSION_LABEL
:
672 case LISTBOX_TEMPLATE_LABEL
:
673 case LISTBOX_IMAGE_TEMPLATE_LABEL
:
674 case LISTBOX_IMAGE_ANCHOR_LABEL
:
680 const int row
= m_pLayout
->rowCount();
682 m_pLayout
->addWidget(label
, row
, 0);
683 m_pLayout
->addWidget(widget
, row
, 1);
684 m_aCustomWidgetsMap
.insert(controlId
, widget
);
688 void SAL_CALL
Qt5FilePicker::initialize(const uno::Sequence
<uno::Any
>& args
)
690 // parameter checking
692 if (args
.getLength() == 0)
693 throw lang::IllegalArgumentException("no arguments", static_cast<XFilePicker2
*>(this), 1);
697 if ((arg
.getValueType() != cppu::UnoType
<sal_Int16
>::get())
698 && (arg
.getValueType() != cppu::UnoType
<sal_Int8
>::get()))
700 throw lang::IllegalArgumentException("invalid argument type",
701 static_cast<XFilePicker2
*>(this), 1);
705 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
707 if (!pSalInst
->IsMainThread())
709 pSalInst
->RunInMainThread([this, args
]() { initialize(args
); });
713 m_aNamedFilterToExtensionMap
.clear();
714 m_aNamedFilterList
.clear();
715 m_aTitleToFilterMap
.clear();
716 m_aCurrentFilter
.clear();
718 sal_Int16 templateId
= -1;
721 QFileDialog::AcceptMode acceptMode
= QFileDialog::AcceptOpen
;
724 case FILEOPEN_SIMPLE
:
727 case FILESAVE_SIMPLE
:
728 acceptMode
= QFileDialog::AcceptSave
;
731 case FILESAVE_AUTOEXTENSION
:
732 acceptMode
= QFileDialog::AcceptSave
;
733 addCustomControl(CHECKBOX_AUTOEXTENSION
);
736 case FILESAVE_AUTOEXTENSION_PASSWORD
:
737 acceptMode
= QFileDialog::AcceptSave
;
738 addCustomControl(CHECKBOX_AUTOEXTENSION
);
739 addCustomControl(CHECKBOX_PASSWORD
);
740 addCustomControl(CHECKBOX_GPGENCRYPTION
);
743 case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS
:
744 acceptMode
= QFileDialog::AcceptSave
;
745 addCustomControl(CHECKBOX_AUTOEXTENSION
);
746 addCustomControl(CHECKBOX_PASSWORD
);
747 addCustomControl(CHECKBOX_GPGENCRYPTION
);
748 addCustomControl(CHECKBOX_FILTEROPTIONS
);
751 case FILESAVE_AUTOEXTENSION_SELECTION
:
752 acceptMode
= QFileDialog::AcceptSave
;
753 addCustomControl(CHECKBOX_AUTOEXTENSION
);
754 addCustomControl(CHECKBOX_SELECTION
);
757 case FILESAVE_AUTOEXTENSION_TEMPLATE
:
758 acceptMode
= QFileDialog::AcceptSave
;
759 addCustomControl(CHECKBOX_AUTOEXTENSION
);
760 addCustomControl(LISTBOX_TEMPLATE
);
763 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE
:
764 addCustomControl(CHECKBOX_LINK
);
765 addCustomControl(CHECKBOX_PREVIEW
);
766 addCustomControl(LISTBOX_IMAGE_TEMPLATE
);
769 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR
:
770 addCustomControl(CHECKBOX_LINK
);
771 addCustomControl(CHECKBOX_PREVIEW
);
772 addCustomControl(LISTBOX_IMAGE_ANCHOR
);
776 addCustomControl(PUSHBUTTON_PLAY
);
779 case FILEOPEN_LINK_PLAY
:
780 addCustomControl(CHECKBOX_LINK
);
781 addCustomControl(PUSHBUTTON_PLAY
);
784 case FILEOPEN_READONLY_VERSION
:
785 addCustomControl(CHECKBOX_READONLY
);
786 addCustomControl(LISTBOX_VERSION
);
789 case FILEOPEN_LINK_PREVIEW
:
790 addCustomControl(CHECKBOX_LINK
);
791 addCustomControl(CHECKBOX_PREVIEW
);
794 case FILEOPEN_PREVIEW
:
795 addCustomControl(CHECKBOX_PREVIEW
);
799 throw lang::IllegalArgumentException("Unknown template",
800 static_cast<XFilePicker2
*>(this), 1);
803 const char* resId
= nullptr;
806 case QFileDialog::AcceptOpen
:
807 resId
= STR_FPICKER_OPEN
;
809 case QFileDialog::AcceptSave
:
810 resId
= STR_FPICKER_SAVE
;
811 m_pFileDialog
->setFileMode(QFileDialog::AnyFile
);
815 m_pFileDialog
->setAcceptMode(acceptMode
);
816 m_pFileDialog
->setWindowTitle(getResString(resId
));
818 css::uno::Reference
<css::awt::XWindow
> xParentWindow
;
819 if (args
.getLength() > 1)
820 args
[1] >>= xParentWindow
;
821 if (xParentWindow
.is())
823 css::uno::Reference
<css::awt::XSystemDependentWindowPeer
> xSysWinPeer(xParentWindow
,
824 css::uno::UNO_QUERY
);
825 if (xSysWinPeer
.is())
827 // the sal_*Int8 handling is strange, but it's public API - no way around
828 css::uno::Sequence
<sal_Int8
> aProcessIdent(16);
829 rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8
*>(aProcessIdent
.getArray()));
830 uno::Any aAny
= xSysWinPeer
->getWindowHandle(
831 aProcessIdent
, css::lang::SystemDependent::SYSTEM_XWINDOW
);
832 css::awt::SystemDependentXWindow xSysWin
;
835 const auto& pFrames
= pSalInst
->getFrames();
836 const long aWindowHandle
= xSysWin
.WindowHandle
;
837 const auto it
= std::find_if(pFrames
.begin(), pFrames
.end(),
838 [&aWindowHandle
](auto pFrame
) -> bool {
839 const SystemEnvData
* pData
= pFrame
->GetSystemData();
840 return pData
&& long(pData
->aWindow
) == aWindowHandle
;
842 if (it
!= pFrames
.end())
843 m_pParentWidget
= static_cast<Qt5Frame
*>(*it
)->asChild();
848 void SAL_CALL
Qt5FilePicker::cancel() { m_pFileDialog
->reject(); }
850 void SAL_CALL
Qt5FilePicker::disposing(const lang::EventObject
& rEvent
)
852 uno::Reference
<XFilePickerListener
> xFilePickerListener(rEvent
.Source
, uno::UNO_QUERY
);
854 if (xFilePickerListener
.is())
856 removeFilePickerListener(xFilePickerListener
);
860 void SAL_CALL
Qt5FilePicker::queryTermination(const css::lang::EventObject
&)
862 throw css::frame::TerminationVetoException();
865 void SAL_CALL
Qt5FilePicker::notifyTermination(const css::lang::EventObject
&)
867 SolarMutexGuard aGuard
;
868 m_pFileDialog
->reject();
871 OUString SAL_CALL
Qt5FilePicker::getImplementationName()
873 return "com.sun.star.ui.dialogs.Qt5FilePicker";
876 sal_Bool SAL_CALL
Qt5FilePicker::supportsService(const OUString
& ServiceName
)
878 return cppu::supportsService(this, ServiceName
);
881 uno::Sequence
<OUString
> SAL_CALL
Qt5FilePicker::getSupportedServiceNames()
883 return FilePicker_getSupportedServiceNames();
886 void Qt5FilePicker::updateAutomaticFileExtension()
888 bool bSetAutoExtension
889 = getValue(CHECKBOX_AUTOEXTENSION
, ControlActions::GET_SELECTED_ITEM
).get
<bool>();
890 if (bSetAutoExtension
)
892 QString sSuffix
= m_aNamedFilterToExtensionMap
.value(m_pFileDialog
->selectedNameFilter());
893 // string is "*.<SUFFIX>" if a specific filter was selected that has exactly one possible file extension
894 if (sSuffix
.lastIndexOf("*.") == 0)
896 sSuffix
= sSuffix
.remove("*.");
897 m_pFileDialog
->setDefaultSuffix(sSuffix
);
901 // fall back to setting none otherwise
904 "Unable to retrieve unambiguous file extension. Will not add any automatically.");
905 bSetAutoExtension
= false;
909 if (!bSetAutoExtension
)
910 m_pFileDialog
->setDefaultSuffix("");
913 void Qt5FilePicker::filterSelected(const QString
&)
915 FilePickerEvent aEvent
;
916 aEvent
.ElementId
= LISTBOX_FILTER
;
917 SAL_INFO("vcl.qt5", "filter changed");
918 if (m_xListener
.is())
919 m_xListener
->controlStateChanged(aEvent
);
922 void Qt5FilePicker::currentChanged(const QString
&)
924 FilePickerEvent aEvent
;
925 SAL_INFO("vcl.qt5", "file selection changed");
926 if (m_xListener
.is())
927 m_xListener
->fileSelectionChanged(aEvent
);
930 OUString
Qt5FilePicker::getDirectory()
932 uno::Sequence
<OUString
> seq
= getSelectedFiles();
933 if (seq
.getLength() > 1)
938 void Qt5FilePicker::setDescription(const OUString
&) {}
940 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */