Changeset View
Changeset View
Standalone View
Standalone View
src/plugins/scripting/kexiscripting/kexiscriptpart.cpp
Show All 16 Lines | 1 | /* This file is part of the KDE project | |||
---|---|---|---|---|---|
17 | along with this library; see the file COPYING.LIB. If not, write to | 17 | along with this library; see the file COPYING.LIB. If not, write to | ||
18 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 18 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
19 | * Boston, MA 02110-1301, USA. | 19 | * Boston, MA 02110-1301, USA. | ||
20 | */ | 20 | */ | ||
21 | 21 | | |||
22 | #include "kexiscriptpart.h" | 22 | #include "kexiscriptpart.h" | ||
23 | #include "kexiscriptdesignview.h" | 23 | #include "kexiscriptdesignview.h" | ||
24 | #include "kexiscriptadaptor.h" | 24 | #include "kexiscriptadaptor.h" | ||
25 | #include "../kexidb/kexidbmodule.h" | ||||
26 | #include "KexiScriptingDebug.h" | ||||
27 | | ||||
25 | #include <kexipart.h> | 28 | #include <kexipart.h> | ||
26 | #include <kexipartitem.h> | 29 | #include <kexipartitem.h> | ||
27 | #include <KexiIcon.h> | 30 | #include <KexiIcon.h> | ||
28 | #include <KexiView.h> | 31 | #include <KexiView.h> | ||
29 | #include <KexiWindow.h> | 32 | #include <KexiWindow.h> | ||
30 | #include <KexiMainWindowIface.h> | 33 | #include <KexiMainWindowIface.h> | ||
31 | #include <kexiproject.h> | 34 | #include <kexiproject.h> | ||
32 | 35 | #include <KDbConnection> | |||
33 | #include <Kross/Manager> | | |||
34 | #include <Kross/ActionCollection> | | |||
35 | 36 | | |||
36 | #include <KConfig> | 37 | #include <KConfig> | ||
37 | #include <KConfigGroup> | 38 | #include <KConfigGroup> | ||
38 | #include <KSharedConfig> | 39 | #include <KSharedConfig> | ||
39 | #include <KMessageBox> | 40 | #include <KMessageBox> | ||
40 | 41 | | |||
41 | #include <QDebug> | 42 | #include <QDebug> | ||
43 | #include <QJSEngine> | ||||
44 | #include <QJSValue> | ||||
45 | #include <QJSValueIterator> | ||||
46 | #include <QDomDocument> | ||||
42 | 47 | | |||
43 | KEXI_PLUGIN_FACTORY(KexiScriptPart, "kexi_scriptplugin.json") | 48 | KEXI_PLUGIN_FACTORY(KexiScriptPart, "kexi_scriptplugin.json") | ||
44 | 49 | | |||
45 | /// \internal | 50 | /// \internal | ||
46 | class Q_DECL_HIDDEN KexiScriptPart::Private | 51 | class Q_DECL_HIDDEN KexiScriptPart::Private | ||
47 | { | 52 | { | ||
48 | public: | 53 | public: | ||
49 | explicit Private(KexiScriptPart* p) | 54 | explicit Private(KexiScriptPart* p) | ||
50 | : p(p) | 55 | : p(p) {} | ||
51 | , actioncollection(new Kross::ActionCollection("projectscripts")) | | |||
52 | , adaptor(0) {} | | |||
53 | ~Private() { | 56 | ~Private() { | ||
54 | delete actioncollection; delete adaptor; | | |||
55 | } | 57 | } | ||
56 | 58 | | |||
59 | QJSEngine engine; | ||||
57 | KexiScriptPart* p; | 60 | KexiScriptPart* p; | ||
58 | Kross::ActionCollection* actioncollection; | 61 | KexiScriptAdaptor adaptor; | ||
59 | KexiScriptAdaptor* adaptor; | 62 | Scripting::KexiDBModule kexidbmodule; | ||
60 | | ||||
61 | Kross::Action* action(const QString &partname) { | | |||
62 | Kross::Action *action = actioncollection->action(partname); | | |||
63 | if (! action) { | | |||
64 | if (! adaptor) | | |||
65 | adaptor = new KexiScriptAdaptor(); | | |||
66 | action = new Kross::Action(p, partname); | | |||
67 | actioncollection->addAction(action); | | |||
68 | action->addObject(adaptor); | | |||
69 | } | | |||
70 | return action; | | |||
71 | } | | |||
72 | }; | 63 | }; | ||
73 | 64 | | |||
74 | KexiScriptPart::KexiScriptPart(QObject *parent, const QVariantList& l) | 65 | KexiScriptPart::KexiScriptPart(QObject *parent, const QVariantList& l) | ||
75 | : KexiPart::Part(parent, | 66 | : KexiPart::Part(parent, | ||
76 | xi18nc("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). " | 67 | xi18nc("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). " | ||
77 | "Use '_' character instead of spaces. First character should be a..z character. " | 68 | "Use '_' character instead of spaces. First character should be a..z character. " | ||
78 | "If you cannot use latin characters in your language, use english word.", | 69 | "If you cannot use latin characters in your language, use english word.", | ||
79 | "script"), | 70 | "script"), | ||
80 | xi18nc("tooltip", "Create new script"), | 71 | xi18nc("tooltip", "Create new script"), | ||
81 | xi18nc("what's this", "Creates new script."), | 72 | xi18nc("what's this", "Creates new script."), | ||
82 | l) | 73 | l) | ||
83 | , d(new Private(this)) | 74 | , d(new Private(this)) | ||
84 | { | 75 | { | ||
76 | d->engine.installExtensions(QJSEngine::ConsoleExtension); | ||||
77 | | ||||
78 | registerMetaObjects(); | ||||
79 | | ||||
80 | QJSValueIterator it(d->engine.globalObject()); | ||||
81 | while (it.hasNext()) { | ||||
82 | it.next(); | ||||
83 | KexiScriptingDebug() << it.name() << ": " << it.value().toString(); | ||||
84 | } | ||||
85 | } | 85 | } | ||
staniek: Maybe we can move this code to execute() and make sure it's executed once? This way we delay… | |||||
86 | 86 | | |||
87 | KexiScriptPart::~KexiScriptPart() | 87 | KexiScriptPart::~KexiScriptPart() | ||
88 | { | 88 | { | ||
89 | delete d; | 89 | delete d; | ||
90 | } | 90 | } | ||
91 | 91 | | |||
92 | bool KexiScriptPart::execute(KexiPart::Item* item, QObject* sender) | 92 | bool KexiScriptPart::execute(KexiPart::Item* item, QObject* sender) | ||
staniek: } -> \n} | |||||
93 | { | 93 | { | ||
94 | Q_UNUSED(sender); | 94 | Q_UNUSED(sender); | ||
95 | if (!item) { | 95 | if (!item) { | ||
96 | qWarning() << "Invalid item."; | 96 | qWarning() << "Invalid item."; | ||
97 | return false; | 97 | return false; | ||
98 | } | 98 | } | ||
99 | 99 | | |||
100 | #if 0 | 100 | QString p = loadData(item); | ||
101 | KexiDialogBase* dialog = new KexiDialogBase(m_mainWin); | | |||
102 | dialog->setId(item->identifier()); | | |||
103 | KexiScriptDesignView* view = dynamic_cast<KexiScriptDesignView*>( | | |||
104 | createView(dialog, dialog, *item, Kexi::DesignViewMode)); | | |||
105 | if (! view) { | | |||
106 | qWarning() << "Failed to create a view."; | | |||
107 | return false; | | |||
108 | } | | |||
109 | 101 | | |||
110 | Kross::Action* scriptaction = view->scriptAction(); | 102 | QJSValue result = execute(p); | ||
111 | if (scriptaction) { | | |||
112 | 103 | | |||
113 | const QString dontAskAgainName = "askExecuteScript"; | 104 | if (result.isError()) { | ||
114 | KSharedConfig::Ptr config = KSharedConfig::openConfig(); | 105 | QString errormessage = result.toString(); | ||
115 | QString dontask = config->readEntry(dontAskAgainName).toLower(); | 106 | long lineno = result.property("lineNumber").toInt(); | ||
116 | 107 | QMessageBox mb(xi18n("Kexi Script"), xi18n("Error executing script at line %1:\n%2").arg(lineno).arg(errormessage), QMessageBox::Critical, QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); | |||
117 | bool exec = (dontask == "yes"); | 108 | mb.exec(); | ||
118 | if (!exec && dontask != "no") { | 109 | return false; | ||
119 | exec = KMessageBox::Yes == KMessageBox::questionYesNo(view, | 110 | } | ||
120 | futureI18n("Do you want to execute the script \"%1\"?\n\n" | | |||
121 | "Scripts obtained from unknown sources can contain dangerous code.", scriptaction->text()), | | |||
122 | futureI18n("Execute Script?"), KGuiItem(futureI18nc("@action:button", "Execute"), koIconName("system-run")), | | |||
123 | dontAskAgainName, KMessageBox::Notify | KMessageBox::Dangerous | | |||
124 | ); | | |||
125 | } | | |||
126 | | ||||
127 | if (exec) { | | |||
128 | //QTimer::singleShot(10, scriptaction, SLOT(activate())); | | |||
129 | d->scriptguiclient->executeScriptAction(scriptaction); | | |||
130 | } | | |||
131 | } | | |||
132 | view->deleteLater(); // not needed any longer. | | |||
133 | #else | | |||
134 | | ||||
135 | Kross::Action *action = d->action(item->name()); | | |||
136 | Q_ASSERT(action); | | |||
137 | action->trigger(); | | |||
138 | #endif | | |||
139 | return true; | 111 | return true; | ||
140 | } | 112 | } | ||
141 | 113 | | |||
142 | void KexiScriptPart::initPartActions() | 114 | QJSValue KexiScriptPart::execute(const QString& program) | ||
143 | { | 115 | { | ||
staniek: Use isEmpty()
If true, should we return true? | |||||
144 | qDebug() << "............."; | 116 | QJSValue val; | ||
staniek: Check QJSValue::isError() | |||||
145 | #if 0 | 117 | | ||
146 | if (m_mainWin) { | 118 | if (!d->engine.globalObject().hasProperty("KDb")) { | ||
147 | // At this stage the KexiPart::Part::m_mainWin should be defined, so | 119 | val = d->engine.newQObject(&d->kexidbmodule); | ||
148 | // that we are able to use it's KXMLGUIClient. | 120 | d->engine.globalObject().setProperty("KDb", val); | ||
Let's define and use KexiScriptingDebug the same way KexiMigrateManagerDebug is used. staniek: Let's define and use KexiScriptingDebug the same way KexiMigrateManagerDebug is used. | |||||
149 | | ||||
150 | // Initialize the ScriptGUIClient. | | |||
151 | d->scriptguiclient = new Kross::Api::ScriptGUIClient(m_mainWin); | | |||
152 | | ||||
153 | // Publish the KexiMainWindow singelton instance. At least the KexiApp | | |||
154 | // scripting-plugin depends on this instance and loading the plugin will | | |||
155 | // fail if it's not avaiable. | | |||
156 | if (! Kross::Api::Manager::scriptManager()->hasChild("KexiMainWindow")) { | | |||
157 | Kross::Api::Manager::scriptManager()->addQObject(m_mainWin, "KexiMainWindow"); | | |||
158 | | ||||
159 | // Add the QAction's provided by the ScriptGUIClient to the | | |||
160 | // KexiMainWindow. | | |||
161 | //FIXME: fix+use createSharedPartAction() whyever it doesn't work as expected right now... | | |||
162 | Q3PopupMenu* popup = m_mainWin->findPopupMenu("tools"); | | |||
163 | if (popup) { | | |||
164 | QAction* execscriptaction = d->scriptguiclient->action("executescriptfile"); | | |||
165 | if (execscriptaction) | | |||
166 | execscriptaction->plug(popup); | | |||
167 | QAction* configscriptaction = d->scriptguiclient->action("configurescripts"); | | |||
168 | if (configscriptaction) | | |||
169 | configscriptaction->plug(popup); | | |||
170 | QAction* scriptmenuaction = d->scriptguiclient->action("installedscripts"); | | |||
171 | if (scriptmenuaction) | | |||
172 | scriptmenuaction->plug(popup); | | |||
173 | /* | | |||
174 | QAction * execscriptmenuaction = d->scriptguiclient->action("executedscripts"); | | |||
175 | if(execscriptmenuaction) | | |||
176 | execscriptmenuaction->plug( popup ); | | |||
177 | QAction * loadedscriptmenuaction = d->scriptguiclient->action("loadedscripts"); | | |||
178 | if(loadedscriptmenuaction) | | |||
179 | loadedscriptmenuaction->plug( popup ); | | |||
180 | */ | | |||
181 | } | 121 | } | ||
122 | | ||||
123 | if (!program.isEmpty()) { | ||||
124 | return d->engine.evaluate(program); | ||||
182 | } | 125 | } | ||
126 | return QJSValue(); | ||||
183 | } | 127 | } | ||
staniek: Better: xi18n("Error executing script at line %1:\n%2") | |||||
184 | #endif | 128 | | ||
129 | void KexiScriptPart::initPartActions() | ||||
130 | { | ||||
131 | | ||||
185 | } | 132 | } | ||
186 | 133 | | |||
187 | void KexiScriptPart::initInstanceActions() | 134 | void KexiScriptPart::initInstanceActions() | ||
188 | { | 135 | { | ||
189 | createSharedAction(Kexi::DesignViewMode, xi18n("Configure Editor..."), | 136 | createSharedAction(Kexi::TextViewMode, xi18n("Configure Editor..."), | ||
190 | koIconName("configure"), QKeySequence(), "script_config_editor"); | 137 | koIconName("configure"), QKeySequence(), "script_config_editor"); | ||
191 | } | 138 | } | ||
192 | 139 | | |||
193 | KexiView* KexiScriptPart::createView(QWidget *parent, | 140 | KexiView* KexiScriptPart::createView(QWidget *parent, | ||
194 | KexiWindow *window, | 141 | KexiWindow *window, | ||
195 | KexiPart::Item *item, | 142 | KexiPart::Item *item, | ||
196 | Kexi::ViewMode viewMode, | 143 | Kexi::ViewMode viewMode, | ||
197 | QMap<QString, QVariant>* staticObjectArgs) | 144 | QMap<QString, QVariant>* staticObjectArgs) | ||
198 | { | 145 | { | ||
199 | Q_ASSERT(item); | 146 | Q_ASSERT(item); | ||
200 | Q_UNUSED(window); | 147 | Q_UNUSED(window); | ||
201 | Q_UNUSED(staticObjectArgs); | 148 | Q_UNUSED(staticObjectArgs); | ||
202 | qDebug() << "............. createView"; | 149 | | ||
203 | QString partname = item->name(); | 150 | QString partname = item->name(); | ||
204 | if (! partname.isNull()) { | 151 | if (! partname.isNull()) { | ||
205 | Kross::Action *action = d->action(partname); | 152 | if (viewMode == Kexi::TextViewMode) { | ||
206 | #if 0 | 153 | return new KexiScriptDesignView(parent); | ||
207 | KexiMainWindow *win = dialog->mainWin(); | | |||
208 | if (!win || !win->project() || !win->project()->dbConnection()) | | |||
209 | return 0; | | |||
210 | Kross::Api::ScriptActionCollection* collection = d->scriptguiclient->getActionCollection("projectscripts"); | | |||
211 | if (! collection) { | | |||
212 | collection = new Kross::Api::ScriptActionCollection(xi18n("Scripts"), d->scriptguiclient->actionCollection(), "projectscripts"); | | |||
213 | d->scriptguiclient->addActionCollection("projectscripts", collection); | | |||
214 | } | | |||
215 | const char* name = partname.toLatin1(); | | |||
216 | Kross::Api::ScriptAction::Ptr scriptaction = collection->action(name); | | |||
217 | if (! scriptaction) { | | |||
218 | scriptaction = new Kross::Api::ScriptAction(partname); | | |||
219 | collection->attach(scriptaction); //!< @todo remove again on unload! | | |||
220 | } | | |||
221 | #endif | | |||
222 | if (viewMode == Kexi::DesignViewMode) { | | |||
223 | return new KexiScriptDesignView(parent, action); | | |||
224 | } | 154 | } | ||
225 | } | 155 | } | ||
226 | return 0; | 156 | return 0; | ||
227 | } | 157 | } | ||
228 | 158 | | |||
229 | KLocalizedString KexiScriptPart::i18nMessage( | 159 | KLocalizedString KexiScriptPart::i18nMessage( | ||
230 | const QString& englishMessage, KexiWindow* window) const | 160 | const QString& englishMessage, KexiWindow* window) const | ||
231 | { | 161 | { | ||
232 | if (englishMessage == "Design of object <resource>%1</resource> has been modified.") | 162 | if (englishMessage == "Design of object <resource>%1</resource> has been modified.") | ||
233 | return kxi18nc(I18NC_NOOP("@info", "Design of script <resource>%1</resource> has been modified.")); | 163 | return kxi18nc(I18NC_NOOP("@info", "Design of script <resource>%1</resource> has been modified.")); | ||
234 | if (englishMessage == "Object <resource>%1</resource> already exists.") | 164 | if (englishMessage == "Object <resource>%1</resource> already exists.") | ||
235 | return kxi18nc(I18NC_NOOP("@info", "Script <resource>%1</resource> already exists.")); | 165 | return kxi18nc(I18NC_NOOP("@info", "Script <resource>%1</resource> already exists.")); | ||
236 | return Part::i18nMessage(englishMessage, window); | 166 | return Part::i18nMessage(englishMessage, window); | ||
237 | } | 167 | } | ||
238 | 168 | | |||
169 | QString KexiScriptPart::loadData(KexiPart::Item* item) | ||||
We have KexiPart::Part::loadSchemaObject() dedicated for reimplementing object loading. Look e.g. how reports use it. staniek: We have KexiPart::Part::loadSchemaObject() dedicated for reimplementing object loading. Look e. | |||||
170 | { | ||||
171 | QString data; | ||||
172 | if (!item) { | ||||
173 | return QString(); | ||||
174 | } | ||||
175 | | ||||
176 | if (true != KexiMainWindowIface::global()->project()->dbConnection()->loadDataBlock( | ||||
staniek: We have more specialized Part::loadDataBlock() | |||||
Yes, however it takes a Window*. This is called only from the execute() method to execute a script without creating a window. Suggestions? piggz: Yes, however it takes a Window*. This is called only from the execute() method to execute a… | |||||
I see. KexiPart::Part::loadSchemaObject() can be still useful if implemented, right? staniek: I see. KexiPart::Part::loadSchemaObject() can be still useful if implemented, right? | |||||
177 | item->identifier(), &data)) | ||||
178 | { | ||||
179 | return QString(); | ||||
180 | } | ||||
181 | | ||||
182 | QString errMsg; | ||||
183 | int errLine; | ||||
184 | int errCol; | ||||
185 | | ||||
186 | QString scriptType; | ||||
187 | | ||||
188 | QDomDocument domdoc; | ||||
189 | bool parsed = domdoc.setContent(data, false, &errMsg, &errLine, &errCol); | ||||
190 | | ||||
191 | if (!parsed) { | ||||
staniek: ! parsed -> !parsed | |||||
192 | KexiScriptingWarning() << "XML parsing error line: " << errLine << " col: " << errCol << " message: " << errMsg; | ||||
staniek: -> KexiScriptingWarning | |||||
193 | return QString(); | ||||
194 | } | ||||
195 | | ||||
196 | QDomElement scriptelem = domdoc.namedItem("script").toElement(); | ||||
197 | if (scriptelem.isNull()) { | ||||
198 | KexiScriptingWarning() << "script domelement is null"; | ||||
staniek: -> KexiScriptingWarning | |||||
199 | return QString(); | ||||
200 | } | ||||
201 | | ||||
202 | scriptType = scriptelem.attribute("scripttype"); | ||||
203 | if (scriptType.isEmpty()) { | ||||
204 | scriptType = "executable"; | ||||
205 | } | ||||
206 | | ||||
207 | if (scriptType == "executable") { | ||||
208 | return scriptelem.text().toUtf8(); | ||||
209 | } else { | ||||
210 | return QString(); | ||||
211 | } | ||||
212 | } | ||||
213 | | ||||
214 | void KexiScriptPart::registerMetaObjects() | ||||
215 | { | ||||
216 | QJSValue meta = d->engine.newQMetaObject(&KexiScriptAdaptor::staticMetaObject); | ||||
217 | d->engine.globalObject().setProperty("KexiScriptAdaptor", meta); | ||||
218 | | ||||
219 | meta = d->engine.newQMetaObject(&Scripting::KexiDBModule::staticMetaObject); | ||||
220 | d->engine.globalObject().setProperty("KDb", meta); | ||||
221 | } | ||||
222 | | ||||
239 | #include "kexiscriptpart.moc" | 223 | #include "kexiscriptpart.moc" |
Maybe we can move this code to execute() and make sure it's executed once? This way we delay the initialization.
We will obviously also run this code if project-global and app-global scripts exist.
Not to myself: Even later we will run this code only if there's user's consent to do it.