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 <fpicker/fpsofficeResMgr.hxx>
21 #include <QtFilePicker.hxx>
22 #include <QtFilePicker.moc>
24 #include <QtFrame.hxx>
25 #include <QtTools.hxx>
26 #include <QtWidget.hxx>
27 #include <QtInstance.hxx>
29 #include <com/sun/star/awt/SystemDependentXWindow.hpp>
30 #include <com/sun/star/awt/XSystemDependentWindowPeer.hpp>
31 #include <com/sun/star/awt/XWindow.hpp>
32 #include <com/sun/star/frame/Desktop.hpp>
33 #include <com/sun/star/frame/TerminationVetoException.hpp>
34 #include <com/sun/star/frame/XDesktop.hpp>
35 #include <com/sun/star/lang/DisposedException.hpp>
36 #include <com/sun/star/lang/IllegalArgumentException.hpp>
37 #include <com/sun/star/lang/SystemDependent.hpp>
38 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
39 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
40 #include <com/sun/star/ui/dialogs/ControlActions.hpp>
41 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
42 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
43 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
44 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
45 #include <cppuhelper/interfacecontainer.h>
46 #include <cppuhelper/supportsservice.hxx>
47 #include <rtl/process.h>
48 #include <sal/log.hxx>
50 #include <QtCore/QDebug>
51 #include <QtCore/QRegularExpression>
52 #include <QtCore/QThread>
53 #include <QtCore/QUrl>
54 #include <QtGui/QClipboard>
55 #include <QtGui/QWindow>
56 #include <QtWidgets/QApplication>
57 #include <QtWidgets/QCheckBox>
58 #include <QtWidgets/QComboBox>
59 #include <QtWidgets/QGridLayout>
60 #include <QtWidgets/QHBoxLayout>
61 #include <QtWidgets/QLabel>
62 #include <QtWidgets/QMessageBox>
63 #include <QtWidgets/QPushButton>
64 #include <QtWidgets/QWidget>
66 #include <unx/geninst.h>
67 #include <fpicker/strings.hrc>
70 using namespace ::com::sun::star
;
71 using namespace ::com::sun::star::ui::dialogs
;
72 using namespace ::com::sun::star::ui::dialogs::TemplateDescription
;
73 using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds
;
74 using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds
;
75 using namespace ::com::sun::star::lang
;
76 using namespace ::com::sun::star::beans
;
77 using namespace ::com::sun::star::uno
;
81 uno::Sequence
<OUString
> FilePicker_getSupportedServiceNames()
83 return { "com.sun.star.ui.dialogs.FilePicker", "com.sun.star.ui.dialogs.SystemFilePicker",
84 "com.sun.star.ui.dialogs.QtFilePicker" };
88 QtFilePicker::QtFilePicker(css::uno::Reference
<css::uno::XComponentContext
> context
,
89 QFileDialog::FileMode eMode
, bool bUseNative
)
90 : QtFilePicker_Base(m_aHelperMutex
)
91 , m_context(std::move(context
))
92 , m_bIsFolderPicker(eMode
== QFileDialog::Directory
)
93 , m_pParentWidget(nullptr)
94 , m_pFileDialog(new QFileDialog(nullptr, {}, QDir::homePath()))
95 , m_pExtraControls(new QWidget())
97 m_pFileDialog
->setOption(QFileDialog::DontUseNativeDialog
, !bUseNative
);
99 m_pFileDialog
->setFileMode(eMode
);
100 m_pFileDialog
->setWindowModality(Qt::ApplicationModal
);
102 if (m_bIsFolderPicker
)
104 m_pFileDialog
->setOption(QFileDialog::ShowDirsOnly
, true);
105 m_pFileDialog
->setWindowTitle(toQString(FpsResId(STR_SVT_FOLDERPICKER_DEFAULT_TITLE
)));
108 m_pLayout
= dynamic_cast<QGridLayout
*>(m_pFileDialog
->layout());
110 setMultiSelectionMode(false);
112 // XFilePickerListener notifications
113 connect(m_pFileDialog
.get(), SIGNAL(filterSelected(const QString
&)), this,
114 SLOT(filterSelected(const QString
&)));
115 connect(m_pFileDialog
.get(), SIGNAL(currentChanged(const QString
&)), this,
116 SLOT(currentChanged(const QString
&)));
118 // update automatic file extension when filter is changed
119 connect(m_pFileDialog
.get(), SIGNAL(filterSelected(const QString
&)), this,
120 SLOT(updateAutomaticFileExtension()));
122 connect(m_pFileDialog
.get(), SIGNAL(finished(int)), this, SLOT(finished(int)));
125 QtFilePicker::~QtFilePicker()
128 auto* pSalInst(GetQtInstance());
130 pSalInst
->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 auto* pSalInst(GetQtInstance());
155 pSalInst
->RunInMainThread(
156 [this, &title
]() { m_pFileDialog
->setWindowTitle(toQString(title
)); });
159 void QtFilePicker::prepareExecute()
161 QWidget
* pTransientParent
= m_pParentWidget
;
162 if (!pTransientParent
)
164 vcl::Window
* pWindow
= ::Application::GetActiveTopWindow();
167 QtFrame
* pFrame
= dynamic_cast<QtFrame
*>(pWindow
->ImplGetFrame());
170 pTransientParent
= pFrame
->asChild();
174 if (!m_aNamedFilterList
.isEmpty())
175 m_pFileDialog
->setNameFilters(m_aNamedFilterList
);
176 if (!m_aCurrentFilter
.isEmpty())
177 m_pFileDialog
->selectNameFilter(m_aCurrentFilter
);
179 updateAutomaticFileExtension();
181 uno::Reference
<css::frame::XDesktop
> xDesktop(css::frame::Desktop::create(m_context
),
184 // will hide the window, so do before show
185 m_pFileDialog
->setParent(pTransientParent
, m_pFileDialog
->windowFlags());
186 m_pFileDialog
->show();
187 xDesktop
->addTerminateListener(this);
190 void QtFilePicker::finished(int nResult
)
193 uno::Reference
<css::frame::XDesktop
> xDesktop(css::frame::Desktop::create(m_context
),
195 xDesktop
->removeTerminateListener(this);
196 m_pFileDialog
->setParent(nullptr, m_pFileDialog
->windowFlags());
198 if (m_xClosedListener
.is())
200 const sal_Int16 nRet
= (QFileDialog::Rejected
== nResult
) ? ExecutableDialogResults::CANCEL
201 : ExecutableDialogResults::OK
;
202 css::ui::dialogs::DialogClosedEvent
aEvent(*this, nRet
);
203 m_xClosedListener
->dialogClosed(aEvent
);
204 m_xClosedListener
.clear();
208 sal_Int16 SAL_CALL
QtFilePicker::execute()
211 auto* pSalInst(GetQtInstance());
213 if (!pSalInst
->IsMainThread())
216 pSalInst
->RunInMainThread([&ret
, this]() { ret
= execute(); });
221 int result
= m_pFileDialog
->exec();
223 if (QFileDialog::Rejected
== result
)
224 return ExecutableDialogResults::CANCEL
;
225 return ExecutableDialogResults::OK
;
228 // XAsynchronousExecutableDialog functions
229 void SAL_CALL
QtFilePicker::setDialogTitle(const OUString
& _rTitle
) { setTitle(_rTitle
); }
232 QtFilePicker::startExecuteModal(const Reference
<css::ui::dialogs::XDialogClosedListener
>& xListener
)
234 m_xClosedListener
= xListener
;
236 m_pFileDialog
->show();
239 void SAL_CALL
QtFilePicker::setMultiSelectionMode(sal_Bool multiSelect
)
242 auto* pSalInst(GetQtInstance());
244 pSalInst
->RunInMainThread([this, multiSelect
]() {
245 if (m_bIsFolderPicker
|| m_pFileDialog
->acceptMode() == QFileDialog::AcceptSave
)
248 m_pFileDialog
->setFileMode(multiSelect
? QFileDialog::ExistingFiles
249 : QFileDialog::ExistingFile
);
253 void SAL_CALL
QtFilePicker::setDefaultName(const OUString
& name
)
256 auto* pSalInst(GetQtInstance());
258 pSalInst
->RunInMainThread([this, &name
]() { m_pFileDialog
->selectFile(toQString(name
)); });
261 void SAL_CALL
QtFilePicker::setDisplayDirectory(const OUString
& dir
)
264 auto* pSalInst(GetQtInstance());
266 pSalInst
->RunInMainThread([this, &dir
]() {
267 QString
qDir(toQString(dir
));
268 m_pFileDialog
->setDirectoryUrl(QUrl(qDir
));
272 OUString SAL_CALL
QtFilePicker::getDisplayDirectory()
276 auto* pSalInst(GetQtInstance());
278 pSalInst
->RunInMainThread(
279 [&ret
, this]() { ret
= toOUString(m_pFileDialog
->directoryUrl().toString()); });
283 uno::Sequence
<OUString
> SAL_CALL
QtFilePicker::getFiles()
285 uno::Sequence
<OUString
> seq
= getSelectedFiles();
286 if (seq
.getLength() > 1)
291 uno::Sequence
<OUString
> SAL_CALL
QtFilePicker::getSelectedFiles()
295 auto* pSalInst(GetQtInstance());
297 pSalInst
->RunInMainThread([&urls
, this]() { urls
= m_pFileDialog
->selectedUrls(); });
299 uno::Sequence
<OUString
> seq(urls
.size());
300 auto seqRange
= asNonConstRange(seq
);
302 auto const trans
= css::uri::ExternalUriReferenceTranslator::create(m_context
);
304 for (const QUrl
& aURL
: urls
)
306 // Unlike LO, QFileDialog (<https://doc.qt.io/qt-5/qfiledialog.html>) apparently always
307 // treats file-system pathnames as UTF-8--encoded, regardless of LANG/LC_CTYPE locale
308 // setting. And pathnames containing byte sequences that are not valid UTF-8 are apparently
309 // filtered out and not even displayed by QFileDialog, so aURL will always have a "payload"
310 // that matches the pathname's byte sequence. So the pathname's byte sequence (which
311 // happens to also be aURL's payload) in the LANG/LC_CTYPE encoding needs to be converted
312 // into LO's internal UTF-8 file URL encoding via
313 // XExternalUriReferenceTranslator::translateToInternal (which looks somewhat paradoxical as
314 // aURL.toEncoded() nominally already has a UTF-8 payload):
315 auto const extUrl
= toOUString(aURL
.toEncoded());
316 auto intUrl
= trans
->translateToInternal(extUrl
);
317 if (intUrl
.isEmpty())
319 // If translation failed, fall back to original URL:
320 SAL_WARN("vcl.qt", "cannot convert <" << extUrl
<< "> from locale encoding to UTF-8");
323 seqRange
[i
++] = intUrl
;
329 void SAL_CALL
QtFilePicker::appendFilter(const OUString
& title
, const OUString
& filter
)
332 auto* pSalInst(GetQtInstance());
334 if (!pSalInst
->IsMainThread())
336 pSalInst
->RunInMainThread([this, &title
, &filter
]() { appendFilter(title
, filter
); });
340 // '/' need to be escaped else they are assumed to be mime types
341 QString sTitle
= toQString(title
).replace("/", "\\/");
343 QString sFilterName
= sTitle
;
344 // the Qt non-native file picker adds the extensions to the filter title, so strip them
345 if (m_pFileDialog
->testOption(QFileDialog::DontUseNativeDialog
))
347 int pos
= sFilterName
.indexOf(" (");
349 sFilterName
.truncate(pos
);
352 QString sGlobFilter
= toQString(filter
);
354 // LibreOffice gives us filters separated by ';' qt dialogs just want space separated
355 sGlobFilter
.replace(";", " ");
357 // make sure "*.*" is not used as "all files"
358 sGlobFilter
.replace("*.*", "*");
360 m_aNamedFilterList
<< QStringLiteral("%1 (%2)").arg(sFilterName
, sGlobFilter
);
361 m_aTitleToFilterMap
[sTitle
] = m_aNamedFilterList
.constLast();
362 m_aNamedFilterToExtensionMap
[m_aNamedFilterList
.constLast()] = sGlobFilter
;
365 void SAL_CALL
QtFilePicker::setCurrentFilter(const OUString
& title
)
368 auto* pSalInst(GetQtInstance());
370 pSalInst
->RunInMainThread([this, &title
]() {
371 m_aCurrentFilter
= m_aTitleToFilterMap
.value(toQString(title
).replace("/", "\\/"));
375 OUString SAL_CALL
QtFilePicker::getCurrentFilter()
379 auto* pSalInst(GetQtInstance());
381 pSalInst
->RunInMainThread([&filter
, this]() {
382 filter
= m_aTitleToFilterMap
.key(m_pFileDialog
->selectedNameFilter());
385 if (filter
.isEmpty())
386 filter
= "ODF Text Document (.odt)";
387 return toOUString(filter
);
390 void SAL_CALL
QtFilePicker::appendFilterGroup(const OUString
& rGroupTitle
,
391 const uno::Sequence
<beans::StringPair
>& filters
)
394 auto* pSalInst(GetQtInstance());
396 if (!pSalInst
->IsMainThread())
398 pSalInst
->RunInMainThread(
399 [this, &rGroupTitle
, &filters
]() { appendFilterGroup(rGroupTitle
, filters
); });
403 const sal_uInt16 length
= filters
.getLength();
404 for (sal_uInt16 i
= 0; i
< length
; ++i
)
406 beans::StringPair aPair
= filters
[i
];
407 appendFilter(aPair
.First
, aPair
.Second
);
411 uno::Any
QtFilePicker::handleGetListValue(const QComboBox
* pWidget
, sal_Int16 nControlAction
)
414 switch (nControlAction
)
416 case ControlActions::GET_ITEMS
:
418 Sequence
<OUString
> aItemList(pWidget
->count());
419 auto aItemListRange
= asNonConstRange(aItemList
);
420 for (sal_Int32 i
= 0; i
< pWidget
->count(); ++i
)
421 aItemListRange
[i
] = toOUString(pWidget
->itemText(i
));
425 case ControlActions::GET_SELECTED_ITEM
:
427 if (!pWidget
->currentText().isEmpty())
428 aAny
<<= toOUString(pWidget
->currentText());
431 case ControlActions::GET_SELECTED_ITEM_INDEX
:
433 if (pWidget
->currentIndex() >= 0)
434 aAny
<<= static_cast<sal_Int32
>(pWidget
->currentIndex());
439 "undocumented/unimplemented ControlAction for a list " << nControlAction
);
445 void QtFilePicker::handleSetListValue(QComboBox
* pWidget
, sal_Int16 nControlAction
,
446 const uno::Any
& rValue
)
448 switch (nControlAction
)
450 case ControlActions::ADD_ITEM
:
454 pWidget
->addItem(toQString(sItem
));
457 case ControlActions::ADD_ITEMS
:
459 Sequence
<OUString
> aStringList
;
460 rValue
>>= aStringList
;
461 for (auto const& sItem
: std::as_const(aStringList
))
462 pWidget
->addItem(toQString(sItem
));
465 case ControlActions::DELETE_ITEM
:
469 pWidget
->removeItem(nPos
);
472 case ControlActions::DELETE_ITEMS
:
477 case ControlActions::SET_SELECT_ITEM
:
481 pWidget
->setCurrentIndex(nPos
);
486 "undocumented/unimplemented ControlAction for a list " << nControlAction
);
490 pWidget
->setEnabled(pWidget
->count() > 0);
493 void SAL_CALL
QtFilePicker::setValue(sal_Int16 controlId
, sal_Int16 nControlAction
,
494 const uno::Any
& value
)
497 auto* pSalInst(GetQtInstance());
499 if (!pSalInst
->IsMainThread())
501 pSalInst
->RunInMainThread([this, controlId
, nControlAction
, &value
]() {
502 setValue(controlId
, nControlAction
, value
);
507 if (m_aCustomWidgetsMap
.contains(controlId
))
509 QWidget
* widget
= m_aCustomWidgetsMap
.value(controlId
);
510 QCheckBox
* cb
= dynamic_cast<QCheckBox
*>(widget
);
512 cb
->setChecked(value
.get
<bool>());
515 QComboBox
* combo
= dynamic_cast<QComboBox
*>(widget
);
517 handleSetListValue(combo
, nControlAction
, value
);
521 SAL_WARN("vcl.qt", "set value on unknown control " << controlId
);
524 uno::Any SAL_CALL
QtFilePicker::getValue(sal_Int16 controlId
, sal_Int16 nControlAction
)
527 auto* pSalInst(GetQtInstance());
529 if (!pSalInst
->IsMainThread())
532 pSalInst
->RunInMainThread([&ret
, this, controlId
, nControlAction
]() {
533 ret
= getValue(controlId
, nControlAction
);
539 if (m_aCustomWidgetsMap
.contains(controlId
))
541 QWidget
* widget
= m_aCustomWidgetsMap
.value(controlId
);
542 QCheckBox
* cb
= dynamic_cast<QCheckBox
*>(widget
);
544 res
<<= cb
->isChecked();
547 QComboBox
* combo
= dynamic_cast<QComboBox
*>(widget
);
549 res
= handleGetListValue(combo
, nControlAction
);
553 SAL_WARN("vcl.qt", "get value on unknown control " << controlId
);
558 void SAL_CALL
QtFilePicker::enableControl(sal_Int16 controlId
, sal_Bool enable
)
561 auto* pSalInst(GetQtInstance());
563 pSalInst
->RunInMainThread([this, controlId
, enable
]() {
564 if (m_aCustomWidgetsMap
.contains(controlId
))
565 m_aCustomWidgetsMap
.value(controlId
)->setEnabled(enable
);
567 SAL_WARN("vcl.qt", "enable unknown control " << controlId
);
571 void SAL_CALL
QtFilePicker::setLabel(sal_Int16 controlId
, const OUString
& label
)
574 auto* pSalInst(GetQtInstance());
576 if (!pSalInst
->IsMainThread())
578 pSalInst
->RunInMainThread([this, controlId
, label
]() { setLabel(controlId
, label
); });
582 if (m_aCustomWidgetsMap
.contains(controlId
))
584 QCheckBox
* cb
= dynamic_cast<QCheckBox
*>(m_aCustomWidgetsMap
.value(controlId
));
586 cb
->setText(toQString(label
));
589 SAL_WARN("vcl.qt", "set label on unknown control " << controlId
);
592 OUString SAL_CALL
QtFilePicker::getLabel(sal_Int16 controlId
)
595 auto* pSalInst(GetQtInstance());
597 if (!pSalInst
->IsMainThread())
600 pSalInst
->RunInMainThread([&ret
, this, controlId
]() { ret
= getLabel(controlId
); });
605 if (m_aCustomWidgetsMap
.contains(controlId
))
607 QCheckBox
* cb
= dynamic_cast<QCheckBox
*>(m_aCustomWidgetsMap
.value(controlId
));
612 SAL_WARN("vcl.qt", "get label on unknown control " << controlId
);
614 return toOUString(label
);
617 QString
QtFilePicker::getResString(TranslateId pResId
)
624 aResString
= toQString(FpsResId(pResId
));
626 return aResString
.replace('~', '&');
629 void QtFilePicker::addCustomControl(sal_Int16 controlId
)
631 QWidget
* widget
= nullptr;
632 QLabel
* label
= nullptr;
634 QCheckBox
* pCheckbox
= nullptr;
638 case CHECKBOX_AUTOEXTENSION
:
639 resId
= STR_SVT_FILEPICKER_AUTO_EXTENSION
;
641 case CHECKBOX_PASSWORD
:
642 resId
= STR_SVT_FILEPICKER_PASSWORD
;
644 case CHECKBOX_FILTEROPTIONS
:
645 resId
= STR_SVT_FILEPICKER_FILTER_OPTIONS
;
647 case CHECKBOX_READONLY
:
648 resId
= STR_SVT_FILEPICKER_READONLY
;
651 resId
= STR_SVT_FILEPICKER_INSERT_AS_LINK
;
653 case CHECKBOX_PREVIEW
:
654 resId
= STR_SVT_FILEPICKER_SHOW_PREVIEW
;
656 case CHECKBOX_SELECTION
:
657 resId
= STR_SVT_FILEPICKER_SELECTION
;
659 case CHECKBOX_GPGENCRYPTION
:
660 resId
= STR_SVT_FILEPICKER_GPGENCRYPT
;
662 case PUSHBUTTON_PLAY
:
663 resId
= STR_SVT_FILEPICKER_PLAY
;
665 case LISTBOX_VERSION
:
666 resId
= STR_SVT_FILEPICKER_VERSION
;
668 case LISTBOX_TEMPLATE
:
669 resId
= STR_SVT_FILEPICKER_TEMPLATES
;
671 case LISTBOX_IMAGE_TEMPLATE
:
672 resId
= STR_SVT_FILEPICKER_IMAGE_TEMPLATE
;
674 case LISTBOX_IMAGE_ANCHOR
:
675 resId
= STR_SVT_FILEPICKER_IMAGE_ANCHOR
;
677 case LISTBOX_VERSION_LABEL
:
678 case LISTBOX_TEMPLATE_LABEL
:
679 case LISTBOX_IMAGE_TEMPLATE_LABEL
:
680 case LISTBOX_IMAGE_ANCHOR_LABEL
:
681 case LISTBOX_FILTER_SELECTOR
:
687 case CHECKBOX_AUTOEXTENSION
:
688 pCheckbox
= new QCheckBox(getResString(resId
), m_pExtraControls
);
689 // to add/remove automatic file extension based on checkbox
690 connect(pCheckbox
, SIGNAL(stateChanged(int)), this,
691 SLOT(updateAutomaticFileExtension()));
694 case CHECKBOX_PASSWORD
:
695 case CHECKBOX_FILTEROPTIONS
:
696 case CHECKBOX_READONLY
:
698 case CHECKBOX_PREVIEW
:
699 case CHECKBOX_SELECTION
:
700 case CHECKBOX_GPGENCRYPTION
:
701 widget
= new QCheckBox(getResString(resId
), m_pExtraControls
);
703 case PUSHBUTTON_PLAY
:
705 case LISTBOX_VERSION
:
706 case LISTBOX_TEMPLATE
:
707 case LISTBOX_IMAGE_ANCHOR
:
708 case LISTBOX_IMAGE_TEMPLATE
:
709 case LISTBOX_FILTER_SELECTOR
:
710 label
= new QLabel(getResString(resId
), m_pExtraControls
);
711 widget
= new QComboBox(m_pExtraControls
);
712 label
->setBuddy(widget
);
714 case LISTBOX_VERSION_LABEL
:
715 case LISTBOX_TEMPLATE_LABEL
:
716 case LISTBOX_IMAGE_TEMPLATE_LABEL
:
717 case LISTBOX_IMAGE_ANCHOR_LABEL
:
723 const int row
= m_pLayout
->rowCount();
725 m_pLayout
->addWidget(label
, row
, 0);
726 m_pLayout
->addWidget(widget
, row
, 1);
727 m_aCustomWidgetsMap
.insert(controlId
, widget
);
731 void SAL_CALL
QtFilePicker::initialize(const uno::Sequence
<uno::Any
>& args
)
733 // parameter checking
735 if (args
.getLength() == 0)
736 throw lang::IllegalArgumentException("no arguments", static_cast<XFilePicker2
*>(this), 1);
740 if ((arg
.getValueType() != cppu::UnoType
<sal_Int16
>::get())
741 && (arg
.getValueType() != cppu::UnoType
<sal_Int8
>::get()))
743 throw lang::IllegalArgumentException("invalid argument type",
744 static_cast<XFilePicker2
*>(this), 1);
748 auto* pSalInst(GetQtInstance());
750 if (!pSalInst
->IsMainThread())
752 pSalInst
->RunInMainThread([this, args
]() { initialize(args
); });
756 m_aNamedFilterToExtensionMap
.clear();
757 m_aNamedFilterList
.clear();
758 m_aTitleToFilterMap
.clear();
759 m_aCurrentFilter
.clear();
761 sal_Int16 templateId
= -1;
764 QFileDialog::AcceptMode acceptMode
= QFileDialog::AcceptOpen
;
767 case FILEOPEN_SIMPLE
:
770 case FILESAVE_SIMPLE
:
771 acceptMode
= QFileDialog::AcceptSave
;
774 case FILESAVE_AUTOEXTENSION
:
775 acceptMode
= QFileDialog::AcceptSave
;
776 addCustomControl(CHECKBOX_AUTOEXTENSION
);
779 case FILESAVE_AUTOEXTENSION_PASSWORD
:
780 acceptMode
= QFileDialog::AcceptSave
;
781 addCustomControl(CHECKBOX_AUTOEXTENSION
);
782 addCustomControl(CHECKBOX_PASSWORD
);
783 addCustomControl(CHECKBOX_GPGENCRYPTION
);
786 case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS
:
787 acceptMode
= QFileDialog::AcceptSave
;
788 addCustomControl(CHECKBOX_AUTOEXTENSION
);
789 addCustomControl(CHECKBOX_PASSWORD
);
790 addCustomControl(CHECKBOX_GPGENCRYPTION
);
791 addCustomControl(CHECKBOX_FILTEROPTIONS
);
794 case FILESAVE_AUTOEXTENSION_SELECTION
:
795 acceptMode
= QFileDialog::AcceptSave
;
796 addCustomControl(CHECKBOX_AUTOEXTENSION
);
797 addCustomControl(CHECKBOX_SELECTION
);
800 case FILESAVE_AUTOEXTENSION_TEMPLATE
:
801 acceptMode
= QFileDialog::AcceptSave
;
802 addCustomControl(CHECKBOX_AUTOEXTENSION
);
803 addCustomControl(LISTBOX_TEMPLATE
);
806 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE
:
807 addCustomControl(CHECKBOX_LINK
);
808 addCustomControl(CHECKBOX_PREVIEW
);
809 addCustomControl(LISTBOX_IMAGE_TEMPLATE
);
812 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR
:
813 addCustomControl(CHECKBOX_LINK
);
814 addCustomControl(CHECKBOX_PREVIEW
);
815 addCustomControl(LISTBOX_IMAGE_ANCHOR
);
819 addCustomControl(PUSHBUTTON_PLAY
);
822 case FILEOPEN_LINK_PLAY
:
823 addCustomControl(CHECKBOX_LINK
);
824 addCustomControl(PUSHBUTTON_PLAY
);
827 case FILEOPEN_READONLY_VERSION
:
828 addCustomControl(CHECKBOX_READONLY
);
829 addCustomControl(LISTBOX_VERSION
);
832 case FILEOPEN_LINK_PREVIEW
:
833 addCustomControl(CHECKBOX_LINK
);
834 addCustomControl(CHECKBOX_PREVIEW
);
837 case FILEOPEN_PREVIEW
:
838 addCustomControl(CHECKBOX_PREVIEW
);
842 throw lang::IllegalArgumentException("Unknown template",
843 static_cast<XFilePicker2
*>(this), 1);
849 case QFileDialog::AcceptOpen
:
850 resId
= STR_FILEDLG_OPEN
;
852 case QFileDialog::AcceptSave
:
853 resId
= STR_FILEDLG_SAVE
;
854 m_pFileDialog
->setFileMode(QFileDialog::AnyFile
);
858 m_pFileDialog
->setAcceptMode(acceptMode
);
859 m_pFileDialog
->setWindowTitle(getResString(resId
));
861 css::uno::Reference
<css::awt::XWindow
> xParentWindow
;
862 if (args
.getLength() > 1)
863 args
[1] >>= xParentWindow
;
864 if (!xParentWindow
.is())
867 css::uno::Reference
<css::awt::XSystemDependentWindowPeer
> xSysWinPeer(xParentWindow
,
868 css::uno::UNO_QUERY
);
869 if (!xSysWinPeer
.is())
872 // the sal_*Int8 handling is strange, but it's public API - no way around
873 css::uno::Sequence
<sal_Int8
> aProcessIdent(16);
874 rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8
*>(aProcessIdent
.getArray()));
876 = xSysWinPeer
->getWindowHandle(aProcessIdent
, css::lang::SystemDependent::SYSTEM_XWINDOW
);
877 css::awt::SystemDependentXWindow xSysWin
;
880 const auto& pFrames
= pSalInst
->getFrames();
881 const tools::Long aWindowHandle
= xSysWin
.WindowHandle
;
883 = std::find_if(pFrames
.begin(), pFrames
.end(), [&aWindowHandle
](auto pFrame
) -> bool {
884 const SystemEnvData
* pData
= pFrame
->GetSystemData();
885 return pData
&& tools::Long(pData
->GetWindowHandle(pFrame
)) == aWindowHandle
;
887 if (it
!= pFrames
.end())
888 m_pParentWidget
= static_cast<QtFrame
*>(*it
)->asChild();
891 void SAL_CALL
QtFilePicker::cancel() { m_pFileDialog
->reject(); }
893 void SAL_CALL
QtFilePicker::disposing(const lang::EventObject
& rEvent
)
895 uno::Reference
<XFilePickerListener
> xFilePickerListener(rEvent
.Source
, uno::UNO_QUERY
);
897 if (xFilePickerListener
.is())
899 removeFilePickerListener(xFilePickerListener
);
903 void SAL_CALL
QtFilePicker::queryTermination(const css::lang::EventObject
&)
905 throw css::frame::TerminationVetoException();
908 void SAL_CALL
QtFilePicker::notifyTermination(const css::lang::EventObject
&)
910 SolarMutexGuard aGuard
;
911 m_pFileDialog
->reject();
914 OUString SAL_CALL
QtFilePicker::getImplementationName()
916 return "com.sun.star.ui.dialogs.QtFilePicker";
919 sal_Bool SAL_CALL
QtFilePicker::supportsService(const OUString
& ServiceName
)
921 return cppu::supportsService(this, ServiceName
);
924 uno::Sequence
<OUString
> SAL_CALL
QtFilePicker::getSupportedServiceNames()
926 return FilePicker_getSupportedServiceNames();
929 void QtFilePicker::updateAutomaticFileExtension()
931 bool bSetAutoExtension
932 = getValue(CHECKBOX_AUTOEXTENSION
, ControlActions::GET_SELECTED_ITEM
).get
<bool>();
933 if (bSetAutoExtension
)
935 QString sSuffix
= m_aNamedFilterToExtensionMap
.value(m_pFileDialog
->selectedNameFilter());
936 // string is "*.<SUFFIX>" if a specific filter was selected that has exactly one possible file extension
937 if (sSuffix
.lastIndexOf("*.") == 0)
939 sSuffix
= sSuffix
.remove("*.");
940 m_pFileDialog
->setDefaultSuffix(sSuffix
);
944 // fall back to setting none otherwise
947 "Unable to retrieve unambiguous file extension. Will not add any automatically.");
948 bSetAutoExtension
= false;
952 if (!bSetAutoExtension
)
953 m_pFileDialog
->setDefaultSuffix("");
956 void QtFilePicker::filterSelected(const QString
&)
958 FilePickerEvent aEvent
;
959 aEvent
.ElementId
= LISTBOX_FILTER
;
960 SAL_INFO("vcl.qt", "filter changed");
961 if (m_xListener
.is())
962 m_xListener
->controlStateChanged(aEvent
);
965 void QtFilePicker::currentChanged(const QString
&)
967 FilePickerEvent aEvent
;
968 SAL_INFO("vcl.qt", "file selection changed");
969 if (m_xListener
.is())
970 m_xListener
->fileSelectionChanged(aEvent
);
973 OUString
QtFilePicker::getDirectory()
975 uno::Sequence
<OUString
> seq
= getSelectedFiles();
976 if (seq
.getLength() > 1)
981 void QtFilePicker::setDescription(const OUString
&) {}
983 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */