Description: Move the Qt file dialog helper into libfm-qt. Author: Hong Jen Yee (PCMan) 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 +#include +#include +#include +#include + +#include + +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(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 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(i); + if(opt->isLabelExplicitlySet(label)) { + dlg_->setLabelText(static_cast(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 libfmQtContext_; + if(!libfmQtContext_) { + // initialize libfm-qt only once + libfmQtContext_ = std::unique_ptr{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 // this private header is subject to changes +#include + +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 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 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