File indexing completed on 2024-04-28 05:49:09
0001 /* This file is part of the Kate project. 0002 * 0003 * SPDX-FileCopyrightText: 2013 Dominik Haumann <dhaumann.org> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kateprojecttreeviewcontextmenu.h" 0009 #include "filehistorywidget.h" 0010 #include "git/gitutils.h" 0011 #include "katefileactions.h" 0012 #include "kateproject.h" 0013 #include "kateprojectinfoviewterminal.h" 0014 #include "kateprojectitem.h" 0015 #include "kateprojectviewtree.h" 0016 0017 #include <KIO/OpenFileManagerWindowJob> 0018 #include <KLocalizedString> 0019 #include <KPropertiesDialog> 0020 #include <KTerminalLauncherJob> 0021 #include <KTextEditor/Document> 0022 0023 #include <QAction> 0024 #include <QApplication> 0025 #include <QClipboard> 0026 #include <QDir> 0027 #include <QFileInfo> 0028 #include <QIcon> 0029 #include <QInputDialog> 0030 #include <QMenu> 0031 #include <QMessageBox> 0032 #include <QMimeDatabase> 0033 #include <QMimeType> 0034 #include <QStandardPaths> 0035 0036 #include <ktexteditor/application.h> 0037 #include <ktexteditor/editor.h> 0038 0039 static QString getName(QWidget *parent) 0040 { 0041 QInputDialog dlg(parent); 0042 dlg.setLabelText(i18n("Enter name:")); 0043 dlg.setOkButtonText(i18n("Add")); 0044 dlg.setInputMode(QInputDialog::TextInput); 0045 0046 int res = dlg.exec(); 0047 bool suc = res == QDialog::Accepted; 0048 if (!suc || dlg.textValue().isEmpty()) { 0049 return {}; 0050 } 0051 return dlg.textValue(); 0052 } 0053 0054 void KateProjectTreeViewContextMenu::exec(const QString &filename, const QModelIndex &index, const QPoint &pos, KateProjectViewTree *parent) 0055 { 0056 /** 0057 * Create context menu 0058 */ 0059 QMenu menu(parent); 0060 0061 /** 0062 * Copy Path, always available, put that to the top 0063 */ 0064 QAction *copyAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy-path")), i18n("Copy Location")); 0065 0066 QAction *addFile = nullptr; 0067 QAction *addFolder = nullptr; 0068 if (index.data(KateProjectItem::TypeRole).toInt() == KateProjectItem::Directory) { 0069 addFile = menu.addAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Add File")); 0070 addFolder = menu.addAction(QIcon::fromTheme(QStringLiteral("folder-new")), i18n("Add Folder")); 0071 } 0072 0073 // we can ATM only handle file renames 0074 QAction *rename = nullptr; 0075 QAction *fileDelete = nullptr; 0076 if (index.data(KateProjectItem::TypeRole).toInt() == KateProjectItem::File) { 0077 rename = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("&Rename")); 0078 fileDelete = menu.addAction(QIcon::fromTheme(QStringLiteral("delete")), i18n("Delete")); 0079 } 0080 0081 /** 0082 * File Properties Dialog 0083 */ 0084 auto filePropertiesAction = menu.addAction(QIcon::fromTheme(QStringLiteral("dialog-object-properties")), i18n("Properties")); 0085 0086 QUrl url = QUrl::fromLocalFile(filename); 0087 menu.addSeparator(); 0088 QMenu *openWithMenu = menu.addMenu(QIcon::fromTheme(QStringLiteral("system-run")), i18n("Open With")); 0089 KateFileActions::prepareOpenWithMenu(url, openWithMenu); 0090 openWithMenu->setEnabled(!openWithMenu->isEmpty()); 0091 0092 /** 0093 * Open external terminal here 0094 */ 0095 if (KateProjectInfoViewTerminal::isLoadable()) { 0096 menu.addAction(QIcon::fromTheme(QStringLiteral("terminal")), i18n("Open Internal Terminal Here"), [parent, &filename]() { 0097 QFileInfo checkFile(filename); 0098 if (checkFile.isFile()) { 0099 parent->openTerminal(checkFile.absolutePath()); 0100 } else { 0101 parent->openTerminal(filename); 0102 } 0103 }); 0104 } 0105 QAction *terminal = menu.addAction(QIcon::fromTheme(QStringLiteral("utilities-terminal")), i18n("Open External Terminal Here")); 0106 0107 /** 0108 * Open Containing folder 0109 */ 0110 auto openContaingFolderAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18n("&Open Containing Folder")); 0111 0112 /** 0113 * Git history 0114 */ 0115 QAction *fileHistory = nullptr; 0116 QMenu gitMenu; // must live as long as the maybe filled menu items should live 0117 if (GitUtils::isGitRepo(QFileInfo(filename).absolutePath())) { 0118 menu.addSeparator(); 0119 fileHistory = menu.addAction(i18n("Show Git History")); 0120 } 0121 0122 auto handleDeleteFile = [parent, index](const QString &path) { 0123 // message box 0124 const QString title = i18n("Delete File"); 0125 const QString text = i18n("Do you want to delete the file '%1'?", path); 0126 if (QMessageBox::Yes == QMessageBox::question(parent, title, text, QMessageBox::No | QMessageBox::Yes, QMessageBox::Yes)) { 0127 const QList<KTextEditor::Document *> openDocuments = KTextEditor::Editor::instance()->application()->documents(); 0128 0129 // if is open, close 0130 for (auto doc : openDocuments) { 0131 if (doc->url().adjusted(QUrl::RemoveScheme) == QUrl(path).adjusted(QUrl::RemoveScheme)) { 0132 KTextEditor::Editor::instance()->application()->closeDocument(doc); 0133 break; 0134 } 0135 } 0136 parent->removeFile(index, path); 0137 } 0138 }; 0139 0140 /** 0141 * run menu and handle the triggered action 0142 */ 0143 if (QAction *const action = menu.exec(pos)) { 0144 if (action == copyAction) { 0145 QApplication::clipboard()->setText(filename); 0146 } else if (action == terminal) { 0147 // handle "open terminal here" 0148 QFileInfo checkFile(filename); 0149 auto *job = new KTerminalLauncherJob(QString()); 0150 if (checkFile.isFile()) { 0151 job->setWorkingDirectory(checkFile.absolutePath()); 0152 } else { 0153 job->setWorkingDirectory(filename); 0154 } 0155 job->start(); 0156 } else if (action->parent() == openWithMenu) { 0157 KateFileActions::showOpenWithMenu(parent, url, action); 0158 } else if (action == openContaingFolderAction) { 0159 KIO::highlightInFileManager({url}); 0160 } else if (fileDelete && action == fileDelete) { 0161 handleDeleteFile(filename); 0162 } else if (action == filePropertiesAction) { 0163 // code copied and adapted from frameworks/kio/src/filewidgets/knewfilemenu.cpp 0164 KFileItem fileItem(url); 0165 QDialog *dlg = new KPropertiesDialog(fileItem, parent); 0166 dlg->setAttribute(Qt::WA_DeleteOnClose); 0167 dlg->show(); 0168 } else if (rename && action == rename) { 0169 /** 0170 * hack: 0171 * We store a reference to project in the item so that 0172 * after rename we can update file2Item map properly. 0173 */ 0174 KateProjectItem *item = parent->project()->itemForFile(index.data(Qt::UserRole).toString()); 0175 if (!item) { 0176 return; 0177 } 0178 item->setData(QVariant::fromValue(parent->project()), KateProjectItem::ProjectRole); 0179 0180 /** start the edit */ 0181 parent->edit(index); 0182 } else if (action == fileHistory) { 0183 FileHistory::showFileHistory(index.data(Qt::UserRole).toString()); 0184 } else if (addFile && action == addFile) { 0185 QString name = getName(parent); 0186 if (!name.isEmpty()) { 0187 parent->addFile(index, name); 0188 } 0189 } else if (addFolder && action == addFolder) { 0190 QString name = getName(parent); 0191 if (!name.isEmpty()) { 0192 parent->addDirectory(index, name); 0193 } 0194 } else { 0195 // One of the git actions was triggered 0196 } 0197 } 0198 }