File indexing completed on 2024-05-12 09:55:41
0001 /* This file is part of the KDE project 0002 * 0003 * SPDX-FileCopyrightText: 2018 Gregor Mi <codestruct@posteo.org> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 **/ 0007 0008 #include "katefileactions.h" 0009 0010 #include "hostprocess.h" 0011 0012 #include <ktexteditor/application.h> 0013 #include <ktexteditor/document.h> 0014 #include <ktexteditor/editor.h> 0015 0016 #include <KApplicationTrader> 0017 #include <KIO/ApplicationLauncherJob> 0018 #include <KIO/CopyJob> 0019 #include <KIO/DeleteJob> 0020 #include <KIO/JobUiDelegateFactory> 0021 #include <KIO/OpenFileManagerWindowJob> 0022 #include <KJobWidgets> 0023 #include <KLocalizedString> 0024 #include <KMessageBox> 0025 #include <KPropertiesDialog> 0026 #include <KService> 0027 #include <kio_version.h> 0028 0029 #include <QAction> 0030 #include <QApplication> 0031 #include <QClipboard> 0032 #include <QDir> 0033 #include <QInputDialog> 0034 #include <QMenu> 0035 #include <QMimeDatabase> 0036 #include <QMimeType> 0037 #include <QProcess> 0038 #include <QStandardPaths> 0039 #include <QUrl> 0040 0041 void KateFileActions::copyFilePathToClipboard(KTextEditor::Document *doc) 0042 { 0043 const QUrl url = doc->url(); 0044 // ensure we prefer native separators, bug 381052 0045 QApplication::clipboard()->setText(url.isLocalFile() ? QDir::toNativeSeparators(url.toLocalFile()) : url.url()); 0046 } 0047 0048 void KateFileActions::openContainingFolder(KTextEditor::Document *doc) 0049 { 0050 KIO::highlightInFileManager({doc->url()}); 0051 } 0052 0053 void KateFileActions::openFilePropertiesDialog(QWidget *parent, KTextEditor::Document *doc) 0054 { 0055 KFileItem fileItem(doc->url()); 0056 QDialog *dlg = new KPropertiesDialog(fileItem, parent); 0057 dlg->setAttribute(Qt::WA_DeleteOnClose); 0058 dlg->show(); 0059 } 0060 0061 void KateFileActions::renameDocumentFile(QWidget *parent, KTextEditor::Document *doc) 0062 { 0063 if (!doc) { 0064 return; 0065 } 0066 0067 const QUrl oldFileUrl = doc->url(); 0068 if (oldFileUrl.isEmpty()) { 0069 return; 0070 } 0071 0072 const QString oldFileName = doc->url().fileName(); 0073 bool ok = false; 0074 QString newFileName = QInputDialog::getText(parent, i18n("Rename file"), i18n("New file name"), QLineEdit::Normal, oldFileName, &ok); 0075 if (!ok || (newFileName == oldFileName)) { 0076 return; 0077 } 0078 0079 QUrl newFileUrl = oldFileUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); 0080 newFileUrl.setPath(newFileUrl.path() + QLatin1Char('/') + newFileName); 0081 0082 if (!newFileUrl.isValid()) { 0083 return; 0084 } 0085 0086 if (!doc->closeUrl()) { 0087 return; 0088 } 0089 0090 doc->waitSaveComplete(); 0091 0092 KIO::CopyJob *job = KIO::move(oldFileUrl, newFileUrl); 0093 parent->connect(parent, &QObject::destroyed, job, [job]() { 0094 job->kill(); 0095 }); 0096 0097 // Associate the job with the parent widget, in case of renaming conflicts the ask-user-dialog 0098 // is window-modal by default 0099 KJobWidgets::setWindow(job, parent); 0100 0101 parent->connect(job, &KJob::result, parent, [parent, doc, oldFileUrl](KJob *job) { 0102 auto *copyJob = static_cast<KIO::CopyJob *>(job); 0103 if (!copyJob->error()) { 0104 doc->openUrl(copyJob->destUrl()); 0105 doc->documentSavedOrUploaded(doc, true); 0106 } else { 0107 KMessageBox::error(parent, 0108 i18n("File \"%1\" could not be moved to \"%2\"", 0109 oldFileUrl.toDisplayString(QUrl::PreferLocalFile), 0110 copyJob->destUrl().toDisplayString(QUrl::PreferLocalFile))); 0111 doc->openUrl(oldFileUrl); 0112 } 0113 }); 0114 } 0115 0116 void KateFileActions::deleteDocumentFile(QWidget *parent, KTextEditor::Document *doc) 0117 { 0118 if (!doc) { 0119 return; 0120 } 0121 0122 const auto &&url = doc->url(); 0123 0124 if (url.isEmpty()) { 0125 return; 0126 } 0127 0128 bool go = (KMessageBox::warningContinueCancel(parent, 0129 i18n("Do you really want to delete file \"%1\"?", url.toDisplayString()), 0130 i18n("Delete file"), 0131 KStandardGuiItem::del(), 0132 KStandardGuiItem::cancel(), 0133 QStringLiteral("filetreedeletefile")) 0134 == KMessageBox::Continue); 0135 0136 if (!go) { 0137 return; 0138 } 0139 0140 // If the document is modified, user will be asked if he wants to save it. 0141 // This confirmation is useless when deleting, so we mark the document as unmodified. 0142 doc->setModified(false); 0143 0144 if (!KTextEditor::Editor::instance()->application()->closeDocument(doc)) { 0145 return; // no extra message, the internals of ktexteditor should take care of that. 0146 } 0147 0148 if (url.isValid()) { 0149 KIO::DeleteJob *job = KIO::del(url); 0150 if (!job->exec()) { 0151 KMessageBox::error(parent, i18n("File \"%1\" could not be deleted.", url.toDisplayString())); 0152 } 0153 } 0154 } 0155 0156 QList<std::pair<QString, QString>> KateFileActions::supportedDiffTools() 0157 { 0158 // query once if the tools are there in the path and store that 0159 // we will disable the actions for the tools not found 0160 static QList<std::pair<QString, QString>> resultList{{QStringLiteral("kdiff3"), safeExecutableName(QStringLiteral("kdiff3"))}, 0161 {QStringLiteral("kompare"), safeExecutableName(QStringLiteral("kompare"))}, 0162 {QStringLiteral("meld"), safeExecutableName(QStringLiteral("meld"))}}; 0163 return resultList; 0164 } 0165 0166 bool KateFileActions::compareWithExternalProgram(KTextEditor::Document *documentA, KTextEditor::Document *documentB, const QString &diffExecutable) 0167 { 0168 Q_ASSERT(documentA); 0169 Q_ASSERT(documentB); 0170 0171 QProcess process; 0172 QStringList arguments; 0173 arguments << documentA->url().toLocalFile() << documentB->url().toLocalFile(); 0174 return process.startDetached(diffExecutable, arguments); 0175 } 0176 0177 void KateFileActions::prepareOpenWithMenu(const QUrl &url, QMenu *menu) 0178 { 0179 // dh: in bug #307699, this slot is called when launching the Kate application 0180 // unfortunately, no one ever could reproduce except users. 0181 0182 QMimeDatabase db; 0183 QMimeType mime = db.mimeTypeForUrl(url); 0184 0185 menu->clear(); 0186 0187 // get a list of appropriate services. 0188 const KService::List offers = KApplicationTrader::queryByMimeType(mime.name()); 0189 QAction *a = nullptr; 0190 0191 // add all default open-with-actions except "Kate" 0192 for (const auto &service : offers) { 0193 if (service->name() == QLatin1String("Kate")) { 0194 continue; 0195 } 0196 a = menu->addAction(QIcon::fromTheme(service->icon()), service->name()); 0197 a->setData(service->entryPath()); 0198 } 0199 0200 // append "Other..." to call the KDE "open with" dialog. 0201 menu->addSeparator(); 0202 QAction *other = menu->addAction(QIcon::fromTheme(QStringLiteral("system-run")), i18n("&Other Application...")); 0203 other->setData(QString()); 0204 } 0205 0206 void KateFileActions::showOpenWithMenu(QWidget *parent, const QUrl &url, QAction *action) 0207 { 0208 KService::Ptr app = KService::serviceByDesktopPath(action->data().toString()); 0209 // If app is null, ApplicationLauncherJob will invoke the open-with dialog 0210 auto *job = new KIO::ApplicationLauncherJob(app); 0211 job->setUrls({url}); 0212 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, parent)); 0213 job->start(); 0214 }