diff --git a/CMakeLists.txt b/CMakeLists.txt index b28c68c..6fa3b02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,91 +1,90 @@ cmake_minimum_required(VERSION 2.8.12) project(PlasmaIntegration) set(PROJECT_VERSION "5.10.2") set(PROJECT_VERSION_MAJOR 5) include(FeatureSummary) find_package(ECM 5.17.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(GenerateExportHeader) include(ECMPackageConfigHelpers) include(ECMSetupVersion) include(ECMGenerateHeaders) include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) set(REQUIRED_QT_VERSION 5.5.0) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Widgets DBus X11Extras) -set(KF5_VERSION "5.18.0") # handled by release scripts -set(KF5_DEP_VERSION "5.17.0") # handled by release scripts +set(KF5_DEP_VERSION "5.33.0") find_package(KF5Config ${KF5_DEP_VERSION} REQUIRED) find_package(KF5ConfigWidgets ${KF5_DEP_VERSION} REQUIRED) find_package(KF5I18n ${KF5_DEP_VERSION} REQUIRED) find_package(KF5IconThemes ${KF5_DEP_VERSION} REQUIRED) find_package(KF5KIO ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Notifications ${KF5_DEP_VERSION} REQUIRED) find_package(KF5WidgetsAddons ${KF5_DEP_VERSION} REQUIRED) find_package(KF5WindowSystem ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Wayland 5.5 CONFIG REQUIRED) find_package(XCB COMPONENTS XCB) set_package_properties(XCB PROPERTIES DESCRIPTION "X protocol C-language Binding" URL "http://xcb.freedesktop.org" TYPE REQUIRED PURPOSE "Required to pass style properties to native Windows on X11 Platform" ) find_package(Breeze ${PROJECT_VERSION} CONFIG) set_package_properties(Breeze PROPERTIES TYPE REQUIRED PURPOSE "For setting the default QStyle name") # dependencies for QPA plugin if(Qt5Core_VERSION VERSION_LESS "5.8.0") find_package(Qt5PlatformSupport REQUIRED) else() find_package(Qt5ThemeSupport REQUIRED) endif() if(Qt5Core_VERSION VERSION_LESS "5.8.0") add_definitions(-DQ_FONTCONFIGDATABASE) set(QT5PLATFORMSUPPORT_LIBS Qt5PlatformSupport::Qt5PlatformSupport) else() set(QT5PLATFORMSUPPORT_LIBS Qt5ThemeSupport::Qt5ThemeSupport ) endif() add_definitions(-DTRANSLATION_DOMAIN=\"plasmaintegration5\") if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install(po) endif() add_subdirectory(src) add_subdirectory(autotests) add_subdirectory(tests) find_package(FontNotoSans) set_package_properties(FontNotoSans PROPERTIES PURPOSE "Default sans-serif font -- this is not detected automatically, pass -DCMAKE_DISABLE_FIND_PACKAGE_FontNotoSans=true to mark it ignored." URL "https://www.google.com/get/noto/" TYPE RUNTIME ) find_package(FontHack) set_package_properties(FontHack PROPERTIES PURPOSE "Default monospace font -- this is not detected automatically, pass -DCMAKE_DISABLE_FIND_PACKAGE_FontHack=true to mark it ignored." URL "http://sourcefoundry.org/hack/" TYPE RUNTIME ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/autotests/kfiledialog_unittest.cpp b/autotests/kfiledialog_unittest.cpp index e0cda9e..ea82570 100644 --- a/autotests/kfiledialog_unittest.cpp +++ b/autotests/kfiledialog_unittest.cpp @@ -1,403 +1,421 @@ /* This file is part of the KDE libraries * Copyright 2014 Dominik Haumann * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QFileDialog::ViewMode) Q_DECLARE_METATYPE(QFileDialog::FileMode) Q_DECLARE_METATYPE(KFile::FileView) Q_DECLARE_METATYPE(KFile::Modes) class KFileDialog_UnitTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { qputenv("KDE_FORK_SLAVES", "yes"); } void init() { } void cleanupTestCase() { } void testSetNameFilters() { QFileDialog dialog; QStringList nameFilterList = QStringList() << QStringLiteral("c (*.cpp)") << QStringLiteral("h (*.h)"); dialog.setNameFilters(nameFilterList); QCOMPARE(dialog.nameFilters(), nameFilterList); + + dialog.show(); + KFileWidget *fw = findFileWidget(); + QVERIFY(fw); + QCOMPARE(fw->currentFilter(), QStringLiteral("*.cpp")); + } + + void testSetNameFilterNoParenthesis() + { + QFileDialog dialog; + + QStringList nameFilterList = QStringList() << QStringLiteral("*.cpp") << QStringLiteral("*.h"); + dialog.setNameFilters(nameFilterList); + QCOMPARE(dialog.nameFilters(), nameFilterList); + dialog.show(); + KFileWidget *fw = findFileWidget(); + QVERIFY(fw); + QCOMPARE(fw->currentFilter(), QStringLiteral("*.cpp")); } void testSelectNameFilter() { QFileDialog dialog; QStringList nameFilterList = QStringList() << QStringLiteral("c (*.cpp)") << QStringLiteral("h (*.h)"); dialog.setNameFilters(nameFilterList); QCOMPARE(dialog.nameFilters(), nameFilterList); QString selectNameFilter(QStringLiteral("h (*.h)")); dialog.selectNameFilter(selectNameFilter); QEXPECT_FAIL("", "Does currently not work. Works, once the dialog gets shown, though.", Continue); QCOMPARE(dialog.selectedNameFilter(), selectNameFilter); dialog.show(); QCOMPARE(dialog.selectedNameFilter(), selectNameFilter); } #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) void testSelectedMimeTypeFilter_data() { QTest::addColumn("mimeTypeFilters"); QTest::addColumn("targetMimeTypeFilter"); const auto headerMime = QStringLiteral("text/x-chdr"); const auto jsonMime = QStringLiteral("application/json"); const auto zipMime = QStringLiteral("application/zip"); QTest::newRow("single mime filter (C header file)") << QStringList {headerMime} << headerMime; QTest::newRow("single mime filter (JSON file)") << QStringList {jsonMime} << jsonMime; QTest::newRow("multiple mime filters") << QStringList {jsonMime, zipMime} << jsonMime; } void testSelectedMimeTypeFilter() { QFileDialog dialog; QFETCH(QStringList, mimeTypeFilters); dialog.setMimeTypeFilters(mimeTypeFilters); QFETCH(QString, targetMimeTypeFilter); dialog.selectMimeTypeFilter(targetMimeTypeFilter); dialog.show(); QCOMPARE(dialog.selectedMimeTypeFilter(), targetMimeTypeFilter); } void testFallbackOnFirstFilterInSaveMode() { QFileDialog dialog; dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setMimeTypeFilters({QStringLiteral("application/json"), QStringLiteral("application/zip")}); dialog.show(); QCOMPARE(dialog.selectedMimeTypeFilter(), QStringLiteral("application/json")); } #endif void testSetDirectory() { QFileDialog dialog; dialog.setDirectory(QDir::rootPath()); QCOMPARE(dialog.directory().absolutePath(), QDir::rootPath()); } void testSelectUrl() { QTemporaryFile tempFile(m_tempDir.path()+"/kfiledialogtest_XXXXXX"); tempFile.setAutoRemove(true); tempFile.open(); QString tempName = tempFile.fileName(); QUrl url = QUrl::fromLocalFile(tempName); int idx = tempName.lastIndexOf('/'); QUrl directoryUrl = QUrl::fromLocalFile(tempName.left(idx+1)); QFileDialog dialog; dialog.selectUrl(url); dialog.show(); // check if dialog was set to base directory url of the passed file url QCOMPARE(dialog.directoryUrl(), directoryUrl); } void testGetSaveFileUrl() { QObject lambdaGuard; QTemporaryFile tempFile(m_tempDir.path()+"/kfiledialogtest_XXXXXX"); tempFile.open(); const QString tempName = tempFile.fileName(); const QUrl url = QUrl::fromLocalFile(tempName); // Need to use a lambda and not just QTest::qWaitForWindowExposed(); // because with the static getSaveFileUrl we do not have access // to the QFileDialog object, so instead we hook to a signal KFileWidget::OperationMode saveFileOperationMode = KFileWidget::Other; connect(qApp, &QGuiApplication::focusWindowChanged, &lambdaGuard, [&saveFileOperationMode] { KFileWidget *fileWidget = findFileWidget(); saveFileOperationMode = fileWidget->operationMode(); qApp->activeWindow()->close(); }); QFileDialog::getSaveFileUrl(0, QString(), url); QCOMPARE(saveFileOperationMode, KFileWidget::Saving); } void testViewMode() { // Open a file dialog, and change view mode to tree { QFileDialog dialog; dialog.show(); KFileWidget *fw = findFileWidget(); QVERIFY(fw); fw->setViewMode(KFile::Tree); fw->slotCancel(); // the saving happens there } // Open another one, and check that the view mode is now tree, change it to simple { QFileDialog dialog; dialog.show(); KFileWidget *fw = findFileWidget(); QVERIFY(fw); // real show() is delayed to next event. QTest::qWaitForWindowExposed(fw->window()); KDirOperator *op = fw->dirOperator(); QCOMPARE(fileViewToString(op->viewMode()), fileViewToString(KFile::Tree)); fw->setViewMode(KFile::Simple); fw->slotCancel(); } // Open another one, and check that the view mode is now simple { QFileDialog dialog; dialog.show(); KFileWidget *fw = findFileWidget(); QVERIFY(fw); KDirOperator *op = fw->dirOperator(); QCOMPARE(fileViewToString(op->viewMode()), fileViewToString(KFile::Simple)); fw->setViewMode(KFile::Detail); fw->slotCancel(); } } void testOpenDialog() { // Open parentless { QFileDialog dialog; dialog.open(); KFileWidget *fw = findFileWidget(); QVERIFY(fw); // real show() is delayed to next event. QTest::qWaitForWindowExposed(fw->window()); QCOMPARE(fw->isVisible(), true); fw->slotCancel(); } // Open with parent { QWidget w; w.show(); QFileDialog dialog(&w); dialog.open(); KFileWidget *fw = findFileWidget(); QVERIFY(fw); // real show() is delayed to next event. QTest::qWaitForWindowExposed(fw->window()); QCOMPARE(fw->isVisible(), true); fw->slotCancel(); } } void testShowDialog() { // Show parentless { QFileDialog dialog; dialog.show(); KFileWidget *fw = findFileWidget(); QVERIFY(fw); // real show() is delayed to next event. QTest::qWaitForWindowExposed(fw->window()); QCOMPARE(fw->isVisible(), true); fw->slotCancel(); } // Show with parent { QWidget w; w.show(); QFileDialog dialog(&w); dialog.show(); KFileWidget *fw = findFileWidget(); QVERIFY(fw); // real show() is delayed to next event. QTest::qWaitForWindowExposed(fw->window()); QCOMPARE(fw->isVisible(), true); fw->slotCancel(); } } void testSetFileMode_data() { QTest::addColumn("qtFileMode"); QTest::addColumn("kdeFileMode"); QTest::newRow("anyfile") << QFileDialog::AnyFile << KFile::Modes(KFile::File); QTest::newRow("existingfile") << QFileDialog::ExistingFile << KFile::Modes(KFile::File | KFile::ExistingOnly); QTest::newRow("directory") << QFileDialog::Directory << KFile::Modes(KFile::Directory | KFile::ExistingOnly); QTest::newRow("existingfiles") << QFileDialog::ExistingFiles << KFile::Modes(KFile::Files | KFile::ExistingOnly); } void testSetFileMode() { QFETCH(QFileDialog::FileMode, qtFileMode); QFETCH(KFile::Modes, kdeFileMode); QFileDialog dialog; dialog.setFileMode(qtFileMode); dialog.show(); KFileWidget *fw = findFileWidget(); QVERIFY(fw); QCOMPARE(fw->mode(), kdeFileMode); QCOMPARE(dialog.fileMode(), qtFileMode); } void testSaveOverwrite_data() { QTest::addColumn("qtOverwriteOption"); QTest::addColumn("messageBoxExpected"); QTest::newRow("checkoverwrite") << false << true; QTest::newRow("allowoverwrite") << true << false; } void testSaveOverwrite() { QFETCH(bool, qtOverwriteOption); QFETCH(bool, messageBoxExpected); QTemporaryFile tempFile(m_tempDir.path()+"/kfiledialogtest_XXXXXX"); tempFile.setAutoRemove(true); tempFile.open(); QString tempName = tempFile.fileName(); tempFile.close(); int idx = tempName.lastIndexOf('/'); QFileDialog dialog; dialog.setAcceptMode(QFileDialog::AcceptSave); if (qtOverwriteOption) dialog.setOption(QFileDialog::DontConfirmOverwrite); dialog.setDirectory(tempName.left(idx+1)); dialog.selectFile(tempName.mid(idx+1)); dialog.open(); KFileWidget *fw = findFileWidget(); QVERIFY(fw); QTest::qWaitForWindowExposed(fw->window()); QCOMPARE(fw->isVisible(), true); bool timerRun = false; QTimer::singleShot(3500, this, [&] { timerRun = true; QDialog *msgbox = findMessageBox(); if (msgbox) { QTest::qWaitForWindowExposed(msgbox); QCOMPARE(msgbox->isVisible(), true); msgbox->close(); QVERIFY(messageBoxExpected); } else { QVERIFY(!messageBoxExpected); } }); fw->slotOk(); QTRY_VERIFY(timerRun); } private: QTemporaryDir m_tempDir; static QString fileViewToString(KFile::FileView fv) { switch (fv) { case KFile::Detail: return QStringLiteral("Detail"); case KFile::Simple: return QStringLiteral("Simple"); case KFile::Tree: return QStringLiteral("Tree"); case KFile::DetailTree: return QStringLiteral("DetailTree"); default: break; } return QStringLiteral("ERROR"); } static KFileWidget *findFileWidget() { QList widgets; foreach (QWidget *widget, QApplication::topLevelWidgets()) { KFileWidget *fw = widget->findChild(); if (fw) { widgets.append(fw); } } Q_ASSERT(widgets.count() == 1); return (widgets.count() == 1) ? widgets.first() : Q_NULLPTR; } static QDialog *findMessageBox() { QList widgets; foreach (QWidget *widget, QApplication::topLevelWidgets()) { QDialog *dlg = widget->findChild(); if (dlg) { widgets.append(dlg); } } return (widgets.count() == 1) ? widgets.first() : Q_NULLPTR; } }; QTEST_MAIN(KFileDialog_UnitTest) #include "kfiledialog_unittest.moc" diff --git a/src/platformtheme/kdeplatformfiledialoghelper.cpp b/src/platformtheme/kdeplatformfiledialoghelper.cpp index 6da28ea..22020ec 100644 --- a/src/platformtheme/kdeplatformfiledialoghelper.cpp +++ b/src/platformtheme/kdeplatformfiledialoghelper.cpp @@ -1,444 +1,446 @@ /* This file is part of the KDE libraries * Copyright 2013 Aleix Pol Gonzalez * Copyright 2014 Martin Klapetek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kdeplatformfiledialoghelper.h" #include "kdeplatformfiledialogbase_p.h" #include "kdirselectdialog_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { /* * Map a Qt filter string into a KDE one. */ static QString qt2KdeFilter(const QStringList &f) { QString filter; QTextStream str(&filter, QIODevice::WriteOnly); QStringList list(f); list.replaceInStrings(QStringLiteral("/"), QStringLiteral("\\/")); QStringList::const_iterator it(list.constBegin()), end(list.constEnd()); bool first = true; for (; it != end; ++it) { int ob = it->lastIndexOf(QLatin1Char('(')), cb = it->lastIndexOf(QLatin1Char(')')); + if (first) { + first = false; + } else { + str << '\n'; + } if (-1 != cb && ob < cb) { - if (first) { - first = false; - } else { - str << '\n'; - } str << it->mid(ob + 1, (cb - ob) - 1) << '|' << it->mid(0, ob); + } else { + str << (*it); } } return filter; } /* * Map a KDE filter string into a Qt one. */ static QString kde2QtFilter(const QStringList &list, const QString &kde) { QStringList::const_iterator it(list.constBegin()), end(list.constEnd()); int pos; for (; it != end; ++it) { if (-1 != (pos = it->indexOf(kde)) && pos > 0 && (QLatin1Char('(') == (*it)[pos - 1] || QLatin1Char(' ') == (*it)[pos - 1]) && it->length() >= kde.length() + pos && (QLatin1Char(')') == (*it)[pos + kde.length()] || QLatin1Char(' ') == (*it)[pos + kde.length()])) { return *it; } } return QString(); } } KDEPlatformFileDialog::KDEPlatformFileDialog() : KDEPlatformFileDialogBase() , m_fileWidget(new KFileWidget(QUrl(), this)) { setLayout(new QVBoxLayout); connect(m_fileWidget, SIGNAL(filterChanged(QString)), SIGNAL(filterSelected(QString))); layout()->addWidget(m_fileWidget); m_buttons = new QDialogButtonBox(this); m_buttons->addButton(m_fileWidget->okButton(), QDialogButtonBox::AcceptRole); m_buttons->addButton(m_fileWidget->cancelButton(), QDialogButtonBox::RejectRole); connect(m_buttons, SIGNAL(rejected()), m_fileWidget, SLOT(slotCancel())); connect(m_fileWidget->okButton(), SIGNAL(clicked(bool)), m_fileWidget, SLOT(slotOk())); connect(m_fileWidget, SIGNAL(accepted()), m_fileWidget, SLOT(accept())); connect(m_fileWidget, SIGNAL(accepted()), SLOT(accept())); connect(m_fileWidget->cancelButton(), SIGNAL(clicked(bool)), SLOT(reject())); layout()->addWidget(m_buttons); } QUrl KDEPlatformFileDialog::directory() { return m_fileWidget->baseUrl(); } QList KDEPlatformFileDialog::selectedFiles() { return m_fileWidget->selectedUrls(); } void KDEPlatformFileDialog::selectFile(const QUrl &filename) { QUrl dirUrl = filename.adjusted(QUrl::RemoveFilename); m_fileWidget->setUrl(dirUrl); - m_fileWidget->setSelection(filename.toString()); + m_fileWidget->setSelectedUrl(filename); } void KDEPlatformFileDialog::setViewMode(QFileDialogOptions::ViewMode view) { switch (view) { case QFileDialogOptions::ViewMode::Detail: m_fileWidget->setViewMode(KFile::FileView::Detail); break; case QFileDialogOptions::ViewMode::List: m_fileWidget->setViewMode(KFile::FileView::Simple); break; default: m_fileWidget->setViewMode(KFile::FileView::Default); break; } } void KDEPlatformFileDialog::setFileMode(QFileDialogOptions::FileMode mode) { switch (mode) { case QFileDialogOptions::FileMode::AnyFile: m_fileWidget->setMode(KFile::File); break; case QFileDialogOptions::FileMode::ExistingFile: m_fileWidget->setMode(KFile::Mode::File | KFile::Mode::ExistingOnly); break; case QFileDialogOptions::FileMode::Directory: m_fileWidget->setMode(KFile::Mode::Directory | KFile::Mode::ExistingOnly); break; case QFileDialogOptions::FileMode::ExistingFiles: m_fileWidget->setMode(KFile::Mode::Files | KFile::Mode::ExistingOnly); break; default: m_fileWidget->setMode(KFile::File); break; } } void KDEPlatformFileDialog::setCustomLabel(QFileDialogOptions::DialogLabel label, const QString &text) { if (label == QFileDialogOptions::Accept) { // OK button m_fileWidget->okButton()->setText(text); } else if (label == QFileDialogOptions::Reject) { // Cancel button m_fileWidget->cancelButton()->setText(text); } else if (label == QFileDialogOptions::LookIn) { // Location label m_fileWidget->setLocationLabel(text); } } QString KDEPlatformFileDialog::selectedMimeTypeFilter() { if (m_fileWidget->filterWidget()->isMimeFilter()) { const auto mimeTypeFromFilter = QMimeDatabase().mimeTypeForName(m_fileWidget->filterWidget()->currentFilter()); // If one does not call selectMimeTypeFilter(), KFileFilterCombo::currentFilter() returns invalid mimeTypes, // such as "application/json application/zip". if (mimeTypeFromFilter.isValid()) { return mimeTypeFromFilter.name(); } } if (selectedFiles().isEmpty()) { return QString(); } // Works for both KFile::File and KFile::Files modes. return QMimeDatabase().mimeTypeForUrl(selectedFiles().at(0)).name(); } QString KDEPlatformFileDialog::selectedNameFilter() { return m_fileWidget->filterWidget()->currentFilter(); } void KDEPlatformFileDialog::selectMimeTypeFilter(const QString &filter) { m_fileWidget->filterWidget()->setCurrentFilter(filter); } void KDEPlatformFileDialog::selectNameFilter(const QString &filter) { m_fileWidget->filterWidget()->setCurrentFilter(filter); } void KDEPlatformFileDialog::setDirectory(const QUrl &directory) { if (!directory.isLocalFile()) { // Qt can not determine if the remote URL points to a file or a // directory, that is why options()->initialDirectory() always returns // the full URL. KIO::StatJob *job = KIO::stat(directory); KJobWidgets::setWindow(job, this); if (job->exec()) { KIO::UDSEntry entry = job->statResult(); if (!entry.isDir()) { // this is probably a file remove the file part m_fileWidget->setUrl(directory.adjusted(QUrl::RemoveFilename)); - m_fileWidget->setSelection(directory.fileName()); + m_fileWidget->setSelectedUrl(directory); } else { m_fileWidget->setUrl(directory); } } } else { m_fileWidget->setUrl(directory); } } bool KDEPlatformFileDialogHelper::isSupportedUrl(const QUrl& url) const { return KProtocolInfo::protocols().contains(url.scheme()); } //////////////////////////////////////////////// KDEPlatformFileDialogHelper::KDEPlatformFileDialogHelper() : QPlatformFileDialogHelper() , m_dialog(new KDEPlatformFileDialog) { connect(m_dialog, SIGNAL(closed()), SLOT(saveSize())); connect(m_dialog, SIGNAL(finished(int)), SLOT(saveSize())); connect(m_dialog, SIGNAL(currentChanged(QUrl)), SIGNAL(currentChanged(QUrl))); connect(m_dialog, SIGNAL(directoryEntered(QUrl)), SIGNAL(directoryEntered(QUrl))); connect(m_dialog, SIGNAL(fileSelected(QUrl)), SIGNAL(fileSelected(QUrl))); connect(m_dialog, SIGNAL(filesSelected(QList)), SIGNAL(filesSelected(QList))); connect(m_dialog, SIGNAL(filterSelected(QString)), SIGNAL(filterSelected(QString))); connect(m_dialog, SIGNAL(accepted()), SIGNAL(accept())); connect(m_dialog, SIGNAL(rejected()), SIGNAL(reject())); } KDEPlatformFileDialogHelper::~KDEPlatformFileDialogHelper() { saveSize(); delete m_dialog; } void KDEPlatformFileDialogHelper::initializeDialog() { if (options()->testOption(QFileDialogOptions::ShowDirsOnly)) { m_dialog->deleteLater(); m_dialog = new KDirSelectDialog(options()->initialDirectory()); connect(m_dialog, SIGNAL(accepted()), SIGNAL(accept())); connect(m_dialog, SIGNAL(rejected()), SIGNAL(reject())); if (!options()->windowTitle().isEmpty()) m_dialog->setWindowTitle(options()->windowTitle()); } else { // needed for accessing m_fileWidget KDEPlatformFileDialog *dialog = qobject_cast(m_dialog); dialog->m_fileWidget->setOperationMode(options()->acceptMode() == QFileDialogOptions::AcceptOpen ? KFileWidget::Opening : KFileWidget::Saving); if (options()->windowTitle().isEmpty()) { dialog->setWindowTitle(options()->acceptMode() == QFileDialogOptions::AcceptOpen ? i18nc("@title:window", "Open File") : i18nc("@title:window", "Save File")); } else { dialog->setWindowTitle(options()->windowTitle()); } setDirectory(options()->initialDirectory()); //dialog->setViewMode(options()->viewMode()); // don't override our options, fixes remembering the chosen view mode and sizes! dialog->setFileMode(options()->fileMode()); // custom labels if (options()->isLabelExplicitlySet(QFileDialogOptions::Accept)) { // OK button dialog->setCustomLabel(QFileDialogOptions::Accept, options()->labelText(QFileDialogOptions::Accept)); } else if (options()->isLabelExplicitlySet(QFileDialogOptions::Reject)) { // Cancel button dialog->setCustomLabel(QFileDialogOptions::Reject, options()->labelText(QFileDialogOptions::Reject)); } else if (options()->isLabelExplicitlySet(QFileDialogOptions::LookIn)) { // Location label dialog->setCustomLabel(QFileDialogOptions::LookIn, options()->labelText(QFileDialogOptions::LookIn)); } const QStringList mimeFilters = options()->mimeTypeFilters(); const QStringList nameFilters = options()->nameFilters(); if (!mimeFilters.isEmpty()) { QString defaultMimeFilter; if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) defaultMimeFilter = options()->initiallySelectedMimeTypeFilter(); #endif if (defaultMimeFilter.isEmpty()) { defaultMimeFilter = mimeFilters.at(0); } } dialog->m_fileWidget->setMimeFilter(mimeFilters, defaultMimeFilter); if ( mimeFilters.contains( QStringLiteral("inode/directory") ) ) dialog->m_fileWidget->setMode( dialog->m_fileWidget->mode() | KFile::Directory ); } else if (!nameFilters.isEmpty()) { dialog->m_fileWidget->setFilter(qt2KdeFilter(nameFilters)); } #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) if (!options()->initiallySelectedMimeTypeFilter().isEmpty()) { selectMimeTypeFilter(options()->initiallySelectedMimeTypeFilter()); } else if (!options()->initiallySelectedNameFilter().isEmpty()) { selectNameFilter(options()->initiallySelectedNameFilter()); } #else if (!options()->initiallySelectedNameFilter().isEmpty()) { selectNameFilter(options()->initiallySelectedNameFilter()); } #endif // overwrite option if (options()->testOption(QFileDialogOptions::FileDialogOption::DontConfirmOverwrite)) { dialog->m_fileWidget->setConfirmOverwrite(false); } else if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { dialog->m_fileWidget->setConfirmOverwrite(true); } } } void KDEPlatformFileDialogHelper::exec() { restoreSize(); // KDEPlatformFileDialog::show() will always be called during QFileDialog::exec() // discard the delayed show() it and use exec() and it will call show() for us. // We can't hide and show it here because of https://bugreports.qt.io/browse/QTBUG-48248 m_dialog->discardDelayedShow(); m_dialog->exec(); } void KDEPlatformFileDialogHelper::hide() { m_dialog->discardDelayedShow(); m_dialog->hide(); } void KDEPlatformFileDialogHelper::saveSize() { KSharedConfig::Ptr conf = KSharedConfig::openConfig(); KConfigGroup group = conf->group("FileDialogSize"); KWindowConfig::saveWindowSize(m_dialog->windowHandle(), group); } void KDEPlatformFileDialogHelper::restoreSize() { m_dialog->winId(); // ensure there's a window created KSharedConfig::Ptr conf = KSharedConfig::openConfig(); KWindowConfig::restoreWindowSize(m_dialog->windowHandle(), conf->group("FileDialogSize")); // NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform // window was created -> QTBUG-40584. We therefore copy the size here. // TODO: remove once this was resolved in QWidget QPA m_dialog->resize(m_dialog->windowHandle()->size()); } bool KDEPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) { initializeDialog(); m_dialog->setWindowFlags(windowFlags); m_dialog->setWindowModality(windowModality); restoreSize(); m_dialog->windowHandle()->setTransientParent(parent); // Use a delayed show here to delay show() after the internal Qt invisible QDialog. // The delayed call shouldn't matter, because for other "real" native QPlatformDialog // implementation like Mac and Windows, the native dialog is not necessarily // show up immediately. m_dialog->delayedShow(); return true; } QList KDEPlatformFileDialogHelper::selectedFiles() const { return m_dialog->selectedFiles(); } #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) QString KDEPlatformFileDialogHelper::selectedMimeTypeFilter() const { return m_dialog->selectedMimeTypeFilter(); } void KDEPlatformFileDialogHelper::selectMimeTypeFilter(const QString &filter) { m_dialog->selectMimeTypeFilter(filter); } #endif QString KDEPlatformFileDialogHelper::selectedNameFilter() const { return kde2QtFilter(options()->nameFilters(), m_dialog->selectedNameFilter()); } QUrl KDEPlatformFileDialogHelper::directory() const { return m_dialog->directory(); } void KDEPlatformFileDialogHelper::selectFile(const QUrl &filename) { m_dialog->selectFile(filename); // Qt 5 at least <= 5.8.0 does not derive the directory from the passed url // and set the initialDirectory option accordingly, also not for known schemes // like file://, so we have to do it ourselves options()->setInitialDirectory(m_dialog->directory()); } void KDEPlatformFileDialogHelper::setDirectory(const QUrl &directory) { if (!directory.isEmpty()) { m_dialog->setDirectory(directory); } } void KDEPlatformFileDialogHelper::selectNameFilter(const QString &filter) { m_dialog->selectNameFilter(qt2KdeFilter(QStringList(filter))); } void KDEPlatformFileDialogHelper::setFilter() { } bool KDEPlatformFileDialogHelper::defaultNameFilterDisables() const { return false; }