diff --git a/debian/changelog b/debian/changelog index 2243559..60375d2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ lxqt-qtplugin (0.13.0-0ubuntu4) UNRELEASED; urgency=medium * Bump Standards-version to 4.1.5, no changes needed. * Lubuntuify the package. + * Dynamically load libfm-qt on runtime instead of on build time. -- Simon Quigley Mon, 30 Jul 2018 19:16:20 -0500 diff --git a/debian/control b/debian/control index bc9f440..7085659 100644 --- a/debian/control +++ b/debian/control @@ -6,7 +6,6 @@ Section: x11 Priority: optional Build-Depends: debhelper (>= 11), libdbusmenu-qt5-dev, - libfm-qt-dev (>= 0.13.0), libkf5windowsystem-dev, libqt5svg5-dev, libqt5x11extras5-dev, @@ -25,7 +24,8 @@ Architecture: any Multi-Arch: same Depends: ${misc:Depends}, ${shlibs:Depends} -Recommends: lxqt-session, +Recommends: libfm-qt5 (>= 0.13.1-5ubuntu4), + lxqt-session, lxqt-config Suggests: lxqt | lxqt-core Description: LXQt system integration plugin for Qt diff --git a/debian/patches/dynamically-load-file-dialog.patch b/debian/patches/dynamically-load-file-dialog.patch new file mode 100644 index 0000000..8e9d053 --- /dev/null +++ b/debian/patches/dynamically-load-file-dialog.patch @@ -0,0 +1,432 @@ +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; + } diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..cb2bdaf --- /dev/null +++ b/debian/patches/series @@ -0,0 +1 @@ +dynamically-load-file-dialog.patch