kateprojecttreeviewcontextmenu.cpp 8.31 KB
Newer Older
1
2
/*  This file is part of the Kate project.
 *
3
 *  SPDX-FileCopyrightText: 2013 Dominik Haumann <dhaumann.org>
4
 *
5
 *  SPDX-License-Identifier: LGPL-2.0-or-later
6
7
8
 */

#include "kateprojecttreeviewcontextmenu.h"
9
#include "git/gitutils.h"
Waqar Ahmed's avatar
Waqar Ahmed committed
10
#include "kateprojectviewtree.h"
11

Christoph Cullmann's avatar
Christoph Cullmann committed
12
#include <KApplicationTrader>
Ahmad Samir's avatar
Ahmad Samir committed
13
14
#include <KIO/ApplicationLauncherJob>
#include <KIO/JobUiDelegate>
15
#include <KIO/OpenFileManagerWindowJob>
Christoph Cullmann's avatar
Christoph Cullmann committed
16
#include <KLocalizedString>
17
18
#include <KNS3/KMoreTools>
#include <KNS3/KMoreToolsMenuFactory>
19
#include <KPropertiesDialog>
Christoph Cullmann's avatar
Christoph Cullmann committed
20

21
#include <QAction>
22
23
#include <QApplication>
#include <QClipboard>
24
#include <QDir>
25
26
#include <QFileInfo>
#include <QIcon>
27
#include <QInputDialog>
28
#include <QMenu>
29
#include <QMessageBox>
Dominik Haumann's avatar
Dominik Haumann committed
30
#include <QMimeDatabase>
31
32
#include <QMimeType>
#include <QStandardPaths>
33

Krzysztof Stokop's avatar
Krzysztof Stokop committed
34
#include <KToolInvocation>
35
36
37
38
#include <ktexteditor/editor.h>
#include <ktexteditor/application.h>


39

40
static QString getName()
41
{
42
    QInputDialog dlg;
Waqar Ahmed's avatar
Waqar Ahmed committed
43
    dlg.setLabelText(i18n("Enter name:"));
44
45
46
47
48
49
50
    dlg.setOkButtonText(i18n("Add"));
    dlg.setInputMode(QInputDialog::TextInput);

    int res = dlg.exec();
    bool suc = res == QDialog::Accepted;
    if (!suc || dlg.textValue().isEmpty()) {
        return {};
51
    }
52
53
    return dlg.textValue();
}
54

Waqar Ahmed's avatar
Waqar Ahmed committed
55
void KateProjectTreeViewContextMenu::exec(const QString &filename, const QModelIndex &index, const QPoint &pos, KateProjectViewTree *parent)
56
{
57
    /**
58
     * Create context menu
59
60
61
     */
    QMenu menu;

62
63
    QAction *addFile = nullptr;
    QAction *addFolder = nullptr;
64
    QAction* fileDelete = nullptr;
65
    if (index.data(KateProjectItem::TypeRole).toInt() == KateProjectItem::Directory) {
Waqar Ahmed's avatar
Waqar Ahmed committed
66
67
        addFile = menu.addAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Add File"));
        addFolder = menu.addAction(QIcon::fromTheme(QStringLiteral("folder-new")), i18n("Add Folder"));
68
69
    }

70
71
72
    /**
     * Copy Path
     */
73
    QAction *copyAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy File Path"));
Krzysztof Stokop's avatar
Krzysztof Stokop committed
74
    
75
76
77
    /**
     * Open terminal here
     */
Krzysztof Stokop's avatar
Krzysztof Stokop committed
78
    QAction* terminal = menu.addAction(QIcon::fromTheme(QStringLiteral("utilities-terminal")), i18n("Open Terminal Here"));
Krzysztof Stokop's avatar
Krzysztof Stokop committed
79
    
80
    /**
81
     * Handle "open with",
82
83
84
     * find correct mimetype to query for possible applications
     */
    QMenu *openWithMenu = menu.addMenu(i18n("Open With"));
Krzysztof Stokop's avatar
Krzysztof Stokop committed
85
    QMimeType mimeType = QMimeDatabase().mimeTypeForFile(filename);
Nicolas Fella's avatar
Nicolas Fella committed
86
    const KService::List offers = KApplicationTrader::queryByMimeType(mimeType.name());
87
    // For each one, insert a menu item...
88
    for (const auto &service : offers) {
89
        if (service->name() == QLatin1String("Kate")) {
90
            continue; // omit Kate
91
92
93
        }
        QAction *action = openWithMenu->addAction(QIcon::fromTheme(service->icon()), service->name());
        action->setData(service->entryPath());
94
    }
95
96
    // Perhaps disable menu, if no entries
    openWithMenu->setEnabled(!openWithMenu->isEmpty());
97

98
    /**
99
     * Open Containing folder
100
     */
101
    auto openContaingFolderAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18n("&Open Containing Folder"));
