/* * lua menue * * (C) 2014 by martii * (C) 2014-2015 M. Liebmann (micha-bbg) * (C) 2016 Sven Hoefer (svenhoefer) * (C) 2015 Jacek Jendrzej (SatBaby) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include "luainstance.h" #include "lua_menue.h" CLuaInstMenu* CLuaInstMenu::getInstance() { static CLuaInstMenu* LuaInstMenu = NULL; if(!LuaInstMenu) LuaInstMenu = new CLuaInstMenu(); return LuaInstMenu; } bool CLuaMenuChangeObserver::changeNotify(lua_State *L, const std::string &luaAction, const std::string &luaId, void *Data) { const char *optionValue = (const char *) Data; lua_pushglobaltable(L); lua_getfield(L, -1, luaAction.c_str()); lua_remove(L, -2); lua_pushstring(L, luaId.c_str()); lua_pushstring(L, optionValue); int status = lua_pcall(L, 2 /* two args */, 1 /* one result */, 0); if (status) { bool isString = lua_isstring(L,-1); const char *null = "NULL"; fprintf(stderr, "[CLuaMenuChangeObserver::%s:%d] error in script: %s\n", __func__, __LINE__, isString ? lua_tostring(L, -1): null); DisplayErrorMessage(isString ? lua_tostring(L, -1):null, "Lua Script Error:"); } double res = lua_isnumber(L, -1) ? lua_tonumber(L, -1) : 0; return (((int)res == menu_return::RETURN_REPAINT) || ((int)res == menu_return::RETURN_EXIT_REPAINT)); } void CLuaInstMenu::MenuRegister(lua_State *L) { luaL_Reg meth[] = { { "new", CLuaInstMenu::MenuNew }, { "addKey", CLuaInstMenu::MenuAddKey }, { "addItem", CLuaInstMenu::MenuAddItem }, { "exec", CLuaInstMenu::MenuExec }, { "hide", CLuaInstMenu::MenuHide }, { "setActive", CLuaInstMenu::MenuSetActive }, { "setName", CLuaInstMenu::MenuSetName }, { "setSelected", CLuaInstMenu::MenuSetSelected }, { "setValue", CLuaInstMenu::MenuSetValue }, { "paintItem", CLuaInstMenu::MenuPaintItem }, { "__gc", CLuaInstMenu::MenuDelete }, { NULL, NULL } }; luaL_newmetatable(L, "menu"); luaL_setfuncs(L, meth, 0); lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); lua_setglobal(L, "menu"); // keep misspelled "menue" for backwards-compatibility luaL_newmetatable(L, "menu"); luaL_setfuncs(L, meth, 0); lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); lua_setglobal(L, "menue"); } CLuaMenu *CLuaInstMenu::MenuCheck(lua_State *L, int n) { return *(CLuaMenu **) luaL_checkudata(L, n, "menu"); } CLuaMenu::CLuaMenu() { m = NULL; observ = new CLuaMenuChangeObserver(); } CLuaMenu::~CLuaMenu() { itemmap.clear(); delete m; delete observ; } CLuaMenuForwarder::CLuaMenuForwarder(lua_State *_L, std::string _luaAction, std::string _luaId) { Init(_L, _luaAction, _luaId); } void CLuaMenuForwarder::Init(lua_State *_L, std::string _luaAction, std::string _luaId) { L = _L; luaAction = _luaAction; luaId = _luaId; } CLuaMenuForwarder::~CLuaMenuForwarder() { } int CLuaMenuForwarder::exec(CMenuTarget* /*parent*/, const std::string & /*actionKey*/) { int res = menu_return::RETURN_REPAINT; if (!luaAction.empty()) { lua_pushglobaltable(L); lua_getfield(L, -1, luaAction.c_str()); lua_remove(L, -2); lua_pushstring(L, luaId.c_str()); int status = lua_pcall(L, 1 /* one arg */, 1 /* one result */, 0); if (status) { bool isString = lua_isstring(L,-1); const char *null = "NULL"; fprintf(stderr, "[CLuaMenuForwarder::%s:%d] error in script: %s\n", __func__, __LINE__, isString ? lua_tostring(L, -1):null); DisplayErrorMessage(isString ? lua_tostring(L, -1):null, "Lua Script Error:"); } if (lua_isnumber(L, -1)) res = (int) lua_tonumber(L, -1); lua_pop(L, 1); } return res; } CLuaMenuFilebrowser::CLuaMenuFilebrowser(lua_State *_L, std::string _luaAction, std::string _luaId, std::string *_value, bool _dirMode) : CLuaMenuForwarder(_L, _luaAction, _luaId) { Init(_value, _dirMode); } void CLuaMenuFilebrowser::Init(std::string *_value, bool _dirMode) { value = _value; dirMode = _dirMode; } int CLuaMenuFilebrowser::exec(CMenuTarget* /*parent*/, const std::string& /*actionKey*/) { int res = menu_return::RETURN_REPAINT; CFileBrowser fileBrowser; fileBrowser.Dir_Mode = dirMode; CFileFilter fileFilter; for (std::vector::iterator it = filter.begin(); it != filter.end(); ++it) fileFilter.addFilter(*it); if (!filter.empty()) fileBrowser.Filter = &fileFilter; std::string tmpValue = (dirMode) ? *value : getPathName(*value); if (fileBrowser.exec(tmpValue.c_str()) == true) *value = fileBrowser.getSelectedFile()->Name; if (!luaAction.empty()) { lua_pushglobaltable(L); lua_getfield(L, -1, luaAction.c_str()); lua_remove(L, -2); lua_pushstring(L, luaId.c_str()); lua_pushstring(L, value->c_str()); int status = lua_pcall(L, 2 /* two arg */, 1 /* one result */, 0); if (status) { bool isString = lua_isstring(L,-1); const char *null = "NULL"; fprintf(stderr, "[CLuaMenuFilebrowser::%s:%d] error in script: %s\n", __func__, __LINE__, isString ? lua_tostring(L, -1):null); DisplayErrorMessage(isString ? lua_tostring(L, -1):null, "Lua Script Error:"); } if (lua_isnumber(L, -1)) res = (int) lua_tonumber(L, -1); lua_pop(L, 1); } return res; } CLuaMenuStringinput::CLuaMenuStringinput(lua_State *_L, std::string _luaAction, std::string _luaId, const char *_name, std::string *_value, int _size, std::string _valid_chars, CChangeObserver *_observ, const char *_icon, bool _sms) : CLuaMenuForwarder(_L, _luaAction, _luaId) { Init(_name, _value, _size, _valid_chars, _observ, _icon, _sms); } void CLuaMenuStringinput::Init(const char *_name, std::string *_value, int _size, std::string _valid_chars, CChangeObserver *_observ, const char *_icon, bool _sms) { name = _name; value = _value; size = _size; valid_chars = _valid_chars; icon = _icon; observ = _observ; sms = _sms; } int CLuaMenuStringinput::exec(CMenuTarget* /*parent*/, const std::string & /*actionKey*/) { int res = menu_return::RETURN_REPAINT; CStringInput *i; if (sms) i = new CStringInputSMS((char *)name, value, size, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, valid_chars.c_str(), observ, icon); else i = new CStringInput((char *)name, value, size, NONEXISTANT_LOCALE, NONEXISTANT_LOCALE, valid_chars.c_str(), observ, icon); i->exec(NULL, ""); delete i; if (!luaAction.empty()) { lua_pushglobaltable(L); lua_getfield(L, -1, luaAction.c_str()); lua_remove(L, -2); lua_pushstring(L, luaId.c_str()); lua_pushstring(L, value->c_str()); int status = lua_pcall(L, 2 /* two arg */, 1 /* one result */, 0); if (status) { bool isString = lua_isstring(L,-1); const char *null = "NULL"; fprintf(stderr, "[CLuaMenuStringinput::%s:%d] error in script: %s\n", __func__, __LINE__, isString ? lua_tostring(L, -1):null); DisplayErrorMessage(isString ? lua_tostring(L, -1):null, "Lua Script Error:"); } if (lua_isnumber(L, -1)) res = (int) lua_tonumber(L, -1); lua_pop(L, 2); } return res; } CLuaMenuKeyboardinput::CLuaMenuKeyboardinput(lua_State *_L, std::string _luaAction, std::string _luaId, const char *_name, std::string *_value, int _size, CChangeObserver *_observ, const char *_icon, std::string _help, std::string _help2) : CLuaMenuForwarder(_L, _luaAction, _luaId) { Init(_name, _value, _size, _observ, _icon, _help, _help2); } void CLuaMenuKeyboardinput::Init(const char *_name, std::string *_value, int _size, CChangeObserver *_observ, const char *_icon, std::string _help, std::string _help2) { name = _name; value = _value; size = _size; icon = _icon; observ = _observ; help = _help; help2 = _help2; } int CLuaMenuKeyboardinput::exec(CMenuTarget* /*parent*/, const std::string & /*actionKey*/) { int res = menu_return::RETURN_REPAINT; CKeyboardInput *i; i = new CKeyboardInput((char *)name, value, size, observ, icon, help, help2); i->exec(NULL, ""); delete i; if (!luaAction.empty()) { lua_pushglobaltable(L); lua_getfield(L, -1, luaAction.c_str()); lua_remove(L, -2); lua_pushstring(L, luaId.c_str()); lua_pushstring(L, value->c_str()); int status = lua_pcall(L, 2 /* two arg */, 1 /* one result */, 0); if (status) { bool isString = lua_isstring(L,-1); const char *null = "NULL"; fprintf(stderr, "[CLuaMenuKeyboardinput::%s:%d] error in script: %s\n", __func__, __LINE__, isString ? lua_tostring(L, -1):null); DisplayErrorMessage(isString ? lua_tostring(L, -1):null, "Lua Script Error:"); } if (lua_isnumber(L, -1)) res = (int) lua_tonumber(L, -1); lua_pop(L, 2); } return res; } int CLuaInstMenu::MenuNew(lua_State *L) { CMenuWidget *m; if (lua_istable(L, 1)) { std::string name; tableLookup(L, "name", name) || tableLookup(L, "title", name); std::string icon; tableLookup(L, "icon", icon); lua_Integer mwidth; if (tableLookup(L, "mwidth", mwidth)) m = new CMenuWidget(name.c_str(), icon.c_str(), mwidth); else m = new CMenuWidget(name.c_str(), icon.c_str()); } else m = new CMenuWidget(); CLuaMenu **udata = (CLuaMenu **) lua_newuserdata(L, sizeof(CLuaMenu *)); *udata = new CLuaMenu(); (*udata)->m = m; luaL_getmetatable(L, "menu"); lua_setmetatable(L, -2); return 1; } int CLuaInstMenu::MenuAddKey(lua_State *L) { CLuaMenu *D = MenuCheck(L, 1); if (!D) return 0; lua_assert(lua_istable(L, 2)); std::string action; tableLookup(L, "action", action); std::string id; tableLookup(L, "id", id); lua_Unsigned directkey = CRCInput::RC_nokey; tableLookup(L, "directkey", directkey); if ((!action.empty()) && (directkey != CRCInput::RC_nokey)) { CLuaMenuForwarder *forwarder = new CLuaMenuForwarder(L, action, id); D->m->addKey(directkey, forwarder, action); D->targets.push_back(forwarder); } return 0; } int CLuaInstMenu::MenuAddItem(lua_State *L) { CLuaMenu *D = MenuCheck(L, 1); if (!D) { lua_pushnil(L); return 1; } lua_assert(lua_istable(L, 2)); CMenuItem *mi = NULL; CLuaMenuItem i = {}; D->items.push_back(i); CLuaMenuItem *b = &D->items.back(); tableLookup(L, "name", b->name); std::string type; tableLookup(L, "type", type); if (type == "back") { D->m->addItem(GenericMenuBack); } else if (type == "next") { D->m->addItem(GenericMenuNext); } else if (type == "cancel") { D->m->addItem(GenericMenuCancel); } else if (type == "separator") { D->m->addItem(GenericMenuSeparator); } else if ((type == "separatorline") || (type == "subhead")) { if (!b->name.empty()) { int flag = (type == "separatorline") ? CMenuSeparator::LINE : CMenuSeparator::SUB_HEAD; D->m->addItem(new CMenuSeparator(CMenuSeparator::STRING | flag, b->name.c_str(), NONEXISTANT_LOCALE)); } else D->m->addItem(GenericMenuSeparatorLine); } else { std::string right_icon_str; tableLookup(L, "right_icon", right_icon_str); std::string action; tableLookup(L, "action", action); std::string value; tableLookup(L, "value", value); std::string hint; tableLookup(L, "hint", hint); std::string hint_icon_str; tableLookup(L, "hint_icon", hint_icon_str); std::string icon_str; tableLookup(L, "icon", icon_str); std::string id; tableLookup(L, "id", id); std::string tmp; char *right_icon = NULL; if (!right_icon_str.empty()) { right_icon = strdup(right_icon_str.c_str()); D->tofree.push_back(right_icon); } char *hint_icon = NULL; if (!hint_icon_str.empty()) { hint_icon = strdup(hint_icon_str.c_str()); D->tofree.push_back(hint_icon); } char *icon = NULL; if (!icon_str.empty()) { icon = strdup(icon_str.c_str()); D->tofree.push_back(icon); } lua_Unsigned directkey = CRCInput::RC_nokey; tableLookup(L, "directkey", directkey); lua_Integer pulldown = false; tableLookup(L, "pulldown", pulldown); bool enabled = true; if (!(tableLookup(L, "enabled", enabled) || tableLookup(L, "active", enabled))) { tmp = "true"; if (tableLookup(L, "enabled", tmp) || tableLookup(L, "active", tmp)) paramBoolDeprecated(L, tmp.c_str()); enabled = (tmp == "true" || tmp == "1" || tmp == "yes"); } tableLookup(L, "range", tmp); int range_from = 0, range_to = 99; sscanf(tmp.c_str(), "%d,%d", &range_from, &range_to); if (type == "forwarder") { b->str_val = value; CLuaMenuForwarder *forwarder = new CLuaMenuForwarder(L, action, id); mi = new CMenuForwarder(b->name, enabled, b->str_val, forwarder, NULL/*ActionKey*/, directkey, icon, right_icon); if (!hint.empty() || hint_icon) mi->setHint(hint_icon, hint); D->targets.push_back(forwarder); } else if (type == "chooser") { int options_count = 0; lua_pushstring(L, "options"); lua_gettable(L, -2); if (lua_istable(L, -1)) for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 2)) { lua_pushvalue(L, -2); options_count++; } lua_pop(L, 1); if (options_count == 0) { D->m->addItem(new CMenuSeparator(CMenuSeparator::STRING | CMenuSeparator::LINE, "ERROR! (options_count)", NONEXISTANT_LOCALE)); lua_pushnil(L); return 1; } CMenuOptionChooser::keyval_ext *kext = (CMenuOptionChooser::keyval_ext *)calloc(options_count, sizeof(CMenuOptionChooser::keyval_ext)); D->tofree.push_back(kext); lua_pushstring(L, "options"); lua_gettable(L, -2); b->int_val = 0; if (lua_istable(L, -1)) { int j = 0; for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 2)) { lua_pushvalue(L, -2); const char *key = lua_tostring(L, -1); const char *val = lua_tostring(L, -2); kext[j].key = atoi(key); kext[j].value = NONEXISTANT_LOCALE; kext[j].valname = strdup(val?val:"ERROR"); D->tofree.push_back((void *)kext[j].valname); if (!strcmp(value.c_str(), kext[j].valname)) b->int_val = kext[j].key; j++; } } lua_pop(L, 1); mi = new CMenuOptionChooser(b->name.c_str(), &b->int_val, kext, options_count, enabled, D->observ, directkey, icon, pulldown); } else if (type == "numeric") { b->int_val = range_from; sscanf(value.c_str(), "%d", &b->int_val); mi = new CMenuOptionNumberChooser(b->name, &b->int_val, enabled, range_from, range_to, D->observ, 0, 0, NONEXISTANT_LOCALE, pulldown); } else if (type == "string") { b->str_val = value; mi = new CMenuOptionStringChooser(b->name.c_str(), &b->str_val, enabled, D->observ, directkey, icon, pulldown); } else if (type == "stringinput") { b->str_val = value; std::string valid_chars = "abcdefghijklmnopqrstuvwxyz0123456789!\"ยง$%&/()=?-.@,_: "; tableLookup(L, "valid_chars", valid_chars); lua_Integer sms = 0; tableLookup(L, "sms", sms); lua_Integer size = 30; tableLookup(L, "size", size); CLuaMenuStringinput *stringinput = new CLuaMenuStringinput(L, action, id, b->name.c_str(), &b->str_val, size, valid_chars, D->observ, icon, sms); mi = new CMenuForwarder(b->name, enabled, b->str_val, stringinput, NULL/*ActionKey*/, directkey, icon, right_icon); D->targets.push_back(stringinput); } else if (type == "keyboardinput") { b->str_val = value; lua_Integer size = 0; tableLookup(L, "size", size); std::string help = ""; tableLookup(L, "help", help); std::string help2 = ""; tableLookup(L, "help2", help2); CLuaMenuKeyboardinput *keyboardinput = new CLuaMenuKeyboardinput(L, action, id, b->name.c_str(), &b->str_val, size, D->observ, icon, help, help2); mi = new CMenuForwarder(b->name, enabled, b->str_val, keyboardinput, NULL/*ActionKey*/, directkey, icon, right_icon); D->targets.push_back(keyboardinput); } else if (type == "filebrowser") { b->str_val = value; lua_Integer dirMode = 0; tableLookup(L, "dir_mode", dirMode); CLuaMenuFilebrowser *filebrowser = new CLuaMenuFilebrowser(L, action, id, &b->str_val, dirMode); lua_pushstring(L, "filter"); lua_gettable(L, -2); if (lua_istable(L, -1)) for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 2)) { lua_pushvalue(L, -2); const char *val = lua_tostring(L, -2); filebrowser->addFilter(val); } lua_pop(L, 1); mi = new CMenuForwarder(b->name, enabled, b->str_val, filebrowser, NULL/*ActionKey*/, directkey, icon, right_icon); D->targets.push_back(filebrowser); } if (mi) { mi->setLua(L, action, id); if (!hint.empty() || hint_icon) mi->setHint(hint_icon, hint); D->m->addItem(mi); } } if (mi) { lua_Integer id = D->itemmap.size() + 1; D->itemmap.insert(itemmap_pair_t(id, mi)); lua_pushinteger(L, id); } else lua_pushnil(L); return 1; } int CLuaInstMenu::MenuExec(lua_State *L) { CLuaMenu *D = MenuCheck(L, 1); if (!D) return 0; D->m->exec(NULL, ""); D->m->hide(); return 0; } int CLuaInstMenu::MenuHide(lua_State *L) { CLuaMenu *D = MenuCheck(L, 1); if (!D) return 0; D->m->hide(); return 0; } int CLuaInstMenu::MenuSetActive(lua_State *L) { lua_assert(lua_istable(L, 2)); CLuaMenu *D = MenuCheck(L, 1); if (!D) return 0; lua_Integer id; tableLookup(L, "item", id); bool activ; tableLookup(L, "activ", activ); CMenuItem* item = NULL; for (itemmap_iterator_t it = D->itemmap.begin(); it != D->itemmap.end(); ++it) { if (it->first == id) { item = it->second; break; } } if (item) item->setActive(activ); return 0; } int CLuaInstMenu::MenuSetName(lua_State *L) { lua_assert(lua_istable(L, 2)); CLuaMenu *D = MenuCheck(L, 1); if (!D) return 0; lua_Integer id; tableLookup(L, "item", id); std::string name; tableLookup(L, "name", name); CMenuItem* item = NULL; for (itemmap_iterator_t it = D->itemmap.begin(); it != D->itemmap.end(); ++it) { if (it->first == id) { item = it->second; break; } } if (item) item->setName(name); return 0; } int CLuaInstMenu::MenuDelete(lua_State *L) { CLuaMenu *D = MenuCheck(L, 1); if (!D) return 0; while (!D->targets.empty()) { delete D->targets.back(); D->targets.pop_back(); } while (!D->tofree.empty()) { free(D->tofree.back()); D->tofree.pop_back(); } delete D; return 0; } int CLuaInstMenu::MenuSetSelected(lua_State *L) { CLuaMenu *D = MenuCheck(L, 1); if (!D) return 0; lua_assert(lua_istable(L, 2)); lua_Integer preselected; tableLookup(L, "preselected", preselected); D->m->setSelected(preselected); return 0; } int CLuaInstMenu::MenuSetValue(lua_State *L) { lua_assert(lua_istable(L, 2)); CLuaMenu *D = MenuCheck(L, 1); if (!D) return 0; lua_Integer id; tableLookup(L, "item", id); std::string value; tableLookup(L, "value", value); CMenuItem* item = NULL; for (itemmap_iterator_t it = D->itemmap.begin(); it != D->itemmap.end(); ++it) { if (it->first == id) { item = it->second; break; } } if (item) static_cast(item)->setOption(value); return 0; } int CLuaInstMenu::MenuPaintItem(lua_State *L) { lua_assert(lua_istable(L, 2)); CLuaMenu *D = MenuCheck(L, 1); if (!D) return 0; lua_Integer id; tableLookup(L, "item", id); CMenuItem* item = NULL; for (itemmap_iterator_t it = D->itemmap.begin(); it != D->itemmap.end(); ++it) { if (it->first == id) { item = it->second; break; } } if (item) item->paint(); return 0; }