1 | /* This file is part of the KDE project |
2 | Copyright (C) 2014 Martin Sandsmark <martin.sandsmark@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 "replicodeplugin.h" |
21 | #include "replicodeconfigpage.h" |
22 | #include "replicodesettings.h" |
23 | #include "replicodeconfig.h" |
24 | |
25 | #include <QtGlobal> |
26 | #include <QProcess> |
27 | #include <QTemporaryFile> |
28 | #include <QPushButton> |
29 | |
30 | #include <KPluginFactory> |
31 | #include <KPluginLoader> |
32 | #include <KLocale> |
33 | #include <KAction> |
34 | #include <KActionCollection> |
35 | #include <KAboutData> |
36 | #include <kate/application.h> |
37 | #include <QDebug> |
38 | #include <qfileinfo.h> |
39 | #include <kate/mainwindow.h> |
40 | #include <QMessageBox> |
41 | #include <KGlobal> |
42 | #include <KConfigGroup> |
43 | #include <KTextEditor/View> |
44 | #include <KTextEditor/Document> |
45 | #include <QListWidget> |
46 | #include <QVBoxLayout> |
47 | #include <QFormLayout> |
48 | #include <QProcess> |
49 | |
50 | K_PLUGIN_FACTORY(KateReplicodeFactory, registerPlugin<ReplicodePlugin>();) |
51 | K_EXPORT_PLUGIN(KateReplicodeFactory("katereplicodeplugin" )) |
52 | |
53 | ReplicodePlugin::ReplicodePlugin(QObject* parent, const QList< QVariant > &args) |
54 | : Kate::Plugin(qobject_cast<Kate::Application*>(parent)) |
55 | { |
56 | Q_UNUSED(args); |
57 | } |
58 | |
59 | ReplicodePlugin::~ReplicodePlugin() |
60 | { |
61 | } |
62 | uint ReplicodePlugin::configPages() const |
63 | { |
64 | return 1; |
65 | } |
66 | |
67 | QString ReplicodePlugin::configPageFullName(uint number) const |
68 | { |
69 | Q_ASSERT(number == 0); |
70 | Q_UNUSED(number); |
71 | return i18n("Replicode settings" ); |
72 | } |
73 | |
74 | QString ReplicodePlugin::configPageName(uint number) const |
75 | { |
76 | Q_ASSERT(number == 0); |
77 | Q_UNUSED(number); |
78 | return i18n("Replicode" ); |
79 | } |
80 | |
81 | |
82 | KIcon ReplicodePlugin::configPageIcon(uint number) const |
83 | { |
84 | Q_UNUSED(number); |
85 | return KIcon("code-block" ); |
86 | } |
87 | |
88 | Kate::PluginConfigPage* ReplicodePlugin::configPage(uint number, QWidget* parent, const char* name) |
89 | { |
90 | Q_UNUSED(name); |
91 | Q_UNUSED(number); |
92 | Q_ASSERT(number == 0); |
93 | return new ReplicodeConfigPage(parent, name); |
94 | } |
95 | |
96 | ReplicodeView::ReplicodeView(Kate::MainWindow* mainWindow) |
97 | : Kate::PluginView(mainWindow), Kate::XMLGUIClient(KateReplicodeFactory::componentData()), m_mainWindow(mainWindow), m_settingsFile(0), m_executor(0) |
98 | { |
99 | m_runAction = new KAction(KIcon("code-block" ), i18n("Run replicode" ), this); |
100 | m_runAction->setShortcut(Qt::Key_F8); |
101 | connect(m_runAction, SIGNAL(triggered()), SLOT(runReplicode())); |
102 | actionCollection()->addAction("katereplicode_run" , m_runAction); |
103 | |
104 | m_stopAction = new KAction(KIcon("process-stop" ), i18n("Stop replicode" ), this); |
105 | m_stopAction->setShortcut(Qt::Key_F9); |
106 | connect(m_stopAction, SIGNAL(triggered()), SLOT(stopReplicode())); |
107 | actionCollection()->addAction("katereplicode_stop" , m_stopAction); |
108 | m_stopAction->setEnabled(false); |
109 | |
110 | m_toolview = mainWindow->createToolView("kate_private_plugin_katereplicodeplugin_run" , Kate::MainWindow::Bottom, SmallIcon("code-block" ), i18n("Replicode Output" )); |
111 | m_replicodeOutput = new QListWidget(m_toolview); |
112 | m_replicodeOutput->setSelectionMode(QAbstractItemView::ContiguousSelection); |
113 | connect(m_replicodeOutput, SIGNAL(itemActivated(QListWidgetItem*)), SLOT(outputClicked(QListWidgetItem*))); |
114 | mainWindow->hideToolView(m_toolview); |
115 | |
116 | m_configSidebar = mainWindow->createToolView("kate_private_plugin_katereplicodeplugin_config" , Kate::MainWindow::Right, SmallIcon("code-block" ), i18n("Replicode Config" )); |
117 | m_configView = new ReplicodeConfig(m_configSidebar); |
118 | |
119 | m_runButton = new QPushButton(i18nc("shortcut for action" , "Run (%1)" , m_runAction->shortcut().toString())); |
120 | m_stopButton = new QPushButton(i18nc("shortcut for action" , "Stop (%1)" , m_stopAction->shortcut().toString())); |
121 | m_stopButton->setEnabled(false); |
122 | |
123 | QFormLayout *l = qobject_cast<QFormLayout*>(m_configView->widget(0)->layout()); |
124 | Q_ASSERT(l); |
125 | l->addRow(m_runButton, m_stopButton); |
126 | connect(m_runButton, SIGNAL(clicked()), m_runAction, SLOT(trigger())); |
127 | connect(m_stopButton, SIGNAL(clicked()), m_stopAction, SLOT(trigger())); |
128 | |
129 | m_mainWindow->guiFactory()->addClient(this); |
130 | connect(m_mainWindow, SIGNAL(viewChanged()), SLOT(viewChanged())); |
131 | } |
132 | |
133 | ReplicodeView::~ReplicodeView() |
134 | { |
135 | m_mainWindow->guiFactory()->removeClient(this); |
136 | delete m_executor; |
137 | delete m_settingsFile; |
138 | } |
139 | |
140 | void ReplicodeView::viewChanged() |
141 | { |
142 | if (m_mainWindow->activeView() && m_mainWindow->activeView()->document() && m_mainWindow->activeView()->document()->url().fileName().endsWith(".replicode" )) { |
143 | m_mainWindow->showToolView(m_configSidebar); |
144 | } else { |
145 | m_mainWindow->hideToolView(m_configSidebar); |
146 | m_mainWindow->hideToolView(m_toolview); |
147 | } |
148 | } |
149 | |
150 | void ReplicodeView::runReplicode() |
151 | { |
152 | m_mainWindow->showToolView(m_toolview); |
153 | KTextEditor::View *editor = m_mainWindow->activeView(); |
154 | if (!editor || !editor->document()) { |
155 | QMessageBox::warning(m_mainWindow->centralWidget(), i18n("Unable to find active file" ), i18n("Can't find active file to run!" )); |
156 | return; |
157 | } |
158 | |
159 | if (editor->document()->isEmpty()) { |
160 | QMessageBox::warning(m_mainWindow->centralWidget(), i18n("Empty document" ), i18n("Can't execute an empty document" )); |
161 | return; |
162 | } |
163 | |
164 | QFileInfo sourceFile = QFileInfo(editor->document()->url().toLocalFile()); |
165 | |
166 | if (!sourceFile.isReadable()) { |
167 | QMessageBox::warning(m_mainWindow->centralWidget(), i18n("No file" ), i18n("Unable to open source file for reading." )); |
168 | return; |
169 | } |
170 | |
171 | KConfigGroup config(KGlobal::config(), "Replicode" ); |
172 | QString executorPath = config.readEntry<QString>("replicodePath" , QString()); |
173 | if (executorPath.isEmpty()) { |
174 | QMessageBox::warning(m_mainWindow->centralWidget(), i18n("Can't find replicode executor" ), i18n("Unable to find replicode executor.\nPlease go to settings and set the path to the Replicode executor." )); |
175 | return; |
176 | } |
177 | |
178 | if (m_configView->settingsObject()->userOperatorPath.isEmpty()) { |
179 | QMessageBox::warning(m_mainWindow->centralWidget(), i18n("Can't find user operator library" ), i18n("Unable to find user operator library.\nPlease go to settings and set the path to the library." )); |
180 | } |
181 | |
182 | if (m_settingsFile) delete m_settingsFile; |
183 | |
184 | m_settingsFile = new QTemporaryFile; |
185 | if (!m_settingsFile->open()) { |
186 | delete m_settingsFile; |
187 | m_settingsFile = 0; |
188 | QMessageBox::warning(m_mainWindow->window(), tr("Unable to create file" ), tr("Unable to create temporary file!" )); |
189 | } |
190 | m_configView->settingsObject()->writeXml(m_settingsFile, sourceFile.canonicalFilePath()); |
191 | m_settingsFile->close(); |
192 | |
193 | m_replicodeOutput->clear(); |
194 | |
195 | if (m_executor) delete m_executor; |
196 | m_executor = new QProcess(this); |
197 | m_executor->setWorkingDirectory(sourceFile.canonicalPath()); |
198 | connect(m_executor, SIGNAL(readyReadStandardError()), SLOT(gotStderr())); |
199 | connect(m_executor, SIGNAL(readyReadStandardOutput()), SLOT(gotStdout())); |
200 | connect(m_executor, SIGNAL(finished(int)), SLOT(replicodeFinished())); |
201 | connect(m_executor, SIGNAL(error(QProcess::ProcessError)), SLOT(runErrored(QProcess::ProcessError))); |
202 | qDebug() << executorPath << sourceFile.canonicalPath(); |
203 | m_completed = false; |
204 | m_executor->start(executorPath, QStringList() << m_settingsFile->fileName(), QProcess::ReadOnly); |
205 | |
206 | m_runAction->setEnabled(false); |
207 | m_runButton->setEnabled(false); |
208 | m_stopAction->setEnabled(true); |
209 | m_stopButton->setEnabled(true); |
210 | } |
211 | |
212 | void ReplicodeView::stopReplicode() |
213 | { |
214 | if (m_executor) { |
215 | m_executor->kill(); |
216 | } |
217 | } |
218 | |
219 | void ReplicodeView::outputClicked(QListWidgetItem *item) |
220 | { |
221 | QString output = item->text(); |
222 | QStringList pieces = output.split(':'); |
223 | |
224 | if (pieces.length() < 2) return; |
225 | |
226 | QFileInfo file(pieces[0]); |
227 | if (!file.isReadable()) return; |
228 | |
229 | bool ok = false; |
230 | int lineNumber = pieces[1].toInt(&ok); |
231 | qDebug() << lineNumber; |
232 | if (!ok) return; |
233 | |
234 | KTextEditor::View *doc = m_mainWindow->openUrl(pieces[0]); |
235 | doc->setCursorPosition(KTextEditor::Cursor(lineNumber, 0)); |
236 | qDebug() << doc->cursorPosition().line(); |
237 | } |
238 | |
239 | void ReplicodeView::runErrored(QProcess::ProcessError error) |
240 | { |
241 | Q_UNUSED(error); |
242 | QListWidgetItem *item = new QListWidgetItem(i18n("Replicode execution failed: %1" , m_executor->errorString())); |
243 | item->setForeground(Qt::red); |
244 | m_replicodeOutput->addItem(item); |
245 | m_replicodeOutput->scrollToBottom(); |
246 | m_completed = true; |
247 | } |
248 | |
249 | void ReplicodeView::replicodeFinished() |
250 | { |
251 | if (!m_completed) { |
252 | QListWidgetItem *item = new QListWidgetItem(i18n("Replicode execution finished!" )); |
253 | item->setForeground(Qt::blue); |
254 | m_replicodeOutput->addItem(item); |
255 | m_replicodeOutput->scrollToBottom(); |
256 | } |
257 | |
258 | m_runAction->setEnabled(true); |
259 | m_runButton->setEnabled(true); |
260 | m_stopAction->setEnabled(false); |
261 | m_stopButton->setEnabled(false); |
262 | // delete m_executor; |
263 | // delete m_settingsFile; |
264 | // m_executor = 0; |
265 | // m_settingsFile = 0; |
266 | } |
267 | |
268 | void ReplicodeView::gotStderr() |
269 | { |
270 | QString output = m_executor->readAllStandardError(); |
271 | foreach(QString line, output.split('\n')) { |
272 | line = line.simplified(); |
273 | if (line.isEmpty()) continue; |
274 | QListWidgetItem *item = new QListWidgetItem(line); |
275 | item->setForeground(Qt::red); |
276 | m_replicodeOutput->addItem(item); |
277 | } |
278 | m_replicodeOutput->scrollToBottom(); |
279 | } |
280 | |
281 | void ReplicodeView::gotStdout() |
282 | { |
283 | QString output = m_executor->readAllStandardOutput(); |
284 | foreach(QString line, output.split('\n')) { |
285 | line = line.simplified(); |
286 | if (line.isEmpty()) continue; |
287 | QListWidgetItem *item = new QListWidgetItem(' ' + line); |
288 | if (line[0] == '>') item->setForeground(Qt::gray); |
289 | m_replicodeOutput->addItem(item); |
290 | } |
291 | m_replicodeOutput->scrollToBottom(); |
292 | } |
293 | |
294 | #include "replicodeview.moc" |
295 | #include "replicodeplugin.moc" |
296 | |