1 /* Copyright (c) 2015 Gerald Knizia
2 *
3 * This file is part of the IboView program (see: http://www.iboview.org)
4 *
5 * IboView is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * IboView 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
12 * GNU General Public License for details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
16 *
17 * Please see IboView documentation in README.txt for:
18 * -- A list of included external software and their licenses. The included
19 * external software's copyright is not touched by this agreement.
20 * -- Notes on re-distribution and contributions to/further development of
21 * the IboView software
22 */
23
24 #include "Iv.h"
25 // #include <iostream>
26 // #include <fstream>
27 // #include <string>
28 // #include <sstream>
29
30 #include <QScriptEngine>
31 #include <QTextStream>
32 #include <QFile>
33
34 // #include <chaiscript/chaiscript.hpp>
35 // #include <chaiscript/dispatchkit/function_call.hpp>
36 // #include <boost/format.hpp>
37 // #include <boost/shared_ptr.hpp>
38 // using boost::format;
39
40 // boost::shared_ptr<chaiscript::ChaiScript> create_chaiscript_object();
41
42 #include "IvScript.h"
43 #include "CxColor.h"
44 // #include "cppformat/format.h"
45 // #include "CtIo.h"
46
~IApplication()47 IApplication::~IApplication()
48 {}
49
~IView3d()50 IView3d::~IView3d()
51 {}
52
53 // void IView3d::set_option(QString const &OptionName, bool f) {
54 // if (f)
55 // set_option(OptionName, int(1));
56 // else
57 // set_option(OptionName, int(-1));
58 // }
59 // void IView3d::set_option(QString const &OptionName, int i) {
60 // set_option(OptionName, double(i));
61 // }
62
63
64 // // will load an entire file into the stringstream str. Returns false if failed.
65 // bool LoadFileIntoMemory( std::string &sFileContent,
66 // std::string const &FileName, unsigned *pFileLength )
67 // {
68 // // read the entire file into an stringstream object.
69 // std::ifstream
70 // File( FileName.c_str() );
71 // std::size_t
72 // FileLength;
73 // if ( false == File.good() )
74 // return false;
75 // File.seekg( 0, std::ios::end );
76 // FileLength = File.tellg();
77 // if ( 0 != pFileLength )
78 // *pFileLength = FileLength;
79 // sFileContent.clear();
80 // sFileContent.resize(FileLength, 0);
81 // File.seekg( 0, std::ios::beg );
82 // File.read( &sFileContent[0], FileLength );
83 // // pFileContent.resize(2 + FileLength);
84 // // memset( &pFileContent[0], 0, 2 + FileLength );
85 // // File.seekg( 0, std::ios::beg );
86 // // File.read( &pFileContent[0], FileLength );
87 // return true;
88 // };
89
hsv_uint(float h,float s,float v)90 uint32_t hsv_uint(float h, float s, float v) {
91 return 0xff000000 | (uint32_t)ct::Hsv(h,s,v).uint32();
92 // return 0xff000000 + (uint32_t)ct::FColor(h,s,v);
93 // return 0xfffffffe;
94 // return (uint32_t)ct::Hsv(h,s,v);
95 }
96
97 uint32_t ColFromAlpha(float a);
98
hsva_uint(float h,float s,float v,float a)99 uint32_t hsva_uint(float h, float s, float v, float a) {
100 return ColFromAlpha(a) | (uint32_t)ct::Hsv(h,s,v).uint32();
101 // return 0xff000000 + (uint32_t)ct::FColor(h,s,v);
102 // return 0xfffffffe;
103 // return (uint32_t)ct::Hsv(h,s,v);
104 }
105
106 // static std::string s_format_i(std::string const &Format, int val) {
107 // std::stringstream str;
108 // str << boost::format(Format) % val;
109 // return str.str();
110 // }
111 //
112 // static std::string s_format_f(std::string const &Format, double val) {
113 // std::stringstream str;
114 // str << boost::format(Format) % val;
115 // return str.str();
116 // }
117 //
118 // static std::string s_format_s(std::string const &Format, std::string const &val) {
119 // std::stringstream str;
120 // str << boost::format(Format) % val;
121 // return str.str();
122 // }
123
s_format_i(QString const & Format,int val)124 static QString s_format_i(QString const &Format, int val) {
125 QString r;
126 r.sprintf(Format.toLatin1().constData(), val);
127 return r;
128 }
129
s_format_f(QString const & Format,double val)130 static QString s_format_f(QString const &Format, double val) {
131 QString r;
132 r.sprintf(Format.toLatin1().constData(), val);
133 return r;
134 }
135
s_format_s(QString const & Format,QString const & val)136 static QString s_format_s(QString const &Format, QString const &val) {
137 QString r;
138 r.sprintf(Format.toLatin1().constData(), val.toUtf8().constData());
139 return r;
140 }
141
142
143
s_Invoke_format(QScriptContext * context,QScriptEngine *)144 static QScriptValue s_Invoke_format(QScriptContext *context, QScriptEngine * /*engine*/)
145 {
146 int
147 iArg0,
148 nArgs = context->argumentCount();
149 QString
150 s;
151 // if (context->thisObject().isUndefined()) {
152 if (!context->thisObject().isString()) {
153 // called as stand-alone function. Take first argument as base string.
154 // (the 'thisObject' is still defined: it is returned as "[object global]".)
155 s = qscriptvalue_cast<QString>(context->argument(0));
156 // IvEmit("!! s_Invoke_format: Taking free-standing version. s = '%1'", s);
157 iArg0 = 1;
158 nArgs -= 1;
159 } else {
160 // take "this" as base string and rest as arguments.
161 s = qscriptvalue_cast<QString>(context->thisObject());
162 // IvEmit("!! s_Invoke_format: Taking object version. s = '%1'", s);
163 iArg0 = 0;
164 }
165 // for (int i = iArg0; i < nArgs; ++ i)
166 // s = s.arg(qscriptvalue_cast<QString>(context->argument(i)));
167 #if 1
168 // can only do basic replacements.
169 for (int i = 0; i < nArgs; ++ i) {
170 s.replace(QString("{%1}").arg(i), qscriptvalue_cast<QString>(context->argument(iArg0+i)));
171 }
172 #else
173 fmt::Writer w;
174 fmt::BasicFormatter<char> const &cstr = w.Format(s.toStdString());
175 fmt::BasicFormatter<char> &str = const_cast<fmt::BasicFormatter<char>&>(cstr);
176
177 for (int i = 0; i < nArgs; ++ i) {
178 QScriptValue arg = context->argument(iArg0+i);
179 if (arg.isNumber()) {
180 qsreal q = arg.toNumber();
181 // ECMA script does not distinguish between floats and integers...
182 if (q == long(q))
183 str << long(q);
184 else
185 str << q;
186 } else {
187 str << arg.toString().toStdString();
188 }
189 s = QString::fromStdString(w.str());
190 }
191 #endif
192 return QScriptValue(s);
193 }
194
195
196 #define FARG(FType,iArg) qscriptvalue_cast<FType>(context->argument((iArg)))
197
198 // QScriptValue callee = context->callee();
199 // if (context->argumentCount() == 1) // writing?
200 // callee.setProperty("value", context->argument(0));
201 // return callee.property("value");
202 // return QScriptValue(hsva_uint(context->argument(0).toUInt32()));
s_Invoke_hsva(QScriptContext * context,QScriptEngine *)203 static QScriptValue s_Invoke_hsva(QScriptContext *context, QScriptEngine * /*engine*/) {
204 return QScriptValue(hsva_uint(FARG(float, 0), FARG(float, 1), FARG(float,2), FARG(float,3)));
205 }
206
207 // static QScriptValue s_Invoke_fmti(QScriptContext *context, QScriptEngine * /*engine*/) {
208 // return QScriptValue(QString::fromStdString(s_format_i(FARG(QString,0).toStdString(), FARG(int,1))));
209 // }
210 // static QScriptValue s_Invoke_fmtf(QScriptContext *context, QScriptEngine * /*engine*/) {
211 // return QScriptValue(QString::fromStdString(s_format_f(FARG(QString,0).toStdString(), FARG(double,1))));
212 // }
213 //
214 // static QScriptValue s_Invoke_fmts(QScriptContext *context, QScriptEngine * /*engine*/) {
215 // return QScriptValue(QString::fromStdString(s_format_s(FARG(QString,0).toStdString(), FARG(QString,1).toStdString())));
216 // }
217 //
218 // ^- note: we can probably do this much better now with QtScript...
219
s_Invoke_fmti(QScriptContext * context,QScriptEngine *)220 static QScriptValue s_Invoke_fmti(QScriptContext *context, QScriptEngine * /*engine*/) {
221 return QScriptValue(s_format_i(FARG(QString,0), FARG(int,1)));
222 }
223
s_Invoke_fmtf(QScriptContext * context,QScriptEngine *)224 static QScriptValue s_Invoke_fmtf(QScriptContext *context, QScriptEngine * /*engine*/) {
225 return QScriptValue(s_format_f(FARG(QString,0), FARG(double,1)));
226 }
227
s_Invoke_fmts(QScriptContext * context,QScriptEngine *)228 static QScriptValue s_Invoke_fmts(QScriptContext *context, QScriptEngine * /*engine*/) {
229 return QScriptValue(s_format_s(FARG(QString,0), FARG(QString,1)));
230 }
231
s_Invoke_irgb(QScriptContext * context,QScriptEngine *)232 static QScriptValue s_Invoke_irgb(QScriptContext *context, QScriptEngine * /*engine*/) {
233 return QScriptValue(ct::irgb(FARG(quint32,0)));
234 }
235
s_Invoke_replace_ext(QScriptContext * context,QScriptEngine *)236 static QScriptValue s_Invoke_replace_ext(QScriptContext *context, QScriptEngine * /*engine*/) {
237 return QScriptValue(ReplaceExt(FARG(QString,0), FARG(QString,1)));
238 }
239
s_Invoke_remove_path(QScriptContext * context,QScriptEngine *)240 static QScriptValue s_Invoke_remove_path(QScriptContext *context, QScriptEngine * /*engine*/) {
241 return QScriptValue(RemovePath(FARG(QString,0)));
242 }
243
244
245 #undef FARG
246
AddScriptObject(QScriptEngine & ScriptEngine,QObject * pObject,QString const & Name)247 static void AddScriptObject(QScriptEngine &ScriptEngine, QObject *pObject, QString const &Name)
248 {
249 QScriptValue
250 // ObjectValue = ScriptEngine.newQObject(pObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeChildObjects | QScriptEngine::ExcludeSuperClassContents);
251 // ObjectValue = ScriptEngine.newQObject(pObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassContents);
252 ObjectValue = ScriptEngine.newQObject(pObject, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassMethods);
253 ScriptEngine.globalObject().setProperty(Name, ObjectValue);
254 }
255
AddScriptFunction(QScriptEngine & ScriptEngine,QScriptEngine::FunctionSignature pFn,QString const & Name)256 static void AddScriptFunction(QScriptEngine &ScriptEngine, QScriptEngine::FunctionSignature pFn, QString const &Name)
257 {
258 QScriptValue
259 FunctionValue = ScriptEngine.newFunction(pFn);
260 ScriptEngine.globalObject().setProperty(Name, FunctionValue);
261 }
262
ExecScript(IApplication * app,IView3d * view,QString const & ScriptText,QString const & ScriptName)263 void ExecScript(IApplication *app, IView3d *view, QString const &ScriptText, QString const &ScriptName) {
264 // std::cout << QString("--- exec script '%1'").arg(ScriptName).toStdString() << std::endl;
265 IvEmit("--- exec script '%1'", ScriptName);
266 QScriptEngine
267 ScriptEngine;
268
269 AddScriptObject(ScriptEngine, app, "app");
270 // FIXME: added IApplication as both "app" and "doc" until things get sorted out.
271 AddScriptObject(ScriptEngine, app, "doc");
272 AddScriptObject(ScriptEngine, view, "view");
273 AddScriptFunction(ScriptEngine, s_Invoke_hsva, "hsva");
274 AddScriptFunction(ScriptEngine, s_Invoke_irgb, "irgb");
275 AddScriptFunction(ScriptEngine, s_Invoke_fmti, "fmti");
276 AddScriptFunction(ScriptEngine, s_Invoke_fmtf, "fmtf");
277 AddScriptFunction(ScriptEngine, s_Invoke_fmts, "fmts");
278 AddScriptFunction(ScriptEngine, s_Invoke_format, "format");
279 AddScriptFunction(ScriptEngine, s_Invoke_replace_ext, "replace_ext"); // replace a file extension by another file extension
280 AddScriptFunction(ScriptEngine, s_Invoke_remove_path, "remove_path"); // strip off path from a file name
281
282 // add 'format' also to prototype of standard class 'String'.
283 // This should then allow for "blabla {0} blabla {1}".format("wheee","whooo")-type calls.
284 QScriptValue
285 FormatFunction = ScriptEngine.newFunction(s_Invoke_format);
286 QScriptValue
287 StringProto = ScriptEngine.globalObject().property("String").property("prototype");
288 StringProto.setProperty("format", FormatFunction);
289
290 // std::string sInputScript;
291 // LoadFileIntoMemory(sInputScript, FileName.toStdString(), 0);
292
293 QScriptValue
294 // res = ScriptEngine.evaluate(QString::fromStdString(sInputScript));
295 res = ScriptEngine.evaluate(ScriptText);
296 if (ScriptEngine.hasUncaughtException()) {
297 int iErrorLine = ScriptEngine.uncaughtExceptionLineNumber();
298 // std::cerr << QString("\n!ERROR during execution of script:\n%1:%2: %3\n").arg(FileName, QString::number(iErrorLine), res.toString()).toStdString() << std::endl;
299 IvNotify(NOTIFY_Error, "Error during script execution:\n" + QString("%1:%2: %3\n").arg(ScriptName, QString::number(iErrorLine), res.toString()));
300 }
301 IvEmit("--- script terminated.");
302 }
303
LoadTextFileViaQt(QString const & FileName)304 QString LoadTextFileViaQt(QString const &FileName)
305 {
306 QFile
307 File(FileName);
308 if (!File.open(QIODevice::ReadOnly | QIODevice::Text)) {
309 // std::cerr << "!ERROR: Failed to open script file '%s'" % FileName << std::endl;
310 IvNotify(NOTIFY_Error, QString("Failed to open script file '%1'").arg(FileName));
311 return "";
312 }
313 QTextStream
314 Stream(&File);
315 Stream.setAutoDetectUnicode(true);
316 // ^- deals with UTF-16 and UTF-32. I guess it will default to UTF8?
317 // I didn't quite get this in the docs.
318 QString
319 Result = Stream.readAll();
320 File.close();
321 return Result;
322 }
323
324
325 // void ExecScript(FMainWindow *ui, std::string FileName) {
ExecScript(IApplication * app,IView3d * view,QString const & FileName)326 void ExecScript(IApplication *app, IView3d *view, QString const &FileName)
327 {
328 // IvEmit("--- exec script '%1'", FileName);
329 QString
330 ScriptText = LoadTextFileViaQt(FileName);
331 ExecScript(app, view, ScriptText, FileName);
332 }
333