From 5697065e11fd593042ad572ba032b96fddad9cb5 Mon Sep 17 00:00:00 2001 From: Stefano Karapetsas Date: Wed, 29 Jan 2014 16:07:41 +0100 Subject: [PATCH] Add tool to generate plugin templates --- tools/generate-plugin.py | 182 +++++++++++++++++ tools/plugin_template/Makefile.am | 37 ++++ tools/plugin_template/pluma-plugin.c | 184 ++++++++++++++++++ tools/plugin_template/pluma-plugin.desktop.in | 12 ++ tools/plugin_template/pluma-plugin.h | 76 ++++++++ tools/preprocessor.py | 161 +++++++++++++++ 6 files changed, 652 insertions(+) create mode 100755 tools/generate-plugin.py create mode 100644 tools/plugin_template/Makefile.am create mode 100644 tools/plugin_template/pluma-plugin.c create mode 100644 tools/plugin_template/pluma-plugin.desktop.in create mode 100644 tools/plugin_template/pluma-plugin.h create mode 100644 tools/preprocessor.py diff --git a/tools/generate-plugin.py b/tools/generate-plugin.py new file mode 100755 index 0000000..6b222f7 --- /dev/null +++ b/tools/generate-plugin.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# generate-plugin.py - pluma plugin skeletton generator +# This file is part of pluma +# +# Copyright (C) 2006 - Steve Frécinaux +# +# pluma 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. +# +# pluma 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 pluma; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA + +import re +import os +import sys +import getopt +from datetime import date +import preprocessor + +# Default values of command line options +options = { + 'language' : 'c', + 'description' : 'Type here a short description of your plugin', + 'author' : os.getenv('USERNAME'), + 'email' : os.getenv('LOGNAME') + '@email.com', + 'standalone' : False, + 'with-side-pane' : False, + 'with-bottom-pane' : False, + 'with-menu' : False, + 'with-config-dlg' : False +} + +USAGE = """Usage: + %s [OPTIONS...] pluginname +""" % os.path.basename(sys.argv[0]) +HELP = USAGE + """ +generate skeleton source tree for a new pluma plugin. + +Options: + --author Set the author name + --email Set the author email + --description Set the description you want for your new plugin + --standalone Is this plugin intended to be distributed as a + standalone package ? (N/A) + --language / -l Set the language (C) [default: %(language)s] + --with-$feature Enable $feature + --without-$feature Disable $feature + --help / -h Show this message and exits + +Features: + config-dlg Plugin configuration dialog + menu Plugin menu entries + side-pane Side pane item (N/A) + bottom-pane Bottom pane item (N/A) +""" % options + +TEMPLATE_DIR = os.path.join(os.path.dirname(sys.argv[0]), "plugin_template") + +# Parsing command line options +try: + opts, args = getopt.getopt(sys.argv[1:], + 'l:h', + ['language=', + 'description=', + 'author=', + 'email=', + 'standalone', + 'with-menu' , 'without-menu', + 'with-side-pane' , 'without-side-pane', + 'with-bottom-pane' , 'without-bottom-pane', + 'with-config-dlg' , 'without-config-dlg', + 'help']) +except getopt.error, exc: + print >>sys.stderr, '%s: %s' % (sys.argv[0], str(exc)) + print >>sys.stderr, USAGE + sys.exit(1) + +for opt, arg in opts: + if opt in ('-h', '--help'): + print >>sys.stderr, HELP + sys.exit(0) + + elif opt in ('--description', '--author', '--email'): + options[opt[2:]] = arg + + elif opt in ('-l', '--language'): + options['language'] = arg.lower() + + elif opt == '--standalone': + options['standalone'] = True + + elif opt[0:7] == '--with-': + options['with-' + opt[7:]] = True + + elif opt[0:10] == '--without-': + options['with-' + opt[10:]] = False + +# What's the new plugin name ? +if len(args) < 1: + print >>sys.stderr, USAGE + sys.exit(1) + +plugin_name = args[0] +plugin_id = re.sub('[^a-z0-9_]', '', plugin_name.lower().replace(' ', '_')) +plugin_module = plugin_id.replace('_', '-') + +directives = { + 'PLUGIN_NAME' : plugin_name, + 'PLUGIN_MODULE' : plugin_module, + 'PLUGIN_ID' : plugin_id, + 'AUTHOR_FULLNAME' : options['author'], + 'AUTHOR_EMAIL' : options['email'], + 'DATE_YEAR' : date.today().year, + 'DESCRIPTION' : options['description'], +} + +# Files to be generated by the preprocessor, in the form "template : outfile" +output_files = { + 'Makefile.am': '%s/Makefile.am' % plugin_module, + 'pluma-plugin.desktop.in': '%s/%s.pluma-plugin.desktop.in' % (plugin_module, plugin_module) +} + +if options['language'] == 'c': + output_files['pluma-plugin.c'] = '%s/%s-plugin.c' % (plugin_module, plugin_module) + output_files['pluma-plugin.h'] = '%s/%s-plugin.h' % (plugin_module, plugin_module) +else: + print >>sys.stderr, 'Value of --language should be C' + print >>sys.stderr, USAGE + sys.exit(1) + +if options['standalone']: + output_files['configure.ac'] = 'configure.ac' + +if options['with-side-pane']: + directives['WITH_SIDE_PANE'] = True + +if options['with-bottom-pane']: + directives['WITH_BOTTOM_PANE'] = True + +if options['with-menu']: + directives['WITH_MENU'] = True + +if options['with-config-dlg']: + directives['WITH_CONFIGURE_DIALOG'] = True + + +# Generate the plugin base +for infile, outfile in output_files.iteritems(): + print 'Processing %s\n' \ + ' into %s...' % (infile, outfile) + + infile = os.path.join(TEMPLATE_DIR, infile) + outfile = os.path.join(os.getcwd(), outfile) + + if not os.path.isfile(infile): + print >>sys.stderr, 'Input file does not exist : %s.' % os.path.basename(infile) + continue + + # Make sure the destination directory exists + if not os.path.isdir(os.path.split(outfile)[0]): + os.makedirs(os.path.split(outfile)[0]) + + # Variables relative to the generated file + directives['DIRNAME'], directives['FILENAME'] = os.path.split(outfile) + + # Generate the file + preprocessor.process(infile, outfile, directives.copy()) + +print 'Done.' + +# ex:ts=4:et: diff --git a/tools/plugin_template/Makefile.am b/tools/plugin_template/Makefile.am new file mode 100644 index 0000000..93c79ae --- /dev/null +++ b/tools/plugin_template/Makefile.am @@ -0,0 +1,37 @@ +# ##(PLUGIN_NAME) + +plugindir = $(PLUMA_PLUGINS_LIBS_DIR) + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(PLUMA_CFLAGS) \ + $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED_CFLAGS) \ + -DPLUMA_LOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" + +plugin_LTLIBRARIES = lib##(PLUGIN_MODULE).la + +lib##(PLUGIN_MODULE)_la_SOURCES = \ + ##(PLUGIN_MODULE)-plugin.h \ + ##(PLUGIN_MODULE)-plugin.c + +lib##(PLUGIN_MODULE)_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS) +lib##(PLUGIN_MODULE)_la_LIBADD = $(PLUMA_LIBS) + +# UI files (if you use gtkbuilder for your plugin, list those files here) +uidir = $(PLUMA_PLUGINS_DATA_DIR)/##(PLUGIN_MODULE) +ui_DATA = + +plugin_in_files = ##(PLUGIN_MODULE).pluma-plugin.desktop.in + +%.pluma-plugin: %.pluma-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache + +plugin_DATA = $(plugin_in_files:.pluma-plugin.desktop.in=.pluma-plugin) + +EXTRA_DIST = $(plugin_in_files) + +CLEANFILES = $(plugin_DATA) $(ui_DATA) + +DISTCLEANFILES = $(plugin_DATA) $(ui_DATA) + +-include $(top_srcdir)/git.mk diff --git a/tools/plugin_template/pluma-plugin.c b/tools/plugin_template/pluma-plugin.c new file mode 100644 index 0000000..4974f12 --- /dev/null +++ b/tools/plugin_template/pluma-plugin.c @@ -0,0 +1,184 @@ +/* + * ##(FILENAME) - ##(DESCRIPTION) + * + * Copyright (C) ##(DATE_YEAR) - ##(AUTHOR_FULLNAME) + * + * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "##(PLUGIN_MODULE)-plugin.h" + +#include +#include + +#define WINDOW_DATA_KEY "##(PLUGIN_ID.camel)PluginWindowData" + +#define ##(PLUGIN_ID.upper)_PLUGIN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), TYPE_##(PLUGIN_ID.upper)_PLUGIN, ##(PLUGIN_ID.camel)PluginPrivate)) + +struct _##(PLUGIN_ID.camel)PluginPrivate +{ + gpointer dummy; +}; + +PLUMA_PLUGIN_REGISTER_TYPE (##(PLUGIN_ID.camel)Plugin, ##(PLUGIN_ID.lower)_plugin) + +##ifdef WITH_MENU +/* UI string. See pluma-ui.xml for reference */ +static const gchar ui_str = + "" + " " + " " + " " + ""; + +/* UI actions */ +static const GtkActionEntry action_entries[] = + { + /* Put your actions here */ + }; + +typedef struct +{ + GtkActionGroup *action_group; + guint ui_id; +} WindowData; +##endif + +static void +##(PLUGIN_ID.lower)_plugin_init (##(PLUGIN_ID.camel)Plugin *plugin) +{ + plugin->priv = ##(PLUGIN_ID.upper)_PLUGIN_GET_PRIVATE (plugin); + + pluma_debug_message (DEBUG_PLUGINS, + "##(PLUGIN_ID.camel)Plugin initializing"); +} + +static void +##(PLUGIN_ID.lower)_plugin_finalize (GObject *object) +{ + pluma_debug_message (DEBUG_PLUGINS, + "##(PLUGIN_ID.camel)Plugin finalizing"); + + G_OBJECT_CLASS (##(PLUGIN_ID.lower)_plugin_parent_class)->finalize (object); +} + +##ifdef WITH_MENU +static void +free_window_data (WindowData *data) +{ + g_return_if_fail (data != NULL); + + g_object_unref (data->action_group); + g_free (data); +} +##endif + +static void +impl_activate (PlumaPlugin *plugin, + PlumaWindow *window) +{ +##ifdef WITH_MENU + GtkUIManager *manager; + WindowData *data; +##endif + + pluma_debug (DEBUG_PLUGINS); + +##ifdef WITH_MENU + data = g_new (WindowData, 1); + manager = pluma_window_get_ui_manager (window); + + data->action_group = gtk_action_group_new ("##(PLUGIN_ID.camel)PluginActions"); + gtk_action_group_set_translation_domain (data->action_group, + GETTEXT_PACKAGE); + gtk_action_group_add_actions (data->action_group, + action_entries, + G_N_ELEMENTS (action_entries), + window); + + gtk_ui_manager_insert_action_group (manager, data->action_group, -1); + + data->ui_id = gtk_ui_manager_add_ui_from_string (manager, ui_str, + -1, NULL); + + g_object_set_data_full (G_OBJECT (window), + WINDOW_DATA_KEY, + data, + (GDestroyNotify) free_window_data); +##endif +} + +static void +impl_deactivate (PlumaPlugin *plugin, + PlumaWindow *window) +{ +##ifdef WITH_MENU + GtkUIManager *manager; + WindowData *data; +##endif + + pluma_debug (DEBUG_PLUGINS); + +##ifdef WITH_MENU + manager = pluma_window_get_ui_manager (window); + + data = (WindowData *) g_object_get_data (G_OBJECT (window), + WINDOW_DATA_KEY); + g_return_if_fail (data != NULL); + + gtk_ui_manager_remove_ui (manager, data->ui_id); + gtk_ui_manager_remove_action_group (manager, data->action_group); + + g_object_set_data (G_OBJECT (window), WINDOW_DATA_KEY, NULL); +##endif +} + +static void +impl_update_ui (PlumaPlugin *plugin, + PlumaWindow *window) +{ + pluma_debug (DEBUG_PLUGINS); +} + +##ifdef WITH_CONFIGURE_DIALOG +static GtkWidget * +impl_create_configure_dialog (PlumaPlugin *plugin) +{ + pluma_debug (DEBUG_PLUGINS); +} +##endif + +static void +##(PLUGIN_ID.lower)_plugin_class_init (##(PLUGIN_ID.camel)PluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + PlumaPluginClass *plugin_class = PLUMA_PLUGIN_CLASS (klass); + + object_class->finalize = ##(PLUGIN_ID.lower)_plugin_finalize; + + plugin_class->activate = impl_activate; + plugin_class->deactivate = impl_deactivate; + plugin_class->update_ui = impl_update_ui; +##ifdef WITH_CONFIGURE_DIALOG + plugin_class->create_configure_dialog = impl_create_configure_dialog; +##endif + + g_type_class_add_private (object_class, + sizeof (##(PLUGIN_ID.camel)PluginPrivate)); +} diff --git a/tools/plugin_template/pluma-plugin.desktop.in b/tools/plugin_template/pluma-plugin.desktop.in new file mode 100644 index 0000000..c12b91b --- /dev/null +++ b/tools/plugin_template/pluma-plugin.desktop.in @@ -0,0 +1,12 @@ +[Pluma Plugin] +##ifdef WITH_PYTHON +Loader=python +##endif +Module=##(PLUGIN_MODULE) +IAge=2 +_Name=##(PLUGIN_NAME) +_Description=##(DESCRIPTION) +Icon=pluma-plugin +Authors=##(AUTHOR_FULLNAME) <##(AUTHOR_EMAIL.lower)> +Copyright=Copyright © ##(DATE_YEAR) ##(AUTHOR_FULLNAME) +Website=http://www.mate-desktop.org diff --git a/tools/plugin_template/pluma-plugin.h b/tools/plugin_template/pluma-plugin.h new file mode 100644 index 0000000..5a8305b --- /dev/null +++ b/tools/plugin_template/pluma-plugin.h @@ -0,0 +1,76 @@ +/* + * ##(FILENAME) - ##(DESCRIPTION) + * + * Copyright (C) ##(DATE_YEAR) - ##(AUTHOR_FULLNAME) + * + * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __##(PLUGIN_ID.upper)_PLUGIN_H__ +#define __##(PLUGIN_ID.upper)_PLUGIN_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define TYPE_##(PLUGIN_ID.upper)_PLUGIN (##(PLUGIN_ID.lower)_plugin_get_type ()) +#define ##(PLUGIN_ID.upper)_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TYPE_##(PLUGIN_ID.upper)_PLUGIN, ##(PLUGIN_ID.camel)Plugin)) +#define ##(PLUGIN_ID.upper)_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TYPE_##(PLUGIN_ID.upper)_PLUGIN, ##(PLUGIN_ID.camel)PluginClass)) +#define IS_##(PLUGIN_ID.upper)_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TYPE_##(PLUGIN_ID.upper)_PLUGIN)) +#define IS_##(PLUGIN_ID.upper)_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TYPE_##(PLUGIN_ID.upper)_PLUGIN)) +#define ##(PLUGIN_ID.upper)_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TYPE_##(PLUGIN_ID.upper)_PLUGIN, ##(PLUGIN_ID.camel)PluginClass)) + +/* Private structure type */ +typedef struct _##(PLUGIN_ID.camel)PluginPrivate ##(PLUGIN_ID.camel)PluginPrivate; + +/* + * Main object structure + */ +typedef struct _##(PLUGIN_ID.camel)Plugin ##(PLUGIN_ID.camel)Plugin; + +struct _##(PLUGIN_ID.camel)Plugin +{ + PlumaPlugin parent_instance; + + /*< private >*/ + ##(PLUGIN_ID.camel)PluginPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _##(PLUGIN_ID.camel)PluginClass ##(PLUGIN_ID.camel)PluginClass; + +struct _##(PLUGIN_ID.camel)PluginClass +{ + PlumaPluginClass parent_class; +}; + +/* + * Public methods + */ +GType ##(PLUGIN_ID.lower)_plugin_get_type (void) G_GNUC_CONST; + +/* All the plugins must implement this function */ +G_MODULE_EXPORT GType register_pluma_plugin (GTypeModule *module); + +G_END_DECLS + +#endif /* __##(PLUGIN_ID.upper)_PLUGIN_H__ */ diff --git a/tools/preprocessor.py b/tools/preprocessor.py new file mode 100644 index 0000000..f5409ca --- /dev/null +++ b/tools/preprocessor.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- + +# preprocessor.py - simple preprocessor for plugin template files +# This file is part of pluma +# +# Copyright (C) 2006 - Steve Frécinaux +# +# pluma 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. +# +# pluma 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 pluma; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA + +import sys +import re + +class DeepnessException(Exception): + def __init__(self): + Exception.__init__(self) + +statements = [re.compile("^##\s*%s\s*$" % pattern) for pattern in + ['(?Pifdef|ifndef)\s+(?P[^\s]+)', + '(?Pelif|if)\s+(?P.+)', + '(?Pelse|endif)', + '(?Pdefine)\s+(?P[^\s]+)(\s+(?P.+))?', + '(?Pundef)\s+(?P[^\s]+)']] +variable = re.compile("##\((?P[a-zA-Z_][a-zA-Z0-9_]*)(?P(\.[a-z]+)+)?\)") + + +def _eval(expr, macros): + return eval(expr, + {'defined': lambda x: macros.has_key(x)}, + macros) + +def _subvar(match, macros): + name = match.group('name') + if name in macros: + val = str(macros[name]) + if val is None: + return '' + else: + return '' + + mods = match.group('mods') + if mods is not None: + for mod in mods[1:].split('.'): + if mod == 'lower': + val = val.lower() + elif mod == 'upper': + val = val.upper() + elif mod == 'camel': + val = ''.join(i.capitalize() + for i in val.split('_')) + return val + +def process(infile = sys.stdin, outfile = sys.stdout, macros = {}): + if not isinstance(infile, file): + infile = open(infile, mode = 'r') + close_infile = True + else: + close_infile = False + + if not isinstance(outfile, file): + outfile = open(outfile, mode = 'w') + close_outfile = True + else: + close_outfile = False + + deepness = 0 + writing_disabled = None + + for line in infile: + # Skip comments + if line[0:3].lower() == '##c': + continue + + # Check whether current line is a preprocessor directive + for statement in statements: + match = statement.match(line) + if match: break + + if match is not None: + stmt = match.group('stmt') + + if stmt == "define": + if writing_disabled is None: + key = match.group('key') + val = match.group('val') + macros[key] = val + + elif stmt == "undef": + if writing_disabled is None: + key = match.group('key') + if key in macros: + del macros[key] + + elif stmt == "ifdef": + deepness += 1 + if writing_disabled is None and \ + match.group('key') not in macros: + writing_disabled = deepness + + elif stmt == "ifndef": + deepness += 1 + if writing_disabled is None and \ + match.group('key') in macros: + writing_disabled = deepness + + elif stmt == "if": + deepness += 1 + if writing_disabled is None and \ + not _eval(match.group('expr'), macros): + writing_disabled = deepness + + elif stmt == "elif": + if deepness == 0: + raise DeepnessException() + if writing_disabled is None and \ + not _eval(match.group('expr'), macros): + writing_disabled = deepness + elif writing_disabled == deepness: + writing_disabled = None + + elif stmt == "else": + if deepness == 0: + raise DeepnessException() + if writing_disabled is None: + writing_disabled = deepness + elif writing_disabled == deepness: + writing_disabled = None + + elif stmt == "endif": + if deepness == 0: + raise DeepnessException() + if writing_disabled is not None and \ + writing_disabled == deepness: + writing_disabled = None + deepness -= 1 + + # Do variable substitution in the remaining lines + elif writing_disabled is None: + outfile.write(re.sub(variable, + lambda m: _subvar(m, macros), + line)) + + if deepness != 0: + raise DeepnessException() + + if close_infile: infile.close() + if close_outfile: outfile.close() + +# ex:ts=4:et: