Merge pull request #920 from andreasb242/scripting

Scripting
presentation
andreasb242 7 years ago committed by GitHub
commit 739f9bddb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .settings/.gitignore
  2. 15
      .settings/language.settings.xml
  3. 5
      plugins/Example/main.lua
  4. 27
      src/plugin/Plugin.cpp
  5. 11
      src/plugin/Plugin.h
  6. 2
      src/plugin/PluginController.cpp
  7. 86
      src/plugin/luapi_application.h
  8. 18
      src/util/XojMsgBox.cpp
  9. 11
      src/util/XojMsgBox.h

@ -0,0 +1 @@
/language.settings.xml

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project>
<configuration id="0.1190743019" name="Default">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider copy-of="extension" id="org.eclipse.cdt.autotools.core.LibtoolGCCBuildCommandParser"/>
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="587297783307056298" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
</extension>
</configuration>
</project>

@ -6,7 +6,7 @@ var_dump = require "var_dump"
function initUi() function initUi()
print("Hello from Example: Plugin initUi called\n"); print("Hello from Example: Plugin initUi called\n");
ref = app.registerUi({["menu"] = "Test123", ["callback"] = "exampleCallback"}); ref = app.registerUi({["menu"] = "Test123", ["callback"] = "exampleCallback", ["accelerator"] = "<Control>t"});
print("Menu reference:"); print("Menu reference:");
var_dump(ref); var_dump(ref);
@ -15,5 +15,6 @@ end
-- Callback if the menu item is executed -- Callback if the menu item is executed
function exampleCallback() function exampleCallback()
app.msgbox("Test123", "yes,no"); result = app.msgbox("Test123", {[1] = "Yes", [2] = "No"});
print("result = " .. result)
end end

