File indexing completed on 2024-05-05 16:08:25

0001 // -*- c++ -*-
0002 /* This file is part of the KDE libraries
0003     Copyright (C) 1997, 1998 Richard Moore <rich@kde.org>
0004                   1998 Stephan Kulow <coolo@kde.org>
0005                   1998 Daniel Grana <grana@ie.iwi.unibe.ch>
0006                   1999,2000,2001,2002,2003 Carsten Pfeiffer <pfeiffer@kde.org>
0007                   2003 Clarence Dang <dang@kde.org>
0008                   2008 Jarosław Staniek <staniek@kde.org>
0009                   2009 David Jarvie <djarvie@kde.org>
0010 
0011     This library is free software; you can redistribute it and/or
0012     modify it under the terms of the GNU Library General Public
0013     License as published by the Free Software Foundation; either
0014     version 2 of the License, or (at your option) any later version.
0015 
0016     This library is distributed in the hope that it will be useful,
0017     but WITHOUT ANY WARRANTY; without even the implied warranty of
0018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0019     Library General Public License for more details.
0020 
0021     You should have received a copy of the GNU Library General Public License
0022     along with this library; see the file COPYING.LIB.  If not, write to
0023     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0024     Boston, MA 02110-1301, USA.
0025 */
0026 
0027 #include "kfiledialog.h"
0028 
0029 #include <QCheckBox>
0030 #include <QKeyEvent>
0031 #include <QFileDialog>
0032 #include <QApplication>
0033 #include <QDesktopWidget>
0034 #include <QPushButton>
0035 #include <QVBoxLayout>
0036 #include <qmimedatabase.h>
0037 
0038 #include <kimageio.h>
0039 #include <klocalizedstring.h>
0040 #include <krecentdocument.h>
0041 #include <QDebug>
0042 #include <kwindowsystem.h>
0043 #include "kfilewidget.h"
0044 #include "krecentdirs.h"
0045 #include "kservice.h"
0046 #include <ksharedconfig.h>
0047 #include <kwindowconfig.h>
0048 
0049 //From defaults-kfile.h in KIOFileWidgets
0050 #define ConfigGroup QLatin1String("KFileDialog Settings")
0051 
0052 /** File dialogs are native by default on Windows. */
0053 #if defined(Q_OS_WIN)
0054 const bool NATIVE_FILEDIALOGS_BY_DEFAULT = true;
0055 #else
0056 const bool NATIVE_FILEDIALOGS_BY_DEFAULT = false;
0057 #endif
0058 
0059 static QStringList mime2KdeFilter(const QStringList &mimeTypes, QString *allExtensions = nullptr)
0060 {
0061     QMimeDatabase db;
0062     QStringList kdeFilter;
0063     QStringList allExt;
0064     foreach (const QString &mimeType, mimeTypes) {
0065         QMimeType mime(db.mimeTypeForName(mimeType));
0066         if (mime.isValid()) {
0067             allExt += mime.globPatterns();
0068             kdeFilter.append(mime.globPatterns().join(QLatin1String(" ")) +
0069                              QLatin1Char('|') +
0070                              mime.comment());
0071         }
0072     }
0073     if (allExtensions) {
0074         allExt.sort();
0075         *allExtensions = allExt.join(QLatin1String(" "));
0076     }
0077     return kdeFilter;
0078 }
0079 /** @return File dialog filter in Qt format for @a filters
0080  *          or "All files (*)" for empty list.
0081  */
0082 static QString qtFilter(const QStringList &_filters)
0083 {
0084     QString converted;
0085     const QStringList filters = _filters;
0086 
0087     foreach (const QString &current, filters) {
0088         QString new_f; //filter part
0089         QString new_name; //filter name part
0090         int p = current.indexOf('|');
0091         if (p == -1) {
0092             new_f = current;
0093             new_name = current; // nothing better found
0094         } else {
0095             new_f = current.left(p);
0096             new_name = current.mid(p + 1);
0097         }
0098         //Qt filters assume anything in () is the file extension list
0099         new_name = new_name.replace('(', '[').replace(')', ']').trimmed();
0100 
0101         //convert everything to lower case and remove dupes (doesn't matter on win32)
0102         QStringList allfiltersUnique;
0103         const QStringList origList(new_f.split(' ', QString::SkipEmptyParts));
0104         foreach (const QString &origFilter, origList) {
0105             if (!allfiltersUnique.contains(origFilter, Qt::CaseInsensitive)) {
0106                 allfiltersUnique += origFilter.toLower();
0107             }
0108         }
0109 
0110         if (!converted.isEmpty()) {
0111             converted += ";;";
0112         }
0113 
0114         converted += (new_name + " (" + allfiltersUnique.join(" ") + QLatin1Char(')'));
0115     }
0116 
0117     // Strip escape characters from escaped '/' characters.
0118     converted.replace("\\/", "/");
0119 
0120     return converted;
0121 }
0122 
0123 /** @return File dialog filter in Qt format for @a filter in KDE format
0124  *          or "All files (*)" for empty filter.
0125  */
0126 static QString qtFilter(const QString &filter)
0127 {
0128     // Qt format: "some text (*.first *.second)" or "All files (*)" separated by ;;
0129     // KDE format: "*.first *.second|Description" or "*|Description", separated by \n (Description is optional)
0130     QStringList filters;
0131     if (filter.isEmpty()) {
0132         filters += i18n("*|All files");
0133     } else {
0134         // check if it's a mimefilter
0135         int pos = filter.indexOf('/');
0136         if (pos > 0 && filter[pos - 1] != '\\') {
0137             filters = mime2KdeFilter(filter.split(QLatin1Char(' '), QString::SkipEmptyParts));
0138         } else {
0139             filters = filter.split('\n', QString::SkipEmptyParts);
0140         }
0141     }
0142     return qtFilter(filters);
0143 }
0144 
0145 class KFileDialogPrivate
0146 {
0147 public:
0148     /** Data used for native mode. */
0149     class Native
0150     {
0151     public:
0152         Native()
0153             : mode(KFile::File),
0154               operationMode(KFileWidget::Opening)
0155         {
0156         }
0157         /** @return previously set (global) start dir or the first url
0158          selected using setSelection() or setUrl() if the start dir is empty. */
0159         QUrl startDir() const
0160         {
0161             if (!s_startDir.isEmpty()) {
0162                 return s_startDir;
0163             }
0164             if (!selectedUrls.isEmpty()) {
0165                 return selectedUrls.first();
0166             }
0167             return QUrl();
0168         }
0169         /** @return previously set (global) start dir or @p defaultDir
0170          if the start dir is empty. */
0171         static QUrl staticStartDir(const QUrl &defaultDir)
0172         {
0173             if (s_startDir.isEmpty()) {
0174                 return defaultDir;
0175             }
0176             return s_startDir;
0177         }
0178         static QUrl s_startDir;
0179         static bool s_allowNative;  // as fallback when we can't use native dialog
0180         QString filter;
0181         QString selectedFilter;
0182         QStringList mimeTypes;
0183         QList<QUrl> selectedUrls;
0184         KFile::Modes mode;
0185         KFileWidget::OperationMode operationMode;
0186     };
0187 
0188     KFileDialogPrivate()
0189         : native(nullptr),
0190           w(nullptr),
0191           cfgGroup(KSharedConfig::openConfig(), ConfigGroup)
0192     {
0193         if (cfgGroup.readEntry("Native", NATIVE_FILEDIALOGS_BY_DEFAULT) &&
0194                 KFileDialogPrivate::Native::s_allowNative) {
0195             native = new Native;
0196         }
0197     }
0198 
0199     static bool isNative()
0200     {
0201         if (!KFileDialogPrivate::Native::s_allowNative) {
0202             return false;
0203         }
0204         KConfigGroup cfgGroup(KSharedConfig::openConfig(), ConfigGroup);
0205         return cfgGroup.readEntry("Native", NATIVE_FILEDIALOGS_BY_DEFAULT);
0206     }
0207 
0208     static QString getOpenFileName(const QUrl &startDir, const QString &filter,
0209                                    QWidget *parent, const QString &caption,
0210                                    QString *selectedFilter);
0211     static QUrl getOpenUrl(const QUrl &startDir, const QString &filter,
0212                            QWidget *parent, const QString &caption,
0213                            QString *selectedFilter);
0214     static QStringList getOpenFileNames(const QUrl &startDir, const QString &filter,
0215                                         QWidget *parent, const QString &caption,
0216                                         QString *selectedFilter);
0217     static QList<QUrl> getOpenUrls(const QUrl &startDir, const QString &filter,
0218                                    QWidget *parent, const QString &caption,
0219                                    QString *selectedFilter);
0220     static QString getSaveFileName(const QUrl &dir, const QString &filter,
0221                                    QWidget *parent, const QString &caption,
0222                                    KFileDialog::Options options, QString *selectedFilter);
0223     static QUrl getSaveUrl(const QUrl &dir, const QString &filter,
0224                            QWidget *parent, const QString &caption,
0225                            KFileDialog::Options options, QString *selectedFilter);
0226 
0227     ~KFileDialogPrivate()
0228     {
0229         delete native;
0230     }
0231 
0232     Native *native;
0233     KFileWidget *w;
0234     KConfigGroup cfgGroup;
0235 };
0236 
0237 QUrl KFileDialogPrivate::Native::s_startDir;
0238 bool KFileDialogPrivate::Native::s_allowNative = true;
0239 
0240 KFileDialog::KFileDialog(const QUrl &startDir, const QString &filter,
0241                          QWidget *parent, QWidget *customWidget)
0242 #ifdef Q_OS_WIN
0243     : QDialog(parent, Qt::WindowMinMaxButtonsHint),
0244 #else
0245     : QDialog(parent),
0246 #endif
0247       d(new KFileDialogPrivate)
0248 
0249 {
0250     if (d->native) {
0251         KFileDialogPrivate::Native::s_startDir = startDir;
0252         // check if it's a mimefilter
0253         int pos = filter.indexOf('/');
0254         if (pos > 0 && filter[pos - 1] != '\\') {
0255             setMimeFilter(filter.split(QLatin1Char(' '), QString::SkipEmptyParts));
0256         } else {
0257             setFilter(filter);
0258         }
0259         return;
0260     }
0261 
0262     d->w = new KFileWidget(startDir, this);
0263     KFileWidget *fileQWidget = d->w;
0264 
0265     KWindowConfig::restoreWindowSize(windowHandle(), d->cfgGroup); // call this before the fileQWidget is set as the main widget.
0266     // otherwise the sizes for the components are not obeyed (ereslibre)
0267 
0268     d->w->setFilter(filter);
0269     QVBoxLayout *layout = new QVBoxLayout;
0270     layout->addWidget(fileQWidget);
0271     setLayout(layout);
0272 
0273     d->w->okButton()->show();
0274     connect(d->w->okButton(), SIGNAL(clicked()), SLOT(slotOk()));
0275     d->w->cancelButton()->show();
0276     connect(d->w->cancelButton(), SIGNAL(clicked()), SLOT(slotCancel()));
0277 
0278     // Publish signals
0279     connect(fileQWidget, SIGNAL(fileSelected(QUrl)),
0280             SIGNAL(fileSelected(QUrl)));
0281     connect(fileQWidget, SIGNAL(fileHighlighted(QUrl)),
0282             SIGNAL(fileHighlighted(QUrl)));
0283     connect(fileQWidget, SIGNAL(selectionChanged()),
0284             SIGNAL(selectionChanged()));
0285     connect(fileQWidget, SIGNAL(filterChanged(QString)),
0286             SIGNAL(filterChanged(QString)));
0287 
0288     connect(fileQWidget, SIGNAL(accepted()), SLOT(accept()));
0289     //connect(fileQWidget, SIGNAL(canceled()), SLOT(slotCancel()));
0290 
0291     if (customWidget) {
0292         d->w->setCustomWidget(QString(), customWidget);
0293     }
0294 }
0295 
0296 KFileDialog::~KFileDialog()
0297 {
0298     delete d;
0299 }
0300 
0301 void KFileDialog::setLocationLabel(const QString &text)
0302 {
0303     if (d->w) {
0304         d->w->setLocationLabel(text);
0305     }
0306 }
0307 
0308 void KFileDialog::setFilter(const QString &filter)
0309 {
0310     if (d->native) {
0311         d->native->filter = filter;
0312         return;
0313     }
0314     d->w->setFilter(filter);
0315 }
0316 
0317 QString KFileDialog::currentFilter() const
0318 {
0319     if (d->w) {
0320         return d->w->currentFilter();
0321     }
0322     return QString();    // not available
0323 }
0324 
0325 void KFileDialog::setMimeFilter(const QStringList &mimeTypes,
0326                                 const QString &defaultType)
0327 {
0328     if (d->native) {
0329         QString allExtensions;
0330         QStringList filters = mime2KdeFilter(mimeTypes, &allExtensions);
0331         if (defaultType.isEmpty() && (mimeTypes.count() > 1)) {
0332             filters.prepend(allExtensions + QLatin1Char('|') + i18n("All Supported Files"));
0333         }
0334         d->native->filter = filters.join(QLatin1String("\n"));
0335         return;
0336     }
0337     d->w->setMimeFilter(mimeTypes, defaultType);
0338 }
0339 
0340 void KFileDialog::clearFilter()
0341 {
0342     if (d->native) {
0343         d->native->filter.clear();
0344         return;
0345     }
0346     d->w->clearFilter();
0347 }
0348 
0349 QString KFileDialog::currentMimeFilter() const
0350 {
0351     if (d->native) {
0352         // adapted from qt2KdeFilter
0353         QString filter = d->native->selectedFilter.split(";;").replaceInStrings("/", "\\/")[0];
0354         filter = filter.mid(filter.indexOf('(') + 1, filter.indexOf(')') - filter.indexOf('(') - 1);
0355         QMimeDatabase db;
0356         QString mimetype = db.mimeTypeForFile("test" + filter.mid(1).split(' ')[0], QMimeDatabase::MatchExtension).name();
0357         return mimetype;
0358     }
0359     return d->w->currentMimeFilter();
0360 }
0361 
0362 QMimeType KFileDialog::currentFilterMimeType()
0363 {
0364     QMimeDatabase db;
0365     return db.mimeTypeForName(currentMimeFilter());
0366 }
0367 
0368 void KFileDialog::setPreviewWidget(KPreviewWidgetBase *w)
0369 {
0370     if (d->w) {
0371         d->w->setPreviewWidget(w);
0372     }
0373 }
0374 
0375 void KFileDialog::setInlinePreviewShown(bool show)
0376 {
0377     if (d->w) {
0378         d->w->setInlinePreviewShown(show);
0379     }
0380 }
0381 
0382 // This is only used for the initial size when no configuration has been saved
0383 QSize KFileDialog::sizeHint() const
0384 {
0385     if (d->w) {
0386         return d->w->dialogSizeHint();
0387     }
0388     return QSize();
0389 }
0390 
0391 // This slot still exists mostly for compat purposes; for subclasses which reimplement slotOk
0392 void KFileDialog::slotOk()
0393 {
0394     if (d->w) {
0395         d->w->slotOk();
0396     }
0397 }
0398 
0399 // This slot still exists mostly for compat purposes; for subclasses which reimplement accept
0400 void KFileDialog::accept()
0401 {
0402     if (d->w) {
0403         setResult(QDialog::Accepted);   // keep old behavior; probably not needed though
0404         d->w->accept();
0405         KConfigGroup cfgGroup(KSharedConfig::openConfig(), ConfigGroup);
0406         QDialog::accept();
0407     }
0408 }
0409 
0410 // This slot still exists mostly for compat purposes; for subclasses which reimplement slotCancel
0411 void KFileDialog::slotCancel()
0412 {
0413     if (d->w) {
0414         d->w->slotCancel();
0415         reject();
0416     }
0417 }
0418 
0419 void KFileDialog::setUrl(const QUrl &url, bool clearforward)
0420 {
0421     if (d->native) {
0422         d->native->selectedUrls.clear();
0423         d->native->selectedUrls.append(url);
0424         return;
0425     }
0426     d->w->setUrl(url, clearforward);
0427 }
0428 
0429 void KFileDialog::setSelection(const QString &name)
0430 {
0431     if (d->native) {
0432         d->native->selectedUrls.clear();
0433         d->native->selectedUrls.append(QUrl(name)); // could be relative
0434         return;
0435     }
0436     d->w->setSelection(name);
0437 }
0438 
0439 QString KFileDialog::getOpenFileName(const QUrl &startDir,
0440                                      const QString &filter,
0441                                      QWidget *parent, const QString &caption)
0442 {
0443     return KFileDialogPrivate::getOpenFileName(startDir, filter, parent, caption, nullptr);
0444 }
0445 
0446 QString KFileDialogPrivate::getOpenFileName(const QUrl &startDir,
0447         const QString &filter,
0448         QWidget *parent,
0449         const QString &caption,
0450         QString *selectedFilter)
0451 {
0452     if (KFileDialogPrivate::isNative() && (!startDir.isValid() || startDir.isLocalFile())) {
0453         return QFileDialog::getOpenFileName(
0454                    parent,
0455                    caption.isEmpty() ? i18n("Open") : caption,
0456                    KFileDialogPrivate::Native::staticStartDir(startDir).toLocalFile(),
0457                    qtFilter(filter),
0458                    selectedFilter);
0459 // TODO use extra args?     QString * selectedFilter = 0, Options options = 0
0460     }
0461     KFileDialog dlg(startDir, filter, parent);
0462 
0463     dlg.setOperationMode(KFileDialog::Opening);
0464     dlg.setMode(KFile::File | KFile::LocalOnly | KFile::ExistingOnly);
0465     dlg.setWindowTitle(caption.isEmpty() ? i18n("Open") : caption);
0466 
0467     dlg.exec();
0468     if (selectedFilter) {
0469         *selectedFilter = dlg.currentMimeFilter();
0470     }
0471     return dlg.selectedFile();
0472 }
0473 
0474 QString KFileDialog::getOpenFileNameWId(const QUrl &startDir,
0475                                         const QString &filter,
0476                                         WId parent_id, const QString &caption)
0477 {
0478     if (KFileDialogPrivate::isNative() && (!startDir.isValid() || startDir.isLocalFile())) {
0479         return KFileDialog::getOpenFileName(startDir, filter, nullptr, caption);    // everything we can do...
0480     }
0481     QWidget *parent = QWidget::find(parent_id);
0482     KFileDialogPrivate::Native::s_allowNative = false;
0483     KFileDialog dlg(startDir, filter, parent);
0484     if (parent == nullptr && parent_id != 0) {
0485         dlg.setAttribute(Qt::WA_NativeWindow, true);
0486         KWindowSystem::setMainWindow(dlg.windowHandle(), parent_id);
0487     }
0488 
0489     dlg.setOperationMode(KFileDialog::Opening);
0490     dlg.setMode(KFile::File | KFile::LocalOnly | KFile::ExistingOnly);
0491     dlg.setWindowTitle(caption.isEmpty() ? i18n("Open") : caption);
0492 
0493     dlg.exec();
0494 
0495     return dlg.selectedFile();
0496 }
0497 
0498 QStringList KFileDialog::getOpenFileNames(const QUrl &startDir,
0499         const QString &filter,
0500         QWidget *parent,
0501         const QString &caption)
0502 {
0503     return KFileDialogPrivate::getOpenFileNames(startDir, filter, parent, caption, nullptr);
0504 }
0505 
0506 QStringList KFileDialogPrivate::getOpenFileNames(const QUrl &startDir,
0507         const QString &filter,
0508         QWidget *parent,
0509         const QString &caption,
0510         QString *selectedFilter)
0511 {
0512     if (KFileDialogPrivate::isNative() && (!startDir.isValid() || startDir.isLocalFile())) {
0513         return QFileDialog::getOpenFileNames(
0514                    parent,
0515                    caption.isEmpty() ? i18n("Open") : caption,
0516                    KFileDialogPrivate::Native::staticStartDir(startDir).toLocalFile(),
0517                    qtFilter(filter), selectedFilter);
0518 // TODO use extra args?  QString * selectedFilter = 0, Options options = 0
0519     }
0520     KFileDialogPrivate::Native::s_allowNative = false;
0521     KFileDialog dlg(startDir, filter, parent);
0522 
0523     dlg.setOperationMode(KFileDialog::Opening);
0524     dlg.setMode(KFile::Files | KFile::LocalOnly | KFile::ExistingOnly);
0525     dlg.setWindowTitle(caption.isEmpty() ? i18n("Open") : caption);
0526 
0527     dlg.exec();
0528     if (selectedFilter) {
0529         *selectedFilter = dlg.currentMimeFilter();
0530     }
0531     return dlg.selectedFiles();
0532 }
0533 
0534 QUrl KFileDialog::getOpenUrl(const QUrl &startDir, const QString &filter,
0535                              QWidget *parent, const QString &caption)
0536 {
0537     return KFileDialogPrivate::getOpenUrl(startDir, filter, parent, caption, nullptr);
0538 }
0539 QUrl KFileDialogPrivate::getOpenUrl(const QUrl &startDir, const QString &filter,
0540                                     QWidget *parent, const QString &caption,
0541                                     QString *selectedFilter)
0542 {
0543     if (KFileDialogPrivate::isNative() && (!startDir.isValid() || startDir.isLocalFile())) {
0544         const QString fileName(KFileDialogPrivate::getOpenFileName(
0545                                    startDir, filter, parent, caption, selectedFilter));
0546         return fileName.isEmpty() ? QUrl() : QUrl::fromLocalFile(fileName);
0547     }
0548     KFileDialogPrivate::Native::s_allowNative = false;
0549     KFileDialog dlg(startDir, filter, parent);
0550 
0551     dlg.setOperationMode(KFileDialog::Opening);
0552     dlg.setMode(KFile::File | KFile::ExistingOnly);
0553     dlg.setWindowTitle(caption.isEmpty() ? i18n("Open") : caption);
0554 
0555     dlg.exec();
0556     if (selectedFilter) {
0557         *selectedFilter = dlg.currentMimeFilter();
0558     }
0559     return dlg.selectedUrl();
0560 }
0561 
0562 QList<QUrl> KFileDialog::getOpenUrls(const QUrl &startDir,
0563                                      const QString &filter,
0564                                      QWidget *parent,
0565                                      const QString &caption)
0566 {
0567     return KFileDialogPrivate::getOpenUrls(startDir, filter, parent, caption, nullptr);
0568 }
0569 
0570 QList<QUrl> KFileDialogPrivate::getOpenUrls(const QUrl &startDir,
0571         const QString &filter,
0572         QWidget *parent,
0573         const QString &caption,
0574         QString *selectedFilter)
0575 {
0576     if (KFileDialogPrivate::isNative() && (!startDir.isValid() || startDir.isLocalFile())) {
0577         const QStringList fileNames(KFileDialogPrivate::getOpenFileNames(
0578                                         startDir, filter, parent, caption, selectedFilter));
0579         QList<QUrl> urls;
0580         Q_FOREACH (const QString &file, fileNames) {
0581             urls.append(QUrl::fromLocalFile(file));
0582         }
0583         return urls;
0584     }
0585     KFileDialogPrivate::Native::s_allowNative = false;
0586 
0587     KFileDialog dlg(startDir, filter, parent);
0588 
0589     dlg.setOperationMode(KFileDialog::Opening);
0590     dlg.setMode(KFile::Files | KFile::ExistingOnly);
0591     dlg.setWindowTitle(caption.isEmpty() ? i18n("Open") : caption);
0592 
0593     dlg.exec();
0594     if (selectedFilter) {
0595         *selectedFilter = dlg.currentMimeFilter();
0596     }
0597     return dlg.selectedUrls();
0598 }
0599 
0600 void KFileDialog::setConfirmOverwrite(bool enable)
0601 {
0602     if (d->w && operationMode() == KFileDialog::Saving) {
0603         d->w->setConfirmOverwrite(enable);
0604     }
0605 }
0606 
0607 QUrl KFileDialog::getExistingDirectoryUrl(const QUrl &startDir,
0608         QWidget *parent,
0609         const QString &caption)
0610 {
0611     return QFileDialog::getExistingDirectoryUrl(parent, caption, KFileDialogPrivate::Native::staticStartDir(startDir));
0612 }
0613 
0614 QString KFileDialog::getExistingDirectory(const QUrl &startDir,
0615         QWidget *parent,
0616         const QString &caption)
0617 {
0618     if (KFileDialogPrivate::isNative() && (!startDir.isValid() || startDir.isLocalFile())) {
0619         return QFileDialog::getExistingDirectory(parent, caption,
0620                 KFileDialogPrivate::Native::staticStartDir(startDir).toLocalFile(),
0621                 QFileDialog::ShowDirsOnly);
0622     }
0623     QUrl url = KFileDialog::getExistingDirectoryUrl(startDir, parent, caption);
0624     if (url.isValid()) {
0625         return url.toLocalFile();
0626     }
0627     return QString();
0628 }
0629 
0630 QUrl KFileDialog::getImageOpenUrl(const QUrl &startDir, QWidget *parent,
0631                                   const QString &caption)
0632 {
0633     if (KFileDialogPrivate::isNative() && (!startDir.isValid() || startDir.isLocalFile())) { // everything we can do...
0634         const QStringList mimetypes(KImageIO::mimeTypes(KImageIO::Reading));
0635         return KFileDialog::getOpenUrl(startDir, mimetypes.join(" "), parent, caption);
0636     }
0637     const QStringList mimetypes = KImageIO::mimeTypes(KImageIO::Reading);
0638     KFileDialogPrivate::Native::s_allowNative = false;
0639     KFileDialog dlg(startDir, mimetypes.join(" "), parent);
0640 
0641     dlg.setOperationMode(KFileDialog::Opening);
0642     dlg.setMode(KFile::File | KFile::ExistingOnly);
0643     dlg.setWindowTitle(caption.isEmpty() ? i18n("Open") : caption);
0644     dlg.setInlinePreviewShown(true);
0645 
0646     dlg.exec();
0647 
0648     return dlg.selectedUrl();
0649 }
0650 
0651 QUrl KFileDialog::selectedUrl() const
0652 {
0653     if (d->native) {
0654         return d->native->selectedUrls.isEmpty() ? QUrl() : d->native->selectedUrls.first();
0655     }
0656     return d->w->selectedUrl();
0657 }
0658 
0659 QList<QUrl> KFileDialog::selectedUrls() const
0660 {
0661     if (d->native) {
0662         return d->native->selectedUrls;
0663     }
0664     return d->w->selectedUrls();
0665 }
0666 
0667 QString KFileDialog::selectedFile() const
0668 {
0669     if (d->native) {
0670         return selectedUrl().toLocalFile();
0671     }
0672     return d->w->selectedFile();
0673 }
0674 
0675 QStringList KFileDialog::selectedFiles() const
0676 {
0677     if (d->native) {
0678         QStringList lst;
0679         Q_FOREACH (const QUrl &u, selectedUrls()) {
0680             if (u.isLocalFile()) {
0681                 lst.append(u.toLocalFile());
0682             }
0683         }
0684         return lst;
0685     }
0686     return d->w->selectedFiles();
0687 }
0688 
0689 QUrl KFileDialog::baseUrl() const
0690 {
0691     if (d->native) {
0692         return selectedUrl().isEmpty() ? QUrl() : QUrl::fromLocalFile(selectedUrl().path());    /* shouldn't that be .directory()? */
0693     }
0694     return d->w->baseUrl();
0695 }
0696 
0697 QString KFileDialog::getSaveFileName(const QUrl &dir, const QString &filter,
0698                                      QWidget *parent,
0699                                      const QString &caption)
0700 {
0701     //TODO KDE5: replace this method by the method below (with default parameter values in declaration)
0702     // Set no confirm-overwrite mode for backwards compatibility
0703     return KFileDialogPrivate::getSaveFileName(dir, filter, parent, caption, Options(), nullptr);
0704 }
0705 
0706 QString KFileDialog::getSaveFileName(const QUrl &dir, const QString &filter,
0707                                      QWidget *parent,
0708                                      const QString &caption, Options options)
0709 {
0710     return KFileDialogPrivate::getSaveFileName(dir, filter, parent, caption, options, nullptr);
0711 }
0712 
0713 QString KFileDialogPrivate::getSaveFileName(const QUrl &dir, const QString &filter,
0714         QWidget *parent, const QString &caption,
0715         KFileDialog::Options options, QString *selectedFilter)
0716 {
0717     if (KFileDialogPrivate::isNative()) {
0718         bool defaultDir = dir.isEmpty();
0719         bool specialDir = !defaultDir && dir.scheme() == "kfiledialog";
0720         QUrl startDir;
0721         QString recentDirClass;
0722         if (specialDir) {
0723             startDir = KFileDialog::getStartUrl(dir, recentDirClass);
0724         } else if (!specialDir && !defaultDir) {
0725             if (!dir.isLocalFile()) {
0726                 qWarning() << "non-local start dir " << dir;
0727             }
0728             startDir = dir;
0729         }
0730 
0731         QFileDialog::Options opts = (options & KFileDialog::ConfirmOverwrite) ? QFileDialog::Options() : QFileDialog::DontConfirmOverwrite;
0732         const QString result = QFileDialog::getSaveFileName(
0733                                    parent,
0734                                    caption.isEmpty() ? i18n("Save As") : caption,
0735                                    KFileDialogPrivate::Native::staticStartDir(startDir).toLocalFile(),
0736                                    qtFilter(filter),
0737 // TODO use extra args?     QString * selectedFilter = 0, Options opts = 0
0738                                    selectedFilter, opts);
0739         if (!result.isEmpty()) {
0740             if (!recentDirClass.isEmpty()) {
0741                 KRecentDirs::add(recentDirClass, QUrl::fromLocalFile(result).toString());
0742             }
0743             KRecentDocument::add(result);
0744         }
0745         return result;
0746     }
0747 
0748     KFileDialog dlg(dir, filter, parent);
0749 
0750     dlg.setOperationMode(KFileDialog::Saving);
0751     dlg.setMode(KFile::File | KFile::LocalOnly);
0752     dlg.setConfirmOverwrite(options & KFileDialog::ConfirmOverwrite);
0753     dlg.setInlinePreviewShown(options & KFileDialog::ShowInlinePreview);
0754     dlg.setWindowTitle(caption.isEmpty() ? i18n("Save As") : caption);
0755 
0756     dlg.exec();
0757 
0758     QString filename = dlg.selectedFile();
0759     if (!filename.isEmpty()) {
0760         KRecentDocument::add(filename);
0761     }
0762 
0763     return filename;
0764 }
0765 
0766 QString KFileDialog::getSaveFileNameWId(const QUrl &dir, const QString &filter,
0767                                         WId parent_id,
0768                                         const QString &caption)
0769 {
0770     //TODO KDE5: replace this method by the method below (with default parameter values in declaration)
0771     // Set no confirm-overwrite mode for backwards compatibility
0772     return getSaveFileNameWId(dir, filter, parent_id, caption, Options());
0773 }
0774 
0775 QString KFileDialog::getSaveFileNameWId(const QUrl &dir, const QString &filter,
0776                                         WId parent_id,
0777                                         const QString &caption, Options options)
0778 {
0779     if (KFileDialogPrivate::isNative()) {
0780         return KFileDialog::getSaveFileName(dir, filter, nullptr, caption, options); // everything we can do...
0781     }
0782 
0783     QWidget *parent = QWidget::find(parent_id);
0784     KFileDialog dlg(dir, filter, parent);
0785     if (parent == nullptr && parent_id != 0) {
0786         dlg.setAttribute(Qt::WA_NativeWindow, true);
0787         KWindowSystem::setMainWindow(dlg.windowHandle(), parent_id);
0788     }
0789 
0790     dlg.setOperationMode(KFileDialog::Saving);
0791     dlg.setMode(KFile::File | KFile::LocalOnly);
0792     dlg.setConfirmOverwrite(options & ConfirmOverwrite);
0793     dlg.setInlinePreviewShown(options & ShowInlinePreview);
0794     dlg.setWindowTitle(caption.isEmpty() ? i18n("Save As") : caption);
0795 
0796     dlg.exec();
0797 
0798     QString filename = dlg.selectedFile();
0799     if (!filename.isEmpty()) {
0800         KRecentDocument::add(filename);
0801     }
0802 
0803     return filename;
0804 }
0805 
0806 QUrl KFileDialog::getSaveUrl(const QUrl &dir, const QString &filter,
0807                              QWidget *parent, const QString &caption)
0808 {
0809     //TODO KDE5: replace this method by the method below (with default parameter values in declaration)
0810     // Set no confirm-overwrite mode for backwards compatibility
0811     return KFileDialogPrivate::getSaveUrl(dir, filter, parent, caption, Options(), nullptr);
0812 }
0813 
0814 QUrl KFileDialog::getSaveUrl(const QUrl &dir, const QString &filter,
0815                              QWidget *parent, const QString &caption, Options options)
0816 {
0817     return KFileDialogPrivate::getSaveUrl(dir, filter, parent, caption, options, nullptr);
0818 }
0819 
0820 QUrl KFileDialogPrivate::getSaveUrl(const QUrl &dir, const QString &filter,
0821                                     QWidget *parent, const QString &caption,
0822                                     KFileDialog::Options options, QString *selectedFilter)
0823 {
0824     if (KFileDialogPrivate::isNative() && (!dir.isValid() || dir.isLocalFile())) {
0825         const QString fileName(KFileDialogPrivate::getSaveFileName(
0826                                    dir, filter, parent, caption, options, selectedFilter));
0827         return fileName.isEmpty() ? QUrl() : QUrl::fromLocalFile(fileName);
0828     }
0829 
0830     KFileDialogPrivate::Native::s_allowNative = false;
0831 
0832     KFileDialog dlg(dir, filter, parent);
0833 
0834     dlg.setOperationMode(KFileDialog::Saving);
0835     dlg.setMode(KFile::File);
0836     dlg.setConfirmOverwrite(options & KFileDialog::ConfirmOverwrite);
0837     dlg.setInlinePreviewShown(options & KFileDialog::ShowInlinePreview);
0838     dlg.setWindowTitle(caption.isEmpty() ? i18n("Save As") : caption);
0839 
0840     dlg.exec();
0841     if (selectedFilter) {
0842         *selectedFilter = dlg.currentMimeFilter();
0843     }
0844     QUrl url = dlg.selectedUrl();
0845     if (url.isValid()) {
0846         KRecentDocument::add(url);
0847     }
0848 
0849     return url;
0850 }
0851 
0852 void KFileDialog::setMode(KFile::Modes m)
0853 {
0854     if (d->native) {
0855         d->native->mode = m;
0856     } else {
0857         d->w->setMode(m);
0858     }
0859 }
0860 
0861 KFile::Modes KFileDialog::mode() const
0862 {
0863     if (d->native) {
0864         return d->native->mode;
0865     }
0866     return d->w->mode();
0867 }
0868 
0869 QPushButton *KFileDialog::okButton() const
0870 {
0871     if (d->w) {
0872         return d->w->okButton();
0873     }
0874     return nullptr;
0875 }
0876 
0877 QPushButton *KFileDialog::cancelButton() const
0878 {
0879     if (d->w) {
0880         return d->w->cancelButton();
0881     }
0882     return nullptr;
0883 }
0884 
0885 KUrlComboBox *KFileDialog::locationEdit() const
0886 {
0887     if (d->w) {
0888         return d->w->locationEdit();
0889     }
0890     return nullptr;
0891 }
0892 
0893 KFileFilterCombo *KFileDialog::filterWidget() const
0894 {
0895     if (d->w) {
0896         return d->w->filterWidget();
0897     }
0898     return nullptr;
0899 }
0900 
0901 KActionCollection *KFileDialog::actionCollection() const
0902 {
0903     if (d->w) {
0904         return d->w->actionCollection();
0905     }
0906     return nullptr;
0907 }
0908 
0909 void KFileDialog::setKeepLocation(bool keep)
0910 {
0911     if (d->w) {
0912         d->w->setKeepLocation(keep);
0913     }
0914 }
0915 
0916 bool KFileDialog::keepsLocation() const
0917 {
0918     if (d->w) {
0919         return d->w->keepsLocation();
0920     }
0921     return false;
0922 }
0923 
0924 void KFileDialog::setOperationMode(OperationMode mode)
0925 {
0926     if (d->native) {
0927         d->native->operationMode = static_cast<KFileWidget::OperationMode>(mode);
0928     } else {
0929         d->w->setOperationMode(static_cast<KFileWidget::OperationMode>(mode));
0930     }
0931 }
0932 
0933 KFileDialog::OperationMode KFileDialog::operationMode() const
0934 {
0935     if (d->native) {
0936         return static_cast<KFileDialog::OperationMode>(d->native->operationMode);
0937     }
0938     return static_cast<KFileDialog::OperationMode>(d->w->operationMode());
0939 }
0940 
0941 void KFileDialog::keyPressEvent(QKeyEvent *e)
0942 {
0943     if (d->w) {
0944         if (e->key() == Qt::Key_Escape) {
0945             e->accept();
0946             d->w->cancelButton()->animateClick();
0947         } else {
0948             QDialog::keyPressEvent(e);
0949         }
0950     }
0951 }
0952 
0953 void KFileDialog::hideEvent(QHideEvent *e)
0954 {
0955     if (d->w) {
0956         KWindowConfig::saveWindowSize(windowHandle(), d->cfgGroup, KConfigBase::Persistent);
0957 
0958         QDialog::hideEvent(e);
0959     }
0960 }
0961 
0962 // static
0963 QUrl KFileDialog::getStartUrl(const QUrl &startDir,
0964                               QString &recentDirClass)
0965 {
0966     return KFileWidget::getStartUrl(startDir, recentDirClass);
0967 }
0968 
0969 void KFileDialog::setStartDir(const QUrl &directory)
0970 {
0971     if (KFileDialogPrivate::isNative()) {
0972         KFileDialogPrivate::Native::s_startDir = directory;
0973     }
0974     KFileWidget::setStartDir(directory);
0975 }
0976 
0977 KToolBar *KFileDialog::toolBar() const
0978 {
0979     if (d->w) {
0980         return d->w->toolBar();
0981     }
0982     return nullptr;
0983 }
0984 
0985 KFileWidget *KFileDialog::fileWidget()
0986 {
0987     return d->w;
0988 }
0989 
0990 #ifdef Q_OS_WIN
0991 int KFileDialog::exec()
0992 {
0993     if (!d->native || !KFileDialogPrivate::Native::s_allowNative) {
0994         KFileDialogPrivate::Native::s_allowNative = true;
0995         return QDialog::exec();
0996     }
0997 
0998 // not clear here to let KFileDialogPrivate::Native::startDir() return a useful value
0999 // d->native->selectedUrls.clear();
1000     int res = QDialog::Rejected;
1001     switch (d->native->operationMode) {
1002     case KFileWidget::Opening:
1003     case KFileWidget::Other:
1004         if (d->native->mode & KFile::File) {
1005             QUrl url(KFileDialogPrivate::getOpenUrl(
1006                          d->native->startDir(), d->native->filter, parentWidget(), windowTitle(), &d->native->selectedFilter));
1007             if (url.isEmpty() || !url.isValid()) {
1008                 res = QDialog::Rejected;
1009                 break;
1010             }
1011             d->native->selectedUrls.clear();
1012             d->native->selectedUrls.append(url);
1013             res = QDialog::Accepted;
1014             break;
1015         } else if (d->native->mode & KFile::Files) {
1016             QList<QUrl> urls(KFileDialogPrivate::getOpenUrls(
1017                                  d->native->startDir(), d->native->filter, parentWidget(), windowTitle(), &d->native->selectedFilter));
1018             if (urls.isEmpty()) {
1019                 res = QDialog::Rejected;
1020                 break;
1021             }
1022             d->native->selectedUrls = urls;
1023             res = QDialog::Accepted;
1024             break;
1025         } else if (d->native->mode & KFile::Directory) {
1026             QUrl url(KFileDialog::getExistingDirectoryUrl(
1027                          d->native->startDir(), parentWidget(), windowTitle()));
1028             if (url.isEmpty() || !url.isValid()) {
1029                 res = QDialog::Rejected;
1030                 break;
1031             }
1032             d->native->selectedUrls.clear();
1033             d->native->selectedUrls.append(url);
1034             res = QDialog::Accepted;
1035             break;
1036         }
1037         break;
1038     case KFileWidget::Saving:
1039         if (d->native->mode & KFile::File) {
1040             QUrl url(KFileDialogPrivate::getSaveUrl(
1041                          d->native->startDir(), d->native->filter, parentWidget(), windowTitle(), Options(), &d->native->selectedFilter));
1042             if (url.isEmpty() || !url.isValid())  {
1043                 res = QDialog::Rejected;
1044                 break;
1045             }
1046             d->native->selectedUrls.clear();
1047             d->native->selectedUrls.append(url);
1048             res = QDialog::Accepted;
1049             break;
1050         } else if (d->native->mode & KFile::Directory) {
1051             QUrl url(KFileDialog::getExistingDirectoryUrl(
1052                          d->native->startDir(), parentWidget(), windowTitle()));
1053             if (url.isEmpty() || !url.isValid()) {
1054                 res = QDialog::Rejected;
1055                 break;
1056             }
1057             d->native->selectedUrls.clear();
1058             d->native->selectedUrls.append(url);
1059             res = QDialog::Accepted;
1060             break;
1061         }
1062         break;
1063     default:;
1064     }
1065 
1066     setResult(res);
1067     emit finished(res);
1068 
1069     if (res == QDialog::Accepted) {
1070         emit accepted();
1071     } else {
1072         emit rejected();
1073     }
1074 
1075     return res;
1076 }
1077 #endif // Q_OS_WIN
1078 
1079 #include "moc_kfiledialog.cpp"