From bbf8e882c2ea76c2d400decfbd6d5b44978eb3fa Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Wed, 26 Aug 2015 23:58:31 +0200 Subject: [PATCH] demoproxy: add a demo proxy showcasing how to create a proxy plugin This demo proxy does not actually do anything useful. It simply loads pseudo-plugins from an ini-style file. The point is that there will be a plugin in the PM dialog for each ini. Each ini-plugin also causes a menu item to be generated. --- plugins/Makefile.am | 10 ++- plugins/demoproxy.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++ plugins/demoproxytest.px | 15 ++++ po/POTFILES.skip | 1 + 4 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 plugins/demoproxy.c create mode 100644 plugins/demoproxytest.px diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 510beb681..36847000e 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -1,7 +1,8 @@ # Adapted from Pidgin's plugins/Makefile.am, thanks EXTRA_DIST = \ - makefile.win32 + makefile.win32 \ + demoproxytest.px plugindir = $(libdir)/geany @@ -11,6 +12,7 @@ plugins_include_HEADERS = \ geanyplugin.h demoplugin_la_LDFLAGS = -module -avoid-version -no-undefined +demoproxy_la_LDFLAGS = -module -avoid-version -no-undefined classbuilder_la_LDFLAGS = -module -avoid-version -no-undefined htmlchars_la_LDFLAGS = -module -avoid-version -no-undefined export_la_LDFLAGS = -module -avoid-version -no-undefined @@ -30,9 +32,11 @@ plugin_LTLIBRARIES = \ # Plugins not to be installed noinst_LTLIBRARIES = \ - demoplugin.la + demoplugin.la \ + demoproxy.la demoplugin_la_SOURCES = demoplugin.c +demoproxy_la_SOURCES = demoproxy.c classbuilder_la_SOURCES = classbuilder.c htmlchars_la_SOURCES = htmlchars.c export_la_SOURCES = export.c @@ -41,6 +45,7 @@ filebrowser_la_SOURCES = filebrowser.c splitwindow_la_SOURCES = splitwindow.c demoplugin_la_CFLAGS = -DG_LOG_DOMAIN=\""Demoplugin"\" -DLOCALEDIR=\""$(LOCALEDIR)"\" +demoproxy_la_CFLAGS = -DG_LOG_DOMAIN=\""Demoproxy"\" classbuilder_la_CFLAGS = -DG_LOG_DOMAIN=\""Classbuilder"\" htmlchars_la_CFLAGS = -DG_LOG_DOMAIN=\""HTMLChars"\" export_la_CFLAGS = -DG_LOG_DOMAIN=\""Export"\" @@ -49,6 +54,7 @@ filebrowser_la_CFLAGS = -DG_LOG_DOMAIN=\""FileBrowser"\" splitwindow_la_CFLAGS = -DG_LOG_DOMAIN=\""SplitWindow"\" demoplugin_la_LIBADD = $(top_builddir)/src/libgeany.la $(GTK_LIBS) +demoproxy_la_LIBADD = $(top_builddir)/src/libgeany.la $(GTK_LIBS) classbuilder_la_LIBADD = $(top_builddir)/src/libgeany.la $(GTK_LIBS) htmlchars_la_LIBADD = $(top_builddir)/src/libgeany.la $(GTK_LIBS) export_la_LIBADD = $(top_builddir)/src/libgeany.la $(GTK_LIBS) -lm diff --git a/plugins/demoproxy.c b/plugins/demoproxy.c new file mode 100644 index 000000000..9e4eecdf5 --- /dev/null +++ b/plugins/demoproxy.c @@ -0,0 +1,202 @@ +/* + * demoproxy.c - this file is part of Geany, a fast and lightweight IDE + * + * Copyright 2015 Thomas Martitz + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * Demo proxy - example of a basic proxy plugin for Geany. Sub-plugins add menu items to the + * Tools menu and have a help dialog. + * + * Note: This is compiled but not installed by default. On Unix, you can install it by compiling + * Geany and then copying (or symlinking) to the plugins/demoproxy.so and + * plugins/demoproxytest.px files to ~/.config/geany/plugins + * - it will be loaded at next startup. + */ + +/* plugin API, always comes first */ +#include "geanyplugin.h" + +typedef struct { + GKeyFile *file; + gchar *help_text; + GSList *menu_items; +} +PluginContext; + + +static gboolean proxy_init(GeanyPlugin *plugin, gpointer pdata) +{ + PluginContext *data; + gint i = 0; + gchar *text; + + data = (PluginContext *) pdata; + + /* Normally, you would instruct the VM/interpreter to call into the actual plugin. The + * plugin would be identified by pdata. Because there is no interpreter for + * .ini files we do it inline, as this is just a demo */ + data->help_text = g_key_file_get_locale_string(data->file, "Help", "text", NULL, NULL); + while (TRUE) + { + GtkWidget *item; + gchar *key = g_strdup_printf("item%d", i++); + text = g_key_file_get_locale_string(data->file, "Init", key, NULL, NULL); + g_free(key); + + if (!text) + break; + + item = gtk_menu_item_new_with_label(text); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu), item); + gtk_widget_set_sensitive(item, FALSE); + data->menu_items = g_slist_prepend(data->menu_items, (gpointer) item); + g_free(text); + } + + return TRUE; +} + + +static void proxy_help(GeanyPlugin *plugin, gpointer pdata) +{ + PluginContext *data; + GtkWidget *dialog; + + data = (PluginContext *) pdata; + + dialog = gtk_message_dialog_new( + GTK_WINDOW(plugin->geany_data->main_widgets->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "%s", data->help_text); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), + _("(From the %s plugin)"), plugin->info->name); + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + + +static void proxy_cleanup(GeanyPlugin *plugin, gpointer pdata) +{ + PluginContext *data = (PluginContext *) pdata; + + g_slist_free_full(data->menu_items, (GDestroyNotify) gtk_widget_destroy); + g_free(data->help_text); +} + + +static gint demoproxy_probe(GeanyPlugin *proxy, const gchar *filename, gpointer pdata) +{ + /* We know the extension is right (Geany checks that). For demo purposes we perform an + * additional check. This is not necessary when the extension is unique enough. */ + gboolean match = FALSE; + gchar linebuf[128]; + FILE *f = fopen(filename, "r"); + if (f != NULL) + { + if (fgets(linebuf, sizeof(linebuf), f) != NULL) + match = utils_str_equal(linebuf, "#!!PROXY_MAGIC!!\n"); + fclose(f); + } + return match ? PROXY_MATCHED : PROXY_IGNORED; +} + + +static gpointer demoproxy_load(GeanyPlugin *proxy, GeanyPlugin *plugin, + const gchar *filename, gpointer pdata) +{ + GKeyFile *file; + gboolean result; + + file = g_key_file_new(); + result = g_key_file_load_from_file(file, filename, 0, NULL); + + if (result) + { + PluginContext *data = g_new0(PluginContext, 1); + data->file = file; + + plugin->info->name = g_key_file_get_locale_string(data->file, "Info", "name", NULL, NULL); + plugin->info->description = g_key_file_get_locale_string(data->file, "Info", "description", NULL, NULL); + plugin->info->version = g_key_file_get_locale_string(data->file, "Info", "version", NULL, NULL); + plugin->info->author = g_key_file_get_locale_string(data->file, "Info", "author", NULL, NULL); + + plugin->funcs->init = proxy_init; + plugin->funcs->help = proxy_help; + plugin->funcs->cleanup = proxy_cleanup; + + /* Cannot pass g_free as free_func be Geany calls it before unloading, and since + * demoproxy_unload() accesses the data this would be catastrophic */ + GEANY_PLUGIN_REGISTER_FULL(plugin, 225, data, NULL); + return data; + } + + g_key_file_free(file); + return NULL; +} + + +static void demoproxy_unload(GeanyPlugin *proxy, GeanyPlugin *plugin, gpointer load_data, gpointer pdata) +{ + PluginContext *data = load_data; + + g_free((gchar *)plugin->info->name); + g_free((gchar *)plugin->info->description); + g_free((gchar *)plugin->info->version); + g_free((gchar *)plugin->info->author); + + g_key_file_free(data->file); + g_free(data); +} + + +/* Called by Geany to initialize the plugin. */ +static gboolean demoproxy_init(GeanyPlugin *plugin, gpointer pdata) +{ + const gchar *extensions[] = { "ini", "px", NULL }; + + plugin->proxy_funcs->probe = demoproxy_probe; + plugin->proxy_funcs->load = demoproxy_load; + plugin->proxy_funcs->unload = demoproxy_unload; + + return geany_plugin_register_proxy(plugin, extensions); +} + + +/* Called by Geany before unloading the plugin. */ +static void demoproxy_cleanup(GeanyPlugin *plugin, gpointer data) +{ +} + + +G_MODULE_EXPORT +void geany_load_module(GeanyPlugin *plugin) +{ + plugin->info->name = _("Demo Proxy"); + plugin->info->description = _("Example Proxy."); + plugin->info->version = "0.1"; + plugin->info->author = _("The Geany developer team"); + + plugin->funcs->init = demoproxy_init; + plugin->funcs->cleanup = demoproxy_cleanup; + + GEANY_PLUGIN_REGISTER(plugin, 225); +} diff --git a/plugins/demoproxytest.px b/plugins/demoproxytest.px new file mode 100644 index 000000000..ee10fc2b1 --- /dev/null +++ b/plugins/demoproxytest.px @@ -0,0 +1,15 @@ +#!!PLUXY_MAGIC!! + +[Init] +item0 = Bam +item1 = Foo +item2 = Bar + +[Help] +text = I'm a simple test. Nothing to see! + +[Info] +name = Demo Pluxy Tester +description = I'm a simple test. Nothing to see! +version = 0.1 +author = The Geany developer team diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 4b6a8895c..c535edcdb 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -6,5 +6,6 @@ geany.desktop.in geany.glade # no need to translate these files plugins/demoplugin.c +plugins/demoproxy.c doc/stash-example.c doc/stash-gui-example.c -- 2.11.4.GIT