102
    
103
104
105
106
107
108
109
    if(index.data(KateProjectItem::TypeRole).toInt() == KateProjectItem::File)
    {
        /**
        * File delete dialog
        */
        fileDelete = menu.addAction(QIcon::fromTheme(QStringLiteral("delete")), i18n("Delete"));
    }
110

111
112
113
114
115
    /**
     * File Properties Dialog
     */
    auto filePropertiesAction = menu.addAction(QIcon::fromTheme(QStringLiteral("dialog-object-properties")), i18n("Properties"));

116
    QAction *fileHistory = nullptr;
117
118
119
    /**
     * Git menu
     */
Laurent Montel's avatar
Laurent Montel committed
120
    KMoreToolsMenuFactory menuFactory(QStringLiteral("kate/addons/project/git-tools"));
Gregor Mi's avatar
Gregor Mi committed
121
    QMenu gitMenu; // must live as long as the maybe filled menu items should live
122
123
124
    if (GitUtils::isGitRepo(QFileInfo(filename).absolutePath())) {
        fileHistory = menu.addAction(i18n("Show File History"));

125
        menuFactory.fillMenuFromGroupingNames(&gitMenu, {QLatin1String("git-clients-and-actions")}, QUrl::fromLocalFile(filename));
126
        menu.addSection(i18n("Git:"));
127
128
        const auto gitActions = gitMenu.actions();
        for (auto action : gitActions) {
129
            menu.addAction(action);
130
        }
131
    }
132

Ahmad Samir's avatar
Ahmad Samir committed
133
134
135
136
137
138
139
140
    auto handleOpenWith = [parent](QAction *action, const QString &filename) {
        KService::Ptr app = KService::serviceByDesktopPath(action->data().toString());
        // If app is null, ApplicationLauncherJob will invoke the open-with dialog
        auto *job = new KIO::ApplicationLauncherJob(app);
        job->setUrls({QUrl::fromLocalFile(filename)});
        job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, parent));
        job->start();
    };
Krzysztof Stokop's avatar
Krzysztof Stokop committed
141
    
142
    auto handleDeleteFile = [parent, index](const QString &path)
143
144
145
146
147
    {
        //message box
        const QString title = i18n("Confirm deleting: %1", path);
        const QString text = i18n("Do you want to delete: %1 ?", path);
        
148
        if (QMessageBox::Yes == QMessageBox::question(parent, title, text, QMessageBox::No | QMessageBox::Yes, QMessageBox::No))
149
        {
Krzysztof Stokop's avatar
Krzysztof Stokop committed
150
            const QList< KTextEditor::Document* > openDocuments = KTextEditor::Editor::instance()->application()->documents();
151
152
153
              
            //if is open, close
            for(auto doc : openDocuments)
154
            {
155
                if(doc->url().adjusted(QUrl::RemoveScheme) == QUrl(path).adjusted(QUrl::RemoveScheme))
156
                {
157
158
                    KTextEditor::Editor::instance()->application()->closeDocument(doc);
                    break;
159
160
                }
            }
161
            parent->removeFile(index, path);
162
163
        }
    };
Ahmad Samir's avatar
Ahmad Samir committed
164

