Kross

scriptingplugin.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2008 Paulo Moura Guedes <[email protected]>
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 "scriptingplugin.h"
21 
22 #include <krun.h>
23 #include <kxmlguifactory.h>
24 #include <kactioncollection.h>
25 #include <kross/core/manager.h>
26 #include <kross/core/actioncollection.h>
27 
28 #include <QAction>
29 #include <QPointer>
30 #include <QTextStream>
31 #include <QDirIterator>
32 #include <QUrl>
33 #include <QStandardPaths>
34 
35 using namespace Kross;
36 
37 struct Object {
38  QPointer<QObject> object;
40  Object(QObject *obj, ChildrenInterface::Options opt): object(obj), options(opt) {}
41 };
42 
43 /// \internal d-pointer class
44 class ScriptingPlugin::ScriptingPluginPrivate
45 {
46 public:
47  QString collectionName;
48  QString userActionsFile;
49  QString referenceActionsDir;
50  QHash<QString, Object> objects;
51 
52  QDomElement menuFromName(QString const &name, const QDomDocument &document)
53  {
54  QDomElement menuBar = document.documentElement().firstChildElement("MenuBar");
55  QDomElement menu = menuBar.firstChildElement("Menu");
56  for (; !menu.isNull(); menu = menu.nextSiblingElement("Menu")) {
57  if (menu.attribute("name") == name) {
58  return menu;
59  }
60  }
61  return QDomElement();
62  }
63 };
64 
66  : KParts::Plugin(parent)
67  , d(new ScriptingPluginPrivate())
68 {
69  d->userActionsFile = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1Char('/') + "scripts/scriptactions.rc";
70  d->collectionName = "scripting-plugin";
71 }
72 
73 ScriptingPlugin::ScriptingPlugin(const QString &collectionName, const QString &userActionsFile, const QString &referenceActionsDir, QObject *parent)
74  : KParts::Plugin(parent)
75  , d(new ScriptingPluginPrivate())
76 {
77  d->collectionName = collectionName;
78  d->userActionsFile = userActionsFile;
79  d->referenceActionsDir = referenceActionsDir;
80 }
81 
83 {
84  if (QFile::exists(d->userActionsFile)) {
85  save();
86  }
87 
88  Kross::ActionCollection *collection = Kross::Manager::self().actionCollection()->collection(d->collectionName);
89  if (collection) {
90  collection->setParentCollection(nullptr);
91  collection->deleteLater();
92  }
93 
94  delete d;
95 }
96 
97 void ScriptingPlugin::setDOMDocument(const QDomDocument &document, bool merge)
98 {
99  QDomDocument doc = buildDomDocument(document);
100  KXMLGUIClient::setDOMDocument(doc, merge);
101 }
102 
103 void ScriptingPlugin::addObject(QObject *object, const QString &name)
104 {
105  QString n = name.isNull() ? object->objectName() : name;
106  d->objects.insert(n, Object(object, ChildrenInterface::NoOption));
107 }
108 
110 {
111  QString n = name.isNull() ? object->objectName() : name;
112  d->objects.insert(n, Object(object, options));
113 }
114 
115 QDomDocument ScriptingPlugin::buildDomDocument(const QDomDocument &document)
116 {
117  Kross::ActionCollection *collection = Kross::Manager::self().actionCollection()->collection(d->collectionName);
118  if (!collection) {
119  collection = new Kross::ActionCollection(d->collectionName, Kross::Manager::self().actionCollection());
120  }
121 
122  QStringList allActionFiles;
123  const QStringList scriptDirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("scripts/") + d->referenceActionsDir, QStandardPaths::LocateDirectory);
124  Q_FOREACH (const QString &scriptDir, scriptDirs) {
125  QDirIterator it(scriptDir, QStringList() << QStringLiteral("*.rc"));
126  while (it.hasNext()) {
127  allActionFiles.append(it.next());
128  }
129  }
130 
131  //move userActionsFile to the end so that it updates existing actions and adds new ones.
132  int pos = allActionFiles.indexOf(d->userActionsFile);
133  if (pos != -1) {
134  allActionFiles.append(allActionFiles.takeAt(pos));
135  } else if (QFile::exists(d->userActionsFile)) { //in case d->userActionsFile isn't in the standard local dir
136  allActionFiles.append(d->userActionsFile);
137  }
138 
139  QStringList searchPath = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("scripts/") + d->referenceActionsDir, QStandardPaths::LocateDirectory);
140  foreach (const QString &file, allActionFiles) {
141  QFile f(file);
142  if (!f.open(QIODevice::ReadOnly)) {
143  continue;
144  }
145 
146  collection->readXml(&f, searchPath + QStringList(QFileInfo(f).absolutePath()));
147  f.close();
148 
149  }
150 
151  QDomDocument doc(document);
152  buildDomDocument(doc, collection);
153 
154  return doc;
155 }
156 
157 void ScriptingPlugin::buildDomDocument(QDomDocument &document,
158  Kross::ActionCollection *collection)
159 {
160  QDomElement menuElement = d->menuFromName(collection->name(), document);
161 
162  foreach (Kross::Action *action, collection->actions()) {
163  QHashIterator<QString, Object> i(d->objects);
164  while (i.hasNext()) {
165  i.next();
166  action->addObject(i.value().object, i.key(), i.value().options);
167  }
168 
169  // Create and append new Menu element if doesn't exist
170  if (menuElement.isNull()) {
171  menuElement = document.createElement("Menu");
172  menuElement.setAttribute("name", collection->name());
173  menuElement.setAttribute("noMerge", "0");
174 
175  QDomElement textElement = document.createElement("text");
176  textElement.appendChild(document.createTextNode(collection->text()));
177  menuElement.appendChild(textElement);
178 
179  Kross::ActionCollection *parentCollection = collection->parentCollection();
180  QDomElement root;
181  if (parentCollection) {
182  QDomElement parentMenuElement = d->menuFromName(parentCollection->name(), document);
183  if (!parentMenuElement.isNull()) {
184  root = parentMenuElement;
185  }
186  }
187  if (root.isNull()) {
188  root = document.documentElement().firstChildElement("MenuBar");
189  }
190  root.appendChild(menuElement);
191  }
192 
193  // Create and append new Action element
194  QDomElement newActionElement = document.createElement("Action");
195  newActionElement.setAttribute("name", action->name());
196 
197  menuElement.appendChild(newActionElement);
198 
199  QAction *adaptor = new QAction(action->text(), action);
200  connect(adaptor, SIGNAL(triggered()), action, SLOT(trigger()));
201  adaptor->setEnabled(action->isEnabled());
202  adaptor->setIcon(action->icon());
203  actionCollection()->addAction(action->name(), adaptor);
204  }
205 
206  foreach (const QString &collectionname, collection->collections()) {
207  Kross::ActionCollection *c = collection->collection(collectionname);
208  if (c->isEnabled()) {
209  buildDomDocument(document, c);
210  }
211  }
212 }
213 
214 void ScriptingPlugin::save()
215 {
216  QFile f(d->userActionsFile);
217  if (!f.open(QIODevice::WriteOnly)) {
218  return;
219  }
220 
221  Kross::ActionCollection *collection = Kross::Manager::self().actionCollection()->collection(d->collectionName);
222  bool collectionEmpty = !collection || (collection->actions().empty() && collection->collections().empty());
223 
224  if (!collectionEmpty) {
225  QStringList searchPath = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("scripts/") + d->referenceActionsDir, QStandardPaths::LocateDirectory);
226  searchPath.append(QFileInfo(d->userActionsFile).absolutePath());
227  if (collection->writeXml(&f, 2, searchPath)) {
228  //qDebug() << "Successfully saved file: " << d->userActionsFile;
229  }
230  } else {
231  QTextStream out(&f);
232  QString xml =
233  "<!-- "
234  "\n"
235  "Collection name attribute represents the name of the menu, e.g., to use menu \"File\" use \"file\" or \"Help\" use \"help\". You can add new menus."
236  "\n\n\n"
237  "If you type a relative script file beware that this script is located in $XDG_DATA_HOME/applicationname/"
238  "\n\n"
239  "The following example adds an action with the text \"Export...\" into the \"File\" menu"
240  "\n\n"
241  "<KrossScripting>"
242  "\n"
243  "<collection name=\"file\" text=\"File\" comment=\"File menu\">"
244  "\n"
245  "<script name=\"export\" text=\"Export...\" comment=\"Export content\" file=\"export.py\" />"
246  "\n"
247  "</collection>"
248  "\n"
249  "</KrossScripting>"
250  "\n"
251  "-->";
252 
253  out << xml;
254  }
255  f.close();
256 }
257 
259 {
260  if (!QFile::exists(d->userActionsFile)) {
261  QString dir = QFileInfo(d->userActionsFile).absolutePath();
262  QDir().mkpath(dir);
263 
264  save();
265  }
266 
267  //TODO very funny! this should use ui/view.h instead --Nick
268  KRun::runUrl(QUrl::fromLocalFile(d->userActionsFile), QString("text/plain"), nullptr, false);
269 }
270 
272 {
273  QFile::remove(d->userActionsFile);
274 }
275 
276 #include "moc_scriptingplugin.cpp"
void append(const T &value)
QAction * addAction(const QString &name, const QObject *receiver=nullptr, const char *member=nullptr)
bool isNull() const const
The class Object does provide us scripting objects like class instances to the C++ world.
Definition: object.h:86
bool remove()
void setParentCollection(ActionCollection *parent)
Set the parent to parent. NOTE: Do not use setParent().
QStringList collections() const
bool isNull() const const
T takeAt(int i)
Object()
Default constructor.
Definition: object.cpp:29
virtual void setDOMDocument(const QDomDocument &document, bool merge=false)
QDomText createTextNode(const QString &value)
ActionCollection * parentCollection() const
QString writableLocation(QStandardPaths::StandardLocation type)
QDomElement createElement(const QString &tagName)
bool exists() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool isEnabled() const
Return the enable this ActionCollection has.
void setAttribute(const QString &name, const QString &value)
bool empty() const const
void setIcon(const QIcon &icon)
void deleteLater()
void addObject(QObject *object, const QString &name, ChildrenInterface::Options options)
Add a QObject to the list of children.
QUrl fromLocalFile(const QString &localFile)
bool mkpath(const QString &dirPath) const const
QDomElement nextSiblingElement(const QString &tagName) const const
The Action class is an abstract container to deal with scripts like a single standalone script file.
Definition: action.h:112
int indexOf(QStringView str, int from) const const
QDomElement documentElement() const const
QDomElement firstChildElement(const QString &tagName) const const
QString absolutePath() const const
bool readXml(const QDomElement &element, const QDir &directory=QDir())
Load child Action and ActionCollection instances this collection has from the element .
QAction * action(const char *name) const
virtual KActionCollection * actionCollection() const
void setEnabled(bool)
QDomNode appendChild(const QDomNode &newChild)
ActionCollection * collection(const QString &name) const
~ScriptingPlugin() override
Destructor.
QStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
@ NoOption
No additional options. This is the default.
static bool runUrl(const QUrl &url, const QString &mimetype, QWidget *window, bool tempFile=false, bool runExecutables=true, const QString &suggestedFileName=QString(), const QByteArray &asn=QByteArray())
void setDOMDocument(const QDomDocument &document, bool merge=false) override
Re-implement in order to load additional kross scripting rc files.
virtual void slotEditScriptActions()
This slot will open/create a scriptactions.rc file in XDG_DATA_HOME/application/scripts/ which will o...
QString attribute(const QString &name, const QString &defValue) const const
virtual void slotResetScriptActions()
Deletes the user rc file, which has the effect of falling back to the default script actions (if any)...
ScriptingPlugin(QObject *parent=nullptr)
Constructor.
The ActionCollection class manages collections of Action instances.
Options
Additional options that could be defined for a QObject instance.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Dec 5 2023 04:09:32 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.