1 /***************************************************************************
2 * KoScriptingPart.cpp
3 * This file is part of the KDE project
4 * copyright (C) 2006-2007 Sebastian Sauer <mail@dipe.org>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this program; see the file COPYING. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 ***************************************************************************/
21
22 #include "KoScriptingPart.h"
23
24 #include "KoScriptingModule.h"
25 #include "KoScriptManager.h"
26 #include "KoScriptingDocker.h"
27 #include "KoKrossDebug.h"
28 // calligra
29 #include <KoView.h>
30 #include <KoMainWindow.h>
31 // KF5
32 #include <kactioncollection.h>
33 #include <klocalizedstring.h>
34 #include <kactionmenu.h>
35 #include <kmessagebox.h>
36 #include <kross/core/manager.h>
37 #include <kross/core/interpreter.h>
38 #include <kross/core/actioncollection.h>
39 // Qt
40 #include <QApplication>
41 #include <QStandardPaths>
42 #include <QDirIterator>
43 #include <QMenu>
44 #include <QFileDialog>
45
46
47 /// \internal d-pointer class.
48 class Q_DECL_HIDDEN KoScriptingPart::Private
49 {
50 public:
51 /**
52 * The \a KoScriptingModule instance that provides the base class for
53 * Kross module functionality for Calligra applications.
54 */
55 QPointer<KoScriptingModule> module;
56
57 /**
58 * The \a KActionMenu instance that provides a menu of all enabled
59 * collections and there actions as tree.
60 */
61 KActionMenu *scriptsmenu;
62
63 /**
64 * The list of \a Kross::Action instances this \a KoScriptingPart instance
65 * owns. Each action is executed within a specific part instance where
66 * each part has exactly one \a KoView instance. That way we are able to bind
67 * a script explicit to a specific view.
68 */
69 QList<Kross::Action*> actions;
70 };
71
KoScriptingPart(KoScriptingModule * const module)72 KoScriptingPart::KoScriptingPart(KoScriptingModule *const module)
73 : d(new Private())
74 {
75 d->module = module;
76 Q_ASSERT(d->module);
77
78 QAction *execAction = new QAction(i18n("Execute Script File..."), this);
79 actionCollection()->addAction("executescriptfile", execAction);
80 connect(execAction, SIGNAL(triggered(bool)), this, SLOT(slotShowExecuteScriptFile()));
81
82 d->scriptsmenu = new KActionMenu(i18n("Scripts"), this);
83 actionCollection()->addAction("scripts", d->scriptsmenu);
84 connect(d->scriptsmenu->menu(), SIGNAL(aboutToShow()), this, SLOT(slotMenuAboutToShow()));
85
86 QAction *manageraction = new QAction(i18n("Script Manager..."), this);
87 actionCollection()->addAction("scriptmanager", manageraction);
88 connect(manageraction, SIGNAL(triggered(bool)), this, SLOT(slotShowScriptManager()));
89
90 connect(&Kross::Manager::self(), SIGNAL(started(Kross::Action*)), this, SLOT(slotStarted(Kross::Action*)));
91 //connect(&Kross::Manager::self(), SIGNAL(finished(Kross::Action*)), this, SLOT(slotFinished(Kross::Action*)));
92
93 if (Kross::Manager::self().property("configfile") == QVariant::Invalid) {
94 QString file = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/scripts/scripts.rc");
95 QStringList files;
96 const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, "scripts", QStandardPaths::LocateDirectory);
97 foreach(const QString& dir, dirs) {
98 QDirIterator it(dir, QStringList() << QStringLiteral("*.rc"));
99 while (it.hasNext()) {
100 files.append(it.next());
101 }
102 }
103
104 Kross::Manager::self().setProperty("configfile", file);
105 Kross::Manager::self().setProperty("configfiles", files);
106
107 if (QFileInfo(file).exists())
108 Kross::Manager::self().actionCollection()->readXmlFile(file);
109 else
110 foreach(const QString &f, files)
111 Kross::Manager::self().actionCollection()->readXmlFile(f);
112 }
113
114 KoView *view = d->module->view();
115 KoMainWindow *mainwindow = view ? view->mainWindow() : 0;
116 if (mainwindow) {
117 KoScriptingDockerFactory factory(mainwindow);
118 mainwindow->createDockWidget(&factory);
119 }
120
121 if (view) {
122 if (Kross::ActionCollection *c = Kross::Manager::self().actionCollection()->collection("docker")) {
123 foreach (Kross::Action *a, c->actions()) {
124 if (! a->isEnabled())
125 continue;
126 a->addObject(d->module);
127 KoScriptingDockerFactory f(view, d->module, a);
128 if (mainwindow) mainwindow->createDockWidget(&f);
129 debugKoKross << "Adding scripting docker with id=" << f.id();
130 }
131 }
132 }
133 }
134
~KoScriptingPart()135 KoScriptingPart::~KoScriptingPart()
136 {
137 foreach(Kross::Action *action, d->actions) {
138 if (action)
139 action->finalize();
140 }
141 delete d;
142 }
143
module() const144 KoScriptingModule *KoScriptingPart::module() const
145 {
146 return d->module;
147 }
148
showExecuteScriptFile()149 bool KoScriptingPart::showExecuteScriptFile()
150 {
151 QStringList mimetypes;
152 foreach (const QString &interpretername, Kross::Manager::self().interpreters()) {
153 Kross::InterpreterInfo *info = Kross::Manager::self().interpreterInfo(interpretername);
154 Q_ASSERT(info);
155 mimetypes.append(info->mimeTypes());
156 }
157 QFileDialog filedialog;
158 //QT5TODO: QUrl("kfiledialog:///KrossExecuteScript"), // startdir
159 filedialog.setMimeTypeFilters(mimetypes);
160 filedialog.setWindowTitle(i18n("Execute Script File"));
161 filedialog.setFileMode(QFileDialog::ExistingFile);
162 if (filedialog.exec()) {
163 Kross::Action action(this, "Execute Script File");
164 action.addObject(d->module);
165
166 action.setFile(filedialog.selectedUrls().at(0).path());
167 action.trigger();
168
169 return true;
170 }
171
172 return false;
173 }
174
addMenu(QMenu * menu,Kross::ActionCollection * collection)175 void addMenu(QMenu *menu, Kross::ActionCollection *collection)
176 {
177 foreach (Kross::Action *a, collection->actions()) {
178 if(a->isEnabled()) {
179 menu->addAction(a);
180 }
181 }
182 foreach (const QString &collectionname, collection->collections()) {
183 Kross::ActionCollection *c = collection->collection(collectionname);
184 if (c->isEnabled())
185 addMenu(menu->addMenu(c->text()), c);
186 }
187 }
188
slotMenuAboutToShow()189 void KoScriptingPart::slotMenuAboutToShow()
190 {
191 d->scriptsmenu->menu()->clear();
192 addMenu(d->scriptsmenu->menu(), Kross::Manager::self().actionCollection());
193 }
194
slotShowExecuteScriptFile()195 void KoScriptingPart::slotShowExecuteScriptFile()
196 {
197 showExecuteScriptFile();
198 }
199
slotShowScriptManager()200 void KoScriptingPart::slotShowScriptManager()
201 {
202 KoScriptManagerDialog *dialog = new KoScriptManagerDialog();
203 dialog->exec();
204 dialog->delayedDestruct();
205 }
206
slotStarted(Kross::Action * action)207 void KoScriptingPart::slotStarted(Kross::Action *action)
208 {
209 debugKoKross << "action=" << action->objectName();
210 KoMainWindow *mainwin = dynamic_cast<KoMainWindow*>(qApp->activeWindow());
211 KoView *view = d->module ? d->module->view() : 0;
212 if (view && mainwin && view->mainWindow() == mainwin && view == mainwin->rootView()) {
213 action->addObject(d->module);
214 d->actions.append(action);
215 connect(action, SIGNAL(finished(Kross::Action*)), this, SLOT(slotFinished(Kross::Action*)));
216 connect(action, SIGNAL(finalized(Kross::Action*)), this, SLOT(slotFinalized(Kross::Action*)));
217 myStarted(action);
218 }
219 }
220
slotFinished(Kross::Action * action)221 void KoScriptingPart::slotFinished(Kross::Action *action)
222 {
223 debugKoKross <<"KoScriptingPart::slotFinished action=" << action->objectName();
224 disconnect(action, SIGNAL(finished(Kross::Action*)), this, SLOT(slotFinished(Kross::Action*)));
225 if (d->module && d->module == action->object(d->module->objectName())) {
226 //d->view->document()->setModified(true);
227 //QApplication::restoreOverrideCursor();
228 KoView *view = d->module ? d->module->view() : 0;
229 if (view && view->mainWindow() /* && view == view->mainWindow()->rootView() */) {
230 if (action->hadError()) {
231 if (action->errorTrace().isNull())
232 KMessageBox::error(view, action->errorMessage());
233 else
234 KMessageBox::detailedError(view, action->errorMessage(), action->errorTrace());
235 }
236 }
237 myFinished(action);
238 }
239 }
240
slotFinalized(Kross::Action * action)241 void KoScriptingPart::slotFinalized(Kross::Action *action)
242 {
243 debugKoKross << "action=" << action->objectName();
244 disconnect(action, SIGNAL(finalized(Kross::Action*)), this, SLOT(slotFinalized(Kross::Action*)));
245 d->actions.removeAll(action);
246 if (d->module && d->module == action->object(d->module->objectName())) {
247 myFinalized(action);
248 }
249 }
250