tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / vcl / qt5 / QtFilePicker.cxx
blobe97d8cb39608bc0d29f11a0bc9a9e279f338be96
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
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>
71 #include <utility>
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;
82 namespace
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()
129 SolarMutexGuard g;
130 GetQtInstance().RunInMainThread([this]() {
131 // must delete it in main thread, otherwise
132 // QSocketNotifier::setEnabled() will crash us
133 m_pFileDialog.reset();
137 void SAL_CALL
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;
147 m_xListener.clear();
150 void SAL_CALL QtFilePicker::setTitle(const OUString& title)
152 SolarMutexGuard g;
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();
163 if (pWindow)
165 QtFrame* pFrame = dynamic_cast<QtFrame*>(pWindow->ImplGetFrame());
166 assert(pFrame);
167 if (pFrame)
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),
180 UNO_QUERY_THROW);
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)
190 SolarMutexGuard g;
191 uno::Reference<css::frame::XDesktop> xDesktop(css::frame::Desktop::create(m_context),
192 UNO_QUERY_THROW);
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()
208 SolarMutexGuard g;
209 QtInstance& rQtInstance = GetQtInstance();
210 if (!rQtInstance.IsMainThread())
212 sal_uInt16 ret;
213 rQtInstance.RunInMainThread([&ret, this]() { ret = execute(); });
214 return ret;
217 prepareExecute();
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); }
228 void SAL_CALL
229 QtFilePicker::startExecuteModal(const Reference<css::ui::dialogs::XDialogClosedListener>& xListener)
231 m_xClosedListener = xListener;
232 prepareExecute();
233 m_pFileDialog->show();
236 void SAL_CALL QtFilePicker::setMultiSelectionMode(sal_Bool multiSelect)
238 SolarMutexGuard g;
239 GetQtInstance().RunInMainThread([this, multiSelect]() {
240 if (m_bIsFolderPicker || m_pFileDialog->acceptMode() == QFileDialog::AcceptSave)
241 return;
243 m_pFileDialog->setFileMode(multiSelect ? QFileDialog::ExistingFiles
244 : QFileDialog::ExistingFile);
248 void SAL_CALL QtFilePicker::setDefaultName(const OUString& name)
250 SolarMutexGuard g;
251 GetQtInstance().RunInMainThread(
252 [this, &name]() { m_pFileDialog->selectFile(toQString(name)); });
255 void SAL_CALL QtFilePicker::setDisplayDirectory(const OUString& dir)
257 SolarMutexGuard g;
258 GetQtInstance().RunInMainThread([this, &dir]() {
259 QString qDir(toQString(dir));
260 m_pFileDialog->setDirectoryUrl(QUrl(qDir));
264 OUString SAL_CALL QtFilePicker::getDisplayDirectory()
266 SolarMutexGuard g;
267 OUString ret;
268 GetQtInstance().RunInMainThread(
269 [&ret, this]() { ret = toOUString(m_pFileDialog->directoryUrl().toString()); });
270 return ret;
273 uno::Sequence<OUString> SAL_CALL QtFilePicker::getFiles()
275 uno::Sequence<OUString> seq = getSelectedFiles();
276 if (seq.getLength() > 1)
277 seq.realloc(1);
278 return seq;
281 uno::Sequence<OUString> SAL_CALL QtFilePicker::getSelectedFiles()
283 SolarMutexGuard g;
284 QList<QUrl> urls;
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);
291 size_t i = 0;
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");
309 intUrl = extUrl;
311 seqRange[i++] = intUrl;
314 return seq;
317 void SAL_CALL QtFilePicker::appendFilter(const OUString& title, const OUString& filter)
319 SolarMutexGuard g;
320 QtInstance& rQtInstance = GetQtInstance();
321 if (!rQtInstance.IsMainThread())
323 rQtInstance.RunInMainThread([this, &title, &filter]() { appendFilter(title, filter); });
324 return;
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(" (");
335 if (pos >= 0)
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)
354 SolarMutexGuard g;
355 GetQtInstance().RunInMainThread([this, &title]() {
356 m_aCurrentFilter = m_aTitleToFilterMap.value(toQString(title).replace("/", "\\/"));
360 OUString SAL_CALL QtFilePicker::getCurrentFilter()
362 SolarMutexGuard g;
363 QString filter;
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)
377 SolarMutexGuard g;
378 QtInstance& rQtInstance = GetQtInstance();
379 if (!rQtInstance.IsMainThread())
381 rQtInstance.RunInMainThread(
382 [this, &rGroupTitle, &filters]() { appendFilterGroup(rGroupTitle, filters); });
383 return;
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)
396 uno::Any aAny;
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));
405 aAny <<= aItemList;
406 break;
408 case ControlActions::GET_SELECTED_ITEM:
410 if (!pWidget->currentText().isEmpty())
411 aAny <<= toOUString(pWidget->currentText());
412 break;
414 case ControlActions::GET_SELECTED_ITEM_INDEX:
416 if (pWidget->currentIndex() >= 0)
417 aAny <<= static_cast<sal_Int32>(pWidget->currentIndex());
418 break;
420 default:
421 SAL_WARN("vcl.qt",
422 "undocumented/unimplemented ControlAction for a list " << nControlAction);
423 break;
425 return aAny;
428 void QtFilePicker::handleSetListValue(QComboBox* pWidget, sal_Int16 nControlAction,
429 const uno::Any& rValue)
431 switch (nControlAction)
433 case ControlActions::ADD_ITEM:
435 OUString sItem;
436 rValue >>= sItem;
437 pWidget->addItem(toQString(sItem));
438 break;
440 case ControlActions::ADD_ITEMS:
442 Sequence<OUString> aStringList;
443 rValue >>= aStringList;
444 for (auto const& sItem : aStringList)
445 pWidget->addItem(toQString(sItem));
446 break;
448 case ControlActions::DELETE_ITEM:
450 sal_Int32 nPos = 0;
451 rValue >>= nPos;
452 pWidget->removeItem(nPos);
453 break;
455 case ControlActions::DELETE_ITEMS:
457 pWidget->clear();
458 break;
460 case ControlActions::SET_SELECT_ITEM:
462 sal_Int32 nPos = 0;
463 rValue >>= nPos;
464 pWidget->setCurrentIndex(nPos);
465 break;
467 default:
468 SAL_WARN("vcl.qt",
469 "undocumented/unimplemented ControlAction for a list " << nControlAction);
470 break;
473 pWidget->setEnabled(pWidget->count() > 0);
476 void SAL_CALL QtFilePicker::setValue(sal_Int16 controlId, sal_Int16 nControlAction,
477 const uno::Any& value)
479 SolarMutexGuard g;
480 QtInstance& rQtInstance = GetQtInstance();
481 if (!rQtInstance.IsMainThread())
483 rQtInstance.RunInMainThread([this, controlId, nControlAction, &value]() {
484 setValue(controlId, nControlAction, value);
486 return;
489 if (m_aCustomWidgetsMap.contains(controlId))
491 QWidget* widget = m_aCustomWidgetsMap.value(controlId);
492 QCheckBox* cb = qobject_cast<QCheckBox*>(widget);
493 if (cb)
494 cb->setChecked(value.get<bool>());
495 else
497 QComboBox* combo = qobject_cast<QComboBox*>(widget);
498 if (combo)
499 handleSetListValue(combo, nControlAction, value);
502 else
503 SAL_WARN("vcl.qt", "set value on unknown control " << controlId);
506 uno::Any SAL_CALL QtFilePicker::getValue(sal_Int16 controlId, sal_Int16 nControlAction)
508 SolarMutexGuard g;
509 QtInstance& rQtInstance = GetQtInstance();
510 if (!rQtInstance.IsMainThread())
512 uno::Any ret;
513 rQtInstance.RunInMainThread([&ret, this, controlId, nControlAction]() {
514 ret = getValue(controlId, nControlAction);
516 return ret;
519 uno::Any res(false);
520 if (m_aCustomWidgetsMap.contains(controlId))
522 QWidget* widget = m_aCustomWidgetsMap.value(controlId);
523 QCheckBox* cb = qobject_cast<QCheckBox*>(widget);
524 if (cb)
525 res <<= cb->isChecked();
526 else
528 QComboBox* combo = qobject_cast<QComboBox*>(widget);
529 if (combo)
530 res = handleGetListValue(combo, nControlAction);
533 else
534 SAL_WARN("vcl.qt", "get value on unknown control " << controlId);
536 return res;
539 void SAL_CALL QtFilePicker::enableControl(sal_Int16 controlId, sal_Bool enable)
541 SolarMutexGuard g;
542 GetQtInstance().RunInMainThread([this, controlId, enable]() {
543 if (m_aCustomWidgetsMap.contains(controlId))
544 m_aCustomWidgetsMap.value(controlId)->setEnabled(enable);
545 else
546 SAL_WARN("vcl.qt", "enable unknown control " << controlId);
550 void SAL_CALL QtFilePicker::setLabel(sal_Int16 controlId, const OUString& label)
552 SolarMutexGuard g;
553 QtInstance& rQtInstance = GetQtInstance();
554 if (!rQtInstance.IsMainThread())
556 rQtInstance.RunInMainThread([this, controlId, label]() { setLabel(controlId, label); });
557 return;
560 if (m_aCustomWidgetsMap.contains(controlId))
562 QCheckBox* cb = qobject_cast<QCheckBox*>(m_aCustomWidgetsMap.value(controlId));
563 if (cb)
564 cb->setText(toQString(label));
566 else
567 SAL_WARN("vcl.qt", "set label on unknown control " << controlId);
570 OUString SAL_CALL QtFilePicker::getLabel(sal_Int16 controlId)
572 SolarMutexGuard g;
573 QtInstance& rQtInstance = GetQtInstance();
574 if (!rQtInstance.IsMainThread())
576 OUString ret;
577 rQtInstance.RunInMainThread([&ret, this, controlId]() { ret = getLabel(controlId); });
578 return ret;
581 QString label;
582 if (m_aCustomWidgetsMap.contains(controlId))
584 QCheckBox* cb = qobject_cast<QCheckBox*>(m_aCustomWidgetsMap.value(controlId));
585 if (cb)
586 label = cb->text();
588 else
589 SAL_WARN("vcl.qt", "get label on unknown control " << controlId);
591 return toOUString(label);
594 QString QtFilePicker::getResString(TranslateId pResId)
596 QString aResString;
598 if (!pResId)
599 return aResString;
601 aResString = toQString(FpsResId(pResId));
603 return aResString.replace('~', '&');
606 void QtFilePicker::addCustomControl(sal_Int16 controlId)
608 QWidget* widget = nullptr;
609 QLabel* label = nullptr;
610 TranslateId resId;
611 QCheckBox* pCheckbox = nullptr;
613 switch (controlId)
615 case CHECKBOX_AUTOEXTENSION:
616 resId = STR_SVT_FILEPICKER_AUTO_EXTENSION;
617 break;
618 case CHECKBOX_PASSWORD:
619 resId = STR_SVT_FILEPICKER_PASSWORD;
620 break;
621 case CHECKBOX_FILTEROPTIONS:
622 resId = STR_SVT_FILEPICKER_FILTER_OPTIONS;
623 break;
624 case CHECKBOX_READONLY:
625 resId = STR_SVT_FILEPICKER_READONLY;
626 break;
627 case CHECKBOX_LINK:
628 resId = STR_SVT_FILEPICKER_INSERT_AS_LINK;
629 break;
630 case CHECKBOX_PREVIEW:
631 resId = STR_SVT_FILEPICKER_SHOW_PREVIEW;
632 break;
633 case CHECKBOX_SELECTION:
634 resId = STR_SVT_FILEPICKER_SELECTION;
635 break;
636 case CHECKBOX_GPGENCRYPTION:
637 resId = STR_SVT_FILEPICKER_GPGENCRYPT;
638 break;
639 case CHECKBOX_GPGSIGN:
640 resId = STR_SVT_FILEPICKER_GPGSIGN;
641 break;
642 case PUSHBUTTON_PLAY:
643 resId = STR_SVT_FILEPICKER_PLAY;
644 break;
645 case LISTBOX_VERSION:
646 resId = STR_SVT_FILEPICKER_VERSION;
647 break;
648 case LISTBOX_TEMPLATE:
649 resId = STR_SVT_FILEPICKER_TEMPLATES;
650 break;
651 case LISTBOX_IMAGE_TEMPLATE:
652 resId = STR_SVT_FILEPICKER_IMAGE_TEMPLATE;
653 break;
654 case LISTBOX_IMAGE_ANCHOR:
655 resId = STR_SVT_FILEPICKER_IMAGE_ANCHOR;
656 break;
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:
662 break;
665 switch (controlId)
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);
673 #else
674 connect(pCheckbox, &QCheckBox::stateChanged, this,
675 &QtFilePicker::updateAutomaticFileExtension);
676 #endif
677 widget = pCheckbox;
678 break;
679 case CHECKBOX_PASSWORD:
680 case CHECKBOX_FILTEROPTIONS:
681 case CHECKBOX_READONLY:
682 case CHECKBOX_LINK:
683 case CHECKBOX_PREVIEW:
684 case CHECKBOX_SELECTION:
685 case CHECKBOX_GPGENCRYPTION:
686 case CHECKBOX_GPGSIGN:
687 widget = new QCheckBox(getResString(resId), m_pExtraControls);
688 break;
689 case PUSHBUTTON_PLAY:
690 break;
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);
699 break;
700 case LISTBOX_VERSION_LABEL:
701 case LISTBOX_TEMPLATE_LABEL:
702 case LISTBOX_IMAGE_TEMPLATE_LABEL:
703 case LISTBOX_IMAGE_ANCHOR_LABEL:
704 break;
707 if (widget)
709 const int row = m_pLayout->rowCount();
710 if (label)
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
720 uno::Any arg;
721 if (args.getLength() == 0)
722 throw lang::IllegalArgumentException(u"no arguments"_ustr, static_cast<XFilePicker2*>(this),
725 arg = args[0];
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);
734 SolarMutexGuard g;
735 QtInstance& rQtInstance = GetQtInstance();
736 if (!rQtInstance.IsMainThread())
738 rQtInstance.RunInMainThread([this, args]() { initialize(args); });
739 return;
742 m_aNamedFilterToExtensionMap.clear();
743 m_aNamedFilterList.clear();
744 m_aTitleToFilterMap.clear();
745 m_aCurrentFilter.clear();
747 sal_Int16 templateId = -1;
748 arg >>= templateId;
750 QFileDialog::AcceptMode acceptMode = QFileDialog::AcceptOpen;
751 switch (templateId)
753 case FILEOPEN_SIMPLE:
754 break;
756 case FILESAVE_SIMPLE:
757 acceptMode = QFileDialog::AcceptSave;
758 break;
760 case FILESAVE_AUTOEXTENSION:
761 acceptMode = QFileDialog::AcceptSave;
762 addCustomControl(CHECKBOX_AUTOEXTENSION);
763 break;
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);
772 #endif
773 break;
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);
782 #endif
783 addCustomControl(CHECKBOX_FILTEROPTIONS);
784 break;
786 case FILESAVE_AUTOEXTENSION_SELECTION:
787 acceptMode = QFileDialog::AcceptSave;
788 addCustomControl(CHECKBOX_AUTOEXTENSION);
789 addCustomControl(CHECKBOX_SELECTION);
790 break;
792 case FILESAVE_AUTOEXTENSION_TEMPLATE:
793 acceptMode = QFileDialog::AcceptSave;
794 addCustomControl(CHECKBOX_AUTOEXTENSION);
795 addCustomControl(LISTBOX_TEMPLATE);
796 break;
798 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
799 addCustomControl(CHECKBOX_LINK);
800 addCustomControl(CHECKBOX_PREVIEW);
801 addCustomControl(LISTBOX_IMAGE_TEMPLATE);
802 break;
804 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
805 addCustomControl(CHECKBOX_LINK);
806 addCustomControl(CHECKBOX_PREVIEW);
807 addCustomControl(LISTBOX_IMAGE_ANCHOR);
808 break;
810 case FILEOPEN_PLAY:
811 addCustomControl(PUSHBUTTON_PLAY);
812 break;
814 case FILEOPEN_LINK_PLAY:
815 addCustomControl(CHECKBOX_LINK);
816 addCustomControl(PUSHBUTTON_PLAY);
817 break;
819 case FILEOPEN_READONLY_VERSION:
820 addCustomControl(CHECKBOX_READONLY);
821 addCustomControl(LISTBOX_VERSION);
822 break;
824 case FILEOPEN_LINK_PREVIEW:
825 addCustomControl(CHECKBOX_LINK);
826 addCustomControl(CHECKBOX_PREVIEW);
827 break;
829 case FILEOPEN_PREVIEW:
830 addCustomControl(CHECKBOX_PREVIEW);
831 break;
833 default:
834 throw lang::IllegalArgumentException(u"Unknown template"_ustr,
835 static_cast<XFilePicker2*>(this), 1);
838 TranslateId resId;
839 switch (acceptMode)
841 case QFileDialog::AcceptOpen:
842 resId = STR_FILEDLG_OPEN;
843 break;
844 case QFileDialog::AcceptSave:
845 resId = STR_FILEDLG_SAVE;
846 m_pFileDialog->setFileMode(QFileDialog::AnyFile);
847 break;
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())
857 return;
859 css::uno::Reference<css::awt::XSystemDependentWindowPeer> xSysWinPeer(xParentWindow,
860 css::uno::UNO_QUERY);
861 if (!xSysWinPeer.is())
862 return;
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()));
867 uno::Any aAny
868 = xSysWinPeer->getWindowHandle(aProcessIdent, css::lang::SystemDependent::SYSTEM_XWINDOW);
869 css::awt::SystemDependentXWindow xSysWin;
870 aAny >>= xSysWin;
872 const auto& pFrames = rQtInstance.getFrames();
873 const tools::Long aWindowHandle = xSysWin.WindowHandle;
874 const auto it
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);
934 else
936 // fall back to setting none otherwise
937 SAL_INFO(
938 "vcl.qt",
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)
969 seq.realloc(1);
970 return seq[0];
973 void QtFilePicker::setDescription(const OUString&) {}
975 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */