1220 lines
34 KiB
C
1220 lines
34 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*-
|
|
*
|
|
* mooplugin-python.c
|
|
*
|
|
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
*
|
|
* 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.
|
|
*
|
|
* See COPYING file that comes with this distribution.
|
|
*/
|
|
|
|
#include <Python.h>
|
|
#define NO_IMPORT_PYGOBJECT
|
|
#include "pygobject.h"
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "moopython/mooplugin-python.h"
|
|
#include "mooedit/mooplugin-macro.h"
|
|
#include "mooutils/mooutils-python.h"
|
|
|
|
#ifndef MOO_VERSION
|
|
#define MOO_VERSION NULL
|
|
#endif
|
|
|
|
#define MOO_PYTHON_PLUGIN_ID "moo-python"
|
|
|
|
#define MOO_TYPE_PYTHON_PLUGIN (moo_python_plugin_get_type())
|
|
#define MOO_IS_PYTHON_PLUGIN(obj_) (G_TYPE_CHECK_INSTANCE_TYPE (obj_, MOO_TYPE_PYTHON_PLUGIN))
|
|
#define MOO_PYTHON_PLUGIN(obj_) (G_TYPE_CHECK_INSTANCE_CAST ((obj_), MOO_TYPE_PYTHON_PLUGIN, MooPythonPlugin))
|
|
|
|
#define PLUGIN_SUFFIX ".py"
|
|
|
|
typedef enum {
|
|
HOOK_NEW_WINDOW = 0,
|
|
HOOK_CLOSE_WINDOW,
|
|
HOOK_NEW_DOC,
|
|
HOOK_CLOSE_DOC,
|
|
HOOK_LAST
|
|
} HookType;
|
|
|
|
typedef struct {
|
|
HookType type;
|
|
PyObject *callback;
|
|
PyObject *data;
|
|
} Hook;
|
|
|
|
typedef struct _MooPyPluginData MooPyPluginData;
|
|
|
|
typedef struct {
|
|
MooPlugin parent;
|
|
|
|
GHashTable *hook_ids;
|
|
GSList *hooks[HOOK_LAST];
|
|
int last_id;
|
|
GSList *plugins; /* MooPyPluginInfo* */
|
|
} MooPythonPlugin;
|
|
|
|
|
|
static Hook *hook_new (HookType type,
|
|
PyObject *callback,
|
|
PyObject *data);
|
|
static void hook_free (Hook *hook);
|
|
|
|
static GType moo_python_plugin_get_type (void);
|
|
|
|
static PyObject *moo_python_plugin_add_hook (MooPythonPlugin *plugin,
|
|
HookType type,
|
|
PyObject *callback,
|
|
PyObject *data);
|
|
static void moo_python_plugin_remove_hook (MooPythonPlugin *plugin,
|
|
int id);
|
|
|
|
static void moo_py_plugin_delete (MooPyPluginData *data);
|
|
|
|
|
|
static gboolean
|
|
moo_python_plugin_init (MooPythonPlugin *plugin)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_PYTHON_PLUGIN (plugin), FALSE);
|
|
g_return_val_if_fail (plugin->hook_ids == NULL, FALSE);
|
|
|
|
plugin->hook_ids = g_hash_table_new (g_direct_hash, g_direct_equal);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
prepend_id (gpointer id,
|
|
G_GNUC_UNUSED gpointer hook,
|
|
GSList **list)
|
|
{
|
|
*list = g_slist_prepend (*list, id);
|
|
}
|
|
|
|
static void
|
|
moo_python_plugin_deinit (MooPythonPlugin *plugin)
|
|
{
|
|
GSList *ids = NULL, *l;
|
|
|
|
g_hash_table_foreach (plugin->hook_ids, (GHFunc) prepend_id, &ids);
|
|
|
|
for (l = ids; l != NULL; l = l->next)
|
|
moo_python_plugin_remove_hook (plugin, GPOINTER_TO_INT (l->data));
|
|
|
|
g_hash_table_destroy (plugin->hook_ids);
|
|
plugin->hook_ids = NULL;
|
|
g_slist_free (ids);
|
|
|
|
g_slist_foreach (plugin->plugins, (GFunc) moo_py_plugin_delete, NULL);
|
|
g_slist_free (plugin->plugins);
|
|
plugin->plugins = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
call_hooks (MooPythonPlugin *plugin,
|
|
MooEditWindow *window,
|
|
MooEdit *doc,
|
|
HookType type)
|
|
{
|
|
GSList *l;
|
|
PyObject *py_win, *py_doc;
|
|
|
|
g_return_if_fail (MOO_IS_PYTHON_PLUGIN (plugin));
|
|
g_return_if_fail (MOO_IS_EDIT_WINDOW (window));
|
|
g_return_if_fail (!doc || MOO_IS_EDIT (doc));
|
|
g_return_if_fail (type < HOOK_LAST);
|
|
|
|
py_win = pygobject_new (G_OBJECT (window));
|
|
py_doc = doc ? pygobject_new (G_OBJECT (doc)) : NULL;
|
|
|
|
for (l = plugin->hooks[type]; l != NULL; l = l->next)
|
|
{
|
|
PyObject *result;
|
|
Hook *hook = l->data;
|
|
|
|
PyTuple_SET_ITEM (hook->data, 0, py_win);
|
|
if (py_doc)
|
|
PyTuple_SET_ITEM (hook->data, 1, py_doc);
|
|
|
|
result = PyObject_Call (hook->callback, hook->data, NULL);
|
|
|
|
PyTuple_SET_ITEM (hook->data, 0, NULL);
|
|
if (py_doc)
|
|
PyTuple_SET_ITEM (hook->data, 1, NULL);
|
|
|
|
if (result)
|
|
{
|
|
Py_DECREF (result);
|
|
}
|
|
else
|
|
{
|
|
PyErr_Print ();
|
|
}
|
|
}
|
|
|
|
Py_XDECREF (py_win);
|
|
Py_XDECREF (py_doc);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_python_plugin_attach_win (MooPythonPlugin *plugin,
|
|
MooEditWindow *window)
|
|
{
|
|
g_return_if_fail (MOO_IS_PYTHON_PLUGIN (plugin));
|
|
g_return_if_fail (MOO_IS_EDIT_WINDOW (window));
|
|
call_hooks (plugin, window, NULL, HOOK_NEW_WINDOW);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_python_plugin_detach_win (MooPythonPlugin *plugin,
|
|
MooEditWindow *window)
|
|
{
|
|
g_return_if_fail (MOO_IS_PYTHON_PLUGIN (plugin));
|
|
g_return_if_fail (MOO_IS_EDIT_WINDOW (window));
|
|
call_hooks (plugin, window, NULL, HOOK_CLOSE_WINDOW);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_python_plugin_attach_doc (MooPythonPlugin *plugin,
|
|
MooEdit *doc,
|
|
MooEditWindow *window)
|
|
{
|
|
g_return_if_fail (MOO_IS_PYTHON_PLUGIN (plugin));
|
|
g_return_if_fail (MOO_IS_EDIT_WINDOW (window));
|
|
g_return_if_fail (MOO_IS_EDIT (doc));
|
|
call_hooks (plugin, window, doc, HOOK_NEW_DOC);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_python_plugin_detach_doc (MooPythonPlugin *plugin,
|
|
MooEdit *doc,
|
|
MooEditWindow *window)
|
|
{
|
|
g_return_if_fail (MOO_IS_PYTHON_PLUGIN (plugin));
|
|
g_return_if_fail (MOO_IS_EDIT_WINDOW (window));
|
|
g_return_if_fail (MOO_IS_EDIT (doc));
|
|
call_hooks (plugin, window, doc, HOOK_CLOSE_DOC);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_python_plugin_read_file (G_GNUC_UNUSED MooPythonPlugin *plugin,
|
|
const char *path)
|
|
{
|
|
PyObject *mod, *code;
|
|
char *modname = NULL, *content = NULL;
|
|
GError *error = NULL;
|
|
|
|
g_return_if_fail (path != NULL);
|
|
|
|
if (!g_file_get_contents (path, &content, NULL, &error))
|
|
{
|
|
g_warning ("%s: could not read plugin file", G_STRLOC);
|
|
g_warning ("%s: %s", G_STRLOC, error->message);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
modname = g_strdup_printf ("moo_plugin_%08x%08x", g_random_int (), g_random_int ());
|
|
code = Py_CompileString (content, path, Py_file_input);
|
|
|
|
if (!code)
|
|
{
|
|
PyErr_Print ();
|
|
goto out;
|
|
}
|
|
|
|
mod = PyImport_ExecCodeModule (modname, code);
|
|
Py_DECREF (code);
|
|
|
|
if (!mod)
|
|
{
|
|
PyErr_Print ();
|
|
goto out;
|
|
}
|
|
|
|
Py_DECREF (mod);
|
|
|
|
out:
|
|
g_free (content);
|
|
g_free (modname);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_python_plugin_read_dir (MooPythonPlugin *plugin,
|
|
const char *path)
|
|
{
|
|
GDir *dir;
|
|
const char *name;
|
|
|
|
g_return_if_fail (path != NULL);
|
|
|
|
dir = g_dir_open (path, 0, NULL);
|
|
|
|
if (!dir)
|
|
return;
|
|
|
|
while ((name = g_dir_read_name (dir)))
|
|
{
|
|
char *file_path, *prefix, *suffix;
|
|
|
|
if (!g_str_has_suffix (name, PLUGIN_SUFFIX))
|
|
continue;
|
|
|
|
suffix = g_strrstr (name, PLUGIN_SUFFIX);
|
|
prefix = g_strndup (name, suffix - name);
|
|
|
|
file_path = g_build_filename (path, name, NULL);
|
|
moo_python_plugin_read_file (plugin, file_path);
|
|
|
|
g_free (prefix);
|
|
g_free (file_path);
|
|
}
|
|
|
|
g_dir_close (dir);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_python_plugin_read_dirs (MooPythonPlugin *plugin,
|
|
char **dirs)
|
|
{
|
|
for ( ; dirs && *dirs; ++dirs)
|
|
moo_python_plugin_read_dir (plugin, *dirs);
|
|
}
|
|
|
|
|
|
MOO_PLUGIN_DEFINE_INFO (moo_python, MOO_PYTHON_PLUGIN_ID,
|
|
"Python plugin loader", "A snake",
|
|
"Yevgen Muntyan <muntyan@tamu.edu>",
|
|
MOO_VERSION);
|
|
MOO_PLUGIN_DEFINE_FULL (MooPython, moo_python,
|
|
moo_python_plugin_init, moo_python_plugin_deinit,
|
|
moo_python_plugin_attach_win, moo_python_plugin_detach_win,
|
|
moo_python_plugin_attach_doc, moo_python_plugin_detach_doc,
|
|
NULL, 0, 0);
|
|
|
|
|
|
gboolean
|
|
_moo_python_plugin_init (char **dirs)
|
|
{
|
|
gboolean result;
|
|
|
|
moo_python_plugin_params.visible = FALSE;
|
|
result = moo_plugin_register (MOO_TYPE_PYTHON_PLUGIN);
|
|
|
|
if (result)
|
|
{
|
|
MooPythonPlugin *plugin = moo_plugin_lookup (MOO_PYTHON_PLUGIN_ID);
|
|
g_return_val_if_fail (MOO_IS_PYTHON_PLUGIN (plugin), FALSE);
|
|
moo_python_plugin_read_dirs (plugin, dirs);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
gboolean
|
|
_moo_python_plugin_reload (char **dirs)
|
|
{
|
|
MooPlugin *plugin = moo_plugin_get (MOO_TYPE_PYTHON_PLUGIN);
|
|
|
|
g_return_val_if_fail (plugin != NULL, FALSE);
|
|
|
|
if (moo_plugin_set_enabled (plugin, FALSE) &&
|
|
moo_plugin_set_enabled (plugin, TRUE))
|
|
{
|
|
moo_python_plugin_read_dirs (MOO_PYTHON_PLUGIN (plugin), dirs);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static PyObject*
|
|
moo_python_plugin_add_hook (MooPythonPlugin *plugin,
|
|
HookType type,
|
|
PyObject *callback,
|
|
PyObject *data)
|
|
{
|
|
int id = ++plugin->last_id;
|
|
Hook *hook = hook_new (type, callback, data);
|
|
plugin->hooks[type] = g_slist_prepend (plugin->hooks[type], hook);
|
|
g_hash_table_insert (plugin->hook_ids, GINT_TO_POINTER (id), hook);
|
|
return_Int (id);
|
|
}
|
|
|
|
|
|
PyObject*
|
|
_moo_python_plugin_hook (const char *event,
|
|
PyObject *callback,
|
|
PyObject *data)
|
|
{
|
|
MooPythonPlugin *plugin;
|
|
PyObject *result;
|
|
|
|
plugin = moo_plugin_lookup (MOO_PYTHON_PLUGIN_ID);
|
|
g_return_val_if_fail (MOO_IS_PYTHON_PLUGIN (plugin), NULL);
|
|
|
|
if (!strcmp (event, "new-window"))
|
|
result = moo_python_plugin_add_hook (plugin, HOOK_NEW_WINDOW, callback, data);
|
|
else if (!strcmp (event, "close-window"))
|
|
result = moo_python_plugin_add_hook (plugin, HOOK_CLOSE_WINDOW, callback, data);
|
|
else if (!strcmp (event, "new-doc"))
|
|
result = moo_python_plugin_add_hook (plugin, HOOK_NEW_DOC, callback, data);
|
|
else if (!strcmp (event, "close-doc"))
|
|
result = moo_python_plugin_add_hook (plugin, HOOK_CLOSE_DOC, callback, data);
|
|
else
|
|
return_TypeErr ("invalid event type");
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static Hook*
|
|
hook_new (HookType type,
|
|
PyObject *callback,
|
|
PyObject *data)
|
|
{
|
|
int data_len, extra, i;
|
|
Hook *hook = g_new0 (Hook, 1);
|
|
|
|
hook->type = type;
|
|
hook->callback = callback;
|
|
Py_INCREF (callback);
|
|
|
|
data_len = data ? PyTuple_GET_SIZE (data) : 0;
|
|
|
|
switch (type)
|
|
{
|
|
case HOOK_NEW_WINDOW:
|
|
case HOOK_CLOSE_WINDOW:
|
|
extra = 1;
|
|
break;
|
|
case HOOK_NEW_DOC:
|
|
case HOOK_CLOSE_DOC:
|
|
extra = 2;
|
|
break;
|
|
case HOOK_LAST:
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
hook->data = PyTuple_New (data_len + extra);
|
|
|
|
for (i = 0; i < data_len; ++i)
|
|
{
|
|
PyTuple_SET_ITEM (hook->data, i + extra,
|
|
PyTuple_GET_ITEM (data, i));
|
|
Py_INCREF (PyTuple_GET_ITEM (data, i));
|
|
}
|
|
|
|
return hook;
|
|
}
|
|
|
|
|
|
static void
|
|
moo_python_plugin_remove_hook (MooPythonPlugin *plugin,
|
|
int id)
|
|
{
|
|
Hook *hook;
|
|
|
|
g_return_if_fail (MOO_IS_PYTHON_PLUGIN (plugin));
|
|
g_return_if_fail (id > 0);
|
|
|
|
hook = g_hash_table_lookup (plugin->hook_ids, GINT_TO_POINTER (id));
|
|
g_return_if_fail (hook != NULL);
|
|
|
|
plugin->hooks[hook->type] = g_slist_remove (plugin->hooks[hook->type], hook);
|
|
g_hash_table_remove (plugin->hook_ids, GINT_TO_POINTER (id));
|
|
|
|
hook_free (hook);
|
|
}
|
|
|
|
|
|
static void
|
|
hook_free (Hook *hook)
|
|
{
|
|
if (hook)
|
|
{
|
|
int i, extra = 0;
|
|
|
|
switch (hook->type)
|
|
{
|
|
case HOOK_NEW_WINDOW:
|
|
case HOOK_CLOSE_WINDOW:
|
|
extra = 1;
|
|
break;
|
|
case HOOK_NEW_DOC:
|
|
case HOOK_CLOSE_DOC:
|
|
extra = 2;
|
|
break;
|
|
case HOOK_LAST:
|
|
g_critical ("%s: oops", G_STRLOC);
|
|
extra = 0;
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < extra; ++i)
|
|
{
|
|
PyTuple_SET_ITEM (hook->data, i, Py_None);
|
|
Py_INCREF (Py_None);
|
|
}
|
|
|
|
Py_XDECREF (hook->callback);
|
|
Py_XDECREF (hook->data);
|
|
g_free (hook);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* Python plugins
|
|
*/
|
|
|
|
#define MOO_PY_PLUGIN(obj_) ((MooPyPlugin*)obj_)
|
|
#define MOO_PY_WIN_PLUGIN(obj_) ((MooPyWinPlugin*)obj_)
|
|
#define MOO_PY_DOC_PLUGIN(obj_) ((MooPyDocPlugin*)obj_)
|
|
|
|
typedef struct _MooPyPlugin MooPyPlugin;
|
|
typedef struct _MooPyPluginClass MooPyPluginClass;
|
|
typedef struct _MooPyWinPlugin MooPyWinPlugin;
|
|
typedef struct _MooPyWinPluginClass MooPyWinPluginClass;
|
|
typedef struct _MooPyDocPlugin MooPyDocPlugin;
|
|
typedef struct _MooPyDocPluginClass MooPyDocPluginClass;
|
|
|
|
struct _MooPyPluginData {
|
|
PyObject *py_plugin_type;
|
|
PyObject *py_win_plugin_type;
|
|
PyObject *py_doc_plugin_type;
|
|
GType plugin_type;
|
|
GType win_plugin_type;
|
|
GType doc_plugin_type;
|
|
};
|
|
|
|
struct _MooPyPlugin {
|
|
MooPlugin base;
|
|
MooPyPluginData *class_data;
|
|
PyObject *instance;
|
|
};
|
|
|
|
struct _MooPyPluginClass {
|
|
MooPluginClass base_class;
|
|
MooPyPluginData *data;
|
|
};
|
|
|
|
struct _MooPyWinPlugin {
|
|
MooWinPlugin base;
|
|
MooPyPluginData *class_data;
|
|
PyObject *instance;
|
|
};
|
|
|
|
struct _MooPyWinPluginClass {
|
|
MooWinPluginClass base_class;
|
|
MooPyPluginData *data;
|
|
};
|
|
|
|
struct _MooPyDocPlugin {
|
|
MooDocPlugin base;
|
|
MooPyPluginData *class_data;
|
|
PyObject *instance;
|
|
};
|
|
|
|
struct _MooPyDocPluginClass {
|
|
MooDocPluginClass base_class;
|
|
MooPyPluginData *data;
|
|
};
|
|
|
|
|
|
static gpointer moo_py_plugin_parent_class;
|
|
static gpointer moo_py_win_plugin_parent_class;
|
|
static gpointer moo_py_doc_plugin_parent_class;
|
|
|
|
|
|
static void moo_py_plugin_class_init (MooPyPluginClass *klass,
|
|
MooPyPluginData *data);
|
|
static void moo_py_plugin_instance_init (MooPyPlugin *plugin,
|
|
MooPyPluginClass *klass);
|
|
static void moo_py_plugin_finalize (GObject *object);
|
|
|
|
static void moo_py_win_plugin_class_init (MooPyWinPluginClass *klass,
|
|
MooPyPluginData *data);
|
|
static void moo_py_win_plugin_instance_init (MooPyWinPlugin *plugin,
|
|
MooPyWinPluginClass *klass);
|
|
static void moo_py_win_plugin_finalize (GObject *object);
|
|
|
|
static void moo_py_doc_plugin_class_init (MooPyDocPluginClass *klass,
|
|
MooPyPluginData *data);
|
|
static void moo_py_doc_plugin_instance_init (MooPyDocPlugin *plugin,
|
|
MooPyDocPluginClass *klass);
|
|
static void moo_py_doc_plugin_finalize (GObject *object);
|
|
|
|
static gboolean moo_py_plugin_init (MooPlugin *plugin);
|
|
static void moo_py_plugin_deinit (MooPlugin *plugin);
|
|
static void moo_py_plugin_attach_win (MooPlugin *plugin,
|
|
MooEditWindow *window);
|
|
static void moo_py_plugin_detach_win (MooPlugin *plugin,
|
|
MooEditWindow *window);
|
|
static void moo_py_plugin_attach_doc (MooPlugin *plugin,
|
|
MooEdit *doc,
|
|
MooEditWindow *window);
|
|
static void moo_py_plugin_detach_doc (MooPlugin *plugin,
|
|
MooEdit *doc,
|
|
MooEditWindow *window);
|
|
|
|
static gboolean moo_py_win_plugin_create (MooWinPlugin *plugin);
|
|
static void moo_py_win_plugin_destroy (MooWinPlugin *plugin);
|
|
|
|
static gboolean moo_py_doc_plugin_create (MooDocPlugin *plugin);
|
|
static void moo_py_doc_plugin_destroy (MooDocPlugin *plugin);
|
|
|
|
static MooPluginInfo *get_plugin_info (PyObject *object);
|
|
static GType generate_win_plugin_type (PyObject *py_type,
|
|
MooPyPluginData *data);
|
|
static GType generate_doc_plugin_type (PyObject *py_type,
|
|
MooPyPluginData *data);
|
|
|
|
static GtkWidget *moo_py_plugin_create_prefs_page (MooPlugin *plugin);
|
|
|
|
static void plugin_info_free (MooPluginInfo *info);
|
|
|
|
|
|
static void
|
|
moo_py_plugin_class_init (MooPyPluginClass *klass,
|
|
MooPyPluginData *data)
|
|
{
|
|
MooPluginClass *plugin_class = MOO_PLUGIN_CLASS (klass);
|
|
GObjectClass *gobj_class = G_OBJECT_CLASS (klass);
|
|
|
|
moo_py_plugin_parent_class = g_type_class_peek_parent (klass);
|
|
|
|
klass->data = data;
|
|
gobj_class->finalize = moo_py_plugin_finalize;
|
|
plugin_class->plugin_system_version = MOO_PLUGIN_CURRENT_VERSION;
|
|
|
|
if (data->py_plugin_type)
|
|
{
|
|
plugin_class->init = moo_py_plugin_init;
|
|
plugin_class->deinit = moo_py_plugin_deinit;
|
|
plugin_class->attach_win = moo_py_plugin_attach_win;
|
|
plugin_class->detach_win = moo_py_plugin_detach_win;
|
|
plugin_class->attach_doc = moo_py_plugin_attach_doc;
|
|
plugin_class->detach_doc = moo_py_plugin_detach_doc;
|
|
plugin_class->create_prefs_page = moo_py_plugin_create_prefs_page;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
moo_py_plugin_instance_init (MooPyPlugin *plugin,
|
|
MooPyPluginClass *klass)
|
|
{
|
|
MooPlugin *moo_plugin = MOO_PLUGIN (plugin);
|
|
|
|
plugin->class_data = klass->data;
|
|
|
|
plugin->instance = PyObject_CallObject (klass->data->py_plugin_type, NULL);
|
|
|
|
if (!plugin->instance)
|
|
{
|
|
PyErr_Print ();
|
|
g_warning ("%s: could not create plugin instance", G_STRLOC);
|
|
}
|
|
|
|
moo_plugin->info = get_plugin_info (plugin->instance);
|
|
|
|
if (klass->data->py_win_plugin_type && !klass->data->win_plugin_type)
|
|
klass->data->win_plugin_type = generate_win_plugin_type (klass->data->py_win_plugin_type, klass->data);
|
|
|
|
if (klass->data->py_doc_plugin_type && !klass->data->doc_plugin_type)
|
|
klass->data->doc_plugin_type = generate_doc_plugin_type (klass->data->py_doc_plugin_type, klass->data);
|
|
|
|
moo_plugin->win_plugin_type = klass->data->win_plugin_type;
|
|
moo_plugin->doc_plugin_type = klass->data->doc_plugin_type;
|
|
}
|
|
|
|
|
|
static void
|
|
moo_py_plugin_finalize (GObject *object)
|
|
{
|
|
MooPyPlugin *py_plugin = MOO_PY_PLUGIN (object);
|
|
MooPlugin *plugin = MOO_PLUGIN (object);
|
|
Py_XDECREF (py_plugin->instance);
|
|
plugin_info_free (plugin->info);
|
|
G_OBJECT_CLASS(moo_py_plugin_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
PyObject*
|
|
_moo_python_plugin_register (PyObject *py_plugin_type,
|
|
PyObject *py_win_plugin_type,
|
|
PyObject *py_doc_plugin_type)
|
|
{
|
|
char *plugin_type_name = NULL;
|
|
static GTypeInfo plugin_type_info;
|
|
MooPyPluginData *class_data;
|
|
MooPlugin *base;
|
|
int i;
|
|
|
|
g_return_val_if_fail (py_plugin_type != NULL, NULL);
|
|
g_return_val_if_fail (PyType_Check (py_plugin_type), NULL);
|
|
g_return_val_if_fail (!py_win_plugin_type || PyType_Check (py_win_plugin_type), NULL);
|
|
g_return_val_if_fail (!py_doc_plugin_type || PyType_Check (py_doc_plugin_type), NULL);
|
|
|
|
base = moo_plugin_get (MOO_TYPE_PYTHON_PLUGIN);
|
|
g_return_val_if_fail (base != NULL, NULL);
|
|
g_return_val_if_fail (moo_plugin_initialized (base), NULL);
|
|
|
|
for (i = 0; i < 1000; ++i)
|
|
{
|
|
plugin_type_name = g_strdup_printf ("MooPyPlugin-%08x", g_random_int ());
|
|
if (!g_type_from_name (plugin_type_name))
|
|
break;
|
|
g_free (plugin_type_name);
|
|
plugin_type_name = NULL;
|
|
}
|
|
|
|
if (!plugin_type_name)
|
|
return_RuntimeErr ("could not find name for plugin class");
|
|
|
|
class_data = g_new0 (MooPyPluginData, 1);
|
|
class_data->py_plugin_type = py_plugin_type;
|
|
class_data->py_win_plugin_type = py_win_plugin_type;
|
|
class_data->py_doc_plugin_type = py_doc_plugin_type;
|
|
Py_XINCREF (py_plugin_type);
|
|
Py_XINCREF (py_win_plugin_type);
|
|
Py_XINCREF (py_doc_plugin_type);
|
|
|
|
plugin_type_info.class_size = sizeof (MooPyPluginClass);
|
|
plugin_type_info.class_init = (GClassInitFunc) moo_py_plugin_class_init;
|
|
plugin_type_info.class_data = class_data;
|
|
plugin_type_info.instance_size = sizeof (MooPyPlugin);
|
|
plugin_type_info.instance_init = (GInstanceInitFunc) moo_py_plugin_instance_init;
|
|
|
|
class_data->plugin_type = g_type_register_static (MOO_TYPE_PLUGIN, plugin_type_name,
|
|
&plugin_type_info, 0);
|
|
g_free (plugin_type_name);
|
|
|
|
if (!moo_plugin_register (class_data->plugin_type))
|
|
{
|
|
Py_XDECREF (class_data->py_plugin_type);
|
|
Py_XDECREF (class_data->py_win_plugin_type);
|
|
Py_XDECREF (class_data->py_doc_plugin_type);
|
|
g_free (class_data);
|
|
return_RuntimeErr ("could not register plugin");
|
|
}
|
|
|
|
MOO_PYTHON_PLUGIN(base)->plugins =
|
|
g_slist_prepend (MOO_PYTHON_PLUGIN(base)->plugins, class_data);
|
|
|
|
return_None;
|
|
}
|
|
|
|
|
|
static void
|
|
moo_py_plugin_delete (MooPyPluginData *data)
|
|
{
|
|
g_return_if_fail (data != NULL);
|
|
moo_plugin_unregister (data->plugin_type);
|
|
Py_XDECREF (data->py_plugin_type);
|
|
Py_XDECREF (data->py_win_plugin_type);
|
|
Py_XDECREF (data->py_doc_plugin_type);
|
|
g_free (data);
|
|
}
|
|
|
|
|
|
static void
|
|
plugin_info_free (MooPluginInfo *info)
|
|
{
|
|
if (info)
|
|
{
|
|
g_free ((char*) info->id);
|
|
g_free ((char*) info->name);
|
|
g_free ((char*) info->description);
|
|
g_free ((char*) info->author);
|
|
g_free ((char*) info->version);
|
|
g_free (info->params);
|
|
g_free (info->prefs_params);
|
|
g_free (info);
|
|
}
|
|
}
|
|
|
|
|
|
static char *
|
|
dict_get_string (PyObject *dict,
|
|
const char *key)
|
|
{
|
|
PyObject *val, *strval;
|
|
char *result;
|
|
|
|
val = PyDict_GetItemString (dict, (char*) key);
|
|
|
|
if (!val)
|
|
return NULL;
|
|
|
|
strval = PyObject_Str (val);
|
|
Py_DECREF (val);
|
|
|
|
if (!strval)
|
|
{
|
|
PyErr_Print ();
|
|
return NULL;
|
|
}
|
|
|
|
result = g_strdup (PyString_AS_STRING (strval));
|
|
|
|
Py_DECREF (strval);
|
|
return result;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
dict_get_bool (PyObject *dict,
|
|
const char *key,
|
|
gboolean default_val)
|
|
{
|
|
PyObject *val;
|
|
gboolean result;
|
|
|
|
val = PyDict_GetItemString (dict, (char*) key);
|
|
|
|
if (!val)
|
|
return default_val;
|
|
|
|
result = PyObject_IsTrue (val);
|
|
|
|
Py_DECREF (val);
|
|
return result;
|
|
}
|
|
|
|
|
|
static MooPluginInfo*
|
|
get_plugin_info (PyObject *object)
|
|
{
|
|
MooPluginInfo *info;
|
|
PyObject *result, *meth;
|
|
|
|
if (!PyObject_HasAttrString (object, (char*) "get_info"))
|
|
{
|
|
g_critical ("%s: no get_info attribute", G_STRLOC);
|
|
return NULL;
|
|
}
|
|
|
|
meth = PyObject_GetAttrString (object, (char*) "get_info");
|
|
|
|
if (!meth)
|
|
{
|
|
PyErr_Print ();
|
|
return NULL;
|
|
}
|
|
|
|
if (!PyCallable_Check (meth))
|
|
{
|
|
g_critical ("%s: get_info is not callable", G_STRLOC);
|
|
Py_DECREF (meth);
|
|
return NULL;
|
|
}
|
|
|
|
result = PyObject_CallObject (meth, NULL);
|
|
Py_DECREF (meth);
|
|
|
|
if (!result)
|
|
{
|
|
PyErr_Print ();
|
|
return NULL;
|
|
}
|
|
|
|
if (!PyDict_Check (result))
|
|
{
|
|
g_critical ("%s: not a dict", G_STRLOC);
|
|
Py_DECREF (result);
|
|
return NULL;
|
|
}
|
|
|
|
info = g_new0 (MooPluginInfo, 1);
|
|
info->params = g_new0 (MooPluginParams, 1);
|
|
info->prefs_params = g_new0 (MooPluginPrefsParams, 1);
|
|
|
|
info->id = dict_get_string (result, "id");
|
|
info->name = dict_get_string (result, "name");
|
|
info->description = dict_get_string (result, "description");
|
|
info->author = dict_get_string (result, "author");
|
|
info->version = dict_get_string (result, "version");
|
|
|
|
info->params->enabled = dict_get_bool (result, "enabled", TRUE);
|
|
info->params->visible = dict_get_bool (result, "visible", TRUE);
|
|
|
|
return info;
|
|
}
|
|
|
|
|
|
static GType
|
|
generate_win_plugin_type (PyObject *py_type,
|
|
MooPyPluginData *class_data)
|
|
{
|
|
GType type;
|
|
char *type_name = NULL;
|
|
static GTypeInfo type_info;
|
|
int i;
|
|
|
|
g_return_val_if_fail (py_type != NULL, 0);
|
|
g_return_val_if_fail (PyType_Check (py_type), 0);
|
|
|
|
for (i = 0; i < 1000; ++i)
|
|
{
|
|
type_name = g_strdup_printf ("MooPyWinPlugin-%08x", g_random_int ());
|
|
if (!g_type_from_name (type_name))
|
|
break;
|
|
g_free (type_name);
|
|
type_name = NULL;
|
|
}
|
|
|
|
if (!type_name)
|
|
{
|
|
g_critical ("%s: could not find name for win plugin class", G_STRLOC);
|
|
return 0;
|
|
}
|
|
|
|
type_info.class_size = sizeof (MooPyWinPluginClass);
|
|
type_info.class_init = (GClassInitFunc) moo_py_win_plugin_class_init;
|
|
type_info.class_data = class_data;
|
|
type_info.instance_size = sizeof (MooPyWinPlugin);
|
|
type_info.instance_init = (GInstanceInitFunc) moo_py_win_plugin_instance_init;
|
|
|
|
type = g_type_register_static (MOO_TYPE_WIN_PLUGIN, type_name, &type_info, 0);
|
|
|
|
g_free (type_name);
|
|
return type;
|
|
}
|
|
|
|
static GType
|
|
generate_doc_plugin_type (PyObject *py_type,
|
|
MooPyPluginData *class_data)
|
|
{
|
|
char *type_name = NULL;
|
|
static GTypeInfo type_info;
|
|
int i;
|
|
GType type;
|
|
|
|
g_return_val_if_fail (py_type != NULL, 0);
|
|
g_return_val_if_fail (PyType_Check (py_type), 0);
|
|
|
|
for (i = 0; i < 1000; ++i)
|
|
{
|
|
type_name = g_strdup_printf ("MooPyDocPlugin-%08x", g_random_int ());
|
|
if (!g_type_from_name (type_name))
|
|
break;
|
|
g_free (type_name);
|
|
type_name = NULL;
|
|
}
|
|
|
|
if (!type_name)
|
|
{
|
|
g_critical ("%s: could not find name for doc plugin class", G_STRLOC);
|
|
return 0;
|
|
}
|
|
|
|
type_info.class_size = sizeof (MooPyDocPluginClass);
|
|
type_info.class_init = (GClassInitFunc) moo_py_doc_plugin_class_init;
|
|
type_info.class_data = class_data;
|
|
type_info.instance_size = sizeof (MooPyDocPlugin);
|
|
type_info.instance_init = (GInstanceInitFunc) moo_py_doc_plugin_instance_init;
|
|
|
|
type = g_type_register_static (MOO_TYPE_DOC_PLUGIN, type_name, &type_info, 0);
|
|
|
|
g_free (type_name);
|
|
return type;
|
|
}
|
|
|
|
|
|
static void
|
|
moo_py_win_plugin_class_init (MooPyWinPluginClass *klass,
|
|
MooPyPluginData *data)
|
|
{
|
|
MooWinPluginClass *plugin_class = MOO_WIN_PLUGIN_CLASS (klass);
|
|
GObjectClass *gobj_class = G_OBJECT_CLASS (klass);
|
|
|
|
moo_py_win_plugin_parent_class = g_type_class_peek_parent (klass);
|
|
|
|
klass->data = data;
|
|
gobj_class->finalize = moo_py_win_plugin_finalize;
|
|
plugin_class->create = moo_py_win_plugin_create;
|
|
plugin_class->destroy = moo_py_win_plugin_destroy;
|
|
}
|
|
|
|
static void
|
|
moo_py_win_plugin_instance_init (MooPyWinPlugin *plugin,
|
|
MooPyWinPluginClass *klass)
|
|
{
|
|
PyObject *args;
|
|
|
|
plugin->class_data = klass->data;
|
|
|
|
args = PyTuple_New (0);
|
|
plugin->instance = PyType_GenericNew ((PyTypeObject*) klass->data->py_win_plugin_type, args, NULL);
|
|
Py_DECREF (args);
|
|
}
|
|
|
|
static void
|
|
moo_py_win_plugin_finalize (GObject *object)
|
|
{
|
|
MooPyWinPlugin *plugin = MOO_PY_WIN_PLUGIN (object);
|
|
Py_XDECREF (plugin->instance);
|
|
G_OBJECT_CLASS(moo_py_win_plugin_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_py_doc_plugin_class_init (MooPyDocPluginClass *klass,
|
|
MooPyPluginData *data)
|
|
{
|
|
MooDocPluginClass *plugin_class = MOO_DOC_PLUGIN_CLASS (klass);
|
|
GObjectClass *gobj_class = G_OBJECT_CLASS (klass);
|
|
|
|
moo_py_doc_plugin_parent_class = g_type_class_peek_parent (klass);
|
|
|
|
klass->data = data;
|
|
gobj_class->finalize = moo_py_doc_plugin_finalize;
|
|
plugin_class->create = moo_py_doc_plugin_create;
|
|
plugin_class->destroy = moo_py_doc_plugin_destroy;
|
|
}
|
|
|
|
static void
|
|
moo_py_doc_plugin_instance_init (MooPyDocPlugin *plugin,
|
|
MooPyDocPluginClass *klass)
|
|
{
|
|
PyObject *args;
|
|
|
|
plugin->class_data = klass->data;
|
|
|
|
args = PyTuple_New (0);
|
|
plugin->instance = PyType_GenericNew ((PyTypeObject*) klass->data->py_doc_plugin_type, args, NULL);
|
|
Py_DECREF (args);
|
|
}
|
|
|
|
static void
|
|
moo_py_doc_plugin_finalize (GObject *object)
|
|
{
|
|
MooPyDocPlugin *plugin = MOO_PY_DOC_PLUGIN (object);
|
|
Py_XDECREF (plugin->instance);
|
|
G_OBJECT_CLASS(moo_py_doc_plugin_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
/**************************************************************************/
|
|
/* Plugin methods
|
|
*/
|
|
|
|
static gboolean
|
|
call_any_meth__ (PyObject *instance,
|
|
MooEdit *doc,
|
|
MooEditWindow *window,
|
|
const char *meth_name)
|
|
{
|
|
PyObject *result, *meth, *py_window, *py_doc;
|
|
|
|
if (!instance)
|
|
{
|
|
g_critical ("%s: oops", G_STRLOC);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!PyObject_HasAttrString (instance, (char*) meth_name))
|
|
return TRUE;
|
|
|
|
meth = PyObject_GetAttrString (instance, (char*) meth_name);
|
|
|
|
if (!meth)
|
|
{
|
|
PyErr_Print ();
|
|
return FALSE;
|
|
}
|
|
|
|
if (!PyCallable_Check (meth))
|
|
{
|
|
g_critical ("%s: %s is not callable", G_STRLOC, meth_name);
|
|
Py_DECREF (meth);
|
|
return FALSE;
|
|
}
|
|
|
|
py_window = window ? pygobject_new (G_OBJECT (window)) : NULL;
|
|
py_doc = doc ? pygobject_new (G_OBJECT (doc)) : NULL;
|
|
|
|
if (window)
|
|
{
|
|
if (doc)
|
|
result = PyObject_CallFunction (meth, (char*) "(OO)", py_doc, py_window);
|
|
else
|
|
result = PyObject_CallFunction (meth, (char*) "(O)", py_window);
|
|
}
|
|
else
|
|
{
|
|
result = PyObject_CallObject (meth, NULL);
|
|
}
|
|
|
|
Py_XDECREF (meth);
|
|
Py_XDECREF (py_window);
|
|
Py_XDECREF (py_doc);
|
|
|
|
if (result)
|
|
{
|
|
gboolean bool_result = PyObject_IsTrue (result);
|
|
Py_DECREF (result);
|
|
return bool_result;
|
|
}
|
|
else
|
|
{
|
|
PyErr_Print ();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
call_bool_meth (PyObject *instance,
|
|
MooEdit *doc,
|
|
MooEditWindow *window,
|
|
const char *meth_name)
|
|
{
|
|
return call_any_meth__ (instance, doc, window, meth_name);
|
|
}
|
|
|
|
static void
|
|
call_meth (PyObject *instance,
|
|
MooEdit *doc,
|
|
MooEditWindow *window,
|
|
const char *meth_name)
|
|
{
|
|
call_any_meth__ (instance, doc, window, meth_name);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
moo_py_plugin_init (MooPlugin *plugin)
|
|
{
|
|
return call_bool_meth (MOO_PY_PLUGIN(plugin)->instance, NULL, NULL, "init");
|
|
}
|
|
|
|
static void
|
|
moo_py_plugin_deinit (MooPlugin *plugin)
|
|
{
|
|
call_meth (MOO_PY_PLUGIN(plugin)->instance, NULL, NULL, "deinit");
|
|
}
|
|
|
|
static void
|
|
moo_py_plugin_attach_win (MooPlugin *plugin,
|
|
MooEditWindow *window)
|
|
{
|
|
call_meth (MOO_PY_PLUGIN(plugin)->instance, NULL, window, "attach_win");
|
|
}
|
|
|
|
static void
|
|
moo_py_plugin_detach_win (MooPlugin *plugin,
|
|
MooEditWindow *window)
|
|
{
|
|
call_meth (MOO_PY_PLUGIN(plugin)->instance, NULL, window, "detach_win");
|
|
}
|
|
|
|
static void
|
|
moo_py_plugin_attach_doc (MooPlugin *plugin,
|
|
MooEdit *doc,
|
|
MooEditWindow *window)
|
|
{
|
|
call_meth (MOO_PY_PLUGIN(plugin)->instance, doc, window, "attach_doc");
|
|
}
|
|
|
|
static void
|
|
moo_py_plugin_detach_doc (MooPlugin *plugin,
|
|
MooEdit *doc,
|
|
MooEditWindow *window)
|
|
{
|
|
call_meth (MOO_PY_PLUGIN(plugin)->instance, doc, window, "detach_doc");
|
|
}
|
|
|
|
static gboolean
|
|
moo_py_win_plugin_create (MooWinPlugin *plugin)
|
|
{
|
|
return call_bool_meth (MOO_PY_WIN_PLUGIN(plugin)->instance,
|
|
NULL, plugin->window, "create");
|
|
}
|
|
|
|
static void
|
|
moo_py_win_plugin_destroy (MooWinPlugin *plugin)
|
|
{
|
|
call_meth (MOO_PY_WIN_PLUGIN(plugin)->instance,
|
|
NULL, plugin->window, "destroy");
|
|
}
|
|
|
|
static gboolean
|
|
moo_py_doc_plugin_create (MooDocPlugin *plugin)
|
|
{
|
|
return call_bool_meth (MOO_PY_DOC_PLUGIN(plugin)->instance,
|
|
plugin->doc, plugin->window, "create");
|
|
}
|
|
|
|
static void
|
|
moo_py_doc_plugin_destroy (MooDocPlugin *plugin)
|
|
{
|
|
call_meth (MOO_PY_DOC_PLUGIN(plugin)->instance,
|
|
plugin->doc, plugin->window, "destroy");
|
|
}
|
|
|
|
|
|
static GtkWidget*
|
|
moo_py_plugin_create_prefs_page (MooPlugin *plugin)
|
|
{
|
|
PyObject *result, *meth;
|
|
MooPyPlugin *py_plugin = MOO_PY_PLUGIN (plugin);
|
|
|
|
g_return_val_if_fail (py_plugin->instance != NULL, NULL);
|
|
|
|
if (!PyObject_HasAttrString (py_plugin->instance, (char*) "create_prefs_page"))
|
|
return NULL;
|
|
|
|
meth = PyObject_GetAttrString (py_plugin->instance, (char*) "create_prefs_page");
|
|
|
|
if (!meth)
|
|
{
|
|
PyErr_Print ();
|
|
return NULL;
|
|
}
|
|
|
|
if (!PyCallable_Check (meth))
|
|
{
|
|
g_critical ("%s: create_prefs_page is not callable", G_STRLOC);
|
|
Py_DECREF (meth);
|
|
return NULL;
|
|
}
|
|
|
|
result = PyObject_CallObject (meth, NULL);
|
|
Py_DECREF (meth);
|
|
|
|
if (!result)
|
|
{
|
|
PyErr_Print ();
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
gpointer page = pygobject_get (result);
|
|
g_object_ref (page);
|
|
Py_DECREF (result);
|
|
return page;
|
|
}
|
|
}
|