1 /*
2 * plugin.c
3 *
4 * Copyright 2015 Thomas Martitz <kugel@rockbox.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22
23 #include "geanypy.h"
24 #include "geanypy-keybindings.h"
25
26 #include <glib.h>
27
call_key(gpointer * unused,guint key_id,gpointer data)28 static gboolean call_key(gpointer *unused, guint key_id, gpointer data)
29 {
30 PyObject *callback = data;
31 PyObject *args;
32
33 args = Py_BuildValue("(i)", key_id);
34 PyObject_CallObject(callback, args);
35 Py_DECREF(args);
36 }
37
38
39 /* plugin.py provides an OOP-style wrapper around this so call it like:
40 * class Foo(geany.Plugin):
41 * def __init__(self):
42 * self.set_key_group(...)
43 */
44 static PyObject *
Keybindings_set_key_group(PyObject * self,PyObject * args,PyObject * kwargs)45 Keybindings_set_key_group(PyObject *self, PyObject *args, PyObject *kwargs)
46 {
47 static gchar *kwlist[] = { "plugin", "section_name", "count", "callback", NULL };
48 int count = 0;
49 const gchar *section_name = NULL;
50 GeanyKeyGroup *group = NULL;
51 PyObject *py_callback = NULL;
52 PyObject *py_ret = Py_None;
53 PyObject *py_plugin;
54 gboolean has_cb = FALSE;
55
56 Py_INCREF(Py_None);
57
58 if (PyArg_ParseTupleAndKeywords(args, kwargs, "Osi|O", kwlist,
59 &py_plugin, §ion_name, &count, &py_callback))
60 {
61 GeanyPlugin *plugin = plugin_get(py_plugin);
62 g_return_val_if_fail(plugin != NULL, Py_None);
63
64 has_cb = PyCallable_Check(py_callback);
65 if (has_cb)
66 {
67 Py_INCREF(py_callback);
68 group = plugin_set_key_group_full(plugin, section_name, count,
69 (GeanyKeyGroupFunc) call_key, py_callback,
70 (GDestroyNotify) Py_DecRef);
71 }
72 else
73 group = plugin_set_key_group(plugin, section_name, count, NULL);
74 }
75
76 if (group)
77 {
78 Py_DECREF(py_ret);
79 py_ret = KeyGroup_new_with_geany_key_group(group, has_cb);
80 }
81
82 return py_ret;
83 }
84
85
86 static PyObject *
KeyGroup_add_key_item(KeyGroup * self,PyObject * args,PyObject * kwargs)87 KeyGroup_add_key_item(KeyGroup *self, PyObject *args, PyObject *kwargs)
88 {
89 static gchar *kwlist[] = { "name", "label", "callback", "key_id", "key", "mod" , "menu_item", NULL };
90 int id = -1;
91 int key = 0, mod = 0;
92 const gchar *name = NULL, *label = NULL;
93 PyObject *py_menu_item = NULL;
94 PyObject *py_callback = NULL;
95 GeanyKeyBinding *item = NULL;
96
97 if (PyArg_ParseTupleAndKeywords(args, kwargs, "ss|OiiiO", kwlist,
98 &name, &label, &py_callback, &id, &key, &mod, &py_menu_item))
99 {
100 if (id == -1)
101 id = self->item_index;
102
103 GtkWidget *menu_item = (py_menu_item == NULL || py_menu_item == Py_None)
104 ? NULL : GTK_WIDGET(pygobject_get(py_menu_item));
105 if (PyCallable_Check(py_callback))
106 {
107 Py_INCREF(py_callback);
108 item = keybindings_set_item_full(self->kb_group, id, (guint) key,
109 (GdkModifierType) mod, name, label, menu_item,
110 (GeanyKeyBindingFunc) call_key, py_callback,
111 (GDestroyNotify) Py_DecRef);
112 }
113 else
114 {
115 if (!self->has_cb)
116 g_warning("Either KeyGroup or the Keybinding must have a callback\n");
117 else
118 item = keybindings_set_item(self->kb_group, id, NULL, (guint) key,
119 (GdkModifierType) mod, name, label, menu_item);
120 }
121 Py_XDECREF(py_menu_item);
122
123 self->item_index = id + 1;
124 }
125
126 if (item)
127 {
128 /* Return a tuple containing the key group and the opaque GeanyKeyBinding pointer.
129 * This is in preparation of allowing chained calls like
130 * set_kb_group(X, 3).add_key_item().add_key_item().add_key_item()
131 * without losing access to the keybinding pointer (might become necessary for newer
132 * Geany APIs).
133 * Note that the plain tuple doesn't support the above yet, we've got to subclass it,
134 * but we are prepared without breaking sub-plugins */
135 PyObject *ret = PyTuple_Pack(2, self, PyCapsule_New(item, "GeanyKeyBinding", NULL));
136 return ret;
137 }
138 Py_RETURN_NONE;
139 }
140
141
142 static PyMethodDef
143 KeyGroup_methods[] = {
144 { "add_key_item", (PyCFunction)KeyGroup_add_key_item, METH_KEYWORDS,
145 "Adds an action to the plugin's key group" },
146 { NULL }
147 };
148
149 static PyMethodDef
150 Keybindings_methods[] = {
151 { "set_key_group", (PyCFunction)Keybindings_set_key_group, METH_KEYWORDS,
152 "Sets up a GeanyKeybindingGroup for this plugin." },
153 { NULL }
154 };
155
156
157 static PyGetSetDef
158 KeyGroup_getseters[] = {
159 { NULL },
160 };
161
162
163 static void
KeyGroup_dealloc(KeyGroup * self)164 KeyGroup_dealloc(KeyGroup *self)
165 {
166 g_return_if_fail(self != NULL);
167 self->ob_type->tp_free((PyObject *) self);
168 }
169
170
171 static PyTypeObject KeyGroupType = {
172 PyObject_HEAD_INIT(NULL)
173 0, /* ob_size */
174 "geany.keybindings.KeyGroup", /* tp_name */
175 sizeof(KeyGroup), /* tp_basicsize */
176 0, /* tp_itemsize */
177 (destructor) KeyGroup_dealloc, /* tp_dealloc */
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_print - tp_as_buffer */
179 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
180 "Wrapper around a GeanyKeyGroup structure." ,/* tp_doc */
181 0, 0, 0, 0, 0, 0, /* tp_traverse - tp_iternext */
182 KeyGroup_methods, /* tp_methods */
183 0, /* tp_members */
184 KeyGroup_getseters, /* tp_getset */
185 0, 0, 0, 0, 0, /* tp_base - tp_dictoffset */
186 0, 0, (newfunc) PyType_GenericNew, /* tp_init - tp_alloc, tp_new */
187 };
188
189
initkeybindings(void)190 PyMODINIT_FUNC initkeybindings(void)
191 {
192 PyObject *m;
193
194 if (PyType_Ready(&KeyGroupType) < 0)
195 return;
196
197 m = Py_InitModule3("keybindings", Keybindings_methods, "Keybindings support.");
198
199 Py_INCREF(&KeyGroupType);
200 PyModule_AddObject(m, "KeyGroup", (PyObject *)&KeyGroupType);
201 }
202
KeyGroup_new_with_geany_key_group(GeanyKeyGroup * group,gboolean has_cb)203 PyObject *KeyGroup_new_with_geany_key_group(GeanyKeyGroup *group, gboolean has_cb)
204 {
205 KeyGroup *ret = PyObject_New(KeyGroup, &KeyGroupType);
206
207 ret->kb_group = group;
208 ret->has_cb = has_cb;
209 ret->item_index = 0;
210
211 return (PyObject *) ret;
212 }
213
214