You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

459 lines
8.0 KiB

#include "Plugin.h"
#include <config.h>
#include <i18n.h>
#ifdef ENABLE_PLUGINS
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <lauxlib.h>
}
#include "luapi_application.h"
#define LOAD_FROM_INI(target, group, key) \
{ \
char* value = g_key_file_get_string(config, group, key, NULL); \
if (value != NULL) \
{ \
target = value; \
g_free(value); \
} \
}
/*
** these libs are loaded by lua.c and are readily available to any Lua
** program
*/
static const luaL_Reg loadedlibs[] = {
{ "app", luaopen_app },
{ NULL, NULL }
};
Plugin::Plugin(Control* control, string name, string path)
: control(control),
name(name),
path(path)
{
XOJ_INIT_TYPE(Plugin);
loadIni();
}
Plugin::~Plugin()
{
XOJ_CHECK_TYPE(Plugin);
if (lua)
{
// Clean up, free the Lua state var
lua_close(lua);
lua = NULL;
}
for (MenuEntry* m : menuEntries)
{
delete m;
}
menuEntries.clear();
XOJ_RELEASE_TYPE(Plugin);
}
/**
* Get Plugin from lua engine
*/
Plugin* Plugin::getPluginFromLua(lua_State* lua)
{
lua_getfield(lua, LUA_REGISTRYINDEX, "Xournalpp_Plugin");
if (lua_islightuserdata(lua, -1))
{
Plugin* data = (Plugin*)lua_touserdata(lua, -1);
lua_pop(lua, 1);
XOJ_CHECK_TYPE_OBJ(data, Plugin);
return data;
}
return NULL;
}
/**
* Register toolbar item and all other UI stuff
*/
void Plugin::registerToolbar()
{
XOJ_CHECK_TYPE(Plugin);
if (!this->valid || !this->enabled)
{
return;
}
inInitUi = true;
lua_getglobal(lua, "initUi");
if (lua_isfunction (lua, -1) == 1)
{
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;
}
/**
* Register all menu entries to the menu
*/
void Plugin::registerMenu(GtkWindow* mainWindow, GtkWidget* menu)
{
XOJ_CHECK_TYPE(Plugin);
if (menuEntries.empty() || !this->enabled)
{
// No entries - nothing to do
return;
}
gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
GtkAccelGroup* accelGroup = gtk_accel_group_new();
for (MenuEntry* m : menuEntries)
{
GtkWidget* mi = gtk_menu_item_new_with_label(m->menu.c_str());
m->widget = mi;
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
if (m->accelerator != "")
{
guint acceleratorKey = 0;
GdkModifierType mods = (GdkModifierType)0;
gtk_accelerator_parse(m->accelerator.c_str(), &acceleratorKey, &mods);
gtk_widget_add_accelerator(mi, "activate", accelGroup, acceleratorKey, mods, GTK_ACCEL_VISIBLE);
}
g_signal_connect(mi, "activate", G_CALLBACK(
+[](GtkWidget* bt, MenuEntry* me)
{
XOJ_CHECK_TYPE_OBJ(me, MenuEntry);
me->plugin->executeMenuEntry(me);
}), m);
}
gtk_window_add_accel_group(GTK_WINDOW(mainWindow), accelGroup);
}
/**
* Execute menu entry
*/
void Plugin::executeMenuEntry(MenuEntry* entry)
{
XOJ_CHECK_TYPE(Plugin);
callFunction(entry->callback);
}
/**
* @return the Plugin name
*/
string Plugin::getName()
{
XOJ_CHECK_TYPE(Plugin);
return name;
}
/**
* @return Description of the plugin
*/
string Plugin::getDescription()
{
XOJ_CHECK_TYPE(Plugin);
return description;
}
/**
* Author of the plugin
*/
string Plugin::getAuthor()
{
XOJ_CHECK_TYPE(Plugin);
return author;
}
/**
* Plugin version
*/
string Plugin::getVersion()
{
XOJ_CHECK_TYPE(Plugin);
return version;
}
/**
* The plugin is enabled
*/
bool Plugin::isEnabled()
{
XOJ_CHECK_TYPE(Plugin);
return enabled;
}
/**
* The plugin is enabled
*/
void Plugin::setEnabled(bool enabled)
{
this->enabled = enabled;
}
/**
* The plugin is default enabled
*/
bool Plugin::isDefaultEnabled()
{
XOJ_CHECK_TYPE(Plugin);
return defaultEnabled;
}
/**
* @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, string accelerator)
{
MenuEntry* m = new MenuEntry(this);
m->menu = menu;
m->callback = callback;
m->accelerator = accelerator;
menuEntries.push_back(m);
return menuEntries.size() - 1;
}
/**
* @return The main controller
*/
Control* Plugin::getControl()
{
XOJ_CHECK_TYPE(Plugin);
return control;
}
/**
* Load ini file
*/
void Plugin::loadIni()
{
XOJ_CHECK_TYPE(Plugin);
GKeyFile* config = g_key_file_new();
g_key_file_set_list_separator(config, ',');
string filename = path + "/plugin.ini";
if (!g_key_file_load_from_file(config, filename.c_str(), G_KEY_FILE_NONE, NULL))
{
g_key_file_free(config);
return;
}
LOAD_FROM_INI(author, "about", "author");
LOAD_FROM_INI(version, "about", "version");
LOAD_FROM_INI(description, "about", "description");
if (version == "<xournalpp>")
{
version = PROJECT_VERSION;
}
LOAD_FROM_INI(mainfile, "plugin", "mainfile");
string defaultEnabledStr;
LOAD_FROM_INI(defaultEnabledStr, "default", "enabled");
defaultEnabled = defaultEnabledStr == "true";
enabled = defaultEnabled;
g_key_file_free(config);
this->valid = true;
}
/**
* Load custom Lua Libraries
*/
void Plugin::registerXournalppLibs(lua_State* lua)
{
for (const luaL_Reg* lib = loadedlibs; lib->func; lib++)
{
luaL_requiref(lua, lib->name, lib->func, 1);
// 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
*/
void Plugin::loadScript()
{
XOJ_CHECK_TYPE(Plugin);
if (mainfile == "")
{
this->valid = false;
return;
}
if (mainfile.find("..") != std::string::npos)
{
g_warning("Plugin «%s» contains unsupported path «%s»", name.c_str(), mainfile.c_str());
this->valid = false;
return;
}
if (!this->enabled)
{
return;
}
// Create Lua state variable
lua = luaL_newstate();
// Load Lua libraries
luaL_openlibs(lua);
// Load but don't run the Lua script
string luafile = path + "/" + mainfile;
if (luaL_loadfile(lua, luafile.c_str()))
{
// Error out if file can't be read
g_warning("Could not run plugin Lua file: «%s»", luafile.c_str());
this->valid = false;
return;
}
// Register Plugin object to Lua instance
lua_pushlightuserdata(lua, this);
lua_setfield(lua, LUA_REGISTRYINDEX, "Xournalpp_Plugin");
registerXournalppLibs(lua);
addPluginToLuaPath();
// Run the loaded Lua script
if (lua_pcall(lua, 0, 0, 0) != LUA_OK)
{
const char* errMsg = lua_tostring(lua, -1);
map<int, string> button;
button.insert(std::pair<int, string>(0, _("OK")));
XojMsgBox::showPluginMessage(name, errMsg, button, true);
g_warning("Could not run plugin Lua file: «%s», error: «%s»", luafile.c_str(), errMsg);
this->valid = false;
return;
}
}
/**
* Execute lua function
*/
bool Plugin::callFunction(string fnc)
{
lua_getglobal(lua, fnc.c_str());
// Run the function
if (lua_pcall(lua, 0, 0, 0))
{
const char* errMsg = lua_tostring(lua, -1);
map<int, string> button;
button.insert(std::pair<int, string>(0, _("OK")));
XojMsgBox::showPluginMessage(name, errMsg, button, true);
g_warning("Error in Plugin: «%s», error: «%s»", name.c_str(), errMsg);
return false;
}
return true;
}
/**
* Check if this plugin is valid
*/
bool Plugin::isValid()
{
XOJ_CHECK_TYPE(Plugin);
return valid;
}
#endif