Warning, file /office/calligra/libs/widgetutils/KoFileDialog.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002    Copyright (C) 2013 - 2014 Yue Liu <yue.liu@mail.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KoFileDialog.h"
0021 #include <QDebug>
0022 #include <QFileDialog>
0023 #include <QApplication>
0024 #include <QImageReader>
0025 #include <QClipboard>
0026 
0027 #include <kconfiggroup.h>
0028 #include <ksharedconfig.h>
0029 #include <klocalizedstring.h>
0030 
0031 #include <QUrl>
0032 #include <QMimeDatabase>
0033 #include <QMimeType>
0034 
0035 class Q_DECL_HIDDEN KoFileDialog::Private
0036 {
0037 public:
0038     Private(QWidget *parent_,
0039             KoFileDialog::DialogType dialogType_,
0040             const QString caption_,
0041             const QString defaultDir_,
0042             const QString dialogName_)
0043         : parent(parent_)
0044         , type(dialogType_)
0045         , dialogName(dialogName_)
0046         , caption(caption_)
0047         , defaultDirectory(defaultDir_)
0048         , filterList(QStringList())
0049         , defaultFilter(QString())
0050         , useStaticForNative(false)
0051         , hideDetails(false)
0052         , swapExtensionOrder(false)
0053     {
0054         // Force the native file dialogs on Windows. Except for KDE, the native file dialogs are only possible
0055         // using the static methods. The Qt documentation is wrong here, if it means what it says " By default,
0056         // the native file dialog is used unless you use a subclass of QFileDialog that contains the Q_OBJECT
0057         // macro."
0058 #ifdef Q_OS_WIN
0059         useStaticForNative = true;
0060 #endif
0061         // Non-static KDE file is broken when called with QFileDialog::AcceptSave:
0062         // then the directory above defaultdir is opened, and defaultdir is given as the default file name...
0063         //
0064         // So: in X11, use static methods inside KDE, which give working native dialogs, but non-static outside
0065         // KDE, which gives working Qt dialogs.
0066         //
0067         // Only show the GTK dialog in Gnome, where people deserve it
0068 #ifdef HAVE_X11
0069         if (qgetenv("KDE_FULL_SESSION").size() > 0) {
0070             useStaticForNative = true;
0071         }
0072         if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") {
0073             useStaticForNative = true;
0074             QClipboard *cb = QApplication::clipboard();
0075             cb->blockSignals(true);
0076             swapExtensionOrder = true;
0077         }
0078 
0079 #endif
0080     }
0081 
0082     ~Private()
0083     {
0084         if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") {
0085             useStaticForNative = true;
0086             QClipboard *cb = QApplication::clipboard();
0087             cb->blockSignals(false);
0088         }
0089     }
0090 
0091     QWidget *parent;
0092     KoFileDialog::DialogType type;
0093     QString dialogName;
0094     QString caption;
0095     QString defaultDirectory;
0096     QStringList filterList;
0097     QString defaultFilter;
0098     QScopedPointer<QFileDialog> fileDialog;
0099     QMimeType mimeType;
0100     bool useStaticForNative;
0101     bool hideDetails;
0102     bool swapExtensionOrder;
0103 };
0104 
0105 KoFileDialog::KoFileDialog(QWidget *parent,
0106                            KoFileDialog::DialogType type,
0107                            const QString &dialogName)
0108     : d(new Private(parent, type, "", getUsedDir(dialogName), dialogName))
0109 {
0110 }
0111 
0112 KoFileDialog::~KoFileDialog()
0113 {
0114     delete d;
0115 }
0116 
0117 void KoFileDialog::setCaption(const QString &caption)
0118 {
0119     d->caption = caption;
0120 }
0121 
0122 void KoFileDialog::setDefaultDir(const QString &defaultDir, bool override)
0123 {
0124     if (override || d->defaultDirectory.isEmpty() || !QFile(d->defaultDirectory).exists()) {
0125         QFileInfo f(defaultDir);
0126         d->defaultDirectory = f.absoluteFilePath();
0127     }
0128 }
0129 
0130 void KoFileDialog::setOverrideDir(const QString &overrideDir)
0131 {
0132     d->defaultDirectory = overrideDir;
0133 }
0134 
0135 void KoFileDialog::setImageFilters()
0136 {
0137     QStringList imageMimeTypes;
0138     foreach(const QByteArray &mimeType, QImageReader::supportedMimeTypes()) {
0139         imageMimeTypes << QLatin1String(mimeType);
0140     }
0141     setMimeTypeFilters(imageMimeTypes);
0142 }
0143 
0144 void KoFileDialog::setNameFilter(const QString &filter)
0145 {
0146     d->filterList.clear();
0147     if (d->type == KoFileDialog::SaveFile) {
0148         QStringList mimeList;
0149         d->filterList << splitNameFilter(filter, &mimeList);
0150         d->defaultFilter = d->filterList.first();
0151     }
0152     else {
0153         d->filterList << filter;
0154     }
0155 }
0156 
0157 void KoFileDialog::setNameFilters(const QStringList &filterList,
0158                                   QString defaultFilter)
0159 {
0160     d->filterList.clear();
0161 
0162     if (d->type == KoFileDialog::SaveFile) {
0163         QStringList mimeList;
0164         foreach(const QString &filter, filterList) {
0165             d->filterList << splitNameFilter(filter, &mimeList);
0166         }
0167 
0168         if (!defaultFilter.isEmpty()) {
0169             mimeList.clear();
0170             QStringList defaultFilters = splitNameFilter(defaultFilter, &mimeList);
0171             if (defaultFilters.size() > 0) {
0172                 defaultFilter = defaultFilters.first();
0173             }
0174         }
0175     }
0176     else {
0177         d->filterList = filterList;
0178     }
0179     d->defaultFilter = defaultFilter;
0180 
0181 }
0182 
0183 void KoFileDialog::setMimeTypeFilters(const QStringList &filterList,
0184                                       QString defaultFilter)
0185 {
0186     d->filterList = getFilterStringListFromMime(filterList, true);
0187 
0188     if (!defaultFilter.isEmpty()) {
0189         QStringList defaultFilters = getFilterStringListFromMime(QStringList() << defaultFilter, false);
0190         if (defaultFilters.size() > 0) {
0191             defaultFilter = defaultFilters.first();
0192         }
0193     }
0194     d->defaultFilter = defaultFilter;
0195 }
0196 
0197 void KoFileDialog::setHideNameFilterDetailsOption()
0198 {
0199     d->hideDetails = true;
0200 }
0201 
0202 QStringList KoFileDialog::nameFilters() const
0203 {
0204     return d->filterList;
0205 }
0206 
0207 QString KoFileDialog::selectedNameFilter() const
0208 {
0209     if (!d->useStaticForNative) {
0210         return d->fileDialog->selectedNameFilter();
0211     }
0212     else {
0213         return d->defaultFilter;
0214     }
0215 }
0216 
0217 QString KoFileDialog::selectedMimeType() const
0218 {
0219     if (d->mimeType.isValid()) {
0220         return d->mimeType.name();
0221     }
0222     else {
0223         return "";
0224     }
0225 }
0226 
0227 void KoFileDialog::createFileDialog()
0228 {
0229     d->fileDialog.reset(new QFileDialog(d->parent, d->caption, d->defaultDirectory));
0230 
0231     if (d->type == SaveFile) {
0232         d->fileDialog->setAcceptMode(QFileDialog::AcceptSave);
0233         d->fileDialog->setFileMode(QFileDialog::AnyFile);
0234     }
0235     else { // open / import
0236 
0237         d->fileDialog->setAcceptMode(QFileDialog::AcceptOpen);
0238 
0239         if (d->type == ImportDirectory
0240                 || d->type == OpenDirectory)
0241         {
0242             d->fileDialog->setFileMode(QFileDialog::Directory);
0243             d->fileDialog->setOption(QFileDialog::ShowDirsOnly, true);
0244         }
0245         else { // open / import file(s)
0246             if (d->type == OpenFile
0247                     || d->type == ImportFile)
0248             {
0249                 d->fileDialog->setFileMode(QFileDialog::ExistingFile);
0250             }
0251             else { // files
0252                 d->fileDialog->setFileMode(QFileDialog::ExistingFiles);
0253             }
0254         }
0255     }
0256 
0257     d->fileDialog->setNameFilters(d->filterList);
0258     if (!d->defaultFilter.isEmpty()) {
0259         d->fileDialog->selectNameFilter(d->defaultFilter);
0260     }
0261 
0262     if (d->type == ImportDirectory ||
0263             d->type == ImportFile || d->type == ImportFiles ||
0264             d->type == SaveFile) {
0265         d->fileDialog->setWindowModality(Qt::WindowModal);
0266     }
0267 
0268     if (d->hideDetails) {
0269         d->fileDialog->setOption(QFileDialog::HideNameFilterDetails);
0270     }
0271 
0272     connect(d->fileDialog.data(), SIGNAL(filterSelected(QString)), this, SLOT(filterSelected(QString)));
0273 }
0274 
0275 QString KoFileDialog::filename()
0276 {
0277     QString url;
0278     if (!d->useStaticForNative) {
0279 
0280         if (!d->fileDialog) {
0281             createFileDialog();
0282         }
0283 
0284         if (d->fileDialog->exec() == QDialog::Accepted) {
0285             url = d->fileDialog->selectedFiles().first();
0286         }
0287     }
0288     else {
0289         switch (d->type) {
0290         case OpenFile:
0291         {
0292             url = QFileDialog::getOpenFileName(d->parent,
0293                                                d->caption,
0294                                                d->defaultDirectory,
0295                                                d->filterList.join(";;"),
0296                                                &d->defaultFilter);
0297             break;
0298         }
0299         case OpenDirectory:
0300         {
0301             url = QFileDialog::getExistingDirectory(d->parent,
0302                                                     d->caption,
0303                                                     d->defaultDirectory,
0304                                                     QFileDialog::ShowDirsOnly);
0305             break;
0306         }
0307         case ImportFile:
0308         {
0309             url = QFileDialog::getOpenFileName(d->parent,
0310                                                d->caption,
0311                                                d->defaultDirectory,
0312                                                d->filterList.join(";;"),
0313                                                &d->defaultFilter);
0314             break;
0315         }
0316         case ImportDirectory:
0317         {
0318             url = QFileDialog::getExistingDirectory(d->parent,
0319                                                     d->caption,
0320                                                     d->defaultDirectory,
0321                                                     QFileDialog::ShowDirsOnly);
0322             break;
0323         }
0324         case SaveFile:
0325         {
0326             url = QFileDialog::getSaveFileName(d->parent,
0327                                                d->caption,
0328                                                d->defaultDirectory,
0329                                                d->filterList.join(";;"),
0330                                                &d->defaultFilter);
0331             break;
0332         }
0333         default:
0334             ;
0335         }
0336     }
0337 
0338     if (!url.isEmpty()) {
0339 
0340         if (d->type == SaveFile && QFileInfo(url).suffix().isEmpty()) {
0341             int start = d->defaultFilter.lastIndexOf("*.") + 1;
0342             int end = d->defaultFilter.lastIndexOf(" )");
0343             int n = end - start;
0344             QString extension = d->defaultFilter.mid(start, n);
0345             url.append(extension);
0346         }
0347 
0348         QMimeDatabase db;
0349         d->mimeType = db.mimeTypeForFile(url);
0350         saveUsedDir(url, d->dialogName);
0351     }
0352     return url;
0353 }
0354 
0355 QStringList KoFileDialog::filenames()
0356 {
0357     QStringList urls;
0358 
0359     if (!d->useStaticForNative) {
0360         if (!d->fileDialog) {
0361             createFileDialog();
0362         }
0363         if (d->fileDialog->exec() == QDialog::Accepted) {
0364             urls = d->fileDialog->selectedFiles();
0365         }
0366     }
0367     else {
0368         switch (d->type) {
0369         case OpenFiles:
0370         case ImportFiles:
0371         {
0372             urls = QFileDialog::getOpenFileNames(d->parent,
0373                                                  d->caption,
0374                                                  d->defaultDirectory,
0375                                                  d->filterList.join(";;"),
0376                                                  &d->defaultFilter);
0377             break;
0378         }
0379         default:
0380             ;
0381         }
0382     }
0383     if (urls.size() > 0) {
0384         saveUsedDir(urls.first(), d->dialogName);
0385     }
0386     return urls;
0387 }
0388 
0389 void KoFileDialog::filterSelected(const QString &filter)
0390 {
0391     // "Windows BMP image ( *.bmp )";
0392     int start = filter.lastIndexOf("*.") + 2;
0393     int end = filter.lastIndexOf(" )");
0394     int n = end - start;
0395     QString extension = filter.mid(start, n);
0396     d->defaultFilter = filter;
0397     d->fileDialog->setDefaultSuffix(extension);
0398 }
0399 
0400 QStringList KoFileDialog::splitNameFilter(const QString &nameFilter, QStringList *mimeList)
0401 {
0402     Q_ASSERT(mimeList);
0403 
0404     QStringList filters;
0405     QString description;
0406 
0407     if (nameFilter.contains("(")) {
0408         description = nameFilter.left(nameFilter.indexOf("(") -1).trimmed();
0409     }
0410 
0411     QStringList entries = nameFilter.mid(nameFilter.indexOf("(") + 1).split(" ",QString::SkipEmptyParts );
0412 
0413     foreach(QString entry, entries) {
0414 
0415         entry = entry.remove("*");
0416         entry = entry.remove(")");
0417 
0418         QMimeDatabase db;
0419         QMimeType mime = db.mimeTypeForName("bla" + entry);
0420         if (mime.name() != "application/octet-stream") {
0421             if (!mimeList->contains(mime.name())) {
0422                 mimeList->append(mime.name());
0423                 filters.append(mime.comment() + " ( *" + entry + " )");
0424             }
0425         }
0426         else {
0427             filters.append(entry.remove(".").toUpper() + " " + description + " ( *." + entry + " )");
0428         }
0429     }
0430 
0431     return filters;
0432 }
0433 
0434 const QStringList KoFileDialog::getFilterStringListFromMime(const QStringList &mimeList,
0435                                                             bool withAllSupportedEntry)
0436 {
0437     QStringList mimeSeen;
0438 
0439     QStringList ret;
0440     if (withAllSupportedEntry) {
0441         ret << QString();
0442     }
0443 
0444     for (QStringList::ConstIterator
0445          it = mimeList.begin(); it != mimeList.end(); ++it) {
0446         QMimeDatabase db;
0447         QMimeType mimeType = db.mimeTypeForName(*it);
0448         if (!mimeType.isValid()) {
0449             continue;
0450         }
0451         if (!mimeSeen.contains(mimeType.name())) {
0452             QString oneFilter;
0453             QStringList patterns = mimeType.globPatterns();
0454             QStringList::ConstIterator jt;
0455             for (jt = patterns.constBegin(); jt != patterns.constEnd(); ++jt) {
0456                 if (d->swapExtensionOrder) {
0457                     oneFilter.prepend(*jt + " ");
0458                     if (withAllSupportedEntry) {
0459                         ret[0].prepend(*jt + " ");
0460                     }
0461                 }
0462                 else {
0463                     oneFilter.append(*jt + " ");
0464                     if (withAllSupportedEntry) {
0465                         ret[0].append(*jt + " ");
0466                     }
0467                 }
0468             }
0469             oneFilter = mimeType.comment() + " ( " + oneFilter + ")";
0470             ret << oneFilter;
0471             mimeSeen << mimeType.name();
0472         }
0473     }
0474 
0475     if (withAllSupportedEntry) {
0476         ret[0] = i18n("All supported formats") + " ( " + ret[0] + (")");
0477     }
0478     return ret;
0479 }
0480 
0481 QString KoFileDialog::getUsedDir(const QString &dialogName)
0482 {
0483     if (dialogName.isEmpty()) return "";
0484 
0485     KConfigGroup group =  KSharedConfig::openConfig()->group("File Dialogs");
0486     QString dir = group.readEntry(dialogName);
0487 
0488     return dir;
0489 }
0490 
0491 void KoFileDialog::saveUsedDir(const QString &fileName,
0492                                const QString &dialogName)
0493 {
0494 
0495     if (dialogName.isEmpty()) return;
0496 
0497     QFileInfo fileInfo(fileName);
0498     KConfigGroup group =  KSharedConfig::openConfig()->group("File Dialogs");
0499     group.writeEntry(dialogName, fileInfo.absolutePath());
0500 
0501 }