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