1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <config_gpgme.h>
22 #include <fpicker/fpsofficeResMgr.hxx>
23 #include <QtFilePicker.hxx>
24 #include <QtFilePicker.moc>
26 #include <QtFrame.hxx>
27 #include <QtTools.hxx>
28 #include <QtWidget.hxx>
29 #include <QtInstance.hxx>
31 #include <com/sun/star/awt/SystemDependentXWindow.hpp>
32 #include <com/sun/star/awt/XSystemDependentWindowPeer.hpp>
33 #include <com/sun/star/awt/XWindow.hpp>
34 #include <com/sun/star/frame/Desktop.hpp>
35 #include <com/sun/star/frame/TerminationVetoException.hpp>
36 #include <com/sun/star/frame/XDesktop.hpp>
37 #include <com/sun/star/lang/DisposedException.hpp>
38 #include <com/sun/star/lang/IllegalArgumentException.hpp>
39 #include <com/sun/star/lang/SystemDependent.hpp>
40 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
41 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
42 #include <com/sun/star/ui/dialogs/ControlActions.hpp>
43 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
44 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
45 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
46 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
47 #include <cppuhelper/interfacecontainer.h>
48 #include <cppuhelper/supportsservice.hxx>
49 #include <rtl/process.h>
50 #include <sal/log.hxx>
51 #include <vcl/qt/QtUtils.hxx>
53 #include <QtCore/QDebug>
54 #include <QtCore/QRegularExpression>
55 #include <QtCore/QThread>
56 #include <QtCore/QUrl>
57 #include <QtGui/QClipboard>
58 #include <QtGui/QWindow>
59 #include <QtWidgets/QApplication>
60 #include <QtWidgets/QCheckBox>
61 #include <QtWidgets/QComboBox>
62 #include <QtWidgets/QGridLayout>
63 #include <QtWidgets/QHBoxLayout>
64 #include <QtWidgets/QLabel>
65 #include <QtWidgets/QMessageBox>
66 #include <QtWidgets/QPushButton>
67 #include <QtWidgets/QWidget>
69 #include <unx/geninst.h>
70 #include <fpicker/strings.hrc>
73 using namespace ::com::sun::star
;
74 using namespace ::com::sun::star::ui::dialogs
;
75 using namespace ::com::sun::star::ui::dialogs::TemplateDescription
;
76 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds
;
77 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds
;
78 using namespace ::com::sun::star::lang
;
79 using namespace ::com::sun::star::beans
;
80 using namespace ::com::sun::star::uno
;
84 uno::Sequence
<OUString
> FilePicker_getSupportedServiceNames()
86 return { u
"com.sun.star.ui.dialogs.FilePicker"_ustr
,
87 u
"com.sun.star.ui.dialogs.SystemFilePicker"_ustr
,
88 u
"com.sun.star.ui.dialogs.QtFilePicker"_ustr
};
92 QtFilePicker::QtFilePicker(css::uno::Reference
<css::uno::XComponentContext
> context
,
93 QFileDialog::FileMode eMode
, bool bUseNative
)
94 : QtFilePicker_Base(m_aHelperMutex
)
95 , m_context(std::move(context
))
96 , m_bIsFolderPicker(eMode
== QFileDialog::Directory
)
97 , m_pParentWidget(nullptr)
98 , m_pFileDialog(new QFileDialog(nullptr, {}, QDir::homePath()))
99 , m_pExtraControls(new QWidget())
101 m_pFileDialog
->setOption(QFileDialog::DontUseNativeDialog
, !bUseNative
);
103 m_pFileDialog
->setFileMode(eMode
);
104 m_pFileDialog
->setWindowModality(Qt::ApplicationModal
);
106 if (m_bIsFolderPicker
)
108 m_pFileDialog
->setOption(QFileDialog::ShowDirsOnly
, true);
109 m_pFileDialog
->setWindowTitle(toQString(FpsResId(STR_SVT_FOLDERPICKER_DEFAULT_TITLE
)));
112 m_pLayout
= qobject_cast
<QGridLayout
*>(m_pFileDialog
->layout());
114 setMultiSelectionMode(false);
116 // XFilePickerListener notifications
117 connect(m_pFileDialog
.get(), &QFileDialog::filterSelected
, this, &QtFilePicker::filterSelected
);
118 connect(m_pFileDialog
.get(), &QFileDialog::currentChanged
, this, &QtFilePicker::currentChanged
);
120 // update automatic file extension when filter is changed
121 connect(m_pFileDialog
.get(), &QFileDialog::filterSelected
, this,
122 &QtFilePicker::updateAutomaticFileExtension
);
124 connect(m_pFileDialog
.get(), &QFileDialog::finished
, this, &QtFilePicker::finished
);
127 QtFilePicker::~QtFilePicker()
130 GetQtInstance().RunInMainThread([this]() {
131 // must delete it in main thread, otherwise
132 // QSocketNotifier::setEnabled() will crash us
133 m_pFileDialog
.reset();
138 QtFilePicker::addFilePickerListener(const uno::Reference
<XFilePickerListener
>& xListener
)
140 SolarMutexGuard aGuard
;
141 m_xListener
= xListener
;
144 void SAL_CALL
QtFilePicker::removeFilePickerListener(const uno::Reference
<XFilePickerListener
>&)
146 SolarMutexGuard aGuard
;
150 void SAL_CALL
QtFilePicker::setTitle(const OUString
& title
)
153 GetQtInstance().RunInMainThread(
154 [this, &title
]() { m_pFileDialog
->setWindowTitle(toQString(title
)); });
157 void QtFilePicker::prepareExecute()
159 QWidget
* pTransientParent
= m_pParentWidget
;
160 if (!pTransientParent
)
162 vcl::Window
* pWindow
= ::Application::GetActiveTopWindow();
165 QtFrame
* pFrame
= dynamic_cast<QtFrame
*>(pWindow
->ImplGetFrame());
168 pTransientParent
= pFrame
->asChild();
172 if (!m_aNamedFilterList
.isEmpty())
173 m_pFileDialog
->setNameFilters(m_aNamedFilterList
);
174 if (!m_aCurrentFilter
.isEmpty())
175 m_pFileDialog
->selectNameFilter(m_aCurrentFilter
);
177 updateAutomaticFileExtension();
179 uno::Reference
<css::frame::XDesktop
> xDesktop(css::frame::Desktop::create(m_context
),
182 // will hide the window, so do before show
183 m_pFileDialog
->setParent(pTransientParent
, m_pFileDialog
->windowFlags());
184 m_pFileDialog
->show();
185 xDesktop
->addTerminateListener(this);
188 void QtFilePicker::finished(int nResult
)
191 uno::Reference
<css::frame::XDesktop
> xDesktop(css::frame::Desktop::create(m_context
),
193 xDesktop
->removeTerminateListener(this);
194 m_pFileDialog
->setParent(nullptr, m_pFileDialog
->windowFlags());
196 if (m_xClosedListener
.is())
198 const sal_Int16 nRet
= (QFileDialog::Rejected
== nResult
) ? ExecutableDialogResults::CANCEL
199 : ExecutableDialogResults::OK
;
200 css::ui::dialogs::DialogClosedEvent
aEvent(*this, nRet
);
201 m_xClosedListener
->dialogClosed(aEvent
);
202 m_xClosedListener
.clear();
206 sal_Int16 SAL_CALL
QtFilePicker::execute()
209 QtInstance
& rQtInstance
= GetQtInstance();
210 if (!rQtInstance
.IsMainThread())
213 rQtInstance
.RunInMainThread([&ret
, this]() { ret
= execute(); });
218 int result
= m_pFileDialog
->exec();
220 if (QFileDialog::Rejected
== result
)
221 return ExecutableDialogResults::CANCEL
;
222 return ExecutableDialogResults::OK
;
225 // XAsynchronousExecutableDialog functions
226 void SAL_CALL
QtFilePicker::setDialogTitle(const OUString
& _rTitle
) { setTitle(_rTitle
); }
229 QtFilePicker::startExecuteModal(const Reference
<css::ui::dialogs::XDialogClosedListener
>& xListener
)
231 m_xClosedListener
= xListener
;
233 m_pFileDialog
->show();
236 void SAL_CALL
QtFilePicker::setMultiSelectionMode(sal_Bool multiSelect
)
239 GetQtInstance().RunInMainThread([this, multiSelect
]() {
240 if (m_bIsFolderPicker
|| m_pFileDialog
->acceptMode() == QFileDialog::AcceptSave
)
243 m_pFileDialog
->setFileMode(multiSelect
? QFileDialog::ExistingFiles
244 : QFileDialog::ExistingFile
);
248 void SAL_CALL
QtFilePicker::setDefaultName(const OUString
& name
)
251 GetQtInstance().RunInMainThread(
252 [this, &name
]() { m_pFileDialog
->selectFile(toQString(name
)); });
255 void SAL_CALL
QtFilePicker::setDisplayDirectory(const OUString
& dir
)
258 GetQtInstance().RunInMainThread([this, &dir
]() {
259 QString
qDir(toQString(dir
));
260 m_pFileDialog
->setDirectoryUrl(QUrl(qDir
));
264 OUString SAL_CALL
QtFilePicker::getDisplayDirectory()
268 GetQtInstance().RunInMainThread(
269 [&ret
, this]() { ret
= toOUString(m_pFileDialog
->directoryUrl().toString()); });
273 uno::Sequence
<OUString
> SAL_CALL
QtFilePicker::getFiles()
275 uno::Sequence
<OUString
> seq
= getSelectedFiles();
276 if (seq
.getLength() > 1)
281 uno::Sequence
<OUString
> SAL_CALL
QtFilePicker::getSelectedFiles()
285 GetQtInstance().RunInMainThread([&urls
, this]() { urls
= m_pFileDialog
->selectedUrls(); });
287 uno::Sequence
<OUString
> seq(urls
.size());
288 auto seqRange
= asNonConstRange(seq
);
290 auto const trans
= css::uri::ExternalUriReferenceTranslator::create(m_context
);
292 for (const QUrl
& aURL
: urls
)
294 // Unlike LO, QFileDialog (<https://doc.qt.io/qt-5/qfiledialog.html>) apparently always
295 // treats file-system pathnames as UTF-8--encoded, regardless of LANG/LC_CTYPE locale
296 // setting. And pathnames containing byte sequences that are not valid UTF-8 are apparently
297 // filtered out and not even displayed by QFileDialog, so aURL will always have a "payload"
298 // that matches the pathname's byte sequence. So the pathname's byte sequence (which
299 // happens to also be aURL's payload) in the LANG/LC_CTYPE encoding needs to be converted
300 // into LO's internal UTF-8 file URL encoding via
301 // XExternalUriReferenceTranslator::translateToInternal (which looks somewhat paradoxical as
302 // aURL.toEncoded() nominally already has a UTF-8 payload):
303 auto const extUrl
= toOUString(aURL
.toEncoded());
304 auto intUrl
= trans
->translateToInternal(extUrl
);
305 if (intUrl
.isEmpty())
307 // If translation failed, fall back to original URL:
308 SAL_WARN("vcl.qt", "cannot convert <" << extUrl
<< "> from locale encoding to UTF-8");
311 seqRange
[i
++] = intUrl
;
317 void SAL_CALL
QtFilePicker::appendFilter(const OUString
& title
, const OUString
& filter
)
320 QtInstance
& rQtInstance
= GetQtInstance();
321 if (!rQtInstance
.IsMainThread())
323 rQtInstance
.RunInMainThread([this, &title
, &filter
]() { appendFilter(title
, filter
); });
327 // '/' need to be escaped else they are assumed to be mime types
328 QString sTitle
= toQString(title
).replace("/", "\\/");
330 QString sFilterName
= sTitle
;
331 // the Qt non-native file picker adds the extensions to the filter title, so strip them
332 if (m_pFileDialog
->testOption(QFileDialog::DontUseNativeDialog
))
334 int pos
= sFilterName
.indexOf(" (");
336 sFilterName
.truncate(pos
);
339 QString sGlobFilter
= toQString(filter
);
341 // LibreOffice gives us filters separated by ';' qt dialogs just want space separated
342 sGlobFilter
.replace(";", " ");
344 // make sure "*.*" is not used as "all files"
345 sGlobFilter
.replace("*.*", "*");
347 m_aNamedFilterList
<< QStringLiteral("%1 (%2)").arg(sFilterName
, sGlobFilter
);
348 m_aTitleToFilterMap
[sTitle
] = m_aNamedFilterList
.constLast();
349 m_aNamedFilterToExtensionMap
[m_aNamedFilterList
.constLast()] = sGlobFilter
;
352 void SAL_CALL
QtFilePicker::setCurrentFilter(const OUString
& title
)
355 GetQtInstance().RunInMainThread([this, &title
]() {
356 m_aCurrentFilter
= m_aTitleToFilterMap
.value(toQString(title
).replace("/", "\\/"));
360 OUString SAL_CALL
QtFilePicker::getCurrentFilter()
364 QtInstance
& rQtInstance
= GetQtInstance();
365 rQtInstance
.RunInMainThread([&filter
, this]() {
366 filter
= m_aTitleToFilterMap
.key(m_pFileDialog
->selectedNameFilter());
369 if (filter
.isEmpty())
370 filter
= "ODF Text Document (.odt)";
371 return toOUString(filter
);
374 void SAL_CALL
QtFilePicker::appendFilterGroup(const OUString
& rGroupTitle
,
375 const uno::Sequence
<beans::StringPair
>& filters
)
378 QtInstance
& rQtInstance
= GetQtInstance();
379 if (!rQtInstance
.IsMainThread())
381 rQtInstance
.RunInMainThread(
382 [this, &rGroupTitle
, &filters
]() { appendFilterGroup(rGroupTitle
, filters
); });
386 const sal_uInt16 length
= filters
.getLength();
387 for (sal_uInt16 i
= 0; i
< length
; ++i
)
389 const beans::StringPair
& aPair
= filters
[i
];
390 appendFilter(aPair
.First
, aPair
.Second
);
394 uno::Any
QtFilePicker::handleGetListValue(const QComboBox
* pWidget
, sal_Int16 nControlAction
)
397 switch (nControlAction
)
399 case ControlActions::GET_ITEMS
:
401 Sequence
<OUString
> aItemList(pWidget
->count());
402 auto aItemListRange
= asNonConstRange(aItemList
);
403 for (sal_Int32 i
= 0; i
< pWidget
->count(); ++i
)
404 aItemListRange
[i
] = toOUString(pWidget
->itemText(i
));
408 case ControlActions::GET_SELECTED_ITEM
:
410 if (!pWidget
->currentText().isEmpty())
411 aAny
<<= toOUString(pWidget
->currentText());
414 case ControlActions::GET_SELECTED_ITEM_INDEX
:
416 if (pWidget
->currentIndex() >= 0)
417 aAny
<<= static_cast<sal_Int32
>(pWidget
->currentIndex());
422 "undocumented/unimplemented ControlAction for a list " << nControlAction
);
428 void QtFilePicker::handleSetListValue(QComboBox
* pWidget
, sal_Int16 nControlAction
,
429 const uno::Any
& rValue
)
431 switch (nControlAction
)
433 case ControlActions::ADD_ITEM
:
437 pWidget
->addItem(toQString(sItem
));
440 case ControlActions::ADD_ITEMS
:
442 Sequence
<OUString
> aStringList
;
443 rValue
>>= aStringList
;
444 for (auto const& sItem
: aStringList
)
445 pWidget
->addItem(toQString(sItem
));
448 case ControlActions::DELETE_ITEM
:
452 pWidget
->removeItem(nPos
);
455 case ControlActions::DELETE_ITEMS
:
460 case ControlActions::SET_SELECT_ITEM
:
464 pWidget
->setCurrentIndex(nPos
);
469 "undocumented/unimplemented ControlAction for a list " << nControlAction
);
473 pWidget
->setEnabled(pWidget
->count() > 0);
476 void SAL_CALL
QtFilePicker::setValue(sal_Int16 controlId
, sal_Int16 nControlAction
,
477 const uno::Any
& value
)
480 QtInstance
& rQtInstance
= GetQtInstance();
481 if (!rQtInstance
.IsMainThread())
483 rQtInstance
.RunInMainThread([this, controlId
, nControlAction
, &value
]() {
484 setValue(controlId
, nControlAction
, value
);
489 if (m_aCustomWidgetsMap
.contains(controlId
))
491 QWidget
* widget
= m_aCustomWidgetsMap
.value(controlId
);
492 QCheckBox
* cb
= qobject_cast
<QCheckBox
*>(widget
);
494 cb
->setChecked(value
.get
<bool>());
497 QComboBox
* combo
= qobject_cast
<QComboBox
*>(widget
);
499 handleSetListValue(combo
, nControlAction
, value
);
503 SAL_WARN("vcl.qt", "set value on unknown control " << controlId
);
506 uno::Any SAL_CALL
QtFilePicker::getValue(sal_Int16 controlId
, sal_Int16 nControlAction
)
509 QtInstance
& rQtInstance
= GetQtInstance();
510 if (!rQtInstance
.IsMainThread())
513 rQtInstance
.RunInMainThread([&ret
, this, controlId
, nControlAction
]() {
514 ret
= getValue(controlId
, nControlAction
);
520 if (m_aCustomWidgetsMap
.contains(controlId
))
522 QWidget
* widget
= m_aCustomWidgetsMap
.value(controlId
);
523 QCheckBox
* cb
= qobject_cast
<QCheckBox
*>(widget
);
525 res
<<= cb
->isChecked();
528 QComboBox
* combo
= qobject_cast
<QComboBox
*>(widget
);
530 res
= handleGetListValue(combo
, nControlAction
);
534 SAL_WARN("vcl.qt", "get value on unknown control " << controlId
);
539 void SAL_CALL
QtFilePicker::enableControl(sal_Int16 controlId
, sal_Bool enable
)
542 GetQtInstance().RunInMainThread([this, controlId
, enable
]() {
543 if (m_aCustomWidgetsMap
.contains(controlId
))
544 m_aCustomWidgetsMap
.value(controlId
)->setEnabled(enable
);
546 SAL_WARN("vcl.qt", "enable unknown control " << controlId
);
550 void SAL_CALL
QtFilePicker::setLabel(sal_Int16 controlId
, const OUString
& label
)
553 QtInstance
& rQtInstance
= GetQtInstance();
554 if (!rQtInstance
.IsMainThread())
556 rQtInstance
.RunInMainThread([this, controlId
, label
]() { setLabel(controlId
, label
); });
560 if (m_aCustomWidgetsMap
.contains(controlId
))
562 QCheckBox
* cb
= qobject_cast
<QCheckBox
*>(m_aCustomWidgetsMap
.value(controlId
));
564 cb
->setText(toQString(label
));
567 SAL_WARN("vcl.qt", "set label on unknown control " << controlId
);
570 OUString SAL_CALL
QtFilePicker::getLabel(sal_Int16 controlId
)
573 QtInstance
& rQtInstance
= GetQtInstance();
574 if (!rQtInstance
.IsMainThread())
577 rQtInstance
.RunInMainThread([&ret
, this, controlId
]() { ret
= getLabel(controlId
); });
582 if (m_aCustomWidgetsMap
.contains(controlId
))
584 QCheckBox
* cb
= qobject_cast
<QCheckBox
*>(m_aCustomWidgetsMap
.value(controlId
));
589 SAL_WARN("vcl.qt", "get label on unknown control " << controlId
);
591 return toOUString(label
);
594 QString
QtFilePicker::getResString(TranslateId pResId
)
601 aResString
= toQString(FpsResId(pResId
));
603 return aResString
.replace('~', '&');
606 void QtFilePicker::addCustomControl(sal_Int16 controlId
)
608 QWidget
* widget
= nullptr;
609 QLabel
* label
= nullptr;
611 QCheckBox
* pCheckbox
= nullptr;
615 case CHECKBOX_AUTOEXTENSION
:
616 resId
= STR_SVT_FILEPICKER_AUTO_EXTENSION
;
618 case CHECKBOX_PASSWORD
:
619 resId
= STR_SVT_FILEPICKER_PASSWORD
;
621 case CHECKBOX_FILTEROPTIONS
:
622 resId
= STR_SVT_FILEPICKER_FILTER_OPTIONS
;
624 case CHECKBOX_READONLY
:
625 resId
= STR_SVT_FILEPICKER_READONLY
;
628 resId
= STR_SVT_FILEPICKER_INSERT_AS_LINK
;
630 case CHECKBOX_PREVIEW
:
631 resId
= STR_SVT_FILEPICKER_SHOW_PREVIEW
;
633 case CHECKBOX_SELECTION
:
634 resId
= STR_SVT_FILEPICKER_SELECTION
;
636 case CHECKBOX_GPGENCRYPTION
:
637 resId
= STR_SVT_FILEPICKER_GPGENCRYPT
;
639 case CHECKBOX_GPGSIGN
:
640 resId
= STR_SVT_FILEPICKER_GPGSIGN
;
642 case PUSHBUTTON_PLAY
:
643 resId
= STR_SVT_FILEPICKER_PLAY
;
645 case LISTBOX_VERSION
:
646 resId
= STR_SVT_FILEPICKER_VERSION
;
648 case LISTBOX_TEMPLATE
:
649 resId
= STR_SVT_FILEPICKER_TEMPLATES
;
651 case LISTBOX_IMAGE_TEMPLATE
:
652 resId
= STR_SVT_FILEPICKER_IMAGE_TEMPLATE
;
654 case LISTBOX_IMAGE_ANCHOR
:
655 resId
= STR_SVT_FILEPICKER_IMAGE_ANCHOR
;
657 case LISTBOX_VERSION_LABEL
:
658 case LISTBOX_TEMPLATE_LABEL
:
659 case LISTBOX_IMAGE_TEMPLATE_LABEL
:
660 case LISTBOX_IMAGE_ANCHOR_LABEL
:
661 case LISTBOX_FILTER_SELECTOR
:
667 case CHECKBOX_AUTOEXTENSION
:
668 pCheckbox
= new QCheckBox(getResString(resId
), m_pExtraControls
);
669 // to add/remove automatic file extension based on checkbox
670 #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
671 connect(pCheckbox
, &QCheckBox::checkStateChanged
, this,
672 &QtFilePicker::updateAutomaticFileExtension
);
674 connect(pCheckbox
, &QCheckBox::stateChanged
, this,
675 &QtFilePicker::updateAutomaticFileExtension
);
679 case CHECKBOX_PASSWORD
:
680 case CHECKBOX_FILTEROPTIONS
:
681 case CHECKBOX_READONLY
:
683 case CHECKBOX_PREVIEW
:
684 case CHECKBOX_SELECTION
:
685 case CHECKBOX_GPGENCRYPTION
:
686 case CHECKBOX_GPGSIGN
:
687 widget
= new QCheckBox(getResString(resId
), m_pExtraControls
);
689 case PUSHBUTTON_PLAY
:
691 case LISTBOX_VERSION
:
692 case LISTBOX_TEMPLATE
:
693 case LISTBOX_IMAGE_ANCHOR
:
694 case LISTBOX_IMAGE_TEMPLATE
:
695 case LISTBOX_FILTER_SELECTOR
:
696 label
= new QLabel(getResString(resId
), m_pExtraControls
);
697 widget
= new QComboBox(m_pExtraControls
);
698 label
->setBuddy(widget
);
700 case LISTBOX_VERSION_LABEL
:
701 case LISTBOX_TEMPLATE_LABEL
:
702 case LISTBOX_IMAGE_TEMPLATE_LABEL
:
703 case LISTBOX_IMAGE_ANCHOR_LABEL
:
709 const int row
= m_pLayout
->rowCount();
711 m_pLayout
->addWidget(label
, row
, 0);
712 m_pLayout
->addWidget(widget
, row
, 1);
713 m_aCustomWidgetsMap
.insert(controlId
, widget
);
717 void SAL_CALL
QtFilePicker::initialize(const uno::Sequence
<uno::Any
>& args
)
719 // parameter checking
721 if (args
.getLength() == 0)
722 throw lang::IllegalArgumentException(u
"no arguments"_ustr
, static_cast<XFilePicker2
*>(this),
727 if ((arg
.getValueType() != cppu::UnoType
<sal_Int16
>::get())
728 && (arg
.getValueType() != cppu::UnoType
<sal_Int8
>::get()))
730 throw lang::IllegalArgumentException(u
"invalid argument type"_ustr
,
731 static_cast<XFilePicker2
*>(this), 1);
735 QtInstance
& rQtInstance
= GetQtInstance();
736 if (!rQtInstance
.IsMainThread())
738 rQtInstance
.RunInMainThread([this, args
]() { initialize(args
); });
742 m_aNamedFilterToExtensionMap
.clear();
743 m_aNamedFilterList
.clear();
744 m_aTitleToFilterMap
.clear();
745 m_aCurrentFilter
.clear();
747 sal_Int16 templateId
= -1;
750 QFileDialog::AcceptMode acceptMode
= QFileDialog::AcceptOpen
;
753 case FILEOPEN_SIMPLE
:
756 case FILESAVE_SIMPLE
:
757 acceptMode
= QFileDialog::AcceptSave
;
760 case FILESAVE_AUTOEXTENSION
:
761 acceptMode
= QFileDialog::AcceptSave
;
762 addCustomControl(CHECKBOX_AUTOEXTENSION
);
765 case FILESAVE_AUTOEXTENSION_PASSWORD
:
766 acceptMode
= QFileDialog::AcceptSave
;
767 addCustomControl(CHECKBOX_AUTOEXTENSION
);
768 addCustomControl(CHECKBOX_PASSWORD
);
769 addCustomControl(CHECKBOX_GPGENCRYPTION
);
770 #if HAVE_FEATURE_GPGME
771 addCustomControl(CHECKBOX_GPGSIGN
);
775 case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS
:
776 acceptMode
= QFileDialog::AcceptSave
;
777 addCustomControl(CHECKBOX_AUTOEXTENSION
);
778 addCustomControl(CHECKBOX_PASSWORD
);
779 addCustomControl(CHECKBOX_GPGENCRYPTION
);
780 #if HAVE_FEATURE_GPGME
781 addCustomControl(CHECKBOX_GPGSIGN
);
783 addCustomControl(CHECKBOX_FILTEROPTIONS
);
786 case FILESAVE_AUTOEXTENSION_SELECTION
:
787 acceptMode
= QFileDialog::AcceptSave
;
788 addCustomControl(CHECKBOX_AUTOEXTENSION
);
789 addCustomControl(CHECKBOX_SELECTION
);
792 case FILESAVE_AUTOEXTENSION_TEMPLATE
:
793 acceptMode
= QFileDialog::AcceptSave
;
794 addCustomControl(CHECKBOX_AUTOEXTENSION
);
795 addCustomControl(LISTBOX_TEMPLATE
);
798 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE
:
799 addCustomControl(CHECKBOX_LINK
);
800 addCustomControl(CHECKBOX_PREVIEW
);
801 addCustomControl(LISTBOX_IMAGE_TEMPLATE
);
804 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR
:
805 addCustomControl(CHECKBOX_LINK
);
806 addCustomControl(CHECKBOX_PREVIEW
);
807 addCustomControl(LISTBOX_IMAGE_ANCHOR
);
811 addCustomControl(PUSHBUTTON_PLAY
);
814 case FILEOPEN_LINK_PLAY
:
815 addCustomControl(CHECKBOX_LINK
);
816 addCustomControl(PUSHBUTTON_PLAY
);
819 case FILEOPEN_READONLY_VERSION
:
820 addCustomControl(CHECKBOX_READONLY
);
821 addCustomControl(LISTBOX_VERSION
);
824 case FILEOPEN_LINK_PREVIEW
:
825 addCustomControl(CHECKBOX_LINK
);
826 addCustomControl(CHECKBOX_PREVIEW
);
829 case FILEOPEN_PREVIEW
:
830 addCustomControl(CHECKBOX_PREVIEW
);
834 throw lang::IllegalArgumentException(u
"Unknown template"_ustr
,
835 static_cast<XFilePicker2
*>(this), 1);
841 case QFileDialog::AcceptOpen
:
842 resId
= STR_FILEDLG_OPEN
;
844 case QFileDialog::AcceptSave
:
845 resId
= STR_FILEDLG_SAVE
;
846 m_pFileDialog
->setFileMode(QFileDialog::AnyFile
);
850 m_pFileDialog
->setAcceptMode(acceptMode
);
851 m_pFileDialog
->setWindowTitle(getResString(resId
));
853 css::uno::Reference
<css::awt::XWindow
> xParentWindow
;
854 if (args
.getLength() > 1)
855 args
[1] >>= xParentWindow
;
856 if (!xParentWindow
.is())
859 css::uno::Reference
<css::awt::XSystemDependentWindowPeer
> xSysWinPeer(xParentWindow
,
860 css::uno::UNO_QUERY
);
861 if (!xSysWinPeer
.is())
864 // the sal_*Int8 handling is strange, but it's public API - no way around
865 css::uno::Sequence
<sal_Int8
> aProcessIdent(16);
866 rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8
*>(aProcessIdent
.getArray()));
868 = xSysWinPeer
->getWindowHandle(aProcessIdent
, css::lang::SystemDependent::SYSTEM_XWINDOW
);
869 css::awt::SystemDependentXWindow xSysWin
;
872 const auto& pFrames
= rQtInstance
.getFrames();
873 const tools::Long aWindowHandle
= xSysWin
.WindowHandle
;
875 = std::find_if(pFrames
.begin(), pFrames
.end(), [&aWindowHandle
](auto pFrame
) -> bool {
876 const SystemEnvData
& rData
= pFrame
->GetSystemData();
877 return tools::Long(rData
.GetWindowHandle(pFrame
)) == aWindowHandle
;
879 if (it
!= pFrames
.end())
880 m_pParentWidget
= static_cast<QtFrame
*>(*it
)->asChild();
883 void SAL_CALL
QtFilePicker::cancel() { m_pFileDialog
->reject(); }
885 void SAL_CALL
QtFilePicker::disposing(const lang::EventObject
& rEvent
)
887 uno::Reference
<XFilePickerListener
> xFilePickerListener(rEvent
.Source
, uno::UNO_QUERY
);
889 if (xFilePickerListener
.is())
891 removeFilePickerListener(xFilePickerListener
);
895 void SAL_CALL
QtFilePicker::queryTermination(const css::lang::EventObject
&)
897 throw css::frame::TerminationVetoException();
900 void SAL_CALL
QtFilePicker::notifyTermination(const css::lang::EventObject
&)
902 SolarMutexGuard aGuard
;
903 m_pFileDialog
->reject();
906 OUString SAL_CALL
QtFilePicker::getImplementationName()
908 return u
"com.sun.star.ui.dialogs.QtFilePicker"_ustr
;
911 sal_Bool SAL_CALL
QtFilePicker::supportsService(const OUString
& ServiceName
)
913 return cppu::supportsService(this, ServiceName
);
916 uno::Sequence
<OUString
> SAL_CALL
QtFilePicker::getSupportedServiceNames()
918 return FilePicker_getSupportedServiceNames();
921 void QtFilePicker::updateAutomaticFileExtension()
923 bool bSetAutoExtension
924 = getValue(CHECKBOX_AUTOEXTENSION
, ControlActions::GET_SELECTED_ITEM
).get
<bool>();
925 if (bSetAutoExtension
)
927 QString sSuffix
= m_aNamedFilterToExtensionMap
.value(m_pFileDialog
->selectedNameFilter());
928 // string is "*.<SUFFIX>" if a specific filter was selected that has exactly one possible file extension
929 if (sSuffix
.lastIndexOf("*.") == 0)
931 sSuffix
= sSuffix
.remove("*.");
932 m_pFileDialog
->setDefaultSuffix(sSuffix
);
936 // fall back to setting none otherwise
939 "Unable to retrieve unambiguous file extension. Will not add any automatically.");
940 bSetAutoExtension
= false;
944 if (!bSetAutoExtension
)
945 m_pFileDialog
->setDefaultSuffix("");
948 void QtFilePicker::filterSelected(const QString
&)
950 FilePickerEvent aEvent
;
951 aEvent
.ElementId
= LISTBOX_FILTER
;
952 SAL_INFO("vcl.qt", "filter changed");
953 if (m_xListener
.is())
954 m_xListener
->controlStateChanged(aEvent
);
957 void QtFilePicker::currentChanged(const QString
&)
959 FilePickerEvent aEvent
;
960 SAL_INFO("vcl.qt", "file selection changed");
961 if (m_xListener
.is())
962 m_xListener
->fileSelectionChanged(aEvent
);
965 OUString
QtFilePicker::getDirectory()
967 uno::Sequence
<OUString
> seq
= getSelectedFiles();
968 if (seq
.getLength() > 1)
973 void QtFilePicker::setDescription(const OUString
&) {}
975 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */