Changeset View
Changeset View
Standalone View
Standalone View
addons/externaltools/externaltools.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | This file is part of the Kate text editor of the KDE project. | ||||
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 version 2 as published by the Free Software Foundation. | ||||
7 | | ||||
8 | This library is distributed in the hope that it will be useful, | ||||
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
11 | Library General Public License for more details. | ||||
12 | | ||||
13 | You should have received a copy of the GNU Library General Public License | ||||
14 | along with this library; see the file COPYING.LIB. If not, write to | ||||
15 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
16 | Boston, MA 02110-1301, USA. | ||||
17 | | ||||
18 | --- | ||||
19 | Copyright (C) 2004, Anders Lund <anders@alweb.dk> | ||||
20 | */ | ||||
21 | // TODO | ||||
22 | // Icons | ||||
23 | // Direct shortcut setting | ||||
24 | #include "externaltools.h" | ||||
25 | #include "externaltoolsplugin.h" | ||||
26 | #include <KTextEditor/Document> | ||||
27 | #include <KTextEditor/Editor> | ||||
28 | #include <KTextEditor/View> | ||||
29 | | ||||
30 | #include <KActionCollection> | ||||
31 | #include <KConfig> | ||||
32 | #include <KConfigGroup> | ||||
33 | #include <KIconButton> | ||||
34 | #include <KIconLoader> | ||||
35 | #include <KMessageBox> | ||||
36 | #include <KMimeTypeChooser> | ||||
37 | #include <KRun> | ||||
38 | #include <KSharedConfig> | ||||
39 | #include <KXMLGUIFactory> | ||||
40 | #include <KXmlGuiWindow> | ||||
41 | #include <QComboBox> | ||||
42 | #include <QLineEdit> | ||||
43 | #include <QListWidget> | ||||
44 | #include <QMenu> | ||||
45 | #include <QStandardPaths> | ||||
46 | | ||||
47 | #include <QBitmap> | ||||
48 | #include <QFile> | ||||
49 | #include <QGridLayout> | ||||
50 | #include <QLabel> | ||||
51 | #include <QObject> | ||||
52 | #include <QPushButton> | ||||
53 | #include <QRegExp> | ||||
54 | #include <QTextEdit> | ||||
55 | #include <QToolButton> | ||||
56 | | ||||
57 | #include <unistd.h> | ||||
58 | | ||||
59 | // BEGIN KateExternalTool | ||||
60 | KateExternalTool::KateExternalTool(const QString& name, const QString& command, const QString& icon, | ||||
61 | const QString& tryexec, const QStringList& mimetypes, const QString& acname, | ||||
62 | const QString& cmdname, int save) | ||||
63 | : name(name) | ||||
64 | , command(command) | ||||
65 | , icon(icon) | ||||
66 | , tryexec(tryexec) | ||||
67 | , mimetypes(mimetypes) | ||||
68 | , acname(acname) | ||||
69 | , cmdname(cmdname) | ||||
70 | , save(save) | ||||
71 | { | ||||
72 | // if ( ! tryexec.isEmpty() ) | ||||
73 | hasexec = checkExec(); | ||||
74 | } | ||||
75 | | ||||
76 | bool KateExternalTool::checkExec() | ||||
77 | { | ||||
78 | // if tryexec is empty, it is the first word of command | ||||
79 | if (tryexec.isEmpty()) | ||||
80 | tryexec = command.section(QLatin1Char(' '), 0, 0, QString::SectionSkipEmpty); | ||||
81 | | ||||
82 | // NOTE this code is modified taken from kdesktopfile.cpp, from KDesktopFile::tryExec() | ||||
83 | if (!tryexec.isEmpty()) { | ||||
84 | m_exec = QStandardPaths::findExecutable(tryexec); | ||||
85 | return !m_exec.isEmpty(); | ||||
86 | } | ||||
87 | return false; | ||||
88 | } | ||||
89 | | ||||
90 | bool KateExternalTool::valid(const QString& mt) const | ||||
91 | { | ||||
92 | return mimetypes.isEmpty() || mimetypes.contains(mt); | ||||
93 | } | ||||
94 | // END KateExternalTool | ||||
95 | | ||||
96 | // BEGIN KateExternalToolsCommand | ||||
97 | KateExternalToolsCommand::KateExternalToolsCommand(KateExternalToolsPlugin* plugin) | ||||
98 | : KTextEditor::Command({/* FIXME */}) | ||||
99 | , m_plugin(plugin) | ||||
100 | { | ||||
101 | reload(); | ||||
102 | } | ||||
103 | | ||||
104 | // FIXME | ||||
105 | // const QStringList& KateExternalToolsCommand::cmds() | ||||
106 | // { | ||||
107 | // return m_list; | ||||
108 | // } | ||||
109 | | ||||
110 | void KateExternalToolsCommand::reload() | ||||
111 | { | ||||
112 | m_list.clear(); | ||||
113 | m_map.clear(); | ||||
114 | m_name.clear(); | ||||
115 | | ||||
116 | KConfig _config(QStringLiteral("externaltools"), KConfig::NoGlobals, QStandardPaths::ApplicationsLocation); | ||||
117 | KConfigGroup config(&_config, "Global"); | ||||
118 | const QStringList tools = config.readEntry("tools", QStringList()); | ||||
119 | | ||||
120 | for (QStringList::const_iterator it = tools.begin(); it != tools.end(); ++it) { | ||||
121 | if (*it == QStringLiteral("---")) | ||||
122 | continue; | ||||
123 | | ||||
124 | config = KConfigGroup(&_config, *it); | ||||
125 | | ||||
126 | KateExternalTool t | ||||
127 | = KateExternalTool(config.readEntry(QStringLiteral("name"), ""), config.readEntry("command", ""), | ||||
128 | config.readEntry(QStringLiteral("icon"), ""), config.readEntry("executable", ""), | ||||
129 | config.readEntry(QStringLiteral("mimetypes"), QStringList()), | ||||
130 | config.readEntry(QStringLiteral("acname"), ""), config.readEntry("cmdname", "")); | ||||
131 | // FIXME test for a command name first! | ||||
132 | if (t.hasexec && (!t.cmdname.isEmpty())) { | ||||
133 | m_list.append(QStringLiteral("exttool-") + t.cmdname); | ||||
134 | m_map.insert(QStringLiteral("exttool-") + t.cmdname, t.acname); | ||||
135 | m_name.insert(QStringLiteral("exttool-") + t.cmdname, t.name); | ||||
136 | } | ||||
137 | } | ||||
138 | } | ||||
139 | | ||||
140 | bool KateExternalToolsCommand::exec(KTextEditor::View* view, const QString& cmd, QString& msg, | ||||
141 | const KTextEditor::Range& range) | ||||
142 | { | ||||
143 | Q_UNUSED(msg) | ||||
144 | Q_UNUSED(range) | ||||
145 | | ||||
146 | auto wv = dynamic_cast<QWidget*>(view); | ||||
147 | if (!wv) { | ||||
148 | // qDebug()<<"KateExternalToolsCommand::exec: Could not get view widget"; | ||||
149 | return false; | ||||
150 | } | ||||
151 | | ||||
152 | // qDebug()<<"cmd="<<cmd.trimmed(); | ||||
153 | const QString actionName = m_map[cmd.trimmed()]; | ||||
154 | if (actionName.isEmpty()) | ||||
155 | return false; | ||||
156 | // qDebug()<<"actionName is not empty:"<<actionName; | ||||
157 | /* KateExternalToolsMenuAction *a = | ||||
158 | dynamic_cast<KateExternalToolsMenuAction*>(dmw->action("tools_external")); | ||||
159 | if (!a) return false;*/ | ||||
160 | KateExternalToolsPluginView* extview = m_plugin->extView(wv->window()); | ||||
161 | if (!extview) | ||||
162 | return false; | ||||
163 | if (!extview->externalTools) | ||||
164 | return false; | ||||
165 | // qDebug()<<"trying to find action"; | ||||
166 | QAction* a1 = extview->externalTools->actionCollection()->action(actionName); | ||||
167 | if (!a1) | ||||
168 | return false; | ||||
169 | // qDebug()<<"activating action"; | ||||
170 | a1->trigger(); | ||||
171 | return true; | ||||
172 | } | ||||
173 | | ||||
174 | bool KateExternalToolsCommand::help(KTextEditor::View*, const QString&, QString&) | ||||
175 | { | ||||
176 | return false; | ||||
177 | } | ||||
178 | // END KateExternalToolsCommand | ||||
179 | | ||||
180 | // BEGIN KateExternalToolAction | ||||
181 | KateExternalToolAction::KateExternalToolAction(QObject* parent, KateExternalTool* t) | ||||
182 | : QAction(QIcon::fromTheme(t->icon), t->name, parent) | ||||
183 | , tool(t) | ||||
184 | { | ||||
185 | setText(t->name); | ||||
186 | if (!t->icon.isEmpty()) | ||||
187 | setIcon(QIcon::fromTheme(t->icon)); | ||||
188 | | ||||
189 | connect(this, SIGNAL(triggered(bool)), SLOT(slotRun())); | ||||
190 | } | ||||
191 | | ||||
192 | bool KateExternalToolAction::expandMacro(const QString& str, QStringList& ret) | ||||
193 | { | ||||
194 | KTextEditor::MainWindow* mw = qobject_cast<KTextEditor::MainWindow*>(parent()->parent()); | ||||
195 | Q_ASSERT(mw); | ||||
196 | | ||||
197 | KTextEditor::View* view = mw->activeView(); | ||||
198 | if (!view) | ||||
199 | return false; | ||||
200 | | ||||
201 | KTextEditor::Document* doc = view->document(); | ||||
202 | QUrl url = doc->url(); | ||||
203 | | ||||
204 | if (str == QStringLiteral("URL")) | ||||
205 | ret += url.url(); | ||||
206 | else if (str == QStringLiteral("directory")) // directory of current doc | ||||
207 | ret += url.toString(QUrl::RemoveScheme | QUrl::RemoveFilename); | ||||
208 | else if (str == QStringLiteral("filename")) | ||||
209 | ret += url.fileName(); | ||||
210 | else if (str == QStringLiteral("line")) // cursor line of current doc | ||||
211 | ret += QString::number(view->cursorPosition().line()); | ||||
212 | else if (str == QStringLiteral("col")) // cursor col of current doc | ||||
213 | ret += QString::number(view->cursorPosition().column()); | ||||
214 | else if (str == QStringLiteral("selection")) // selection of current doc if any | ||||
215 | ret += view->selectionText(); | ||||
216 | else if (str == QStringLiteral("text")) // text of current doc | ||||
217 | ret += doc->text(); | ||||
218 | else if (str == QStringLiteral("URLs")) { | ||||
219 | foreach (KTextEditor::Document* it, KTextEditor::Editor::instance()->application()->documents()) | ||||
220 | if (!it->url().isEmpty()) | ||||
221 | ret += it->url().url(); | ||||
222 | } else | ||||
223 | return false; | ||||
224 | | ||||
225 | return true; | ||||
226 | } | ||||
227 | | ||||
228 | KateExternalToolAction::~KateExternalToolAction() | ||||
229 | { | ||||
230 | delete (tool); | ||||
231 | } | ||||
232 | | ||||
233 | void KateExternalToolAction::slotRun() | ||||
234 | { | ||||
235 | // expand the macros in command if any, | ||||
236 | // and construct a command with an absolute path | ||||
237 | QString cmd = tool->command; | ||||
238 | | ||||
239 | auto mw = qobject_cast<KTextEditor::MainWindow*>(parent()->parent()); | ||||
240 | if (!expandMacrosShellQuote(cmd)) { | ||||
241 | KMessageBox::sorry(mw->window(), i18n("Failed to expand the command '%1'.", cmd), i18n("Kate External Tools")); | ||||
242 | return; | ||||
243 | } | ||||
244 | qDebug() << "externaltools: Running command: " << cmd; | ||||
245 | | ||||
246 | // save documents if requested | ||||
247 | if (tool->save == 1) | ||||
248 | mw->activeView()->document()->save(); | ||||
249 | else if (tool->save == 2) { | ||||
250 | foreach (KXMLGUIClient* client, mw->guiFactory()->clients()) { | ||||
251 | if (QAction* a = client->actionCollection()->action(QStringLiteral("file_save_all"))) { | ||||
252 | a->trigger(); | ||||
253 | break; | ||||
254 | } | ||||
255 | } | ||||
256 | } | ||||
257 | | ||||
258 | KRun::runCommand(cmd, tool->tryexec, tool->icon, mw->window()); | ||||
259 | } | ||||
260 | // END KateExternalToolAction | ||||
261 | | ||||
262 | // BEGIN KateExternalToolsMenuAction | ||||
263 | KateExternalToolsMenuAction::KateExternalToolsMenuAction(const QString& text, KActionCollection* collection, | ||||
264 | QObject* parent, KTextEditor::MainWindow* mw) | ||||
265 | : KActionMenu(text, parent) | ||||
266 | , mainwindow(mw) | ||||
267 | { | ||||
268 | | ||||
269 | m_actionCollection = collection; | ||||
270 | | ||||
271 | // connect to view changed... | ||||
272 | connect(mw, &KTextEditor::MainWindow::viewChanged, this, &KateExternalToolsMenuAction::slotViewChanged); | ||||
273 | | ||||
274 | reload(); | ||||
275 | } | ||||
276 | | ||||
277 | KateExternalToolsMenuAction::~KateExternalToolsMenuAction() | ||||
278 | { | ||||
279 | // kDebug() << "deleted KateExternalToolsMenuAction"; | ||||
280 | } | ||||
281 | | ||||
282 | void KateExternalToolsMenuAction::reload() | ||||
283 | { | ||||
284 | bool needs_readd = (m_actionCollection->takeAction(this) != nullptr); | ||||
285 | m_actionCollection->clear(); | ||||
286 | if (needs_readd) | ||||
287 | m_actionCollection->addAction(QStringLiteral("tools_external"), this); | ||||
288 | menu()->clear(); | ||||
289 | | ||||
290 | // load all the tools, and create a action for each of them | ||||
291 | KSharedConfig::Ptr pConfig = KSharedConfig::openConfig(QStringLiteral("externaltools"), KConfig::NoGlobals, | ||||
292 | QStandardPaths::ApplicationsLocation); | ||||
293 | KConfigGroup config(pConfig, "Global"); | ||||
294 | QStringList tools = config.readEntry("tools", QStringList()); | ||||
295 | | ||||
296 | // if there are tools that are present but not explicitly removed, | ||||
297 | // add them to the end of the list | ||||
298 | pConfig->setReadDefaults(true); | ||||
299 | QStringList dtools = config.readEntry("tools", QStringList()); | ||||
300 | int gver = config.readEntry("version", 1); | ||||
301 | pConfig->setReadDefaults(false); | ||||
302 | | ||||
303 | int ver = config.readEntry("version", 0); | ||||
304 | if (ver <= gver) { | ||||
305 | QStringList removed = config.readEntry("removed", QStringList()); | ||||
306 | bool sepadded = false; | ||||
307 | for (QStringList::iterator itg = dtools.begin(); itg != dtools.end(); ++itg) { | ||||
308 | if (!tools.contains(*itg) && !removed.contains(*itg)) { | ||||
309 | if (!sepadded) { | ||||
310 | tools << QStringLiteral("---"); | ||||
311 | sepadded = true; | ||||
312 | } | ||||
313 | tools << *itg; | ||||
314 | } | ||||
315 | } | ||||
316 | | ||||
317 | config.writeEntry("tools", tools); | ||||
318 | config.sync(); | ||||
319 | config.writeEntry("version", gver); | ||||
320 | } | ||||
321 | | ||||
322 | for (QStringList::const_iterator it = tools.constBegin(); it != tools.constEnd(); ++it) { | ||||
323 | if (*it == QStringLiteral("---")) { | ||||
324 | menu()->addSeparator(); | ||||
325 | // a separator | ||||
326 | continue; | ||||
327 | } | ||||
328 | | ||||
329 | config = KConfigGroup(pConfig, *it); | ||||
330 | | ||||
331 | KateExternalTool* t = new KateExternalTool( | ||||
332 | config.readEntry("name", ""), config.readEntry("command", ""), config.readEntry("icon", ""), | ||||
333 | config.readEntry("executable", ""), config.readEntry("mimetypes", QStringList()), | ||||
334 | config.readEntry("acname", ""), config.readEntry("cmdname", ""), config.readEntry("save", 0)); | ||||
335 | | ||||
336 | if (t->hasexec) { | ||||
337 | QAction* a = new KateExternalToolAction(this, t); | ||||
338 | m_actionCollection->addAction(t->acname, a); | ||||
339 | addAction(a); | ||||
340 | } else | ||||
341 | delete t; | ||||
342 | } | ||||
343 | | ||||
344 | config = KConfigGroup(pConfig, "Shortcuts"); | ||||
345 | m_actionCollection->readSettings(&config); | ||||
346 | slotViewChanged(mainwindow->activeView()); | ||||
347 | } | ||||
348 | | ||||
349 | void KateExternalToolsMenuAction::slotViewChanged(KTextEditor::View* view) | ||||
350 | { | ||||
351 | // no active view, oh oh | ||||
352 | if (!view) { | ||||
353 | return; | ||||
354 | } | ||||
355 | | ||||
356 | // try to enable/disable to match current mime type | ||||
357 | KTextEditor::Document* doc = view->document(); | ||||
358 | if (doc) { | ||||
359 | const QString mimeType = doc->mimeType(); | ||||
360 | foreach (QAction* kaction, m_actionCollection->actions()) { | ||||
361 | KateExternalToolAction* action = dynamic_cast<KateExternalToolAction*>(kaction); | ||||
362 | if (action) { | ||||
363 | const QStringList l = action->tool->mimetypes; | ||||
364 | const bool b = (!l.count() || l.contains(mimeType)); | ||||
365 | action->setEnabled(b); | ||||
366 | } | ||||
367 | } | ||||
368 | } | ||||
369 | } | ||||
370 | // END KateExternalToolsMenuAction | ||||
371 | | ||||
372 | // BEGIN ToolItem | ||||
373 | /** | ||||
374 | * This is a QListBoxItem, that has a KateExternalTool. The text is the Name | ||||
375 | * of the tool. | ||||
376 | */ | ||||
377 | class ToolItem : public QListWidgetItem | ||||
378 | { | ||||
379 | public: | ||||
380 | ToolItem(QListWidget* lb, const QPixmap& icon, KateExternalTool* tool) | ||||
381 | : QListWidgetItem(icon, tool->name, lb) | ||||
382 | , tool(tool) | ||||
383 | { | ||||
384 | } | ||||
385 | | ||||
386 | ~ToolItem() {} | ||||
387 | | ||||
388 | KateExternalTool* tool; | ||||
389 | }; | ||||
390 | // END ToolItem | ||||
391 | | ||||
392 | // BEGIN KateExternalToolServiceEditor | ||||
393 | KateExternalToolServiceEditor::KateExternalToolServiceEditor(KateExternalTool* tool, QWidget* parent) | ||||
394 | : QDialog(parent) | ||||
395 | , tool(tool) | ||||
396 | { | ||||
397 | setWindowTitle(i18n("Edit External Tool")); | ||||
398 | | ||||
399 | auto vbox = new QVBoxLayout(this); | ||||
400 | auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); | ||||
401 | connect(buttonBox, &QDialogButtonBox::accepted, this, &KateExternalToolServiceEditor::slotOKClicked); | ||||
402 | connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); | ||||
403 | | ||||
404 | // create a entry for each property | ||||
405 | // fill in the values from the service if available | ||||
406 | auto w = new QWidget(this); | ||||
407 | vbox->addWidget(w); | ||||
408 | vbox->addWidget(buttonBox); | ||||
409 | | ||||
410 | auto lo = new QGridLayout(w); | ||||
411 | lo->setContentsMargins(0, 0, 0, 0); | ||||
412 | | ||||
413 | leName = new QLineEdit(w); | ||||
414 | lo->addWidget(leName, 1, 2); | ||||
415 | auto l = new QLabel(w); | ||||
416 | l->setBuddy(leName); | ||||
417 | l->setText(i18n("&Label:")); | ||||
418 | l->setAlignment(l->alignment() | Qt::AlignRight); | ||||
419 | lo->addWidget(l, 1, 1); | ||||
420 | if (tool) | ||||
421 | leName->setText(tool->name); | ||||
422 | leName->setWhatsThis(i18n("The name will be displayed in the 'Tools->External Tools' menu")); | ||||
423 | | ||||
424 | btnIcon = new KIconButton(w); | ||||
425 | btnIcon->setIconSize(KIconLoader::SizeSmall); | ||||
426 | lo->addWidget(btnIcon, 1, 3); | ||||
427 | if (tool && !tool->icon.isEmpty()) | ||||
428 | btnIcon->setIcon(tool->icon); | ||||
429 | | ||||
430 | teCommand = new QTextEdit(w); | ||||
431 | lo->addWidget(teCommand, 2, 2, 1, 2); | ||||
432 | l = new QLabel(w); | ||||
433 | l->setBuddy(teCommand); | ||||
434 | l->setText(i18n("S&cript:")); | ||||
435 | l->setAlignment(Qt::AlignTop | Qt::AlignRight); | ||||
436 | lo->addWidget(l, 2, 1); | ||||
437 | if (tool) | ||||
438 | teCommand->setText(tool->command); | ||||
439 | teCommand->setWhatsThis(i18n("<p>The script to execute to invoke the tool. The script is passed " | ||||
440 | "to /bin/sh for execution. The following macros " | ||||
441 | "will be expanded:</p>" | ||||
442 | "<ul><li><code>%URL</code> - the URL of the current document.</li>" | ||||
443 | "<li><code>%URLs</code> - a list of the URLs of all open documents.</li>" | ||||
444 | "<li><code>%directory</code> - the URL of the directory containing " | ||||
445 | "the current document.</li>" | ||||
446 | "<li><code>%filename</code> - the filename of the current document.</li>" | ||||
447 | "<li><code>%line</code> - the current line of the text cursor in the " | ||||
448 | "current view.</li>" | ||||
449 | "<li><code>%column</code> - the column of the text cursor in the " | ||||
450 | "current view.</li>" | ||||
451 | "<li><code>%selection</code> - the selected text in the current view.</li>" | ||||
452 | "<li><code>%text</code> - the text of the current document.</li></ul>")); | ||||
453 | | ||||
454 | leExecutable = new QLineEdit(w); | ||||
455 | lo->addWidget(leExecutable, 3, 2, 1, 2); | ||||
456 | l = new QLabel(w); | ||||
457 | l->setBuddy(leExecutable); | ||||
458 | l->setText(i18n("&Executable:")); | ||||
459 | l->setAlignment(l->alignment() | Qt::AlignRight); | ||||
460 | lo->addWidget(l, 3, 1); | ||||
461 | if (tool) | ||||
462 | leExecutable->setText(tool->tryexec); | ||||
463 | leExecutable->setWhatsThis(i18n("The executable used by the command. This is used to check if a tool " | ||||
464 | "should be displayed; if not set, the first word of <em>command</em> " | ||||
465 | "will be used.")); | ||||
466 | | ||||
467 | leMimetypes = new QLineEdit(w); | ||||
468 | lo->addWidget(leMimetypes, 4, 2); | ||||
469 | l = new QLabel(w); | ||||
470 | l->setBuddy(leMimetypes); | ||||
471 | l->setText(i18n("&Mime types:")); | ||||
472 | l->setAlignment(l->alignment() | Qt::AlignRight); | ||||
473 | lo->addWidget(l, 4, 1); | ||||
474 | if (tool) | ||||
475 | leMimetypes->setText(tool->mimetypes.join(QStringLiteral("; "))); | ||||
476 | leMimetypes->setWhatsThis(i18n("A semicolon-separated list of mime types for which this tool should " | ||||
477 | "be available; if this is left empty, the tool is always available. " | ||||
478 | "To choose from known mimetypes, press the button on the right.")); | ||||
479 | | ||||
480 | auto btnMimetype = new QToolButton(w); | ||||
481 | lo->addWidget(btnMimetype, 4, 3); | ||||
482 | btnMimetype->setIcon(QIcon::fromTheme(QStringLiteral("tools-wizard"))); | ||||
483 | connect(btnMimetype, &QToolButton::clicked, this, &KateExternalToolServiceEditor::showMTDlg); | ||||
484 | btnMimetype->setWhatsThis(i18n("Click for a dialog that can help you create a list of mimetypes.")); | ||||
485 | | ||||
486 | cmbSave = new QComboBox(w); | ||||
487 | lo->addWidget(cmbSave, 5, 2, 1, 2); | ||||
488 | l = new QLabel(w); | ||||
489 | l->setBuddy(cmbSave); | ||||
490 | l->setText(i18n("&Save:")); | ||||
491 | l->setAlignment(l->alignment() | Qt::AlignRight); | ||||
492 | lo->addWidget(l, 5, 1); | ||||
493 | cmbSave->addItems({ i18n("None"), i18n("Current Document"), i18n("All Documents") }); | ||||
494 | if (tool) | ||||
495 | cmbSave->setCurrentIndex(tool->save); | ||||
496 | cmbSave->setWhatsThis(i18n("You can choose to save the current or all [modified] documents prior to " | ||||
497 | "running the command. This is helpful if you want to pass URLs to " | ||||
498 | "an application like, for example, an FTP client.")); | ||||
499 | | ||||
500 | leCmdLine = new QLineEdit(w); | ||||
501 | lo->addWidget(leCmdLine, 6, 2, 1, 2); | ||||
502 | l = new QLabel(i18n("&Command line name:"), w); | ||||
503 | l->setBuddy(leCmdLine); | ||||
504 | l->setAlignment(l->alignment() | Qt::AlignRight); | ||||
505 | lo->addWidget(l, 6, 1); | ||||
506 | if (tool) | ||||
507 | leCmdLine->setText(tool->cmdname); | ||||
508 | leCmdLine->setWhatsThis(i18n("If you specify a name here, you can invoke the command from the view " | ||||
509 | "command line with exttool-the_name_you_specified_here. " | ||||
510 | "Please do not use spaces or tabs in the name.")); | ||||
511 | } | ||||
512 | | ||||
513 | void KateExternalToolServiceEditor::slotOKClicked() | ||||
514 | { | ||||
515 | if (leName->text().isEmpty() || teCommand->document()->isEmpty()) { | ||||
516 | QMessageBox::information(this, i18n("External Tool"), i18n("You must specify at least a name and a command")); | ||||
517 | return; | ||||
518 | } | ||||
519 | accept(); | ||||
520 | } | ||||
521 | | ||||
522 | void KateExternalToolServiceEditor::showMTDlg() | ||||
523 | { | ||||
524 | QString text = i18n("Select the MimeTypes for which to enable this tool."); | ||||
525 | QStringList list = leMimetypes->text().split(QRegExp(QStringLiteral("\\s*;\\s*")), QString::SkipEmptyParts); | ||||
526 | KMimeTypeChooserDialog d(i18n("Select Mime Types"), text, list, QStringLiteral("text"), this); | ||||
527 | if (d.exec() == QDialog::Accepted) { | ||||
528 | leMimetypes->setText(d.chooser()->mimeTypes().join(QStringLiteral(";"))); | ||||
529 | } | ||||
530 | } | ||||
531 | // END KateExternalToolServiceEditor | ||||
532 | | ||||
533 | // BEGIN KateExternalToolsConfigWidget | ||||
534 | KateExternalToolsConfigWidget::KateExternalToolsConfigWidget(QWidget* parent, KateExternalToolsPlugin* plugin) | ||||
535 | : KTextEditor::ConfigPage(parent) | ||||
536 | , m_changed(false) | ||||
537 | , m_plugin(plugin) | ||||
538 | { | ||||
539 | setupUi(this); | ||||
540 | | ||||
541 | btnMoveUp->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); | ||||
542 | btnMoveDown->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); | ||||
543 | | ||||
544 | connect(lbTools, &QListWidget::itemSelectionChanged, this, &KateExternalToolsConfigWidget::slotSelectionChanged); | ||||
545 | connect(lbTools, &QListWidget::itemDoubleClicked, this, &KateExternalToolsConfigWidget::slotEdit); | ||||
546 | connect(btnNew, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotNew); | ||||
547 | connect(btnRemove, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotRemove); | ||||
548 | connect(btnEdit, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotEdit); | ||||
549 | connect(btnSeparator, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotInsertSeparator); | ||||
550 | connect(btnMoveUp, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotMoveUp); | ||||
551 | connect(btnMoveDown, &QPushButton::clicked, this, &KateExternalToolsConfigWidget::slotMoveDown); | ||||
552 | | ||||
553 | config = new KConfig(QStringLiteral("externaltools"), KConfig::NoGlobals, QStandardPaths::ApplicationsLocation); | ||||
554 | reset(); | ||||
555 | slotSelectionChanged(); | ||||
556 | } | ||||
557 | | ||||
558 | KateExternalToolsConfigWidget::~KateExternalToolsConfigWidget() | ||||
559 | { | ||||
560 | delete config; | ||||
561 | } | ||||
562 | | ||||
563 | QString KateExternalToolsConfigWidget::name() const | ||||
564 | { | ||||
565 | return i18n("External Tools"); | ||||
566 | } | ||||
567 | | ||||
568 | QString KateExternalToolsConfigWidget::fullName() const | ||||
569 | { | ||||
570 | return i18n("External Tools"); | ||||
571 | } | ||||
572 | | ||||
573 | QIcon KateExternalToolsConfigWidget::icon() const | ||||
574 | { | ||||
575 | return QIcon(); | ||||
576 | } | ||||
577 | | ||||
578 | void KateExternalToolsConfigWidget::reset() | ||||
579 | { | ||||
580 | // m_tools.clear(); | ||||
581 | lbTools->clear(); | ||||
582 | | ||||
583 | // load the files from a KConfig | ||||
584 | const QStringList tools = config->group("Global").readEntry("tools", QStringList()); | ||||
585 | | ||||
586 | for (QStringList::const_iterator it = tools.begin(); it != tools.end(); ++it) { | ||||
587 | if (*it == QStringLiteral("---")) { | ||||
588 | new QListWidgetItem(QStringLiteral("---"), lbTools); | ||||
589 | } else { | ||||
590 | KConfigGroup cg(config, *it); | ||||
591 | | ||||
592 | KateExternalTool* t | ||||
593 | = new KateExternalTool(cg.readEntry("name", ""), cg.readEntry("command", ""), cg.readEntry("icon", ""), | ||||
594 | cg.readEntry("executable", ""), cg.readEntry("mimetypes", QStringList()), | ||||
595 | cg.readEntry("acname"), cg.readEntry("cmdname"), cg.readEntry("save", 0)); | ||||
596 | | ||||
597 | if (t->hasexec) // we only show tools that are also in the menu. | ||||
598 | new ToolItem(lbTools, t->icon.isEmpty() ? blankIcon() : SmallIcon(t->icon), t); | ||||
599 | else | ||||
600 | delete t; | ||||
601 | } | ||||
602 | } | ||||
603 | m_changed = false; | ||||
604 | } | ||||
605 | | ||||
606 | QPixmap KateExternalToolsConfigWidget::blankIcon() | ||||
607 | { | ||||
608 | QPixmap pm(KIconLoader::SizeSmall, KIconLoader::SizeSmall); | ||||
609 | pm.fill(); | ||||
610 | pm.setMask(pm.createHeuristicMask()); | ||||
611 | return pm; | ||||
612 | } | ||||
613 | | ||||
614 | void KateExternalToolsConfigWidget::apply() | ||||
615 | { | ||||
616 | if (!m_changed) | ||||
617 | return; | ||||
618 | m_changed = false; | ||||
619 | | ||||
620 | // save a new list | ||||
621 | // save each item | ||||
622 | QStringList tools; | ||||
623 | for (int i = 0; i < lbTools->count(); i++) { | ||||
624 | if (lbTools->item(i)->text() == QStringLiteral("---")) { | ||||
625 | tools << QStringLiteral("---"); | ||||
626 | continue; | ||||
627 | } | ||||
628 | KateExternalTool* t = static_cast<ToolItem*>(lbTools->item(i))->tool; | ||||
629 | // qDebug()<<"adding tool: "<<t->name; | ||||
630 | tools << t->acname; | ||||
631 | | ||||
632 | KConfigGroup cg(config, t->acname); | ||||
633 | | ||||
634 | cg.writeEntry("name", t->name); | ||||
635 | cg.writeEntry("command", t->command); | ||||
636 | cg.writeEntry("icon", t->icon); | ||||
637 | cg.writeEntry("executable", t->tryexec); | ||||
638 | cg.writeEntry("mimetypes", t->mimetypes); | ||||
639 | cg.writeEntry("acname", t->acname); | ||||
640 | cg.writeEntry("cmdname", t->cmdname); | ||||
641 | cg.writeEntry("save", t->save); | ||||
642 | } | ||||
643 | | ||||
644 | config->group("Global").writeEntry("tools", tools); | ||||
645 | | ||||
646 | // if any tools was removed, try to delete their groups, and | ||||
647 | // add the group names to the list of removed items. | ||||
648 | if (m_removed.count()) { | ||||
649 | for (QStringList::iterator it = m_removed.begin(); it != m_removed.end(); ++it) { | ||||
650 | if (config->hasGroup(*it)) | ||||
651 | config->deleteGroup(*it); | ||||
652 | } | ||||
653 | QStringList removed = config->group("Global").readEntry("removed", QStringList()); | ||||
654 | removed += m_removed; | ||||
655 | | ||||
656 | // clean up the list of removed items, so that it does not contain | ||||
657 | // non-existing groups (we can't remove groups from a non-owned global file). | ||||
658 | config->sync(); | ||||
659 | QStringList::iterator it1 = removed.begin(); | ||||
660 | while (it1 != removed.end()) { | ||||
661 | if (!config->hasGroup(*it1)) | ||||
662 | it1 = removed.erase(it1); | ||||
663 | else | ||||
664 | ++it1; | ||||
665 | } | ||||
666 | config->group("Global").writeEntry("removed", removed); | ||||
667 | } | ||||
668 | | ||||
669 | config->sync(); | ||||
670 | m_plugin->reload(); | ||||
671 | } | ||||
672 | | ||||
673 | void KateExternalToolsConfigWidget::slotSelectionChanged() | ||||
674 | { | ||||
675 | // update button state | ||||
676 | bool hs = lbTools->currentItem() != nullptr; | ||||
677 | btnEdit->setEnabled(hs && dynamic_cast<ToolItem*>(lbTools->currentItem())); | ||||
678 | btnRemove->setEnabled(hs); | ||||
679 | btnMoveUp->setEnabled((lbTools->currentRow() > 0) && hs); | ||||
680 | btnMoveDown->setEnabled((lbTools->currentRow() < (int)lbTools->count() - 1) && hs); | ||||
681 | } | ||||
682 | | ||||
683 | void KateExternalToolsConfigWidget::slotNew() | ||||
684 | { | ||||
685 | // display a editor, and if it is OK'd, create a new tool and | ||||
686 | // create a listbox item for it | ||||
687 | KateExternalToolServiceEditor editor(nullptr, this); | ||||
688 | | ||||
689 | if (editor.exec()) { | ||||
690 | KateExternalTool* t = new KateExternalTool( | ||||
691 | editor.leName->text(), editor.teCommand->toPlainText(), editor.btnIcon->icon(), editor.leExecutable->text(), | ||||
692 | editor.leMimetypes->text().split(QRegExp(QStringLiteral("\\s*;\\s*")), QString::SkipEmptyParts)); | ||||
693 | | ||||
694 | // This is sticky, it does not change again, so that shortcuts sticks | ||||
695 | // TODO check for dups | ||||
696 | t->acname = QStringLiteral("externaltool_") + QString(t->name).remove(QRegExp(QStringLiteral("\\W+"))); | ||||
697 | | ||||
698 | new ToolItem(lbTools, t->icon.isEmpty() ? blankIcon() : SmallIcon(t->icon), t); | ||||
699 | | ||||
700 | emit changed(); | ||||
701 | m_changed = true; | ||||
702 | } | ||||
703 | } | ||||
704 | | ||||
705 | void KateExternalToolsConfigWidget::slotRemove() | ||||
706 | { | ||||
707 | // add the tool action name to a list of removed items, | ||||
708 | // remove the current listbox item | ||||
709 | if (lbTools->currentRow() > -1) { | ||||
710 | ToolItem* i = dynamic_cast<ToolItem*>(lbTools->currentItem()); | ||||
711 | if (i) | ||||
712 | m_removed << i->tool->acname; | ||||
713 | | ||||
714 | delete lbTools->takeItem(lbTools->currentRow()); | ||||
715 | emit changed(); | ||||
716 | m_changed = true; | ||||
717 | } | ||||
718 | } | ||||
719 | | ||||
720 | void KateExternalToolsConfigWidget::slotEdit() | ||||
721 | { | ||||
722 | if (!dynamic_cast<ToolItem*>(lbTools->currentItem())) | ||||
723 | return; | ||||
724 | // show the item in an editor | ||||
725 | KateExternalTool* t = static_cast<ToolItem*>(lbTools->currentItem())->tool; | ||||
726 | KateExternalToolServiceEditor editor(t, this); | ||||
727 | editor.resize(config->group("Editor").readEntry("Size", QSize())); | ||||
728 | if (editor.exec() /*== KDialog::Ok*/) { | ||||
729 | | ||||
730 | bool elementChanged = ((editor.btnIcon->icon() != t->icon) || (editor.leName->text() != t->name)); | ||||
731 | | ||||
732 | t->name = editor.leName->text(); | ||||
733 | t->cmdname = editor.leCmdLine->text(); | ||||
734 | t->command = editor.teCommand->toPlainText(); | ||||
735 | t->icon = editor.btnIcon->icon(); | ||||
736 | t->tryexec = editor.leExecutable->text(); | ||||
737 | t->mimetypes = editor.leMimetypes->text().split(QRegExp(QStringLiteral("\\s*;\\s*")), QString::SkipEmptyParts); | ||||
738 | t->save = editor.cmbSave->currentIndex(); | ||||
739 | | ||||
740 | // if the icon has changed or name changed, I have to renew the listbox item :S | ||||
741 | if (elementChanged) { | ||||
742 | int idx = lbTools->row(lbTools->currentItem()); | ||||
743 | delete lbTools->takeItem(idx); | ||||
744 | lbTools->insertItem(idx, new ToolItem(nullptr, t->icon.isEmpty() ? blankIcon() : SmallIcon(t->icon), t)); | ||||
745 | } | ||||
746 | | ||||
747 | emit changed(); | ||||
748 | m_changed = true; | ||||
749 | } | ||||
750 | | ||||
751 | config->group("Editor").writeEntry("Size", editor.size()); | ||||
752 | config->sync(); | ||||
753 | } | ||||
754 | | ||||
755 | void KateExternalToolsConfigWidget::slotInsertSeparator() | ||||
756 | { | ||||
757 | lbTools->insertItem(lbTools->currentRow() + 1, QStringLiteral("---")); | ||||
758 | emit changed(); | ||||
759 | m_changed = true; | ||||
760 | } | ||||
761 | | ||||
762 | void KateExternalToolsConfigWidget::slotMoveUp() | ||||
763 | { | ||||
764 | // move the current item in the listbox upwards if possible | ||||
765 | QListWidgetItem* item = lbTools->currentItem(); | ||||
766 | if (!item) | ||||
767 | return; | ||||
768 | | ||||
769 | int idx = lbTools->row(item); | ||||
770 | | ||||
771 | if (idx < 1) | ||||
772 | return; | ||||
773 | | ||||
774 | if (dynamic_cast<ToolItem*>(item)) { | ||||
775 | KateExternalTool* tool = static_cast<ToolItem*>(item)->tool; | ||||
776 | delete lbTools->takeItem(idx); | ||||
777 | lbTools->insertItem(idx - 1, | ||||
778 | new ToolItem(nullptr, tool->icon.isEmpty() ? blankIcon() : SmallIcon(tool->icon), tool)); | ||||
779 | } else // a separator! | ||||
780 | { | ||||
781 | delete lbTools->takeItem(idx); | ||||
782 | lbTools->insertItem(idx - 1, new QListWidgetItem(QStringLiteral("---"))); | ||||
783 | } | ||||
784 | | ||||
785 | lbTools->setCurrentRow(idx - 1); | ||||
786 | slotSelectionChanged(); | ||||
787 | emit changed(); | ||||
788 | m_changed = true; | ||||
789 | } | ||||
790 | | ||||
791 | void KateExternalToolsConfigWidget::slotMoveDown() | ||||
792 | { | ||||
793 | // move the current item in the listbox downwards if possible | ||||
794 | QListWidgetItem* item = lbTools->currentItem(); | ||||
795 | if (!item) | ||||
796 | return; | ||||
797 | | ||||
798 | int idx = lbTools->row(item); | ||||
799 | | ||||
800 | if (idx > lbTools->count() - 1) | ||||
801 | return; | ||||
802 | | ||||
803 | if (dynamic_cast<ToolItem*>(item)) { | ||||
804 | KateExternalTool* tool = static_cast<ToolItem*>(item)->tool; | ||||
805 | delete lbTools->takeItem(idx); | ||||
806 | lbTools->insertItem(idx + 1, | ||||
807 | new ToolItem(nullptr, tool->icon.isEmpty() ? blankIcon() : SmallIcon(tool->icon), tool)); | ||||
808 | } else // a separator! | ||||
809 | { | ||||
810 | delete lbTools->takeItem(idx); | ||||
811 | lbTools->insertItem(idx + 1, new QListWidgetItem(QStringLiteral("---"))); | ||||
812 | } | ||||
813 | | ||||
814 | lbTools->setCurrentRow(idx + 1); | ||||
815 | slotSelectionChanged(); | ||||
816 | emit changed(); | ||||
817 | m_changed = true; | ||||
818 | } | ||||
819 | // END KateExternalToolsConfigWidget | ||||
820 | // kate: space-indent on; indent-width 4; replace-tabs on; |