calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / vcl / qt5 / QtFilePicker.cxx
blob8a13ec67235c57c566c8b57060bd0b7a3c9b3b28
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <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>
68 #include <utility>
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;
79 namespace
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()
127 SolarMutexGuard g;
128 auto* pSalInst(GetQtInstance());
129 assert(pSalInst);
130 pSalInst->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 auto* pSalInst(GetQtInstance());
154 assert(pSalInst);
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();
165 if (pWindow)
167 QtFrame* pFrame = dynamic_cast<QtFrame*>(pWindow->ImplGetFrame());
168 assert(pFrame);
169 if (pFrame)
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),
182 UNO_QUERY_THROW);
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)
192 SolarMutexGuard g;
193 uno::Reference<css::frame::XDesktop> xDesktop(css::frame::Desktop::create(m_context),
194 UNO_QUERY_THROW);
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()
210 SolarMutexGuard g;
211 auto* pSalInst(GetQtInstance());
212 assert(pSalInst);
213 if (!pSalInst->IsMainThread())
215 sal_uInt16 ret;
216 pSalInst->RunInMainThread([&ret, this]() { ret = execute(); });
217 return ret;
220 prepareExecute();
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); }
231 void SAL_CALL
232 QtFilePicker::startExecuteModal(const Reference<css::ui::dialogs::XDialogClosedListener>& xListener)
234 m_xClosedListener = xListener;
235 prepareExecute();
236 m_pFileDialog->show();
239 void SAL_CALL QtFilePicker::setMultiSelectionMode(sal_Bool multiSelect)
241 SolarMutexGuard g;
242 auto* pSalInst(GetQtInstance());
243 assert(pSalInst);
244 pSalInst->RunInMainThread([this, multiSelect]() {
245 if (m_bIsFolderPicker || m_pFileDialog->acceptMode() == QFileDialog::AcceptSave)
246 return;
248 m_pFileDialog->setFileMode(multiSelect ? QFileDialog::ExistingFiles
249 : QFileDialog::ExistingFile);
253 void SAL_CALL QtFilePicker::setDefaultName(const OUString& name)
255 SolarMutexGuard g;
256 auto* pSalInst(GetQtInstance());
257 assert(pSalInst);
258 pSalInst->RunInMainThread([this, &name]() { m_pFileDialog->selectFile(toQString(name)); });
261 void SAL_CALL QtFilePicker::setDisplayDirectory(const OUString& dir)
263 SolarMutexGuard g;
264 auto* pSalInst(GetQtInstance());
265 assert(pSalInst);
266 pSalInst->RunInMainThread([this, &dir]() {
267 QString qDir(toQString(dir));
268 m_pFileDialog->setDirectoryUrl(QUrl(qDir));
272 OUString SAL_CALL QtFilePicker::getDisplayDirectory()
274 SolarMutexGuard g;
275 OUString ret;
276 auto* pSalInst(GetQtInstance());
277 assert(pSalInst);
278 pSalInst->RunInMainThread(
279 [&ret, this]() { ret = toOUString(m_pFileDialog->directoryUrl().toString()); });
280 return ret;
283 uno::Sequence<OUString> SAL_CALL QtFilePicker::getFiles()
285 uno::Sequence<OUString> seq = getSelectedFiles();
286 if (seq.getLength() > 1)
287 seq.realloc(1);
288 return seq;
291 uno::Sequence<OUString> SAL_CALL QtFilePicker::getSelectedFiles()
293 SolarMutexGuard g;
294 QList<QUrl> urls;
295 auto* pSalInst(GetQtInstance());
296 assert(pSalInst);
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);
303 size_t i = 0;
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");
321 intUrl = extUrl;
323 seqRange[i++] = intUrl;
326 return seq;
329 void SAL_CALL QtFilePicker::appendFilter(const OUString& title, const OUString& filter)
331 SolarMutexGuard g;
332 auto* pSalInst(GetQtInstance());
333 assert(pSalInst);
334 if (!pSalInst->IsMainThread())
336 pSalInst->RunInMainThread([this, &title, &filter]() { appendFilter(title, filter); });
337 return;
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(" (");
348 if (pos >= 0)
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)
367 SolarMutexGuard g;
368 auto* pSalInst(GetQtInstance());
369 assert(pSalInst);
370 pSalInst->RunInMainThread([this, &title]() {
371 m_aCurrentFilter = m_aTitleToFilterMap.value(toQString(title).replace("/", "\\/"));
375 OUString SAL_CALL QtFilePicker::getCurrentFilter()
377 SolarMutexGuard g;
378 QString filter;
379 auto* pSalInst(GetQtInstance());
380 assert(pSalInst);
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)
393 SolarMutexGuard g;
394 auto* pSalInst(GetQtInstance());
395 assert(pSalInst);
396 if (!pSalInst->IsMainThread())
398 pSalInst->RunInMainThread(
399 [this, &rGroupTitle, &filters]() { appendFilterGroup(rGroupTitle, filters); });
400 return;
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)
413 uno::Any aAny;
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));
422 aAny <<= aItemList;
423 break;
425 case ControlActions::GET_SELECTED_ITEM:
427 if (!pWidget->currentText().isEmpty())
428 aAny <<= toOUString(pWidget->currentText());
429 break;
431 case ControlActions::GET_SELECTED_ITEM_INDEX:
433 if (pWidget->currentIndex() >= 0)
434 aAny <<= static_cast<sal_Int32>(pWidget->currentIndex());
435 break;
437 default:
438 SAL_WARN("vcl.qt",
439 "undocumented/unimplemented ControlAction for a list " << nControlAction);
440 break;
442 return aAny;
445 void QtFilePicker::handleSetListValue(QComboBox* pWidget, sal_Int16 nControlAction,
446 const uno::Any& rValue)
448 switch (nControlAction)
450 case ControlActions::ADD_ITEM:
452 OUString sItem;
453 rValue >>= sItem;
454 pWidget->addItem(toQString(sItem));
455 break;
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));
463 break;
465 case ControlActions::DELETE_ITEM:
467 sal_Int32 nPos = 0;
468 rValue >>= nPos;
469 pWidget->removeItem(nPos);
470 break;
472 case ControlActions::DELETE_ITEMS:
474 pWidget->clear();
475 break;
477 case ControlActions::SET_SELECT_ITEM:
479 sal_Int32 nPos = 0;
480 rValue >>= nPos;
481 pWidget->setCurrentIndex(nPos);
482 break;
484 default:
485 SAL_WARN("vcl.qt",
486 "undocumented/unimplemented ControlAction for a list " << nControlAction);
487 break;
490 pWidget->setEnabled(pWidget->count() > 0);
493 void SAL_CALL QtFilePicker::setValue(sal_Int16 controlId, sal_Int16 nControlAction,
494 const uno::Any& value)
496 SolarMutexGuard g;
497 auto* pSalInst(GetQtInstance());
498 assert(pSalInst);
499 if (!pSalInst->IsMainThread())
501 pSalInst->RunInMainThread([this, controlId, nControlAction, &value]() {
502 setValue(controlId, nControlAction, value);
504 return;
507 if (m_aCustomWidgetsMap.contains(controlId))
509 QWidget* widget = m_aCustomWidgetsMap.value(controlId);
510 QCheckBox* cb = dynamic_cast<QCheckBox*>(widget);
511 if (cb)
512 cb->setChecked(value.get<bool>());
513 else
515 QComboBox* combo = dynamic_cast<QComboBox*>(widget);
516 if (combo)
517 handleSetListValue(combo, nControlAction, value);
520 else
521 SAL_WARN("vcl.qt", "set value on unknown control " << controlId);
524 uno::Any SAL_CALL QtFilePicker::getValue(sal_Int16 controlId, sal_Int16 nControlAction)
526 SolarMutexGuard g;
527 auto* pSalInst(GetQtInstance());
528 assert(pSalInst);
529 if (!pSalInst->IsMainThread())
531 uno::Any ret;
532 pSalInst->RunInMainThread([&ret, this, controlId, nControlAction]() {
533 ret = getValue(controlId, nControlAction);
535 return ret;
538 uno::Any res(false);
539 if (m_aCustomWidgetsMap.contains(controlId))
541 QWidget* widget = m_aCustomWidgetsMap.value(controlId);
542 QCheckBox* cb = dynamic_cast<QCheckBox*>(widget);
543 if (cb)
544 res <<= cb->isChecked();
545 else
547 QComboBox* combo = dynamic_cast<QComboBox*>(widget);
548 if (combo)
549 res = handleGetListValue(combo, nControlAction);
552 else
553 SAL_WARN("vcl.qt", "get value on unknown control " << controlId);
555 return res;
558 void SAL_CALL QtFilePicker::enableControl(sal_Int16 controlId, sal_Bool enable)
560 SolarMutexGuard g;
561 auto* pSalInst(GetQtInstance());
562 assert(pSalInst);
563 pSalInst->RunInMainThread([this, controlId, enable]() {
564 if (m_aCustomWidgetsMap.contains(controlId))
565 m_aCustomWidgetsMap.value(controlId)->setEnabled(enable);
566 else
567 SAL_WARN("vcl.qt", "enable unknown control " << controlId);
571 void SAL_CALL QtFilePicker::setLabel(sal_Int16 controlId, const OUString& label)
573 SolarMutexGuard g;
574 auto* pSalInst(GetQtInstance());
575 assert(pSalInst);
576 if (!pSalInst->IsMainThread())
578 pSalInst->RunInMainThread([this, controlId, label]() { setLabel(controlId, label); });
579 return;
582 if (m_aCustomWidgetsMap.contains(controlId))
584 QCheckBox* cb = dynamic_cast<QCheckBox*>(m_aCustomWidgetsMap.value(controlId));
585 if (cb)
586 cb->setText(toQString(label));
588 else
589 SAL_WARN("vcl.qt", "set label on unknown control " << controlId);
592 OUString SAL_CALL QtFilePicker::getLabel(sal_Int16 controlId)
594 SolarMutexGuard g;
595 auto* pSalInst(GetQtInstance());
596 assert(pSalInst);
597 if (!pSalInst->IsMainThread())
599 OUString ret;
600 pSalInst->RunInMainThread([&ret, this, controlId]() { ret = getLabel(controlId); });
601 return ret;
604 QString label;
605 if (m_aCustomWidgetsMap.contains(controlId))
607 QCheckBox* cb = dynamic_cast<QCheckBox*>(m_aCustomWidgetsMap.value(controlId));
608 if (cb)
609 label = cb->text();
611 else
612 SAL_WARN("vcl.qt", "get label on unknown control " << controlId);
614 return toOUString(label);
617 QString QtFilePicker::getResString(TranslateId pResId)
619 QString aResString;
621 if (!pResId)
622 return aResString;
624 aResString = toQString(FpsResId(pResId));
626 return aResString.replace('~', '&');
629 void QtFilePicker::addCustomControl(sal_Int16 controlId)
631 QWidget* widget = nullptr;
632 QLabel* label = nullptr;
633 TranslateId resId;
634 QCheckBox* pCheckbox = nullptr;
636 switch (controlId)
638 case CHECKBOX_AUTOEXTENSION:
639 resId = STR_SVT_FILEPICKER_AUTO_EXTENSION;
640 break;
641 case CHECKBOX_PASSWORD:
642 resId = STR_SVT_FILEPICKER_PASSWORD;
643 break;
644 case CHECKBOX_FILTEROPTIONS:
645 resId = STR_SVT_FILEPICKER_FILTER_OPTIONS;
646 break;
647 case CHECKBOX_READONLY:
648 resId = STR_SVT_FILEPICKER_READONLY;
649 break;
650 case CHECKBOX_LINK:
651 resId = STR_SVT_FILEPICKER_INSERT_AS_LINK;
652 break;
653 case CHECKBOX_PREVIEW:
654 resId = STR_SVT_FILEPICKER_SHOW_PREVIEW;
655 break;
656 case CHECKBOX_SELECTION:
657 resId = STR_SVT_FILEPICKER_SELECTION;
658 break;
659 case CHECKBOX_GPGENCRYPTION:
660 resId = STR_SVT_FILEPICKER_GPGENCRYPT;
661 break;
662 case PUSHBUTTON_PLAY:
663 resId = STR_SVT_FILEPICKER_PLAY;
664 break;
665 case LISTBOX_VERSION:
666 resId = STR_SVT_FILEPICKER_VERSION;
667 break;
668 case LISTBOX_TEMPLATE:
669 resId = STR_SVT_FILEPICKER_TEMPLATES;
670 break;
671 case LISTBOX_IMAGE_TEMPLATE:
672 resId = STR_SVT_FILEPICKER_IMAGE_TEMPLATE;
673 break;
674 case LISTBOX_IMAGE_ANCHOR:
675 resId = STR_SVT_FILEPICKER_IMAGE_ANCHOR;
676 break;
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:
682 break;
685 switch (controlId)
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()));
692 widget = pCheckbox;
693 break;
694 case CHECKBOX_PASSWORD:
695 case CHECKBOX_FILTEROPTIONS:
696 case CHECKBOX_READONLY:
697 case CHECKBOX_LINK:
698 case CHECKBOX_PREVIEW:
699 case CHECKBOX_SELECTION:
700 case CHECKBOX_GPGENCRYPTION:
701 widget = new QCheckBox(getResString(resId), m_pExtraControls);
702 break;
703 case PUSHBUTTON_PLAY:
704 break;
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);
713 break;
714 case LISTBOX_VERSION_LABEL:
715 case LISTBOX_TEMPLATE_LABEL:
716 case LISTBOX_IMAGE_TEMPLATE_LABEL:
717 case LISTBOX_IMAGE_ANCHOR_LABEL:
718 break;
721 if (widget)
723 const int row = m_pLayout->rowCount();
724 if (label)
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
734 uno::Any arg;
735 if (args.getLength() == 0)
736 throw lang::IllegalArgumentException("no arguments", static_cast<XFilePicker2*>(this), 1);
738 arg = args[0];
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);
747 SolarMutexGuard g;
748 auto* pSalInst(GetQtInstance());
749 assert(pSalInst);
750 if (!pSalInst->IsMainThread())
752 pSalInst->RunInMainThread([this, args]() { initialize(args); });
753 return;
756 m_aNamedFilterToExtensionMap.clear();
757 m_aNamedFilterList.clear();
758 m_aTitleToFilterMap.clear();
759 m_aCurrentFilter.clear();
761 sal_Int16 templateId = -1;
762 arg >>= templateId;
764 QFileDialog::AcceptMode acceptMode = QFileDialog::AcceptOpen;
765 switch (templateId)
767 case FILEOPEN_SIMPLE:
768 break;
770 case FILESAVE_SIMPLE:
771 acceptMode = QFileDialog::AcceptSave;
772 break;
774 case FILESAVE_AUTOEXTENSION:
775 acceptMode = QFileDialog::AcceptSave;
776 addCustomControl(CHECKBOX_AUTOEXTENSION);
777 break;
779 case FILESAVE_AUTOEXTENSION_PASSWORD:
780 acceptMode = QFileDialog::AcceptSave;
781 addCustomControl(CHECKBOX_AUTOEXTENSION);
782 addCustomControl(CHECKBOX_PASSWORD);
783 addCustomControl(CHECKBOX_GPGENCRYPTION);
784 break;
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);
792 break;
794 case FILESAVE_AUTOEXTENSION_SELECTION:
795 acceptMode = QFileDialog::AcceptSave;
796 addCustomControl(CHECKBOX_AUTOEXTENSION);
797 addCustomControl(CHECKBOX_SELECTION);
798 break;
800 case FILESAVE_AUTOEXTENSION_TEMPLATE:
801 acceptMode = QFileDialog::AcceptSave;
802 addCustomControl(CHECKBOX_AUTOEXTENSION);
803 addCustomControl(LISTBOX_TEMPLATE);
804 break;
806 case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
807 addCustomControl(CHECKBOX_LINK);
808 addCustomControl(CHECKBOX_PREVIEW);
809 addCustomControl(LISTBOX_IMAGE_TEMPLATE);
810 break;
812 case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
813 addCustomControl(CHECKBOX_LINK);
814 addCustomControl(CHECKBOX_PREVIEW);
815 addCustomControl(LISTBOX_IMAGE_ANCHOR);
816 break;
818 case FILEOPEN_PLAY:
819 addCustomControl(PUSHBUTTON_PLAY);
820 break;
822 case FILEOPEN_LINK_PLAY:
823 addCustomControl(CHECKBOX_LINK);
824 addCustomControl(PUSHBUTTON_PLAY);
825 break;
827 case FILEOPEN_READONLY_VERSION:
828 addCustomControl(CHECKBOX_READONLY);
829 addCustomControl(LISTBOX_VERSION);
830 break;
832 case FILEOPEN_LINK_PREVIEW:
833 addCustomControl(CHECKBOX_LINK);
834 addCustomControl(CHECKBOX_PREVIEW);
835 break;
837 case FILEOPEN_PREVIEW:
838 addCustomControl(CHECKBOX_PREVIEW);
839 break;
841 default:
842 throw lang::IllegalArgumentException("Unknown template",
843 static_cast<XFilePicker2*>(this), 1);
846 TranslateId resId;
847 switch (acceptMode)
849 case QFileDialog::AcceptOpen:
850 resId = STR_FILEDLG_OPEN;
851 break;
852 case QFileDialog::AcceptSave:
853 resId = STR_FILEDLG_SAVE;
854 m_pFileDialog->setFileMode(QFileDialog::AnyFile);
855 break;
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())
865 return;
867 css::uno::Reference<css::awt::XSystemDependentWindowPeer> xSysWinPeer(xParentWindow,
868 css::uno::UNO_QUERY);
869 if (!xSysWinPeer.is())
870 return;
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()));
875 uno::Any aAny
876 = xSysWinPeer->getWindowHandle(aProcessIdent, css::lang::SystemDependent::SYSTEM_XWINDOW);
877 css::awt::SystemDependentXWindow xSysWin;
878 aAny >>= xSysWin;
880 const auto& pFrames = pSalInst->getFrames();
881 const tools::Long aWindowHandle = xSysWin.WindowHandle;
882 const auto it
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);
942 else
944 // fall back to setting none otherwise
945 SAL_INFO(
946 "vcl.qt",
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)
977 seq.realloc(1);
978 return seq[0];
981 void QtFilePicker::setDescription(const OUString&) {}
983 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */