diff --git a/scribus/ui/customfdialog.cpp b/scribus/ui/customfdialog.cpp index 22da78bf3e8486589e88482245f284380856fb7e..574cc5039bba1e3250827d47e8a2d4570cc10678 100644 --- a/scribus/ui/customfdialog.cpp +++ b/scribus/ui/customfdialog.cpp @@ -493,7 +493,11 @@ CustomFDialog::CustomFDialog(QWidget *parent, const QString& wDir, const QString connect(fileDialog, SIGNAL(filesSelected(const QStringList &)), this, SLOT(accept())); connect(fileDialog, SIGNAL(accepted()), this, SLOT(accept())); connect(fileDialog, SIGNAL(rejected()), this, SLOT(reject())); +#ifndef SCFILEWIDGET_IS_KFILEWIDGET resize(minimumSizeHint()); +#else + fileDialog->disablePreviewOption(); +#endif } diff --git a/scribus/ui/newfile.cpp b/scribus/ui/newfile.cpp index ec68e74047a47d6b15cbc321b477ff121fcef08d..3d0195049a7da5be7e93af3c23257c3322ad074a 100644 --- a/scribus/ui/newfile.cpp +++ b/scribus/ui/newfile.cpp @@ -363,10 +363,10 @@ void NewDoc::createOpenDocPage() openDocLayout->setSpacing(5); m_selectedFile = ""; - fileDialog = new QFileDialog(openDocFrame, tr("Open"), docDir, formats); + fileDialog = new ScFileDialog(openDocFrame, tr("Open"), docDir, formats); fileDialog->setFileMode(QFileDialog::ExistingFile); fileDialog->setAcceptMode(QFileDialog::AcceptOpen); - fileDialog->setOption(QFileDialog::DontUseNativeDialog); +// fileDialog->setOption(QFileDialog::DontUseNativeDialog); fileDialog->setIconProvider(new ImIconProvider()); fileDialog->setNameFilterDetailsVisible(false); fileDialog->setReadOnly(true); @@ -376,8 +376,9 @@ void NewDoc::createOpenDocPage() QListIterator i(b); while (i.hasNext()) i.next()->setVisible(false); - fileDialog->setWindowFlags(Qt::Widget); - openDocLayout->addWidget(fileDialog); + auto dialog = dynamic_cast(fileDialog); + dialog->setWindowFlags(Qt::Widget); + openDocLayout->addWidget(dialog); FileDialogEventCatcher* keyCatcher = new FileDialogEventCatcher(this); QList lv = fileDialog->findChildren(); @@ -390,9 +391,9 @@ void NewDoc::createOpenDocPage() connect(keyCatcher, SIGNAL(homePressed()), this, SLOT(gotoHomeDirectory())); connect(keyCatcher, SIGNAL(parentPressed()), this, SLOT(gotoParentDirectory())); connect(keyCatcher, SIGNAL(enterSelectedPressed()), this, SLOT(gotoSelectedDirectory())); - connect(fileDialog, SIGNAL(currentChanged(const QString &)), this, SLOT(openFileDialogFileClicked(const QString &))); - connect(fileDialog, SIGNAL(filesSelected(const QStringList &)), this, SLOT(openFile())); - connect(fileDialog, SIGNAL(rejected()), this, SLOT(reject())); + connect(dialog, SIGNAL(currentChanged(const QString &)), this, SLOT(openFileDialogFileClicked(const QString &))); + connect(dialog, SIGNAL(filesSelected(const QStringList &)), this, SLOT(openFile())); + connect(dialog, SIGNAL(rejected()), this, SLOT(reject())); } void NewDoc::openFile() diff --git a/scribus/ui/newfile.h b/scribus/ui/newfile.h index d7aa0000131935f9251ca4c101bd0ea7babb6e44..39eb114f0a4007e20fe4de720c8175cffadc12a1 100644 --- a/scribus/ui/newfile.h +++ b/scribus/ui/newfile.h @@ -30,12 +30,20 @@ class QPushButton; #include "ui/customfdialog.h" #include "ui/nftwidget.h" +#include "ui/scfilewidget.h" + class PrefsManager; class MarginWidget; class ScrSpinBox; class ScComboBox; class QFileDialog; +#ifdef SCFILEWIDGET_IS_KFILEWIDGET +using ScFileDialog = KFileDialog; +#else +using ScFileDialog = QFileDialog; +#endif + class SCRIBUS_API PageLayoutsWidget : public QListWidget { Q_OBJECT @@ -100,7 +108,7 @@ public: ScrSpinBox* widthSpinBox; ScrSpinBox* heightSpinBox; QFrame* openDocFrame; - QFileDialog *fileDialog; + ScFileDialog *fileDialog; QFrame* newFromTempFrame; nftwidget *nftGui; QFrame* recentDocFrame; diff --git a/scribus/ui/scfilewidget.cpp b/scribus/ui/scfilewidget.cpp index f6577b9806ca4ecad1fa5b83d6773cdde72b997d..0d2963d9ceaf10fd73191bdafa06f5398ecd86a8 100644 --- a/scribus/ui/scfilewidget.cpp +++ b/scribus/ui/scfilewidget.cpp @@ -18,18 +18,336 @@ for which a new license (GPL+exception) is in place. #include "filedialogeventcatcher.h" #include "scfilewidget.h" +#ifdef SCFILEWIDGET_IS_KFILEWIDGET +#include +#include +#include +#include +#include +#include +#endif + +#ifdef SCFILEWIDGET_IS_KFILEWIDGET +/* + * Map a list of Qt filter strings onto the KDE format. + */ +static QString qt2KdeFilter(const QStringList &f) +{ + QString filter; + QTextStream str(&filter, QIODevice::WriteOnly); + QStringList list(f); + list.replaceInStrings(QStringLiteral("/"), QStringLiteral("\\/")); + QStringList::const_iterator it(list.constBegin()), end(list.constEnd()); + bool first = true; + + // map "description (*.ext1 *.ext2)" to "*.ext1 *.ext2|description" + for (; it != end; ++it) { + int ob = it->lastIndexOf(QLatin1Char('(')), + cb = it->lastIndexOf(QLatin1Char(')')); + + if (-1 != cb && ob < cb) { + if (first) { + first = false; + } else { + str << '\n'; + } + str << it->mid(ob + 1, (cb - ob) - 1) << '|' << it->mid(0, ob); + } + } + + return filter; +} + +/* + * Map a KDE filter string onto a Qt one from the list of known filter strings + */ +static QString kde2QtFilter(const QStringList &list, const QString &kde) +{ + QStringList::const_iterator it(list.constBegin()), end(list.constEnd()); + int pos; + + for (; it != end; ++it) { + if (-1 != (pos = it->indexOf(kde)) && pos > 0 && + (QLatin1Char('(') == (*it)[pos - 1] || QLatin1Char(' ') == (*it)[pos - 1]) && + it->length() >= kde.length() + pos && + (QLatin1Char(')') == (*it)[pos + kde.length()] || QLatin1Char(' ') == (*it)[pos + kde.length()])) { + return *it; + } + } + return QString(); +} + +KFileDialog::KFileDialog(QWidget * parent, const QString &caption, const QString &directory, const QString &filter) + : QDialog(parent, Qt::Widget) + , m_sizeRestored(false) + , m_restoredSize(QSize(-1,-1)) +{ + setLayout(new QVBoxLayout); + m_fileWidget = new KFileWidget(directory.isEmpty() ? QUrl() : QUrl::fromLocalFile(directory), this); + layout()->addWidget(m_fileWidget); + if (!caption.isEmpty()) { + setWindowTitle(caption); + } + if (!filter.isEmpty()) { + setNameFilter(filter); + } + connect(this, &KFileDialog::accepted, m_fileWidget, &KFileWidget::accept); + // sic, [accepted] "Should be used by the caller to call accept()." + connect(m_fileWidget, &KFileWidget::accepted, m_fileWidget, &KFileWidget::accept); + connect(this, &KFileDialog::rejected, m_fileWidget, &KFileWidget::slotCancel); + connect(m_fileWidget, &KFileWidget::accepted, this, &KFileDialog::accept); + connect(m_fileWidget, &KFileWidget::accepted, this, &KFileDialog::accept); + connect(m_fileWidget, &KFileWidget::fileHighlighted, this, &KFileDialog::fileHighlighted); + connect(m_fileWidget, &KFileWidget::accepted, this, &KFileDialog::slotFilesSelected); + connect(m_fileWidget, &KFileWidget::filterChanged, this, &KFileDialog::slotFilterSelected); + connect(m_fileWidget, &KFileWidget::accepted, this, &KFileDialog::saveSize); + connect(this, &KFileDialog::rejected, this, &KFileDialog::saveSize); + connect(this, &KFileDialog::accepted, this, &KFileDialog::saveSize); + m_fileWidget->dirOperator()->setAcceptDrops(true); + disablePreviewOption(); + // save & restore dialog size but only if our parent is a QDialog + m_saveSize = qobject_cast(parent); + restoreSize(); +} + +KFileDialog::~KFileDialog() +{ + saveSize(); +} + +void KFileDialog::restoreSize() +{ + if (!m_sizeRestored && m_saveSize) { + winId(); + KSharedConfig::Ptr conf = KSharedConfig::openConfig(); + KWindowConfig::restoreWindowSize(windowHandle(), conf->group("FileDialogSize")); + m_sizeRestored = true; + m_restoredSize = windowHandle()->size(); + resize(windowHandle()->size()); + } +} + +void KFileDialog::saveSize() +{ + if (m_saveSize) { + KSharedConfig::Ptr conf = KSharedConfig::openConfig(); + KConfigGroup group = conf->group("FileDialogSize"); + KWindowConfig::saveWindowSize(windowHandle(), group); + m_sizeRestored = false; + } +} + +void KFileDialog::show() +{ + restoreSize(); + QDialog::show(); +} + +int KFileDialog::exec() +{ + restoreSize(); + return QDialog::exec(); +} + +void KFileDialog::setAcceptMode(QFileDialog::AcceptMode mode) +{ + m_fileWidget->setOperationMode(mode == QFileDialog::AcceptOpen ? KFileWidget::Opening : KFileWidget::Saving); +} + +QSize KFileDialog::sizeHint() const +{ + return m_restoredSize.isValid() ? m_restoredSize : m_fileWidget->dialogSizeHint(); +} + +QSize KFileDialog::minimumSizeHint() const +{ + return sizeHint(); +} + +QDir KFileDialog::directory() const +{ + return QDir(m_fileWidget->baseUrl().toLocalFile()); +} + +void KFileDialog::setDirectory(const QString &path) +{ + m_fileWidget->setUrl(QUrl::fromLocalFile(path)); +} + +void KFileDialog::setDirectory(const QDir &path) +{ + m_fileWidget->setUrl(QUrl::fromLocalFile(path.absolutePath())); +} + +void KFileDialog::selectFile(const QString &path) +{ + QUrl dirUrl = QUrl::fromLocalFile(path).adjusted(QUrl::RemoveFilename); + m_fileWidget->setUrl(dirUrl); + m_fileWidget->setSelectedUrl(path); +} + +void KFileDialog::setFileMode(QFileDialog::FileMode mode) +{ + KFile::Modes kmode = KFile::LocalOnly; + switch(mode) { + case QFileDialog::AnyFile: + kmode |= KFile::File; + break; + case QFileDialog::ExistingFile: + kmode |= KFile::File | KFile::ExistingOnly; + break; + case QFileDialog::Directory: + kmode |= KFile::Files | KFile::Directory; + break; + case QFileDialog::ExistingFiles: + kmode |= KFile::Files | KFile::ExistingOnly; + break; + case QFileDialog::DirectoryOnly: + kmode |= KFile::Directory; + break; + } + m_fileWidget->setMode(kmode); +} + +QFileDialog::FileMode KFileDialog::fileMode() const +{ + auto kmode = m_fileWidget->mode(); + // remove the LocalOnly flag + kmode &= ~KFile::LocalOnly; + switch (kmode) { + default: + qWarning() << Q_FUNC_INFO << "unforeseen KFileWidget::mode" << kmode; + case KFile::File: + return QFileDialog::AnyFile; + break; + case KFile::File | KFile::ExistingOnly: + return QFileDialog::ExistingFile; + break; + case KFile::Files | KFile::Directory: + return QFileDialog::Directory; + break; + case KFile::Files | KFile::ExistingOnly: + return QFileDialog::ExistingFiles; + break; + case KFile::Directory: + return QFileDialog::DirectoryOnly; + break; + } +} + +void KFileDialog::setNameFilter(const QString &filter) +{ + if (filter.isEmpty()) { + return; + } + const auto list = filter.split(QStringLiteral(";;")); + if (list.isEmpty()) { + return; + } + if (!m_nameFilters.contains(filter)) { + m_nameFilters.append(filter); + } + auto fWidget = m_fileWidget->filterWidget(); + fWidget->setFilter(qt2KdeFilter(list)); + fWidget->setCurrentFilter(fWidget->filters().at(0)); +} + +void KFileDialog::selectNameFilter(const QString &filter) +{ + if (m_nameFilters.contains(filter)) { + const auto listQt = filter.split(QStringLiteral(";;")); + // User can try to select a composed filter, in which case we don't + // really know if s/he wants something other than the 1st filter + // in that list. Just take the 1st. + const auto f = qt2KdeFilter(QStringList(listQt.at(0))); + m_fileWidget->filterWidget()->setCurrentFilter(f); + } else { + qWarning() << Q_FUNC_INFO << "ignoring unknown filter" << filter; + } +} -ScFileWidget::ScFileWidget(QWidget * parent) : QFileDialog(parent, Qt::Widget) +QStringList KFileDialog::nameFilters() const +{ + return m_nameFilters; +} + +QString KFileDialog::selectedNameFilter() const +{ + auto fWidget = m_fileWidget->filterWidget(); + if (fWidget->currentFilter().isEmpty()) { + // this shouldn't happen anymore (happens when calling setCurrentFilter() with + // a composite/multi filter pattern instead of with the 1st element from that pattern). + qWarning() << Q_FUNC_INFO << "empty filter! Known=" << fWidget->filters(); + if (fWidget->filters().isEmpty()) { + fWidget->setFilter(tr("*")); + } else { + fWidget->setCurrentFilter(fWidget->filters().at(0)); + } + } + return kde2QtFilter(m_nameFilters, fWidget->currentFilter()); +} + +void KFileDialog::setNameFilterDetailsVisible(bool) +{ +} + +void KFileDialog::fileHighlighted(const QUrl &url) +{ + emit currentChanged(url.toLocalFile()); +} + +void KFileDialog::slotFilesSelected() +{ + emit filesSelected(m_fileWidget->selectedFiles()); +} + +void KFileDialog::slotFilterSelected(const QString &) +{ + const QString f = selectedNameFilter(); +// qWarning() << Q_FUNC_INFO << filter << "\nvs." << f; + emit filterSelected(f); + reload(); +} + +void KFileDialog::reload() +{ + m_fileWidget->dirOperator()->rereadDir(); + m_fileWidget->dirOperator()->updateDir(); +} + +void KFileDialog::disablePreviewOption() +{ + m_fileWidget->dirOperator()->setPreviewWidget(nullptr); +} + +// unsupported/non-implemented +void KFileDialog::setIconProvider(QFileIconProvider*) {} +void KFileDialog::setReadOnly(bool) {} + +#endif + +#ifndef SCFILEWIDGET_IS_KFILEWIDGET +ScFileWidget::ScFileWidget(QWidget * parent) + : QFileDialog(parent, Qt::Widget) +#else +ScFileWidget::ScFileWidget(QWidget *parent + , const QString &caption + , const QString &directory, const QString &filter + ) + : KFileDialog(parent, caption, directory, filter) +#endif { m_forceDoubleClickActivation = false; - setOption(QFileDialog::DontUseNativeDialog); setSizeGripEnabled(false); setModal(false); - setViewMode(QFileDialog::List); setWindowFlags(Qt::Widget); +#ifndef SCFILEWIDGET_IS_KFILEWIDGET + setOption(QFileDialog::DontUseNativeDialog); + setViewMode(QFileDialog::List); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS + // no point with the KDE widget! QList urls; QUrl computer(QUrl::fromLocalFile(QLatin1String(""))); if (!urls.contains(computer)) @@ -41,9 +359,11 @@ ScFileWidget::ScFileWidget(QWidget * parent) : QFileDialog(parent, Qt::Widget) if (!urls.contains(dt)) urls << dt; setSidebarUrls(urls); +#endif #endif - FileDialogEventCatcher* keyCatcher = new FileDialogEventCatcher(this); + // TODO: do we need this when using the KFileDialog? + FileDialogEventCatcher* keyCatcher = new FileDialogEventCatcher(this); QList lv = findChildren(); QListIterator lvi(lv); while (lvi.hasNext()) @@ -61,8 +381,12 @@ ScFileWidget::ScFileWidget(QWidget * parent) : QFileDialog(parent, Qt::Widget) i.next()->setVisible(false); setMinimumSize(QSize(480, 310)); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); +#ifdef SCFILEWIDGET_IS_KFILEWIDGET + reload(); +#endif } + void ScFileWidget::forceDoubleClickActivation(bool force) { // Hack to make the previews in our file dialogs useable again, @@ -73,17 +397,24 @@ void ScFileWidget::forceDoubleClickActivation(bool force) if (force) setStyleSheet(QStringLiteral("QAbstractItemView { activate-on-singleclick: 0; }")); - else + else setStyleSheet(QString()); m_forceDoubleClickActivation = force; +#ifdef SCFILEWIDGET_IS_KFILEWIDGET + m_fileWidget->dirOperator()->setOnlyDoubleClickSelectsFiles(force); +#endif } QString ScFileWidget::selectedFile() { +#ifdef SCFILEWIDGET_IS_KFILEWIDGET + return m_fileWidget->selectedFile(); +#else QStringList l(selectedFiles()); if (l.count() == 0) return QString(); return l.at(0); +#endif } void ScFileWidget::locationDropped(const QString& fileUrl) @@ -104,14 +435,18 @@ void ScFileWidget::locationDropped(const QString& fileUrl) void ScFileWidget::gotoParentDirectory() { +#ifndef SCFILEWIDGET_IS_KFILEWIDGET QDir d(directory()); d.cdUp(); setDirectory(d); +#else + m_fileWidget->dirOperator()->cdUp(); +#endif } void ScFileWidget::gotoSelectedDirectory() { - QStringList s(selectedFiles()); + QStringList s(m_fileWidget->selectedFiles()); if (!s.isEmpty()) { QFileInfo fi(s.first()); diff --git a/scribus/ui/scfilewidget.h b/scribus/ui/scfilewidget.h index 7ee83382f784cb3c2f5ffd7ce4b733cd3f1e5e81..d4d3b09cad832607bd6d7bd83e4bae69c9962f25 100644 --- a/scribus/ui/scfilewidget.h +++ b/scribus/ui/scfilewidget.h @@ -8,9 +8,82 @@ for which a new license (GPL+exception) is in place. #define SCFILEWIDGET_H #include +#include #include "scribusapi.h" +#define SCFILEWIDGET_IS_KFILEWIDGET +class QFileIconProvider; + +#ifdef SCFILEWIDGET_IS_KFILEWIDGET +class SCRIBUS_API KFileDialog : public QDialog +{ + Q_OBJECT +public: + KFileDialog(QWidget * parent + , const QString &caption = QString() + , const QString &directory = QString(), const QString &filter = QString() + ); + virtual ~KFileDialog(); + + void setAcceptMode(QFileDialog::AcceptMode mode); + QSize sizeHint() const; + QSize minimumSizeHint() const; + QDir directory() const; + void setDirectory(const QString &path); + void setDirectory(const QDir &path); + void selectFile(const QString &path); + QStringList selectedFiles() const { return m_fileWidget->selectedFiles(); } + void setIconProvider(QFileIconProvider*); + void setReadOnly(bool); + void setFileMode(QFileDialog::FileMode mode); + QFileDialog::FileMode fileMode() const; + void setNameFilter(const QString &filter); + void selectNameFilter(const QString &filter); + QStringList nameFilters() const; + QString selectedNameFilter() const; + // obsolete + void setNameFilterDetailsVisible(bool); + + // overloads to target the appropriate parent class + template + QList findChildren(const QString &aName = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const + { + return m_fileWidget->findChildren(aName, options); + } + + void show(); + + void reload(); + void disablePreviewOption(); + +signals: + void currentChanged(const QString &path); + void filesSelected(const QStringList &selected); + void filterSelected(const QString &filter); + +public slots: + virtual int exec(); + +private slots: + void fileHighlighted(const QUrl &url); + void slotFilesSelected(); + void slotFilterSelected(const QString &filter); + void saveSize(); + +private: + void restoreSize(); + KFileWidget *m_fileWidget; + QStringList m_nameFilters; + bool m_sizeRestored; + bool m_saveSize; + QSize m_restoredSize; + friend class ScFileWidget; +}; + +using FileWidgetParentClass = KFileDialog; +#endif + /*! \brief Simple wrapper simulating the QFileDialog as a common widget. Usage is simple - everything is like in QFileDialog except it is not a dialog, it's a widget. It can be used for extension features etc. @@ -22,12 +95,17 @@ user e.g. doubleclicks the "open" file (in the parent (new) dialog): connect(fileWidget, SIGNAL(accepted()), this, SLOT(accept())); \author Petr Vanek */ -class SCRIBUS_API ScFileWidget : public QFileDialog +class SCRIBUS_API ScFileWidget : public FileWidgetParentClass { Q_OBJECT public: - ScFileWidget(QWidget * parent); + ScFileWidget(QWidget * parent +#ifdef SCFILEWIDGET_IS_KFILEWIDGET + , const QString &caption = QString() + , const QString &directory = QString(), const QString &filter = QString() +#endif + ); QString selectedFile(); void forceDoubleClickActivation(bool force); diff --git a/CMakeLists.txt b/CMakeLists.txt index 305e11b06797f209999e5d64463803ba2873e285..d372bfb806087f45304ff65bab4ad9d0ed8f0329 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,10 @@ include(GNUInstallDirs) #enable_testing() #Set the custom CMake module directory where our include/lib finders are -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") +find_package(ECM NO_MODULE) + +set(SCRIBUS_CMAKE_MODULES "${CMAKE_SOURCE_DIR}/cmake/modules") +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${SCRIBUS_CMAKE_MODULES}) ############################################################################################################## ########## toplevel compiler flags ########## @@ -705,6 +708,10 @@ message(STATUS "Qt VERSION: ${Qt5Widgets_VERSION}") #endif() #>> Qt +find_package(KF5KIO) +if(KF5KIO_FOUND) + message(STATUS "----- USE KF5::KIO -----") +endif() #C++11 Support #as of 1.5.5.svn, require C++11 @@ -725,8 +732,8 @@ if (WIN32) # On win32 we can use Qt's zlib and libpng, so we use some # custom cmake includes. This permits us to honour the # USE_QT_ZLIB_PNGLIB flag if passed. - set(PNG_DIR ${CMAKE_MODULE_PATH}) - set(ZLIB_DIR ${CMAKE_MODULE_PATH}) + set(PNG_DIR ${SCRIBUS_CMAKE_MODULES}) + set(ZLIB_DIR ${SCRIBUS_CMAKE_MODULES}) endif() find_package(ZLIB REQUIRED) if (ZLIB_FOUND) @@ -805,7 +812,7 @@ if (RENDER_LIB) endif() endif() -set(CAIRO_DIR ${CMAKE_MODULE_PATH}) +set(CAIRO_DIR ${SCRIBUS_CMAKE_MODULES}) find_package(CAIRO REQUIRED) if(CAIRO_FOUND) message("CAIRO Library Found OK") @@ -837,7 +844,7 @@ endif() #>>LibXML2 #<