@ -1,6 +1,7 @@
#include "Plugin.h" #include "Plugin.h"
#include <config.h> #include <config.h>
#include <i18n.h>
#ifdef ENABLE_PLUGINS #ifdef ENABLE_PLUGINS
@ -118,7 +119,7 @@ void Plugin::registerToolbar()
/** /**
* Register all menu entries to the menu * Register all menu entries to the menu
*/ */
void Plugin::registerMenu(GtkWidget* menu) void Plugin::registerMenu(GtkWindow* mainWindow, GtkWidget* menu)
{ {
XOJ_CHECK_TYPE(Plugin); XOJ_CHECK_TYPE(Plugin);
@ -130,12 +131,23 @@ void Plugin::registerMenu(GtkWidget* menu)
gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new()); gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
GtkAccelGroup* accelGroup = gtk_accel_group_new();
for (MenuEntry* m : menuEntries) for (MenuEntry* m : menuEntries)
{ {
GtkWidget* mi = gtk_menu_item_new_with_label(m->menu.c_str()); GtkWidget* mi = gtk_menu_item_new_with_label(m->menu.c_str());
m->widget = mi; m->widget = mi;
gtk_menu_shell_append(GTK_MENU_SHELL(menu), 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( g_signal_connect(mi, "activate", G_CALLBACK(
+[](GtkWidget* bt, MenuEntry* me) +[](GtkWidget* bt, MenuEntry* me)
{ {
@ -143,6 +155,8 @@ void Plugin::registerMenu(GtkWidget* menu)
me->plugin->executeMenuEntry(me); me->plugin->executeMenuEntry(me);
}), m); }), m);
} }
gtk_window_add_accel_group(GTK_WINDOW(mainWindow), accelGroup);
} }
/** /**
@ -180,11 +194,12 @@ bool Plugin::isInInitUi()
* *
* @return Internal ID, can e.g. be used to disable the menu * @return Internal ID, can e.g. be used to disable the menu
*/ */
int Plugin::registerMenu(string menu, string callback) int Plugin::registerMenu(string menu, string callback, string accelerator)
{ {
MenuEntry* m = new MenuEntry(this); MenuEntry* m = new MenuEntry(this);
m->menu = menu; m->menu = menu;
m->callback = callback; m->callback = callback;
m->accelerator = accelerator;
menuEntries.push_back(m); menuEntries.push_back(m);
return menuEntries.size() - 1; return menuEntries.size() - 1;
@ -315,7 +330,9 @@ void Plugin::loadScript()
if (lua_pcall(lua, 0, 0, 0) != LUA_OK) if (lua_pcall(lua, 0, 0, 0) != LUA_OK)
{ {
const char* errMsg = lua_tostring(lua, -1); const char* errMsg = lua_tostring(lua, -1);
XojMsgBox::showPluginMessage(name, errMsg, MSG_BT_OK, true); 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); g_warning("Could not run plugin Lua file: «%s», error: «%s»", luafile.c_str(), errMsg);
this->valid = false; this->valid = false;
@ -334,7 +351,9 @@ bool Plugin::callFunction(string fnc)
if (lua_pcall(lua, 0, 0, 0)) if (lua_pcall(lua, 0, 0, 0))
{ {
const char* errMsg = lua_tostring(lua, -1); const char* errMsg = lua_tostring(lua, -1);
XojMsgBox::showPluginMessage(name, errMsg, MSG_BT_OK, true); 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); g_warning("Error in Plugin: «%s», error: «%s»", name.c_str(), errMsg);
return false; return false;

@ -58,6 +58,13 @@ public:
* Callback function name * Callback function name
*/ */
string callback; string callback;
/**
* Accelerator key
*
* See https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html#gtk-accelerator-parse
*/
string accelerator;
}; };
class Plugin class Plugin
@ -80,7 +87,7 @@ public:
/** /**
* Register all menu entries to the menu * Register all menu entries to the menu
*/ */
void registerMenu(GtkWidget* menu); void registerMenu(GtkWindow* mainWindow, GtkWidget* menu);
/** /**
* Execute menu entry * Execute menu entry
@ -102,7 +109,7 @@ public:
* *
* @return Internal ID, can e.g. be used to disable the menu * @return Internal ID, can e.g. be used to disable the menu
*/ */
int registerMenu(string menu, string callback); int registerMenu(string menu, string callback, string accelerator);
private: private:
/** /**

@ -109,7 +109,7 @@ void PluginController::registerMenu()
GtkWidget* menuPlugin = control->getWindow()->get("menuPlugin"); GtkWidget* menuPlugin = control->getWindow()->get("menuPlugin");
for (Plugin* p : this->plugins) for (Plugin* p : this->plugins)
{ {
p->registerMenu(menuPlugin); p->registerMenu(control->getGtkWindow(), menuPlugin);
} }
gtk_widget_show_all(menuPlugin); gtk_widget_show_all(menuPlugin);

@ -12,43 +12,38 @@
#include <XojMsgBox.h> #include <XojMsgBox.h>
#include <StringUtils.h> #include <StringUtils.h>
#include <map>
using std::map;
/** /**
* Example: * Example:
* app.msgbox("Test123", "yes,no") * app.msgbox("Test123", {[1] = "Yes", [2] = "No"})
* Return 1 for yes, 2 for no in this example
*/ */
static int applib_msgbox(lua_State* L) static int applib_msgbox(lua_State* L)
{ {
const char* msg = luaL_checkstring(L, 1); const char* msg = luaL_checkstring(L, 1);
const char* flags = luaL_checkstring(L, 2);
int type = 0; // discard any extra arguments passed in
for (string element : StringUtils::split(flags, ',')) lua_settop(L, 2);
luaL_checktype(L, 2, LUA_TTABLE);
lua_pushnil(L);
map<int, string> button;
while (lua_next(L, 2) != 0)
{ {
if (element == "ok") int index = lua_tointeger(L, -2);
{ const char* buttonText = luaL_checkstring(L, -1);
type |= MSG_BT_OK; lua_pop(L, 1);
}
else if (element == "yes") button.insert(button.begin(), std::pair<int, string>(index, buttonText));
{
type |= MSG_BT_YES;
}
else if (element == "no")
{
type |= MSG_BT_NO;
}
else if (element == "cancel")
{
type |= MSG_BT_CANCEL;
}
else
{
g_warning("Plugin: Unsupported button type for app.msgbox «%s»", element.c_str());
}
} }
Plugin* plugin = Plugin::getPluginFromLua(L); Plugin* plugin = Plugin::getPluginFromLua(L);
int result = XojMsgBox::showPluginMessage(plugin->getName(), msg, type); int result = XojMsgBox::showPluginMessage(plugin->getName(), msg, button);
lua_pushinteger(L, result); lua_pushinteger(L, result);
return 1; return 1;
} }
@ -73,30 +68,41 @@ static int applib_registerUi(lua_State* L)
// 'unpack' the table by putting the values onto // 'unpack' the table by putting the values onto
// the stack first. Then convert those stack values // the stack first. Then convert those stack values
// into an appropriate C type. // into an appropriate C type.
lua_getfield(L, 1, "accelerator");
lua_getfield(L, 1, "menu"); lua_getfield(L, 1, "menu");
lua_getfield(L, 1, "callback"); lua_getfield(L, 1, "callback");
// stack now has following: // stack now has following:
// 1 = {"menu"="MenuName", callback="functionName"} // 1 = {"menu"="MenuName", callback="functionName", accelerator="<Control>a"}
// -3 = "<Control>a"
// -2 = "MenuName" // -2 = "MenuName"
// -1 = "functionName" // -1 = "functionName"
const char* accelerator = luaL_optstring(L, -3, NULL);
const char* menu = luaL_optstring(L, -2, NULL); const char* menu = luaL_optstring(L, -2, NULL);
const char* callback = luaL_optstring(L, -1, NULL); const char* callback = luaL_optstring(L, -1, NULL);
if (callback == NULL) if (callback == NULL)
{ {
luaL_error(L, "Missing callback function!"); luaL_error(L, "Missing callback function!");
} }
if (menu == NULL)
{
menu = "";
}
if (accelerator == NULL)
{
accelerator = "";
}
int menuId = -1; int menuId = -1;
int toolbarId = -1; int toolbarId = -1;
if (menu) if (menu)
{ {
menuId = plugin->registerMenu(menu, callback); menuId = plugin->registerMenu(menu, callback, accelerator);
} }
// Make sure to remove all vars which are put to the stack before! // Make sure to remove all vars which are put to the stack before!
lua_pop(L, 2); lua_pop(L, 3);
// Add return value to the Stack // Add return value to the Stack
lua_createtable(L, 0, 2); lua_createtable(L, 0, 2);
@ -119,10 +125,10 @@ static const luaL_Reg applib[] = {
{ "registerUi", applib_registerUi }, { "registerUi", applib_registerUi },
// Placeholder // Placeholder
{"MSG_BT_OK", NULL}, // {"MSG_BT_OK", NULL},
{"MSG_BT_YES", NULL}, // {"MSG_BT_YES", NULL},
{"MSG_BT_NO", NULL}, // {"MSG_BT_NO", NULL},
{"MSG_BT_CANCEL", NULL}, // {"MSG_BT_CANCEL", NULL},
{NULL, NULL} {NULL, NULL}
}; };
@ -133,14 +139,14 @@ static const luaL_Reg applib[] = {
LUAMOD_API int luaopen_app(lua_State* L) LUAMOD_API int luaopen_app(lua_State* L)
{ {
luaL_newlib(L, applib); luaL_newlib(L, applib);
lua_pushnumber(L, MSG_BT_OK); // lua_pushnumber(L, MSG_BT_OK);
lua_setfield(L, -2, "MSG_BT_OK"); // lua_setfield(L, -2, "MSG_BT_OK");
lua_pushnumber(L, MSG_BT_YES); // lua_pushnumber(L, MSG_BT_YES);
lua_setfield(L, -2, "MSG_BT_YES"); // lua_setfield(L, -2, "MSG_BT_YES");
lua_pushnumber(L, MSG_BT_NO); // lua_pushnumber(L, MSG_BT_NO);
lua_setfield(L, -2, "MSG_BT_NO"); // lua_setfield(L, -2, "MSG_BT_NO");
lua_pushnumber(L, MSG_BT_CANCEL); // lua_pushnumber(L, MSG_BT_CANCEL);
lua_setfield(L, -2, "MSG_BT_CANCEL"); // lua_setfield(L, -2, "MSG_BT_CANCEL");
return 1; return 1;
} }

@ -30,7 +30,7 @@ void XojMsgBox::showErrorToUser(GtkWindow* win, string msg)
gtk_widget_destroy(dialog); gtk_widget_destroy(dialog);
} }
int XojMsgBox::showPluginMessage(string pluginName, string msg, int type, bool error) int XojMsgBox::showPluginMessage(string pluginName, string msg, map<int, string> button, bool error)
{ {
string header = string("Xournal++ Plugin «") + pluginName + "»"; string header = string("Xournal++ Plugin «") + pluginName + "»";
@ -52,21 +52,9 @@ int XojMsgBox::showPluginMessage(string pluginName, string msg, int type, bool e
g_object_set_property(G_OBJECT(dialog), "secondary-text", &val); g_object_set_property(G_OBJECT(dialog), "secondary-text", &val);
g_value_unset(&val); g_value_unset(&val);
if (MSG_BT_OK & type) for (auto& kv : button)
{ {
gtk_dialog_add_button(GTK_DIALOG(dialog), _("OK"), MSG_BT_OK); gtk_dialog_add_button(GTK_DIALOG(dialog), kv.second.c_str(), kv.first);
}
if (MSG_BT_YES & type)
{
gtk_dialog_add_button(GTK_DIALOG(dialog), _("Yes"), MSG_BT_YES);
}
if (MSG_BT_NO & type)
{
gtk_dialog_add_button(GTK_DIALOG(dialog), _("No"), MSG_BT_NO);
}
if (MSG_BT_CANCEL & type)
{
gtk_dialog_add_button(GTK_DIALOG(dialog), _("Cancel"), MSG_BT_CANCEL);
} }
int res = gtk_dialog_run(GTK_DIALOG(dialog)); int res = gtk_dialog_run(GTK_DIALOG(dialog));

@ -15,13 +15,8 @@
#include <string> #include <string>
using std::string; using std::string;
#include <map>
enum MsgBoxButtonType { using std::map;
MSG_BT_OK = 1 << 0,
MSG_BT_YES = 1 << 1,
MSG_BT_NO = 1 << 2,
MSG_BT_CANCEL = 1 << 3
};
class XojMsgBox class XojMsgBox
{ {
@ -36,7 +31,7 @@ public:
static void setDefaultWindow(GtkWindow* win); static void setDefaultWindow(GtkWindow* win);
static void showErrorToUser(GtkWindow* win, string msg); static void showErrorToUser(GtkWindow* win, string msg);
static int showPluginMessage(string pluginName, string msg, int type, bool error = false); static int showPluginMessage(string pluginName, string msg, map<int, string> button, bool error = false);
static int replaceFileQuestion(GtkWindow* win, string msg); static int replaceFileQuestion(GtkWindow* win, string msg);
static void showHelp(GtkWindow* win); static void showHelp(GtkWindow* win);
}; };

Loading…
Cancel
Save