diff --git a/CMakeLists.txt b/CMakeLists.txt index 39317a4..329b594 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,78 +1,79 @@ cmake_minimum_required(VERSION 3.0) project(xdg-desktop-portal-kde) set(PROJECT_VERSION "5.14.80") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.11.0") set(KF5_MIN_VERSION "5.48.0") ################# set KDE specific information ################# find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) find_package(GLIB2) set_package_properties(GLIB2 PROPERTIES TYPE OPTIONAL PURPOSE "Required for screencast portal" ) find_package(PipeWire) set_package_properties(PipeWire PROPERTIES TYPE OPTIONAL PURPOSE "Required for screencast portal" ) find_package(GBM) set_package_properties(GBM PROPERTIES TYPE OPTIONAL PURPOSE "Required for screencast portal" ) find_package(Epoxy) set_package_properties(Epoxy PROPERTIES DESCRIPTION "libepoxy" URL "http://github.com/anholt/libepoxy" TYPE OPTIONAL PURPOSE "Required for screencast portal" ) if (PipeWire_FOUND AND GLIB2_FOUND AND GBM_FOUND AND Epoxy_FOUND) set (SCREENCAST_ENABLED true) else() set (SCREENCAST_ENABLED false) endif() add_definitions(-DSCREENCAST_ENABLED=${SCREENCAST_ENABLED}) add_feature_info ("Screencast portal" ${SCREENCAST_ENABLED} "Support for screen sharing") find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core Concurrent DBus PrintSupport Widgets ) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED CoreAddons Config I18n + KIO Notifications Wayland WidgetsAddons ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0) add_subdirectory(data) add_subdirectory(src) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c59c598..38fd2ce 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,70 +1,71 @@ add_definitions(-DTRANSLATION_DOMAIN="xdg-desktop-portal-kde") include_directories(${Qt5PrintSupport_PRIVATE_INCLUDE_DIRS}) set(xdg_desktop_portal_kde_SRCS xdg-desktop-portal-kde.cpp access.cpp accessdialog.cpp appchooser.cpp appchooserdialog.cpp appchooserdialogitem.cpp desktopportal.cpp email.cpp filechooser.cpp inhibit.cpp notification.cpp print.cpp request.cpp session.cpp screenshot.cpp screenshotdialog.cpp settings.cpp ) if (SCREENCAST_ENABLED) set (xdg_desktop_portal_kde_SRCS ${xdg_desktop_portal_kde_SRCS} screencast.cpp screencaststream.cpp screencastwidget.cpp screenchooserdialog.cpp remotedesktop.cpp remotedesktopdialog.cpp waylandintegration.cpp) ki18n_wrap_ui(xdg_desktop_portal_kde_SRCS screenchooserdialog.ui remotedesktopdialog.ui) endif() ki18n_wrap_ui(xdg_desktop_portal_kde_SRCS accessdialog.ui screenshotdialog.ui ) add_executable(xdg-desktop-portal-kde ${xdg_desktop_portal_kde_SRCS}) target_link_libraries(xdg-desktop-portal-kde Qt5::Core Qt5::DBus Qt5::Concurrent Qt5::PrintSupport Qt5::Widgets KF5::CoreAddons KF5::ConfigCore KF5::I18n + KF5::KIOFileWidgets KF5::Notifications KF5::WaylandClient KF5::WidgetsAddons ) if (SCREENCAST_ENABLED) target_link_libraries(xdg-desktop-portal-kde PipeWire::PipeWire GLIB2::GLIB2 ${Epoxy_LIBRARIES} GBM::GBM) endif() install(TARGETS xdg-desktop-portal-kde DESTINATION ${KDE_INSTALL_LIBEXECDIR}) diff --git a/src/filechooser.cpp b/src/filechooser.cpp index 19f4d42..23c663b 100644 --- a/src/filechooser.cpp +++ b/src/filechooser.cpp @@ -1,290 +1,312 @@ /* - * Copyright © 2016 Red Hat, Inc + * Copyright © 2016-2018 Red Hat, Inc * * This program 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) 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authors: * Jan Grulich */ #include "filechooser.h" +#include #include #include #include -#include +#include +#include +#include + #include +#include Q_LOGGING_CATEGORY(XdgDesktopPortalKdeFileChooser, "xdp-kde-file-chooser") // Keep in sync with qflatpakfiledialog from flatpak-platform-plugin Q_DECLARE_METATYPE(FileChooserPortal::Filter) Q_DECLARE_METATYPE(FileChooserPortal::Filters) Q_DECLARE_METATYPE(FileChooserPortal::FilterList) Q_DECLARE_METATYPE(FileChooserPortal::FilterListList) QDBusArgument &operator << (QDBusArgument &arg, const FileChooserPortal::Filter &filter) { arg.beginStructure(); arg << filter.type << filter.filterString; arg.endStructure(); return arg; } const QDBusArgument &operator >> (const QDBusArgument &arg, FileChooserPortal::Filter &filter) { uint type; QString filterString; arg.beginStructure(); arg >> type >> filterString; filter.type = type; filter.filterString = filterString; arg.endStructure(); return arg; } QDBusArgument &operator << (QDBusArgument &arg, const FileChooserPortal::FilterList &filterList) { arg.beginStructure(); arg << filterList.userVisibleName << filterList.filters; arg.endStructure(); return arg; } const QDBusArgument &operator >> (const QDBusArgument &arg, FileChooserPortal::FilterList &filterList) { QString userVisibleName; FileChooserPortal::Filters filters; arg.beginStructure(); arg >> userVisibleName >> filters; filterList.userVisibleName = userVisibleName; filterList.filters = filters; arg.endStructure(); return arg; } +FileDialog::FileDialog(QDialog *parent, Qt::WindowFlags flags) + : QDialog(parent, flags) + , m_fileWidget(new KFileWidget(QUrl(), this)) +{ + setLayout(new QVBoxLayout); + 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); +} + +FileDialog::~FileDialog() +{ +} + FileChooserPortal::FileChooserPortal(QObject *parent) : QDBusAbstractAdaptor(parent) { qDBusRegisterMetaType(); qDBusRegisterMetaType(); qDBusRegisterMetaType(); qDBusRegisterMetaType(); } FileChooserPortal::~FileChooserPortal() { } uint FileChooserPortal::OpenFile(const QDBusObjectPath &handle, const QString &app_id, const QString &parent_window, const QString &title, const QVariantMap &options, QVariantMap &results) { Q_UNUSED(app_id); qCDebug(XdgDesktopPortalKdeFileChooser) << "OpenFile called with parameters:"; qCDebug(XdgDesktopPortalKdeFileChooser) << " handle: " << handle.path(); qCDebug(XdgDesktopPortalKdeFileChooser) << " parent_window: " << parent_window; qCDebug(XdgDesktopPortalKdeFileChooser) << " title: " << title; qCDebug(XdgDesktopPortalKdeFileChooser) << " options: " << options; bool modalDialog = true; bool multipleFiles = false; QString acceptLabel; QStringList nameFilters; QStringList mimeTypeFilters; /* TODO * choices a(ssa(ss)s) * List of serialized combo boxes to add to the file chooser. * * For each element, the first string is an ID that will be returned with the response, te second string is a user-visible label. * The a(ss) is the list of choices, each being a is an ID and a user-visible label. The final string is the initial selection, * or "", to let the portal decide which choice will be initially selected. None of the strings, except for the initial selection, should be empty. * * As a special case, passing an empty array for the list of choices indicates a boolean choice that is typically displayed as a check button, using "true" and "false" as the choices. * Example: [('encoding', 'Encoding', [('utf8', 'Unicode (UTF-8)'), ('latin15', 'Western')], 'latin15'), ('reencode', 'Reencode', [], 'false')] */ if (options.contains(QLatin1String("accept_label"))) { acceptLabel = options.value(QLatin1String("accept_label")).toString(); } if (options.contains(QLatin1String("modal"))) { modalDialog = options.value(QLatin1String("modal")).toBool(); } if (options.contains(QLatin1String("multiple"))) { multipleFiles = options.value(QLatin1String("multiple")).toBool(); } if (options.contains(QLatin1String("filters"))) { FilterListList filterListList = qdbus_cast(options.value(QLatin1String("filters"))); for (const FilterList &filterList : filterListList) { QStringList filterStrings; for (const Filter &filterStruct : filterList.filters) { if (filterStruct.type == 0) { filterStrings << filterStruct.filterString; } else { mimeTypeFilters << filterStruct.filterString; } } if (!filterStrings.isEmpty()) { - nameFilters << QStringLiteral("%1 (%2)").arg(filterList.userVisibleName).arg(filterStrings.join(QLatin1String(" "))); + nameFilters << QStringLiteral("%1|%2").arg(filterStrings.join(QLatin1Char(' '))).arg(filterList.userVisibleName); } } } - QFileDialog *fileDialog = new QFileDialog(); + QScopedPointer fileDialog(new FileDialog()); fileDialog->setWindowTitle(title); fileDialog->setModal(modalDialog); - fileDialog->setFileMode(multipleFiles ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile); - fileDialog->setLabelText(QFileDialog::Accept, !acceptLabel.isEmpty() ? acceptLabel : i18n("Open")); + fileDialog->m_fileWidget->setMode(multipleFiles ? KFile::Mode::File | KFile::Mode::ExistingOnly : KFile::Mode::Files | KFile::Mode::ExistingOnly); + fileDialog->m_fileWidget->okButton()->setText(!acceptLabel.isEmpty() ? acceptLabel : i18n("Open")); if (!nameFilters.isEmpty()) { - fileDialog->setNameFilters(nameFilters); + fileDialog->m_fileWidget->setFilter(nameFilters.join(QLatin1Char('\n'))); } if (!mimeTypeFilters.isEmpty()) { - fileDialog->setMimeTypeFilters(mimeTypeFilters); + fileDialog->m_fileWidget->setMimeFilter(mimeTypeFilters); } if (fileDialog->exec() == QDialog::Accepted) { QStringList files; - for (const QString &filename : fileDialog->selectedFiles()) { + for (const QString &filename : fileDialog->m_fileWidget->selectedFiles()) { QUrl url = QUrl::fromLocalFile(filename); files << url.toDisplayString(); } results.insert(QLatin1String("uris"), files); - fileDialog->deleteLater(); return 0; } - fileDialog->deleteLater(); return 1; } uint FileChooserPortal::SaveFile(const QDBusObjectPath &handle, const QString &app_id, const QString &parent_window, const QString &title, const QVariantMap &options, QVariantMap &results) { Q_UNUSED(app_id); qCDebug(XdgDesktopPortalKdeFileChooser) << "SaveFile called with parameters:"; qCDebug(XdgDesktopPortalKdeFileChooser) << " handle: " << handle.path(); qCDebug(XdgDesktopPortalKdeFileChooser) << " parent_window: " << parent_window; qCDebug(XdgDesktopPortalKdeFileChooser) << " title: " << title; qCDebug(XdgDesktopPortalKdeFileChooser) << " options: " << options; bool modalDialog = true; QString acceptLabel; QString currentName; QString currentFolder; QString currentFile; QStringList nameFilters; QStringList mimeTypeFilters; // TODO parse options - choices if (options.contains(QLatin1String("modal"))) { modalDialog = options.value(QLatin1String("modal")).toBool(); } if (options.contains(QLatin1String("accept_label"))) { acceptLabel = options.value(QLatin1String("accept_label")).toString(); } if (options.contains(QLatin1String("current_name"))) { currentName = options.value(QLatin1String("current_name")).toString(); } if (options.contains(QLatin1String("current_folder"))) { currentFolder = QString::fromUtf8(options.value(QLatin1String("current_folder")).toByteArray()); } if (options.contains(QLatin1String("current_file"))) { currentFile = QString::fromUtf8(options.value(QLatin1String("current_file")).toByteArray()); } if (options.contains(QLatin1String("filters"))) { FilterListList filterListList = qdbus_cast(options.value(QLatin1String("filters"))); for (const FilterList &filterList : filterListList) { QStringList filterStrings; for (const Filter &filterStruct : filterList.filters) { if (filterStruct.type == 0) { filterStrings << filterStruct.filterString; } else { mimeTypeFilters << filterStruct.filterString; } } if (!filterStrings.isEmpty()) { - nameFilters << QStringLiteral("%1 (%2)").arg(filterList.userVisibleName).arg(filterStrings.join(QLatin1String(" "))); + nameFilters << QStringLiteral("%1|%2").arg(filterStrings.join(QLatin1Char(' '))).arg(filterList.userVisibleName); } } } - QFileDialog *fileDialog = new QFileDialog(); + QScopedPointer fileDialog(new FileDialog()); fileDialog->setWindowTitle(title); fileDialog->setModal(modalDialog); - fileDialog->setAcceptMode(QFileDialog::AcceptSave); + fileDialog->m_fileWidget->setOperationMode(KFileWidget::Saving); if (!currentFolder.isEmpty()) { - fileDialog->setDirectoryUrl(QUrl(currentFolder)); + fileDialog->m_fileWidget->setUrl(QUrl::fromLocalFile(currentFolder)); } if (!currentFile.isEmpty()) { - fileDialog->selectFile(currentFile); + fileDialog->m_fileWidget->setSelectedUrl(QUrl::fromLocalFile(currentFile)); } if (!currentName.isEmpty()) { - fileDialog->selectFile(currentName); + const QUrl url = fileDialog->m_fileWidget->baseUrl(); + fileDialog->m_fileWidget->setSelectedUrl(QUrl::fromLocalFile(QStringLiteral("%1/%2").arg(url.toDisplayString(QUrl::StripTrailingSlash), currentName))); } if (!acceptLabel.isEmpty()) { - fileDialog->setLabelText(QFileDialog::Accept, acceptLabel); + fileDialog->m_fileWidget->okButton()->setText(acceptLabel); } if (!nameFilters.isEmpty()) { - fileDialog->setNameFilters(nameFilters); + fileDialog->m_fileWidget->setFilter(nameFilters.join(QLatin1Char('\n'))); } if (!mimeTypeFilters.isEmpty()) { - fileDialog->setMimeTypeFilters(mimeTypeFilters); + fileDialog->m_fileWidget->setMimeFilter(mimeTypeFilters); } if (fileDialog->exec() == QDialog::Accepted) { QStringList files; - for (const QString &filename : fileDialog->selectedFiles()) { - QUrl url = QUrl::fromLocalFile(filename); - files << url.toDisplayString(); - } + QUrl url = QUrl::fromLocalFile(fileDialog->m_fileWidget->selectedFile()); + files << url.toDisplayString(); results.insert(QLatin1String("uris"), files); - fileDialog->deleteLater(); return 0; } - fileDialog->deleteLater(); return 1; } diff --git a/src/filechooser.h b/src/filechooser.h index 06260e7..b1b13da 100644 --- a/src/filechooser.h +++ b/src/filechooser.h @@ -1,65 +1,84 @@ /* - * Copyright © 2016 Red Hat, Inc + * Copyright © 2016-2018 Red Hat, Inc * * This program 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) 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authors: * Jan Grulich */ #ifndef XDG_DESKTOP_PORTAL_KDE_FILECHOOSER_H #define XDG_DESKTOP_PORTAL_KDE_FILECHOOSER_H #include #include #include +#include + +class KFileWidget; +class QDialogButtonBox; + +class FileDialog : public QDialog +{ + Q_OBJECT +public: + friend class FileChooserPortal; + + FileDialog(QDialog *parent = nullptr, Qt::WindowFlags flags = {}); + ~FileDialog(); + +private: + QDialogButtonBox *m_buttons; +protected: + KFileWidget *m_fileWidget; +}; class FileChooserPortal : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.freedesktop.impl.portal.FileChooser") public: // Keep in sync with qflatpakfiledialog from flatpak-platform-plugin typedef struct { uint type; QString filterString; } Filter; typedef QList Filters; typedef struct { QString userVisibleName; Filters filters; } FilterList; typedef QList FilterListList; explicit FileChooserPortal(QObject *parent); ~FileChooserPortal(); public Q_SLOTS: uint OpenFile(const QDBusObjectPath &handle, const QString &app_id, const QString &parent_window, const QString &title, const QVariantMap &options, QVariantMap &results); uint SaveFile(const QDBusObjectPath &handle, const QString &app_id, const QString &parent_window, const QString &title, const QVariantMap &options, QVariantMap &results); }; #endif // XDG_DESKTOP_PORTAL_KDE_FILECHOOSER_H