You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
374 lines
12 KiB
374 lines
12 KiB
Description: Move the Qt file dialog helper into libfm-qt.
|
|
Author: Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
Origin: upstream
|
|
Applied-Upstream: commit:cc63bc7
|
|
Last-Update: 2018-07-30
|
|
--- a/src/CMakeLists.txt
|
|
+++ b/src/CMakeLists.txt
|
|
@@ -83,6 +83,7 @@ set(libfm_SRCS
|
|
filedialog.cpp
|
|
fm-search.c # might be moved to libfm later
|
|
xdndworkaround.cpp
|
|
+ filedialoghelper.cpp
|
|
)
|
|
|
|
set(libfm_UIS
|
|
--- /dev/null
|
|
+++ b/src/filedialoghelper.cpp
|
|
@@ -0,0 +1,290 @@
|
|
+#include "filedialoghelper.h"
|
|
+
|
|
+#include "libfmqt.h"
|
|
+#include "filedialog.h"
|
|
+
|
|
+#include <QWindow>
|
|
+#include <QDebug>
|
|
+#include <QTimer>
|
|
+#include <QSettings>
|
|
+#include <QtGlobal>
|
|
+
|
|
+#include <memory>
|
|
+
|
|
+namespace Fm {
|
|
+
|
|
+inline static const QString viewModeToString(Fm::FolderView::ViewMode value);
|
|
+inline static Fm::FolderView::ViewMode viewModeFromString(const QString& str);
|
|
+
|
|
+FileDialogHelper::FileDialogHelper() {
|
|
+ // can only be used after libfm-qt initialization
|
|
+ dlg_ = std::unique_ptr<Fm::FileDialog>(new Fm::FileDialog());
|
|
+ connect(dlg_.get(), &Fm::FileDialog::accepted, [this]() {
|
|
+ saveSettings();
|
|
+ accept();
|
|
+ });
|
|
+ connect(dlg_.get(), &Fm::FileDialog::rejected, [this]() {
|
|
+ saveSettings();
|
|
+ reject();
|
|
+ });
|
|
+
|
|
+ connect(dlg_.get(), &Fm::FileDialog::fileSelected, this, &FileDialogHelper::fileSelected);
|
|
+ connect(dlg_.get(), &Fm::FileDialog::filesSelected, this, &FileDialogHelper::filesSelected);
|
|
+ connect(dlg_.get(), &Fm::FileDialog::currentChanged, this, &FileDialogHelper::currentChanged);
|
|
+ connect(dlg_.get(), &Fm::FileDialog::directoryEntered, this, &FileDialogHelper::directoryEntered);
|
|
+ connect(dlg_.get(), &Fm::FileDialog::filterSelected, this, &FileDialogHelper::filterSelected);
|
|
+}
|
|
+
|
|
+FileDialogHelper::~FileDialogHelper() {
|
|
+}
|
|
+
|
|
+void FileDialogHelper::exec() {
|
|
+ dlg_->exec();
|
|
+}
|
|
+
|
|
+bool FileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow* parent) {
|
|
+ dlg_->setAttribute(Qt::WA_NativeWindow, true); // without this, sometimes windowHandle() will return nullptr
|
|
+
|
|
+ dlg_->setWindowFlags(windowFlags);
|
|
+ dlg_->setWindowModality(windowModality);
|
|
+
|
|
+ // Reference: KDE implementation
|
|
+ // https://github.com/KDE/plasma-integration/blob/master/src/platformtheme/kdeplatformfiledialoghelper.cpp
|
|
+ dlg_->windowHandle()->setTransientParent(parent);
|
|
+
|
|
+ applyOptions();
|
|
+
|
|
+ loadSettings();
|
|
+ // central positioning with respect to the parent window
|
|
+ if(parent && parent->isVisible()) {
|
|
+ dlg_->move(parent->x() + (parent->width() - dlg_->width()) / 2,
|
|
+ parent->y() + (parent->height() - dlg_->height()) / 2);
|
|
+ }
|
|
+
|
|
+ // NOTE: the timer here is required as a workaround borrowed from KDE. Without this, the dialog UI will be blocked.
|
|
+ // QFileDialog calls our platform plugin to show our own native file dialog instead of showing its widget.
|
|
+ // However, it still creates a hidden dialog internally, and then make it modal.
|
|
+ // So user input from all other windows that are not the children of the QFileDialog widget will be blocked.
|
|
+ // This includes our own dialog. After the return of this show() method, QFileDialog creates its own window and
|
|
+ // then make it modal, which blocks our UI. The timer schedule a delayed popup of our file dialog, so we can
|
|
+ // show again after QFileDialog and override the modal state. Then our UI can be unblocked.
|
|
+ QTimer::singleShot(0, dlg_.get(), &QDialog::show);
|
|
+ dlg_->setFocus();
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void FileDialogHelper::hide() {
|
|
+ dlg_->hide();
|
|
+}
|
|
+
|
|
+bool FileDialogHelper::defaultNameFilterDisables() const {
|
|
+ return false;
|
|
+}
|
|
+
|
|
+void FileDialogHelper::setDirectory(const QUrl& directory) {
|
|
+ dlg_->setDirectory(directory);
|
|
+}
|
|
+
|
|
+QUrl FileDialogHelper::directory() const {
|
|
+ return dlg_->directory();
|
|
+}
|
|
+
|
|
+void FileDialogHelper::selectFile(const QUrl& filename) {
|
|
+ dlg_->selectFile(filename);
|
|
+}
|
|
+
|
|
+QList<QUrl> FileDialogHelper::selectedFiles() const {
|
|
+ return dlg_->selectedFiles();
|
|
+}
|
|
+
|
|
+void FileDialogHelper::setFilter() {
|
|
+ // FIXME: what's this?
|
|
+ // The gtk+ 3 file dialog helper in Qt5 update options in this method.
|
|
+ applyOptions();
|
|
+}
|
|
+
|
|
+void FileDialogHelper::selectNameFilter(const QString& filter) {
|
|
+ dlg_->selectNameFilter(filter);
|
|
+}
|
|
+
|
|
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
|
|
+QString FileDialogHelper::selectedMimeTypeFilter() const {
|
|
+ return dlg_->selectedMimeTypeFilter();
|
|
+}
|
|
+
|
|
+void FileDialogHelper::selectMimeTypeFilter(const QString& filter) {
|
|
+ dlg_->selectMimeTypeFilter(filter);
|
|
+}
|
|
+#endif
|
|
+
|
|
+QString FileDialogHelper::selectedNameFilter() const {
|
|
+ return dlg_->selectedNameFilter();
|
|
+}
|
|
+
|
|
+bool FileDialogHelper::isSupportedUrl(const QUrl& url) const {
|
|
+ return dlg_->isSupportedUrl(url);
|
|
+}
|
|
+
|
|
+void FileDialogHelper::applyOptions() {
|
|
+ auto& opt = options();
|
|
+
|
|
+ // set title
|
|
+ if(opt->windowTitle().isEmpty()) {
|
|
+ dlg_->setWindowTitle(opt->acceptMode() == QFileDialogOptions::AcceptOpen ? tr("Open File")
|
|
+ : tr("Save File"));
|
|
+ }
|
|
+ else {
|
|
+ dlg_->setWindowTitle(opt->windowTitle());
|
|
+ }
|
|
+
|
|
+ dlg_->setFilter(opt->filter());
|
|
+ dlg_->setFileMode(QFileDialog::FileMode(opt->fileMode()));
|
|
+ dlg_->setAcceptMode(QFileDialog::AcceptMode(opt->acceptMode())); // also sets a default label for accept button
|
|
+ // bool useDefaultNameFilters() const;
|
|
+ dlg_->setNameFilters(opt->nameFilters());
|
|
+ if(!opt->mimeTypeFilters().empty()) {
|
|
+ dlg_->setMimeTypeFilters(opt->mimeTypeFilters());
|
|
+ }
|
|
+
|
|
+ dlg_->setDefaultSuffix(opt->defaultSuffix());
|
|
+ // QStringList history() const;
|
|
+
|
|
+ // explicitly set labels
|
|
+ for(int i = 0; i < QFileDialogOptions::DialogLabelCount; ++i) {
|
|
+ auto label = static_cast<QFileDialogOptions::DialogLabel>(i);
|
|
+ if(opt->isLabelExplicitlySet(label)) {
|
|
+ dlg_->setLabelText(static_cast<QFileDialog::DialogLabel>(label), opt->labelText(label));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ auto url = opt->initialDirectory();
|
|
+ if(url.isValid()) {
|
|
+ dlg_->setDirectory(url);
|
|
+ }
|
|
+
|
|
+
|
|
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
|
|
+ auto filter = opt->initiallySelectedMimeTypeFilter();
|
|
+ if(!filter.isEmpty()) {
|
|
+ selectMimeTypeFilter(filter);
|
|
+ }
|
|
+ else {
|
|
+ filter = opt->initiallySelectedNameFilter();
|
|
+ if(!filter.isEmpty()) {
|
|
+ selectNameFilter(opt->initiallySelectedNameFilter());
|
|
+ }
|
|
+ }
|
|
+#else
|
|
+ auto filter = opt->initiallySelectedNameFilter();
|
|
+ if(!filter.isEmpty()) {
|
|
+ selectNameFilter(filter);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ auto selectedFiles = opt->initiallySelectedFiles();
|
|
+ for(const auto& selectedFile: selectedFiles) {
|
|
+ selectFile(selectedFile);
|
|
+ }
|
|
+ // QStringList supportedSchemes() const;
|
|
+}
|
|
+
|
|
+static const QString viewModeToString(Fm::FolderView::ViewMode value) {
|
|
+ QString ret;
|
|
+ switch(value) {
|
|
+ case Fm::FolderView::DetailedListMode:
|
|
+ default:
|
|
+ ret = QLatin1String("Detailed");
|
|
+ break;
|
|
+ case Fm::FolderView::CompactMode:
|
|
+ ret = QLatin1String("Compact");
|
|
+ break;
|
|
+ case Fm::FolderView::IconMode:
|
|
+ ret = QLatin1String("Icon");
|
|
+ break;
|
|
+ case Fm::FolderView::ThumbnailMode:
|
|
+ ret = QLatin1String("Thumbnail");
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+Fm::FolderView::ViewMode viewModeFromString(const QString& str) {
|
|
+ Fm::FolderView::ViewMode ret;
|
|
+ if(str == QLatin1String("Detailed")) {
|
|
+ ret = Fm::FolderView::DetailedListMode;
|
|
+ }
|
|
+ else if(str == QLatin1String("Compact")) {
|
|
+ ret = Fm::FolderView::CompactMode;
|
|
+ }
|
|
+ else if(str == QLatin1String("Icon")) {
|
|
+ ret = Fm::FolderView::IconMode;
|
|
+ }
|
|
+ else if(str == QLatin1String("Thumbnail")) {
|
|
+ ret = Fm::FolderView::ThumbnailMode;
|
|
+ }
|
|
+ else {
|
|
+ ret = Fm::FolderView::DetailedListMode;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void FileDialogHelper::loadSettings() {
|
|
+ QSettings settings(QSettings::UserScope, "lxqt", "filedialog");
|
|
+ settings.beginGroup ("Sizes");
|
|
+ dlg_->resize(settings.value("WindowSize", QSize(700, 500)).toSize());
|
|
+ dlg_->setSplitterPos(settings.value("SplitterPos", 200).toInt());
|
|
+ settings.endGroup();
|
|
+
|
|
+ settings.beginGroup ("View");
|
|
+ dlg_->setViewMode(viewModeFromString(settings.value("Mode", "Detailed").toString()));
|
|
+ settings.endGroup();
|
|
+}
|
|
+
|
|
+void FileDialogHelper::saveSettings() {
|
|
+ QSettings settings(QSettings::UserScope, "lxqt", "filedialog");
|
|
+ settings.beginGroup ("Sizes");
|
|
+ QSize windowSize = dlg_->size();
|
|
+ if(settings.value("WindowSize") != windowSize) { // no redundant write
|
|
+ settings.setValue("WindowSize", windowSize);
|
|
+ }
|
|
+ int splitterPos = dlg_->splitterPos();
|
|
+ if(settings.value("SplitterPos") != splitterPos) {
|
|
+ settings.setValue("SplitterPos", splitterPos);
|
|
+ }
|
|
+ settings.endGroup();
|
|
+
|
|
+ settings.beginGroup ("View");
|
|
+ QString mode = viewModeToString(dlg_->viewMode());
|
|
+ if(settings.value("Mode") != mode) {
|
|
+ settings.setValue("Mode", mode);
|
|
+ }
|
|
+ settings.endGroup();
|
|
+}
|
|
+
|
|
+/*
|
|
+FileDialogPlugin::FileDialogPlugin() {
|
|
+
|
|
+}
|
|
+
|
|
+QPlatformFileDialogHelper *FileDialogPlugin::createHelper() {
|
|
+ return new FileDialogHelper();
|
|
+}
|
|
+*/
|
|
+
|
|
+} // namespace Fm
|
|
+
|
|
+
|
|
+QPlatformFileDialogHelper *createFileDialogHelper() {
|
|
+ // When a process has this environment set, that means glib event loop integration is disabled.
|
|
+ // In this case, libfm just won't work. So let's disable the file dialog helper and return nullptr.
|
|
+ if(qgetenv("QT_NO_GLIB") == "1") {
|
|
+ return nullptr;
|
|
+ }
|
|
+
|
|
+ static std::unique_ptr<Fm::LibFmQt> libfmQtContext_;
|
|
+ if(!libfmQtContext_) {
|
|
+ // initialize libfm-qt only once
|
|
+ libfmQtContext_ = std::unique_ptr<Fm::LibFmQt>{new Fm::LibFmQt()};
|
|
+ }
|
|
+ return new Fm::FileDialogHelper{};
|
|
+}
|
|
--- /dev/null
|
|
+++ b/src/filedialoghelper.h
|
|
@@ -0,0 +1,62 @@
|
|
+#ifndef FILEDIALOGHELPER_H
|
|
+#define FILEDIALOGHELPER_H
|
|
+
|
|
+#include "libfmqtglobals.h"
|
|
+#include <qpa/qplatformdialoghelper.h> // this private header is subject to changes
|
|
+#include <memory>
|
|
+
|
|
+namespace Fm {
|
|
+
|
|
+class FileDialog;
|
|
+
|
|
+class LIBFM_QT_API FileDialogHelper : public QPlatformFileDialogHelper {
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+ FileDialogHelper();
|
|
+
|
|
+ virtual ~FileDialogHelper();
|
|
+
|
|
+ // QPlatformDialogHelper
|
|
+ void exec() override;
|
|
+ bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override;
|
|
+ void hide() override;
|
|
+
|
|
+ // QPlatformFileDialogHelper
|
|
+ bool defaultNameFilterDisables() const override;
|
|
+ void setDirectory(const QUrl &directory) override;
|
|
+ QUrl directory() const override;
|
|
+ void selectFile(const QUrl &filename) override;
|
|
+ QList<QUrl> selectedFiles() const override;
|
|
+ void setFilter() override;
|
|
+ void selectNameFilter(const QString &filter) override;
|
|
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
|
|
+ QString selectedMimeTypeFilter() const override;
|
|
+ void selectMimeTypeFilter(const QString &filter) override;
|
|
+#endif
|
|
+ QString selectedNameFilter() const override;
|
|
+
|
|
+ bool isSupportedUrl(const QUrl &url) const override;
|
|
+
|
|
+private:
|
|
+ void applyOptions();
|
|
+ void loadSettings();
|
|
+ void saveSettings();
|
|
+
|
|
+private:
|
|
+ std::unique_ptr<Fm::FileDialog> dlg_;
|
|
+};
|
|
+
|
|
+} // namespace Fm
|
|
+
|
|
+// export a C API without C++ name mangling so others can dynamically load libfm-qt at runtime
|
|
+// to call this API and get a new QPlatformFileDialogHelper object.
|
|
+
|
|
+extern "C" {
|
|
+
|
|
+// if the process calling this API fail to load libfm-qt, nullptr will be returned instead.
|
|
+LIBFM_QT_API QPlatformFileDialogHelper* createFileDialogHelper();
|
|
+
|
|
+}
|
|
+
|
|
+#endif // FILEDIALOGHELPER_H
|