Added support for .desktop files in themes.
[tagua/yd.git] / src / luaapi / loader.cpp
blob92772beb961fa6af2c3bdd31f3403d58c86aa181
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@sns.it>
3 (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
5 This program 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; either version 2 of the License, or
8 (at your option) any later version.
9 */
12 #include <stdlib.h>
13 #include <iostream>
14 #include <QRect>
15 #include <QDir>
17 #include <KConfigGroup>
18 #include <KDesktopFile>
19 #include <KStandardDirs>
21 #include "common.h"
22 #include "loader/image.h"
23 #include "luaapi/imaging.h"
24 #include "luaapi/loader.h"
25 #include "luaapi/options.h"
27 namespace LuaApi {
29 const luaL_Reg Loader::lualibs[] = {
30 {"", luaopen_base},
31 {LUA_LOADLIBNAME, luaopen_package},
32 {LUA_TABLIBNAME, luaopen_table},
33 {LUA_IOLIBNAME, luaopen_io},
34 {LUA_OSLIBNAME, luaopen_os},
35 {LUA_STRLIBNAME, luaopen_string},
36 {LUA_MATHLIBNAME, luaopen_math},
37 {LUA_DBLIBNAME, luaopen_debug},
38 {LUA_FUNCLIBNAME, luaopen_func},
39 {LUA_COLLECTLIBNAME, luaopen_collect},
40 {NULL, NULL}
43 Loader::Loader(::Loader::Context *ctx)
44 : m_error(false) {
45 lua_State* l = lua_open();
46 m_state = l;
48 for (const luaL_Reg *lib = lualibs; lib->func; lib++) {
49 lua_pushcfunction(l, lib->func);
50 lua_pushstring(l, lib->name);
51 lua_call(l, 1, 0);
54 Wrapper<QRectF>::register_class(l);
55 Wrapper<QPointF>::register_class(l);
56 Wrapper<QColor>::register_class(l);
57 Wrapper<QLinearGradient>::register_class(l);
58 Wrapper<QRadialGradient>::register_class(l);
59 Wrapper<QConicalGradient>::register_class(l);
60 Wrapper<QBrush>::register_class(l);
61 Wrapper<QFont>::register_class(l);
62 Wrapper< ::Loader::Image>::register_class(l);
63 Wrapper< ::Loader::Glyph>::register_class(l);
65 Wrapper<OptList>::register_class(l);
66 Wrapper<BoolOptList>::register_class(l);
67 Wrapper<BoolOptPtr>::register_class(l);
68 Wrapper<IntOptPtr>::register_class(l);
69 Wrapper<StringOptPtr>::register_class(l);
70 Wrapper<UrlOptPtr>::register_class(l);
71 Wrapper<ColorOptPtr>::register_class(l);
72 Wrapper<FontOptPtr>::register_class(l);
73 Wrapper<ComboOptPtr>::register_class(l);
74 Wrapper<SelectOptPtr>::register_class(l);
76 lua_pushlightuserdata(l, ctx);
77 lua_setfield(l, LUA_REGISTRYINDEX, LOADING_CONTEXT);
79 lua_pushlightuserdata(l, this);
80 lua_setfield(l, LUA_REGISTRYINDEX, API_LOADER);
82 lua_pushlightuserdata(m_state, &m_curr_dir);
83 lua_setfield(m_state, LUA_REGISTRYINDEX, CURRENT_DIRECTORY);
85 lua_newtable(l);
86 lua_setglobal(l, "theme");
88 lua_pushcfunction(l, import_func);
89 lua_setglobal(l, "import");
91 lua_pushcfunction(l, read_desktop_file);
92 lua_setglobal(l, "desktop_file");
95 Loader::~Loader() {
96 lua_close(m_state);
99 bool Loader::runFile(const QString& file, bool setdir) {
101 QString path = QDir::cleanPath(
102 QDir::isAbsolutePath(file) ? file : m_curr_dir.filePath(file) );
103 if (!QFile::exists(path)) {
104 // find it in the scripts dir
105 path = KStandardDirs::locate("appdata", "scripts/" + file);
107 if (!QFile::exists(path)) {
108 // give up
109 return false;
112 if(setdir) {
113 QFileInfo f_info( path );
114 m_curr_dir = f_info.dir();
117 bool retv;
118 if(luaL_loadfile(m_state, path.toAscii().constData()) == 0) {
119 if(lua_pcall(m_state, 0, LUA_MULTRET, 0) != 0)
120 retv = false;
121 else
122 retv = true;
124 else
125 retv = false;
127 if(!retv) {
128 m_error = true;
129 m_error_string = QString(lua_tostring(m_state, -1));
130 lua_pop(m_state, 1);
133 return retv;
136 template<typename T>
137 struct Loader::create_value_data {
138 const QString& key;
139 int size;
140 const LuaValueMap* args;
141 bool allow_nil;
142 T out;
143 create_value_data(const QString& _key, int _size, const LuaValueMap* _args, bool _allow_nil)
144 : key(_key)
145 , size(_size)
146 , args(_args)
147 , allow_nil(_allow_nil) {
151 template<typename T>
152 T Loader::getValue(const QString& key, int size, const LuaValueMap* args, bool allow_nil) {
153 StackCheck s(m_state);
155 create_value_data<T> data(key, size, args, allow_nil);
156 if(lua_cpcall(m_state, create_value_func<T>, &data) != 0) {
157 m_error = true;
158 m_error_string = QString(lua_tostring(m_state, -1))+"\nsearched key was: "+key;
159 lua_pop(m_state, 1);
160 return T();
162 return data.out;
165 template ::Loader::Glyph Loader::getValue< ::Loader::Glyph>(const QString&, int, const LuaValueMap*, bool);
166 template OptList Loader::getValue<OptList>(const QString&, int, const LuaValueMap*, bool);
167 template QString Loader::getValue<QString>(const QString&, int, const LuaValueMap*, bool);
168 template QStringList Loader::getValue<QStringList>(const QString&, int, const LuaValueMap*, bool);
169 template QImage Loader::getValue<QImage>(const QString&, int, const LuaValueMap*, bool);
170 template ImageOrMap Loader::getValue<ImageOrMap>(const QString&, int, const LuaValueMap*, bool);
171 template double Loader::getValue<double>(const QString&, int, const LuaValueMap*, bool);
172 template QPointF Loader::getValue<QPointF>(const QString&, int, const LuaValueMap*, bool);
173 template QRectF Loader::getValue<QRectF>(const QString&, int, const LuaValueMap*, bool);
174 template QColor Loader::getValue<QColor>(const QString&, int, const LuaValueMap*, bool);
175 template QBrush Loader::getValue<QBrush>(const QString&, int, const LuaValueMap*, bool);
176 template QFont Loader::getValue<QFont>(const QString&, int, const LuaValueMap*, bool);
177 template LuaValueMap Loader::getValue<LuaValueMap>(const QString&, int, const LuaValueMap*, bool);
179 template<typename T>
180 void Loader::retrieve(create_value_data<T>* d, lua_State *l, int pos) {
181 d->out = *Wrapper<T>::retrieve(l, pos, AssertOk);
184 template<>
185 void Loader::retrieve<double>(create_value_data<double>* d, lua_State *l, int pos) {
186 d->out = lua_tonumber(l, pos);
189 template<>
190 void Loader::retrieve<QString>(create_value_data<QString>* d, lua_State *l, int pos) {
191 d->out = lua_tostring(l, pos);
194 template<>
195 void Loader::retrieve<QColor>(create_value_data<QColor>* d, lua_State *l, int pos) {
196 d->out = Wrapper<QColor>::get(l, pos);
199 template<>
200 void Loader::retrieve<QBrush>(create_value_data<QBrush>* d, lua_State *l, int pos) {
201 d->out = Wrapper<QBrush>::get(l, pos);
204 template<>
205 void Loader::retrieve<QStringList>(create_value_data<QStringList>* d, lua_State *l, int pos) {
206 if(lua_isstring(l, pos))
207 d->out << QString(lua_tostring(l, -1));
208 else if(lua_istable(l, pos)) {
209 lua_pushnil(l);
210 while (lua_next(l, pos<0 ? pos-1 : pos) != 0) {
211 d->out << QString(lua_tostring(l, -1));
212 lua_pop(l, 1);
215 else
216 luaL_error(l, "Can't convert to a QStringList (not string nor table)");
219 template<>
220 void Loader::retrieve<LuaValueMap>(create_value_data<LuaValueMap>* d, lua_State *l, int pos) {
221 if(lua_istable(l, pos)) {
222 lua_pushnil(l);
223 while (lua_next(l, pos<0 ? pos-1 : pos) != 0) {
224 QString key = lua_tostring(l, -2);
225 if(QPointF *res = Wrapper<QPointF>::retrieve(l, -1, Check))
226 d->out[key] = *res;
227 else if(QRectF *res = Wrapper<QRectF>::retrieve(l, -1, Check))
228 d->out[key] = *res;
229 else
230 d->out[key] = lua_tonumber(l, -1);
231 lua_pop(l, 1);
234 else
235 luaL_error(l, "Can't convert to a LuaValueMap (not table)");
238 template<>
239 void Loader::retrieve<QImage>(create_value_data<QImage>* d, lua_State *l, int pos) {
240 ::Loader::Image *retv = Wrapper< ::Loader::Image>::retrieve(l, pos, AssertOk);
241 d->out = retv->image();
244 template<>
245 void Loader::retrieve<ImageOrMap>(create_value_data<ImageOrMap>* d, lua_State *l, int pos) {
246 if(::Loader::Image *img = Wrapper< ::Loader::Image>::retrieve(l, pos))
247 d->out = img->image();
248 else if(lua_istable(l, pos)) {
250 //collect the images in this way to avoid leaking memory if Wrapper::retrieve raises an exception
251 d->out = ImageMap();
252 ImageMap& out = boost::get<ImageMap>(d->out);
254 lua_pushnil(l);
255 while (lua_next(l, pos<0 ? pos-1 : pos) != 0) {
256 QRectF *rect = Wrapper<QRectF>::retrieve(l, -2, AssertOk);
257 ::Loader::Image *img = Wrapper< ::Loader::Image>::retrieve(l, -1, AssertOk);
259 QRect r = rect->toRect();
260 out[rect->toRect()] = img->image();
262 lua_pop(l, 1);
265 else
266 luaL_error(l, "Can't convert to a ImageOrMap (not image nor table)");
269 template<typename T>
270 int Loader::create_value_func(lua_State *l) {
271 StackCheck s(l, -1);
272 create_value_data<T>* data = reinterpret_cast<create_value_data<T>*>(lua_touserdata(l, -1));
273 lua_pop(l, 1);
275 lua_getglobal(l, "theme");
276 lua_getfield(l, -1, data->key.toAscii().constData());
277 lua_remove(l, -2);
279 /* If it is a function call it, or else try to retrieve directly the value */
280 if(lua_isnil(l, -1)) {
281 if(!data->allow_nil)
282 luaL_error(l, "No such entry: %s", data->key.toAscii().constData());
284 else {
285 if(lua_isfunction(l, -1)) {
286 int nparams = 0;
287 if(data->size) {
288 lua_pushnumber(l, data->size);
289 nparams++;
291 if(data->args) {
292 lua_pushvaluemap(l, data->args);
293 nparams++;
295 lua_call(l, nparams, 1);
298 retrieve<T>(data, l, -1);
301 lua_pop(l, 1);
302 return 0;
306 int Loader::import_func(lua_State *l) {
307 lua_getfield(l, LUA_REGISTRYINDEX, API_LOADER);
308 Loader* api = reinterpret_cast<Loader*>(lua_touserdata(l, -1));
309 lua_pop(l, 1);
311 int n = lua_gettop(l);
312 if (n != 1)
313 luaL_error(l, "Wrong argument count for \"import\"");
314 QString file(lua_tostring(l, 1));
315 lua_pop(l, n);
317 if (!api->runFile(file, false)) {
318 luaL_error(l, "Error importing \"%s\":\n%s", file.toAscii().constData(),
319 api->errorString().toAscii().constData() );
322 return 0;
325 int Loader::read_desktop_file(lua_State* l) {
326 lua_getfield(l, LUA_REGISTRYINDEX, API_LOADER);
327 Loader* api = reinterpret_cast<Loader*>(lua_touserdata(l, -1));
328 lua_pop(l, 1);
330 int n = lua_gettop(l);
331 if (n != 0)
332 luaL_error(l, "Wrong argument count for \"desktop_file\"");
333 lua_pop(l, n);
335 QString filePath = api->currDir().filePath("theme.desktop");
336 if (!KDesktopFile::isDesktopFile(filePath)) {
337 // no desktop file
338 return 0;
341 KDesktopFile theme(filePath);
343 // set theme fields according to the desktop file
344 lua_getglobal(l, "theme");
346 QString name = theme.readName();
347 if (!name.isEmpty()) {
348 lua_pushstring(l, "name");
349 lua_pushstring(l, qPrintable(name));
350 lua_settable(l, -3);
353 QString description = theme.readComment();
354 if (!name.isEmpty()) {
355 description.replace("|", "\n");
356 lua_pushstring(l, "description");
357 lua_pushstring(l, qPrintable(description));
358 lua_settable(l, -3);
361 KConfigGroup themeGroup = theme.desktopGroup();
362 QString variants = themeGroup.readEntry("X-Tagua-Variants");
363 if (!variants.isEmpty()) {
364 QStringList varlist = variants.split(QRegExp("\\s*,\\s*"));
365 lua_pushstring(l, "variants");
366 lua_newtable(l);
367 for (int i = 0; i < varlist.size(); i++) {
368 lua_pushnumber(l, i + 1);
369 lua_pushstring(l, qPrintable(varlist[i]));
370 lua_settable(l, -3);
372 lua_settable(l, -3);
375 return 0;
378 } // namespace LuaLoader