Description: Dynamically load libfm-qt Dynamically load libfm-qt on demand to create the file dialog helper to prevent the hard dependency on libfm-qt. This speed up the loading of the QPA plugin and also avoid loading libfm-qt in Qt programs having QT_NO_GLIB=1. Author: Hong Jen Yee (PCMan) Origin: upstream Applied-Upstream: commit:334394a Last-Update: 2018-07-30 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,10 +18,6 @@ find_package(Qt5XdgIconLoader REQUIRED) # Patch Version 0 -# for file dialog support -find_package(Qt5X11Extras REQUIRED) -find_package(fm-qt REQUIRED) - include(LXQtPreventInSourceBuilds) include(LXQtCompilerSettings NO_POLICY_SCOPE) --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,7 +6,6 @@ include_directories( set(qtlxqt_HDRS lxqtplatformtheme.h lxqtsystemtrayicon.h - lxqtfiledialoghelper.h statusnotifieritem/statusnotifieritem.h statusnotifieritem/dbustypes.h ) @@ -15,7 +14,6 @@ set(qtlxqt_SRCS main.cpp lxqtplatformtheme.cpp lxqtsystemtrayicon.cpp - lxqtfiledialoghelper.cpp statusnotifieritem/statusnotifieritem.cpp statusnotifieritem/dbustypes.cpp ) @@ -38,7 +36,6 @@ target_link_libraries(qtlxqt Qt5::DBus dbusmenu-qt5 Qt5XdgIconLoader - fm-qt ) --- a/src/lxqtfiledialoghelper.cpp +++ /dev/null @@ -1,276 +0,0 @@ -#include "lxqtfiledialoghelper.h" - -#include -#include - -#include -#include -#include -#include - -#include - -static std::unique_ptr libfmQtContext_; - -inline static const QString viewModeToString(Fm::FolderView::ViewMode value); -inline static Fm::FolderView::ViewMode viewModeFromString(const QString& str); - -LXQtFileDialogHelper::LXQtFileDialogHelper() { - if(!libfmQtContext_) { - // initialize libfm-qt only once - libfmQtContext_ = std::unique_ptr{new Fm::LibFmQt()}; - } - - // 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, &LXQtFileDialogHelper::fileSelected); - connect(dlg_.get(), &Fm::FileDialog::filesSelected, this, &LXQtFileDialogHelper::filesSelected); - connect(dlg_.get(), &Fm::FileDialog::currentChanged, this, &LXQtFileDialogHelper::currentChanged); - connect(dlg_.get(), &Fm::FileDialog::directoryEntered, this, &LXQtFileDialogHelper::directoryEntered); - connect(dlg_.get(), &Fm::FileDialog::filterSelected, this, &LXQtFileDialogHelper::filterSelected); -} - -LXQtFileDialogHelper::~LXQtFileDialogHelper() { -} - -void LXQtFileDialogHelper::exec() { - dlg_->exec(); -} - -bool LXQtFileDialogHelper::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 LXQtFileDialogHelper::hide() { - dlg_->hide(); -} - -bool LXQtFileDialogHelper::defaultNameFilterDisables() const { - return false; -} - -void LXQtFileDialogHelper::setDirectory(const QUrl& directory) { - dlg_->setDirectory(directory); -} - -QUrl LXQtFileDialogHelper::directory() const { - return dlg_->directory(); -} - -void LXQtFileDialogHelper::selectFile(const QUrl& filename) { - dlg_->selectFile(filename); -} - -QList LXQtFileDialogHelper::selectedFiles() const { - return dlg_->selectedFiles(); -} - -void LXQtFileDialogHelper::setFilter() { - // FIXME: what's this? - // The gtk+ 3 file dialog helper in Qt5 update options in this method. - applyOptions(); -} - -void LXQtFileDialogHelper::selectNameFilter(const QString& filter) { - dlg_->selectNameFilter(filter); -} - -#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) -QString LXQtFileDialogHelper::selectedMimeTypeFilter() const { - return dlg_->selectedMimeTypeFilter(); -} - -void LXQtFileDialogHelper::selectMimeTypeFilter(const QString& filter) { - dlg_->selectMimeTypeFilter(filter); -} -#endif - -QString LXQtFileDialogHelper::selectedNameFilter() const { - return dlg_->selectedNameFilter(); -} - -bool LXQtFileDialogHelper::isSupportedUrl(const QUrl& url) const { - return dlg_->isSupportedUrl(url); -} - -void LXQtFileDialogHelper::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 LXQtFileDialogHelper::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 LXQtFileDialogHelper::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 LXQtFileDialogHelper(); -} -*/ --- a/src/lxqtfiledialoghelper.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef LXQTFILEDIALOGHELPER_H -#define LXQTFILEDIALOGHELPER_H - -#include // this private header is subject to changes -#include - -namespace Fm { -class FileDialog; -} - -class Q_GUI_EXPORT LXQtFileDialogHelper : public QPlatformFileDialogHelper { - Q_OBJECT - -public: - LXQtFileDialogHelper(); - - virtual ~LXQtFileDialogHelper(); - - // 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_; -}; - - -#endif // LXQTFILEDIALOGHELPER_H --- a/src/lxqtplatformtheme.cpp +++ b/src/lxqtplatformtheme.cpp @@ -45,8 +45,14 @@ #include #include #include +#include + + +// Function to create a new Fm::FileDialogHelper object. +// This is dynamically loaded at runtime on demand from libfm-qt. +typedef QPlatformDialogHelper* (*CreateFileDialogHelperFunc)(); +static CreateFileDialogHelperFunc createFileDialogHelper = nullptr; -#include "lxqtfiledialoghelper.h" LXQtPlatformTheme::LXQtPlatformTheme(): iconFollowColorScheme_(true) @@ -222,8 +228,32 @@ bool LXQtPlatformTheme::usePlatformNativ QPlatformDialogHelper *LXQtPlatformTheme::createPlatformDialogHelper(DialogType type) const { if(type == FileDialog && qobject_cast(QCoreApplication::instance())) { // QML may not have qApp - // use our own file dialog - return new LXQtFileDialogHelper(); + // use our own file dialog provided by libfm + + // When a process has this environment set, that means glib event loop integration is disabled. + // In this case, libfm-qt just won't work. So let's disable the file dialog helper and return nullptr. + if(qgetenv("QT_NO_GLIB") == "1") { + return nullptr; + } + + // The createFileDialogHelper() method is dynamically loaded from libfm-qt on demand + if(createFileDialogHelper == nullptr) { + // try to dynamically load libfm-qt.so + QLibrary libfmQtLibrary{"libfm-qt"}; + libfmQtLibrary.load(); + if(!libfmQtLibrary.isLoaded()) { + return nullptr; + } + + // try to resolve the symbol to get the function pointer + createFileDialogHelper = reinterpret_cast(libfmQtLibrary.resolve("createFileDialogHelper")); + if(!createFileDialogHelper) { + return nullptr; + } + } + + // create a new file dialog helper provided by libfm + return createFileDialogHelper(); } return nullptr; }