// -*- c++ -*- /* This file is part of the KDE libraries Copyright (C) 1997, 1998 Richard Moore 1998 Stephan Kulow 1998 Daniel Grana 1999,2000,2001,2002,2003 Carsten Pfeiffer 2003 Clarence Dang 2008 Jarosław Staniek 2009 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfiledialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kabstractfilewidget.h" #include "kabstractfilemodule.h" #include "krecentdirs.h" #include "kservice.h" static KAbstractFileModule* s_module = 0; static const char s_defaultFileModuleName[] = "kfilemodule"; static KAbstractFileModule* loadFileModule( const QString& moduleName ) { KService::Ptr fileModuleService = KService::serviceByDesktopName(moduleName); if (fileModuleService) { return fileModuleService->createInstance(); } return nullptr; } static KAbstractFileModule* fileModule() { if (!s_module) { QString moduleName = KConfig("kdeglobals").group(ConfigGroup).readEntry("file module", s_defaultFileModuleName); if (!(s_module = loadFileModule(moduleName))) { kDebug() << "Failed to load configured file module" << moduleName; if (moduleName != s_defaultFileModuleName) { kDebug() << "Falling back to default file module."; s_module = loadFileModule(s_defaultFileModuleName); } } } return s_module; } class KFileDialogPrivate { public: KFileDialogPrivate() : w(nullptr), cfgGroup(KGlobal::config(), ConfigGroup) { } KAbstractFileWidget* w; KConfigGroup cfgGroup; }; KFileDialog::KFileDialog(const KUrl &startDir, const QString &filter, QWidget* parent, QWidget* customWidget) : KDialog(parent), d(new KFileDialogPrivate()) { QWidget* fileQWidget = fileModule()->createFileWidget(startDir, this); d->w = ::qobject_cast(fileQWidget); setButtons( KDialog::None ); restoreDialogSize(d->cfgGroup); // call this before the fileQWidget is set as the main widget. // otherwise the sizes for the components are not obeyed (ereslibre) d->w->setFilter(filter); setMainWidget(fileQWidget); d->w->okButton()->show(); connect(d->w->okButton(), SIGNAL(clicked()), SLOT(slotOk())); d->w->cancelButton()->show(); connect(d->w->cancelButton(), SIGNAL(clicked()), SLOT(slotCancel())); // Publish signals // TODO: Move the relevant signal declarations from KFileWidget to the // KAbstractFileWidget interface? // Else, all of these connects (including "accepted") are not typesafe. // Answer: you cannot define signals in a non-qobject base class (DF). // I simply documentde them in kabstractfilewidget.h now. kDebug (kfile_area) << "KFileDialog connecting signals"; connect(fileQWidget, SIGNAL(fileSelected(KUrl)), SIGNAL(fileSelected(KUrl))); connect(fileQWidget, SIGNAL(fileHighlighted(KUrl)), SIGNAL(fileHighlighted(KUrl))); connect(fileQWidget, SIGNAL(selectionChanged()), SIGNAL(selectionChanged())); connect(fileQWidget, SIGNAL(filterChanged(QString)), SIGNAL(filterChanged(QString))); connect(fileQWidget, SIGNAL(accepted()), SLOT(accept())); //connect(fileQWidget, SIGNAL(canceled()), SLOT(slotCancel())); if (customWidget) { d->w->setCustomWidget(QString(), customWidget); } } KFileDialog::~KFileDialog() { delete d; } void KFileDialog::setLocationLabel(const QString &text) { d->w->setLocationLabel(text); } void KFileDialog::setFilter(const QString &filter) { d->w->setFilter(filter); } QString KFileDialog::currentFilter() const { return d->w->currentFilter(); } void KFileDialog::setMimeFilter(const QStringList &mimeTypes, const QString &defaultType) { d->w->setMimeFilter(mimeTypes, defaultType); } void KFileDialog::clearFilter() { d->w->clearFilter(); } QString KFileDialog::currentMimeFilter() const { return d->w->currentMimeFilter(); } KMimeType::Ptr KFileDialog::currentFilterMimeType() { return KMimeType::mimeType(currentMimeFilter()); } void KFileDialog::setPreviewWidget(KPreviewWidgetBase *w) { d->w->setPreviewWidget(w); } void KFileDialog::setInlinePreviewShown(bool show) { d->w->setInlinePreviewShown(show); } // This is only used for the initial size when no configuration has been saved QSize KFileDialog::sizeHint() const { int fontSize = fontMetrics().height(); QSize goodSize(48 * fontSize, 30 * fontSize); QSize screenSize = QApplication::desktop()->availableGeometry(this).size(); QSize minSize(screenSize / 2); QSize maxSize(screenSize * qreal(0.9)); return (goodSize.expandedTo(minSize).boundedTo(maxSize)); } // This slot still exists mostly for compat purposes; for subclasses which reimplement slotOk void KFileDialog::slotOk() { d->w->slotOk(); } // This slot still exists mostly for compat purposes; for subclasses which reimplement accept void KFileDialog::accept() { setResult(QDialog::Accepted); // keep old behavior; probably not needed though d->w->accept(); KConfigGroup cfgGroup(KGlobal::config(), ConfigGroup); KDialog::accept(); emit okClicked(); } // This slot still exists mostly for compat purposes; for subclasses which reimplement slotCancel void KFileDialog::slotCancel() { d->w->slotCancel(); reject(); } void KFileDialog::setUrl(const KUrl &url, bool clearforward) { d->w->setUrl(url, clearforward); } void KFileDialog::setSelection(const QString &name) { d->w->setSelection(name); } QString KFileDialog::getOpenFileName(const KUrl &startDir, const QString &filter, QWidget *parent, const QString &caption) { KFileDialog dlg(startDir, filter, parent); dlg.setOperationMode( KFileDialog::Opening ); dlg.setMode(KFile::File | KFile::LocalOnly | KFile::ExistingOnly); dlg.setCaption(caption.isEmpty() ? i18n("Open") : caption); dlg.exec(); return dlg.selectedFile(); } QString KFileDialog::getOpenFileNameWId(const KUrl& startDir, const QString &filter, WId parent_id, const QString &caption) { QWidget* parent = QWidget::find(parent_id); KFileDialog dlg(startDir, filter, parent); if (parent == NULL && parent_id != 0) { KWindowSystem::setMainWindow(&dlg, parent_id); } dlg.setOperationMode(KFileDialog::Opening ); dlg.setMode( KFile::File | KFile::LocalOnly | KFile::ExistingOnly); dlg.setCaption(caption.isEmpty() ? i18n("Open") : caption); dlg.exec(); return dlg.selectedFile(); } QStringList KFileDialog::getOpenFileNames(const KUrl &startDir, const QString &filter, QWidget *parent, const QString &caption) { KFileDialog dlg(startDir, filter, parent); dlg.setOperationMode(KFileDialog::Opening); dlg.setMode(KFile::Files | KFile::LocalOnly | KFile::ExistingOnly); dlg.setCaption(caption.isEmpty() ? i18n("Open") : caption); dlg.exec(); return dlg.selectedFiles(); } KUrl KFileDialog::getOpenUrl(const KUrl &startDir, const QString &filter, QWidget *parent, const QString &caption) { KFileDialog dlg(startDir, filter, parent); dlg.setOperationMode(KFileDialog::Opening); dlg.setMode(KFile::File | KFile::ExistingOnly); dlg.setCaption(caption.isEmpty() ? i18n("Open") : caption); dlg.exec(); return dlg.selectedUrl(); } KUrl::List KFileDialog::getOpenUrls(const KUrl &startDir, const QString &filter, QWidget *parent, const QString &caption) { KFileDialog dlg(startDir, filter, parent); dlg.setOperationMode( KFileDialog::Opening ); dlg.setMode( KFile::Files | KFile::ExistingOnly); dlg.setCaption(caption.isEmpty() ? i18n("Open") : caption); dlg.exec(); return dlg.selectedUrls(); } void KFileDialog::setConfirmOverwrite(bool enable) { if (operationMode() == KFileDialog::Saving) { d->w->setConfirmOverwrite(enable); } } KUrl KFileDialog::getExistingDirectoryUrl(const KUrl &startDir, QWidget *parent, const QString &caption) { return fileModule()->selectDirectory(startDir, false, parent, caption); } QString KFileDialog::getExistingDirectory(const KUrl &startDir, QWidget *parent, const QString &caption) { KUrl url = fileModule()->selectDirectory(startDir, true, parent, caption); if (url.isValid()) { return url.toLocalFile(); } return QString(); } KUrl KFileDialog::getImageOpenUrl(const KUrl &startDir, QWidget *parent, const QString &caption) { const QStringList mimetypes = KImageIO::mimeTypes(KImageIO::Reading); KFileDialog dlg(startDir, mimetypes.join(" "), parent); dlg.setOperationMode(KFileDialog::Opening); dlg.setMode(KFile::File | KFile::ExistingOnly); dlg.setCaption(caption.isEmpty() ? i18n("Open") : caption); dlg.setInlinePreviewShown(true); dlg.exec(); return dlg.selectedUrl(); } KUrl KFileDialog::selectedUrl() const { return d->w->selectedUrl(); } KUrl::List KFileDialog::selectedUrls() const { return d->w->selectedUrls(); } QString KFileDialog::selectedFile() const { return d->w->selectedFile(); } QStringList KFileDialog::selectedFiles() const { return d->w->selectedFiles(); } KUrl KFileDialog::baseUrl() const { return d->w->baseUrl(); } QString KFileDialog::getSaveFileName(const KUrl &dir, const QString &filter, QWidget *parent, const QString &caption, Options options) { KFileDialog dlg(dir, filter, parent); dlg.setOperationMode(KFileDialog::Saving); dlg.setMode(KFile::File | KFile::LocalOnly); dlg.setConfirmOverwrite(options & KFileDialog::ConfirmOverwrite); dlg.setInlinePreviewShown(options & KFileDialog::ShowInlinePreview); dlg.setCaption(caption.isEmpty() ? i18n("Save As") : caption); dlg.exec(); QString filename = dlg.selectedFile(); if (!filename.isEmpty()) { KRecentDocument::add(filename); } return filename; } QString KFileDialog::getSaveFileNameWId(const KUrl &dir, const QString &filter, WId parent_id, const QString &caption, Options options) { QWidget* parent = QWidget::find(parent_id); KFileDialog dlg(dir, filter, parent); if (parent == NULL && parent_id != 0) { KWindowSystem::setMainWindow(&dlg, parent_id); } dlg.setOperationMode(KFileDialog::Saving); dlg.setMode(KFile::File | KFile::LocalOnly); dlg.setConfirmOverwrite(options & ConfirmOverwrite); dlg.setInlinePreviewShown(options & ShowInlinePreview); dlg.setCaption(caption.isEmpty() ? i18n("Save As") : caption); dlg.exec(); QString filename = dlg.selectedFile(); if (!filename.isEmpty()) { KRecentDocument::add(filename); } return filename; } KUrl KFileDialog::getSaveUrl(const KUrl &dir, const QString &filter, QWidget *parent, const QString &caption, Options options) { KFileDialog dlg(dir, filter, parent); dlg.setOperationMode(KFileDialog::Saving); dlg.setMode(KFile::File); dlg.setConfirmOverwrite(options & KFileDialog::ConfirmOverwrite); dlg.setInlinePreviewShown(options & KFileDialog::ShowInlinePreview); dlg.setCaption(caption.isEmpty() ? i18n("Save As") : caption); dlg.exec(); KUrl url = dlg.selectedUrl(); if (url.isValid()) { KRecentDocument::add(url); } return url; } void KFileDialog::setMode(KFile::Modes m) { d->w->setMode(m); } KFile::Modes KFileDialog::mode() const { return d->w->mode(); } KPushButton * KFileDialog::okButton() const { return d->w->okButton(); } KPushButton * KFileDialog::cancelButton() const { return d->w->cancelButton(); } KUrlComboBox* KFileDialog::locationEdit() const { return d->w->locationEdit(); } KFileFilterCombo* KFileDialog::filterWidget() const { return d->w->filterWidget(); } KActionCollection * KFileDialog::actionCollection() const { return d->w->actionCollection(); } void KFileDialog::setKeepLocation(bool keep) { d->w->setKeepLocation(keep); } bool KFileDialog::keepsLocation() const { return d->w->keepsLocation(); } void KFileDialog::setOperationMode(OperationMode mode) { d->w->setOperationMode(static_cast(mode)); } KFileDialog::OperationMode KFileDialog::operationMode() const { return static_cast(d->w->operationMode()); } void KFileDialog::keyPressEvent(QKeyEvent* e) { if (e->key() == Qt::Key_Escape) { e->accept(); d->w->cancelButton()->animateClick(); } else { KDialog::keyPressEvent(e); } } void KFileDialog::hideEvent(QHideEvent* e) { saveDialogSize(d->cfgGroup, KConfigBase::Persistent); KDialog::hideEvent(e); } // static KUrl KFileDialog::getStartUrl(const KUrl &startDir, QString &recentDirClass) { return fileModule()->getStartUrl(startDir, recentDirClass); } void KFileDialog::setStartDir(const KUrl& directory) { fileModule()->setStartDir(directory); } KToolBar* KFileDialog::toolBar() const { return d->w->toolBar(); } KAbstractFileWidget* KFileDialog::fileWidget() { return d->w; } QT_BEGIN_NAMESPACE typedef QString (*_qt_filedialog_existing_directory_hook)(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options); extern _qt_filedialog_existing_directory_hook qt_filedialog_existing_directory_hook; typedef QString (*_qt_filedialog_open_filename_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); extern _qt_filedialog_open_filename_hook qt_filedialog_open_filename_hook; typedef QStringList (*_qt_filedialog_open_filenames_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); extern _qt_filedialog_open_filenames_hook qt_filedialog_open_filenames_hook; typedef QString (*_qt_filedialog_save_filename_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); extern _qt_filedialog_save_filename_hook qt_filedialog_save_filename_hook; QT_END_NAMESPACE /* * This class is used to override Qt's QFileDialog calls with KFileDialog ones. * This is necessary because QPrintDialog calls QFileDialog::getSaveFileName() for * the print to file function. */ class KFileDialogQtOverride { public: KFileDialogQtOverride() { if(!qt_filedialog_existing_directory_hook) qt_filedialog_existing_directory_hook=&getExistingDirectory; if(!qt_filedialog_open_filename_hook) qt_filedialog_open_filename_hook=&getOpenFileName; if(!qt_filedialog_open_filenames_hook) qt_filedialog_open_filenames_hook=&getOpenFileNames; if(!qt_filedialog_save_filename_hook) qt_filedialog_save_filename_hook=&getSaveFileName; } ~KFileDialogQtOverride() { if(qt_filedialog_existing_directory_hook == &getExistingDirectory) qt_filedialog_existing_directory_hook = 0; if(qt_filedialog_open_filename_hook == &getOpenFileName) qt_filedialog_open_filename_hook = 0; if(qt_filedialog_open_filenames_hook == &getOpenFileNames) qt_filedialog_open_filenames_hook=0; if(qt_filedialog_save_filename_hook == &getSaveFileName) qt_filedialog_save_filename_hook=0; } /* * Map a Katie filter string into a KDE one. */ static QString qt2KdeFilter(const QString &f) { QString filter; QTextStream str(&filter, QIODevice::WriteOnly); QStringList list(f.split(";;").replaceInStrings("/", "\\/")); QStringList::const_iterator it(list.begin()), end(list.end()); bool first=true; for(; it!=end; ++it) { int ob=(*it).lastIndexOf('('), cb=(*it).lastIndexOf(')'); if(-1!=cb && ob0 && ('('==(*it)[pos-1] || ' '==(*it)[pos-1]) && (*it).length()>=kde.length()+pos && (')'==(*it)[pos+kde.length()] || ' '==(*it)[pos+kde.length()])) { *sel=*it; return; } } } static QString getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options) { fileModule(); // make sure i18n is initialized properly, needed for pure Qt applications KUrl url(KFileDialog::getExistingDirectory(KUrl(dir), parent, caption)); if (url.isLocalFile()) { return url.pathOrUrl(); } return QString(); } static QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { fileModule(); // make sure i18n is initialized properly, needed for pure Qt applications KFileDialog dlg(KUrl(dir), qt2KdeFilter(filter), parent); dlg.setOperationMode(KFileDialog::Opening); dlg.setMode(KFile::File|KFile::LocalOnly); dlg.setCaption(caption); dlg.exec(); QString rv(dlg.selectedFile()); if (!rv.isEmpty()) { kde2QtFilter(filter, dlg.currentFilter(), selectedFilter); } return rv; } static QStringList getOpenFileNames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { fileModule(); // make sure i18n is initialized properly, needed for pure Qt applications KFileDialog dlg(KUrl(dir), qt2KdeFilter(filter), parent); dlg.setOperationMode(KFileDialog::Opening); dlg.setMode(KFile::Files|KFile::LocalOnly); dlg.setCaption(caption); dlg.exec(); QStringList rv(dlg.selectedFiles()); if (rv.count()) { kde2QtFilter(filter, dlg.currentFilter(), selectedFilter); } return rv; } static QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { fileModule(); // make sure i18n is initialized properly, needed for pure Qt applications KFileDialog dlg(KUrl(dir), qt2KdeFilter(filter), parent); dlg.setOperationMode(KFileDialog::Saving); dlg.setMode(KFile::File|KFile::LocalOnly); dlg.setCaption(caption); dlg.setConfirmOverwrite(!(options & QFileDialog::DontConfirmOverwrite)); dlg.exec(); QString rv(dlg.selectedFile()); if (!rv.isEmpty()) { kde2QtFilter(filter, dlg.currentFilter(), selectedFilter); } return rv; } }; static KFileDialogQtOverride qtOverride; #include "moc_kfiledialog.cpp"