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 }