diff --git a/plugins/Example/main.lua b/plugins/Example/main.lua index 7659565f..4a599d82 100644 --- a/plugins/Example/main.lua +++ b/plugins/Example/main.lua @@ -1,11 +1,19 @@ -- This is an example Xournal++ Plugin - copy this to get started -print("Hello from Lua Plugin\n"); +var_dump = require "var_dump" --- Register all Toolbar actions and intialize all UI stuff +-- Register all Toolbar actions and intialize all UI stuff function initUi() - print("Plugin initUi called\n"); -end + print("Hello from Example: Plugin initUi called\n"); + + ref = app.registerUi({["menu"] = "Test123", ["callback"] = "exampleCallback"}); + print("Menu reference:"); + var_dump(ref); -app.msgbox("Test123", "yes,no") + print("Example plugin registered\n"); +end +-- Callback if the menu item is executed +function exampleCallback() + app.msgbox("Test123", "yes,no"); +end diff --git a/plugins/Example/var_dump.lua b/plugins/Example/var_dump.lua new file mode 100644 index 00000000..9128512f --- /dev/null +++ b/plugins/Example/var_dump.lua @@ -0,0 +1,33 @@ +-- Source: https://gist.github.com/lunixbochs/5b0bb27861a396ab7a86#file-var_dump-lua-L1 + +local function string(o) + return '"' .. tostring(o) .. '"' +end + +local function recurse(o, indent) + if indent == nil then indent = '' end + local indent2 = indent .. ' ' + if type(o) == 'table' then + local s = indent .. '{' .. '\n' + local first = true + for k,v in pairs(o) do + if first == false then s = s .. ', \n' end + if type(k) ~= 'number' then k = string(k) end + s = s .. indent2 .. '[' .. k .. '] = ' .. recurse(v, indent2) + first = false + end + return s .. '\n' .. indent .. '}' + else + return string(o) + end +end + +local function var_dump(...) + local args = {...} + if #args > 1 then + var_dump(args) + else + print(recurse(args[1])) + end +end +return var_dump diff --git a/src/plugin/Plugin.cpp b/src/plugin/Plugin.cpp index c86fcca5..cb5395ca 100644 --- a/src/plugin/Plugin.cpp +++ b/src/plugin/Plugin.cpp @@ -86,18 +86,26 @@ void Plugin::registerToolbar() return; } - // TODO Errorhandling etc. - // lua_register - // lua_atpanic + inInitUi = true; - if (callFunction("initUi")) + lua_getglobal(lua, "initUi"); + if (lua_isfunction (lua, -1) == 1) { - g_message("Plugin «%s» UI initialized", name.c_str()); + if (callFunction("initUi")) + { + g_message("Plugin «%s» UI initialized", name.c_str()); + } + else + { + g_warning("Plugin «%s» init failed!", name.c_str()); + } } else { g_message("Plugin «%s» has no UI init", name.c_str()); } + + inInitUi = false; } /** @@ -110,6 +118,27 @@ string Plugin::getName() return name; } +/** + * @return Flag to check if init ui is currently running + */ +bool Plugin::isInInitUi() +{ + XOJ_CHECK_TYPE(Plugin); + + return inInitUi; +} + +/** + * Register a menu item + * + * @return Internal ID, can e.g. be used to disable the menu + */ +int Plugin::registerMenu(string menu, string callback) +{ + // TODO + return 0; +} + /** * Load ini file */ @@ -150,10 +179,43 @@ void Plugin::registerXournalppLibs(lua_State* lua) for (const luaL_Reg* lib = loadedlibs; lib->func; lib++) { luaL_requiref(lua, lib->name, lib->func, 1); - lua_pop(lua, 1); /* remove lib */ + + // remove lib + lua_pop(lua, 1); } } +/** + * Add the plugin folder to the lua path + */ +void Plugin::addPluginToLuaPath() +{ + lua_getglobal(lua, "package"); + + // get field "path" from table at top of stack (-1) + lua_getfield(lua, -1, "path"); + + // For now: limit the include path to the current plugin folder, for security and compatibility reasons + // grab path string from top of stack + // string curPath = lua_tostring(lua, -1); + // curPath.append(";"); + string curPath; + curPath.append(path); + curPath.append("/?.lua"); + + // get rid of the string on the stack we just pushed on line 5 + lua_pop(lua, 1); + + // push the new one + lua_pushstring(lua, curPath.c_str()); + + // set the field "path" in table at -2 with value at top of stack + lua_setfield(lua, -2, "path"); + + // get rid of package table from top of stack + lua_pop(lua, 1); +} + /** * Load the plugin script */ @@ -196,6 +258,8 @@ void Plugin::loadScript() registerXournalppLibs(lua); + addPluginToLuaPath(); + // Run the loaded Lua script if (lua_pcall(lua, 0, 0, 0) != LUA_OK) { @@ -218,7 +282,10 @@ bool Plugin::callFunction(string fnc) // Run the function if (lua_pcall(lua, 0, 0, 0)) { - // Failed + const char* errMsg = lua_tostring(lua, -1); + XojMsgBox::showPluginMessage(name, errMsg, MSG_BT_OK, true); + + g_warning("Error in Plugin: «%s», error: «%s»", name.c_str(), errMsg); return false; } diff --git a/src/plugin/Plugin.h b/src/plugin/Plugin.h index 9384314c..fc38c762 100644 --- a/src/plugin/Plugin.h +++ b/src/plugin/Plugin.h @@ -41,6 +41,18 @@ public: */ string getName(); + /** + * @return Flag to check if init ui is currently running + */ + bool isInInitUi(); + + /** + * Register a menu item + * + * @return Internal ID, can e.g. be used to disable the menu + */ + int registerMenu(string menu, string callback); + private: /** * Load ini file @@ -62,6 +74,11 @@ private: */ void registerXournalppLibs(lua_State* lua); + /** + * Add the plugin folder to the lua path + */ + void addPluginToLuaPath(); + public: /** * Get Plugin from lua engine @@ -101,6 +118,11 @@ private: */ lua_State* lua = NULL; + /** + * Flag to check if init ui is currently running + */ + bool inInitUi = false; + /** * Flag if the plugin is valid / correct loaded */ diff --git a/src/plugin/PluginController.cpp b/src/plugin/PluginController.cpp index 3fbbb7f3..d0536a3e 100644 --- a/src/plugin/PluginController.cpp +++ b/src/plugin/PluginController.cpp @@ -4,6 +4,8 @@ #include "control/Control.h" #include "gui/GladeSearchpath.h" +#include + #include @@ -13,7 +15,16 @@ PluginController::PluginController(Control* control) XOJ_INIT_TYPE(PluginController); #ifdef ENABLE_PLUGINS - loadPluginsFrom(control->getGladeSearchPath()->getFirstSearchPath() + "/../plugins/"); + string path = control->getGladeSearchPath()->getFirstSearchPath(); + if (StringUtils::endsWith(path, "ui")) + { + path = path.substr(0, path.length() - 2) + "plugins"; + } + else + { + path += "/../plugins"; + } + loadPluginsFrom(path); #endif } diff --git a/src/plugin/luapi_application.h b/src/plugin/luapi_application.h index 06c830b5..532a2431 100644 --- a/src/plugin/luapi_application.h +++ b/src/plugin/luapi_application.h @@ -54,16 +54,77 @@ static int applib_msgbox(lua_State* L) } +/** + * Allow to register menupoints or toolbar icons, this needs to be called from initUi + */ +static int applib_registerUi(lua_State* L) +{ + Plugin* plugin = Plugin::getPluginFromLua(L); + if (!plugin->isInInitUi()) + { + luaL_error(L, "registerUi needs to be called within initUi()"); + } + + // discard any extra arguments passed in + lua_settop(L, 1); + luaL_checktype(L, 1, LUA_TTABLE); + + // Now to get the data out of the table + // 'unpack' the table by putting the values onto + // the stack first. Then convert those stack values + // into an appropriate C type. + lua_getfield(L, 1, "menu"); + lua_getfield(L, 1, "callback"); + // stack now has following: + // 1 = {"menu"="MenuName", callback="functionName"} + // -2 = "MenuName" + // -1 = "functionName" + + const char* menu = luaL_optstring(L, -2, NULL); + const char* callback = luaL_optstring(L, -1, NULL); + if (callback == NULL) + { + luaL_error(L, "Missing callback function!"); + } + + int menuId = -1; + int toolbarId = -1; + + if (menu) + { + plugin->registerMenu(menu, callback); + } + + // Make sure to remove all vars which are put to the stack before! + lua_pop(L, 2); + + // Add return value to the Stack + lua_createtable(L, 0, 2); + + lua_pushstring(L, "menuId"); + lua_pushinteger(L, menuId); + lua_settable(L, -3); /* 3rd element from the stack top */ + + lua_pushstring(L, "toolbarId"); + lua_pushinteger(L, toolbarId); + lua_settable(L, -3); + + return 1; +} + + + static const luaL_Reg applib[] = { - {"msgbox", applib_msgbox}, + { "msgbox", applib_msgbox }, + { "registerUi", applib_registerUi }, - // Placeholder - {"MSG_BT_OK", NULL}, - {"MSG_BT_YES", NULL}, - {"MSG_BT_NO", NULL}, - {"MSG_BT_CANCEL", NULL}, + // Placeholder + {"MSG_BT_OK", NULL}, + {"MSG_BT_YES", NULL}, + {"MSG_BT_NO", NULL}, + {"MSG_BT_CANCEL", NULL}, - {NULL, NULL} + {NULL, NULL} }; /**