diff --git a/debian/changelog b/debian/changelog index 29561e7..7d8e95e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,8 @@ libfm-qt (0.13.1-5ubuntu2) UNRELEASED; urgency=medium * Change Uploaders to Ubuntu uploaders. * Change the debhelper dependency to 11. + * Move in the file dialog from lxqt-qtplugin for better platform + integration. -- Simon Quigley Mon, 30 Jul 2018 19:07:41 -0500 diff --git a/debian/patches/move-in-file-dialog.patch b/debian/patches/move-in-file-dialog.patch new file mode 100644 index 0000000..de4e435 --- /dev/null +++ b/debian/patches/move-in-file-dialog.patch @@ -0,0 +1,373 @@ +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 diff --git a/debian/patches/series b/debian/patches/series index 9ddbc5f..b73a8f2 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -2,3 +2,4 @@ add-lxqt-archiver-integration.patch fix-incorrect-file-info-handling-1.patch fix-incorrect-file-info-handling-2.patch fix-places-font-color.patch +move-in-file-dialog.patch