165
166
167
168
169
    // we can ATM only handle file renames
    QAction *rename = nullptr;
    if (index.data(KateProjectItem::TypeRole).toInt() == KateProjectItem::File) {
        rename = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("&Rename"));
    }
170

171
172
173
    /**
     * run menu and handle the triggered action
     */
Krzysztof Stokop's avatar
Krzysztof Stokop committed
174
175
    if (QAction *const action = menu.exec(pos)) {
        if (action == copyAction) {
176
            QApplication::clipboard()->setText(filename);
Krzysztof Stokop's avatar
Krzysztof Stokop committed
177
        } else if (action == terminal) {
178
            // handle "open terminal here"
Krzysztof Stokop's avatar
Krzysztof Stokop committed
179
180
181
182
183
184
185
            QFileInfo checkFile(filename);
            if (QUrl::fromLocalFile(filename).isLocalFile() && checkFile.isFile()) {
                KToolInvocation::invokeTerminal(QString(), {}, QUrl::fromLocalFile(filename).toString(QUrl::RemoveFilename | QUrl::RemoveScheme));
            } if (QUrl::fromLocalFile(filename).isLocalFile() && checkFile.isDir()) {
                KToolInvocation::invokeTerminal(QString(), {}, filename);
            }
        } else if (action->parentWidget() == openWithMenu) {
186
            // handle "open with"
Ahmad Samir's avatar
Ahmad Samir committed
187
            handleOpenWith(action, filename);
Krzysztof Stokop's avatar
Krzysztof Stokop committed
188
        } else if (action == openContaingFolderAction) {
189
            KIO::highlightInFileManager({QUrl::fromLocalFile(filename)});
190
        } else if (fileDelete && action == fileDelete) {
191
            handleDeleteFile(filename);
Krzysztof Stokop's avatar
Krzysztof Stokop committed
192
        } else if (action == filePropertiesAction) {
193
194
            // code copied and adapted from frameworks/kio/src/filewidgets/knewfilemenu.cpp
            KFileItem fileItem(QUrl::fromLocalFile(filename));
195
            QDialog *dlg = new KPropertiesDialog(fileItem);
196
197
            dlg->setAttribute(Qt::WA_DeleteOnClose);
            dlg->show();
Krzysztof Stokop's avatar
Krzysztof Stokop committed
198
        } else if (rename && action == rename) {
Waqar Ahmed's avatar
Waqar Ahmed committed
199
200
201
202
203
204
            /**
             * hack:
             * We store a reference to project in the item so that
             * after rename we can update file2Item map properly.
             */
            KateProjectItem *item = parent->project()->itemForFile(index.data(Qt::UserRole).toString());
Krzysztof Stokop's avatar
Krzysztof Stokop committed
205
            if (!item) {
Waqar Ahmed's avatar
Waqar Ahmed committed
206
207
208
209
210
                return;
            }
            item->setData(QVariant::fromValue(parent->project()), KateProjectItem::ProjectRole);

            /** start the edit */
Waqar Ahmed's avatar
Waqar Ahmed committed
211
            parent->edit(index);
Krzysztof Stokop's avatar
Krzysztof Stokop committed
212
        } else if (action == fileHistory) {
213
            showFileHistory(index.data(Qt::UserRole).toString());
Krzysztof Stokop's avatar
Krzysztof Stokop committed
214
        } else if (addFile && action == addFile) {
215
            QString name = getName();
Krzysztof Stokop's avatar
Krzysztof Stokop committed
216
            if (!name.isEmpty()) {
217
                parent->addFile(index, name);
218
            }
Krzysztof Stokop's avatar
Krzysztof Stokop committed
219
        } else if (addFolder && action == addFolder) {
220
            QString name = getName();
Krzysztof Stokop's avatar
Krzysztof Stokop committed
221
            if (!name.isEmpty()) {
222
                parent->addDirectory(index, name);
223
            }
Krzysztof Stokop's avatar
Krzysztof Stokop committed
224
        } else {
225
226
            // One of the git actions was triggered
        }
227
228
    }
}