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