Changeset View
Changeset View
Standalone View
Standalone View
plugins/projectserviceurlmapper/kdevprojectserviceurlmapper.cpp
- This file was added.
1 | /* This file is part of KDevelop | ||||
---|---|---|---|---|---|
2 | Copyright 2017 Friedrich W. H. Kossebau <kossebau@kde.org> | ||||
3 | | ||||
4 | This library is free software; you can redistribute it and/or | ||||
5 | modify it under the terms of the GNU Library General Public | ||||
6 | License as published by the Free Software Foundation; either | ||||
7 | version 2 of the License, or (at your option) any later version. | ||||
8 | | ||||
9 | This library is distributed in the hope that it will be useful, | ||||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
12 | Library General Public License for more details. | ||||
13 | | ||||
14 | You should have received a copy of the GNU Library General Public License | ||||
15 | along with this library; see the file COPYING.LIB. If not, write to | ||||
16 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
17 | Boston, MA 02110-1301, USA. | ||||
18 | */ | ||||
19 | | ||||
20 | #include "kdevprojectserviceurlmapper.h" | ||||
21 | | ||||
22 | #include <interfaces/icore.h> | ||||
23 | #include <interfaces/iproject.h> | ||||
24 | #include <interfaces/iprojectcontroller.h> | ||||
25 | #include <interfaces/iplugincontroller.h> | ||||
26 | #include <interfaces/iprojectserviceurlmapper.h> | ||||
27 | #include <interfaces/context.h> | ||||
28 | #include <interfaces/contextmenuextension.h> | ||||
29 | #include <util/path.h> | ||||
30 | #include <language/interfaces/editorcontext.h> | ||||
31 | #include <vcs/vcsrevisioncontext.h> | ||||
32 | #include <vcs/vcsrevision.h> | ||||
33 | | ||||
34 | #include <KPluginFactory> | ||||
35 | #include <KLocalizedString> | ||||
36 | | ||||
37 | #include <QAction> | ||||
38 | #include <QMenu> | ||||
39 | #include <QGuiApplication> | ||||
40 | #include <QClipboard> | ||||
41 | #include <QMimeData> | ||||
42 | #include <QDesktopServices> | ||||
43 | | ||||
44 | using namespace KDevelop; | ||||
45 | | ||||
46 | K_PLUGIN_FACTORY_WITH_JSON(KDevProjectServiceUrlMapperFactory, "kdevprojectserviceurlmapper.json", | ||||
47 | registerPlugin<KDevProjectServiceUrlMapper>();) | ||||
48 | | ||||
49 | KDevProjectServiceUrlMapper::KDevProjectServiceUrlMapper(QObject *parent, const QVariantList& args) | ||||
50 | : KDevelop::IPlugin(QStringLiteral("kdevprojectserviceurlmapper"), parent) | ||||
51 | { | ||||
52 | Q_UNUSED(args); | ||||
53 | } | ||||
54 | | ||||
55 | KDevProjectServiceUrlMapper::~KDevProjectServiceUrlMapper() = default; | ||||
56 | | ||||
57 | QAction* KDevProjectServiceUrlMapper::createMenu(const ProjectServiceUrlData& urlData, QWidget* parent) | ||||
58 | { | ||||
59 | auto menuAction = new QAction(urlData.icon, urlData.name, parent); | ||||
60 | QMenu* menu = new QMenu(parent); | ||||
61 | menuAction->setMenu(menu); | ||||
62 | | ||||
63 | auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("document-open")), i18n("Open Link")); | ||||
64 | action->setData(urlData.url); | ||||
65 | connect(action, &QAction::triggered, this, &KDevProjectServiceUrlMapper::openLink); | ||||
66 | | ||||
67 | action = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy Link")); | ||||
68 | action->setData(urlData.url); | ||||
69 | connect(action, &QAction::triggered, this, &KDevProjectServiceUrlMapper::copyLink); | ||||
70 | | ||||
71 | return menuAction; | ||||
72 | } | ||||
73 | | ||||
74 | KDevelop::ContextMenuExtension KDevProjectServiceUrlMapper::contextMenuExtension(KDevelop::Context* context, | ||||
75 | QWidget* parent) | ||||
76 | { | ||||
77 | ContextMenuExtension extension; | ||||
78 | | ||||
79 | if (context->hasType(Context::EditorContext)) { | ||||
80 | auto editorContext = dynamic_cast<EditorContext*>(context); | ||||
81 | if (!editorContext) { | ||||
82 | return extension; | ||||
83 | } | ||||
84 | | ||||
85 | const QList<IPlugin*> plugins = ICore::self()->pluginController()->allPluginsForExtension("org.kdevelop.IProjectServiceUrlMapper"); | ||||
86 | if (plugins.isEmpty()) { | ||||
87 | return extension; | ||||
88 | } | ||||
89 | | ||||
90 | const QUrl fileUrl = editorContext->url(); | ||||
91 | IProject* project = ICore::self()->projectController()->findProjectForUrl(fileUrl); | ||||
92 | if (!project) { | ||||
93 | return extension; | ||||
94 | } | ||||
95 | | ||||
96 | const QString relativeFilePath = project->path().relativePath(Path(fileUrl)); | ||||
97 | if (relativeFilePath.isEmpty() || relativeFilePath.startsWith(QLatin1String("../"))) { | ||||
98 | return extension; | ||||
99 | } | ||||
100 | | ||||
101 | // TODO: extend EditorContext to also deliver any current selection & extend IProjectServiceUrlMapper | ||||
102 | // to also support selection urls, for those services like phabricator which support it | ||||
103 | const KTextEditor::Cursor position = editorContext->position(); | ||||
104 | | ||||
105 | QList<QAction*> actions; | ||||
106 | | ||||
107 | for (IPlugin* plugin : plugins) { | ||||
108 | IProjectServiceUrlMapper* mapper = plugin->extension<IProjectServiceUrlMapper>(); | ||||
109 | Q_ASSERT(mapper); | ||||
110 | const QVector<ProjectServiceUrlData> projectServiceUrls = mapper->projectServiceUrlsFromTextSelection(fileUrl, position, project); | ||||
111 | | ||||
112 | for (const auto& projectServiceUrlData : projectServiceUrls) { | ||||
113 | actions << createMenu(projectServiceUrlData, parent); | ||||
114 | } | ||||
115 | } | ||||
116 | | ||||
117 | if (!actions.isEmpty()) { | ||||
118 | auto menuAction = new QAction(QIcon::fromTheme(QStringLiteral("link")), i18n("Document in Services"), parent); | ||||
119 | QMenu* menu = new QMenu(parent); | ||||
120 | menuAction->setMenu(menu); | ||||
121 | for (QAction* action : actions) { | ||||
122 | menu->addAction(action); | ||||
123 | } | ||||
124 | extension.addAction(ContextMenuExtension::ExtensionGroup, menuAction); | ||||
125 | } | ||||
126 | } else if (context->hasType(Context::VcsRevisionContext)) { | ||||
127 | auto vcsRevisionContext = dynamic_cast<VcsRevisionContext*>(context); | ||||
128 | if (!vcsRevisionContext) { | ||||
129 | return extension; | ||||
130 | } | ||||
131 | | ||||
132 | const QList<IPlugin*> plugins = ICore::self()->pluginController()->allPluginsForExtension("org.kdevelop.IProjectServiceUrlMapper"); | ||||
133 | if (plugins.isEmpty()) { | ||||
134 | return extension; | ||||
135 | } | ||||
136 | | ||||
137 | IProject* project = vcsRevisionContext->project(); | ||||
138 | if (!project) { | ||||
139 | return extension; | ||||
140 | } | ||||
141 | const auto revision = vcsRevisionContext->revision(); | ||||
142 | | ||||
143 | | ||||
144 | const auto urls = vcsRevisionContext->urls(); | ||||
145 | if (urls.count() == 1) { | ||||
146 | const auto& url = urls.at(0); | ||||
147 | QList<QAction*> perFileActions; | ||||
148 | | ||||
149 | for (IPlugin* plugin : plugins) { | ||||
150 | IProjectServiceUrlMapper* mapper = plugin->extension<IProjectServiceUrlMapper>(); | ||||
151 | Q_ASSERT(mapper); | ||||
152 | const QVector<ProjectServiceUrlData> fileServiceUrls = mapper->projectServiceUrlsFromVcsRevision(url, revision, project); | ||||
153 | | ||||
154 | for (const auto& fileServiceUrlData : fileServiceUrls) { | ||||
155 | perFileActions << createMenu(fileServiceUrlData, parent); | ||||
156 | } | ||||
157 | } | ||||
158 | | ||||
159 | if (!perFileActions.isEmpty()) { | ||||
160 | auto menuAction = new QAction(QIcon::fromTheme(QStringLiteral("link")), i18n("Revision in Services"), parent); | ||||
161 | QMenu* menu = new QMenu(parent); | ||||
162 | menuAction->setMenu(menu); | ||||
163 | for (QAction* action : perFileActions) { | ||||
164 | menu->addAction(action); | ||||
165 | } | ||||
166 | extension.addAction(ContextMenuExtension::VcsGroup, menuAction); | ||||
167 | } | ||||
168 | } | ||||
169 | | ||||
170 | QList<QAction*> perProjectactions; | ||||
171 | | ||||
172 | for (IPlugin* plugin : plugins) { | ||||
173 | IProjectServiceUrlMapper* mapper = plugin->extension<IProjectServiceUrlMapper>(); | ||||
174 | Q_ASSERT(mapper); | ||||
175 | const QVector<ProjectServiceUrlData> projectServiceUrls = mapper->projectServiceUrlsFromVcsRevision(revision, project); | ||||
176 | | ||||
177 | for (const auto& projectServiceUrlData : projectServiceUrls) { | ||||
178 | perProjectactions << createMenu(projectServiceUrlData, parent); | ||||
179 | } | ||||
180 | } | ||||
181 | | ||||
182 | if (!perProjectactions.isEmpty()) { | ||||
183 | auto menuAction = new QAction(QIcon::fromTheme(QStringLiteral("link")), i18n("Revision in Services (All Files)"), parent); | ||||
184 | QMenu* menu = new QMenu(parent); | ||||
185 | menuAction->setMenu(menu); | ||||
186 | for (QAction* action : perProjectactions) { | ||||
187 | menu->addAction(action); | ||||
188 | } | ||||
189 | extension.addAction(ContextMenuExtension::VcsGroup, menuAction); | ||||
190 | } | ||||
191 | } | ||||
192 | | ||||
193 | return extension; | ||||
194 | } | ||||
195 | | ||||
196 | | ||||
197 | void KDevProjectServiceUrlMapper::copyLink() | ||||
198 | { | ||||
199 | auto action = qobject_cast<QAction*>(sender()); | ||||
200 | if (!action) { | ||||
201 | return; | ||||
202 | } | ||||
203 | | ||||
204 | const auto url = action->data().toUrl(); | ||||
205 | if (!url.isValid()) { | ||||
206 | return; | ||||
207 | } | ||||
208 | | ||||
209 | QMimeData* data = new QMimeData; | ||||
210 | data->setUrls(QList<QUrl>{url}); // implicitely also sets text | ||||
211 | QClipboard *clipboard = QGuiApplication::clipboard(); | ||||
212 | // Make sure the url is pastable both with Ctrl-C and with MMB | ||||
213 | clipboard->setMimeData(data, QClipboard::Clipboard); | ||||
214 | clipboard->setMimeData(data, QClipboard::Selection); | ||||
215 | } | ||||
216 | | ||||
217 | | ||||
218 | void KDevProjectServiceUrlMapper::openLink() | ||||
219 | { | ||||
220 | auto action = qobject_cast<QAction*>(sender()); | ||||
221 | if (!action) { | ||||
222 | return; | ||||
223 | } | ||||
224 | | ||||
225 | const auto url = action->data().toUrl(); | ||||
226 | if (!url.isValid()) { | ||||
227 | return; | ||||
228 | } | ||||
229 | | ||||
230 | QDesktopServices::openUrl(url); | ||||
231 | } | ||||
232 | | ||||
233 | | ||||
234 | // needed for QObject class created from K_PLUGIN_FACTORY_WITH_JSON | ||||
235 | #include "kdevprojectserviceurlmapper.moc" |