big dialogs: dlg_format_text: no need to pass the term.
[elinks.git] / src / scripting / python / menu.c
blob9152fc1d45fb174ee8685acb70ed7d12e77f0c47
1 /* Simple menus for Python. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <Python.h>
9 #include "elinks.h"
11 #include "bfu/menu.h"
12 #include "document/document.h"
13 #include "document/view.h"
14 #include "intl/gettext/libintl.h"
15 #include "scripting/python/core.h"
16 #include "session/session.h"
17 #include "terminal/window.h"
18 #include "util/error.h"
19 #include "util/memlist.h"
20 #include "util/memory.h"
21 #include "util/string.h"
22 #include "viewer/text/view.h"
24 /* C wrapper that invokes Python callbacks for menu items. */
26 static void
27 invoke_menu_callback(struct terminal *term, void *data, void *ses)
29 PyObject *callback = data;
30 struct session *saved_python_ses = python_ses;
31 PyObject *result;
33 python_ses = ses;
35 result = PyObject_CallFunction(callback, NULL);
36 if (result)
37 Py_DECREF(result);
38 else
39 alert_python_error();
41 Py_DECREF(callback);
43 python_ses = saved_python_ses;
46 enum python_menu_type {
47 PYTHON_MENU_DEFAULT,
48 PYTHON_MENU_LINK,
49 PYTHON_MENU_TAB,
50 PYTHON_MENU_MAX
53 /* Python interface for displaying simple menus. */
55 static char python_menu_doc[] =
56 PYTHON_DOCSTRING("menu(items[, type]) -> None\n\
57 \n\
58 Display a menu.\n\
59 \n\
60 Arguments:\n\
61 \n\
62 items -- A sequence of tuples. Each tuple must have two elements: a\n\
63 string containing the name of a menu item, and a callable\n\
64 object that will be called without any arguments if the user\n\
65 selects that menu item.\n\
66 \n\
67 Optional arguments:\n\
68 \n\
69 type -- A constant specifying the type of menu to display. By default\n\
70 the menu is displayed at the top of the screen, but if this\n\
71 argument's value is the constant elinks.MENU_TAB then the menu\n\
72 is displayed in the same location as the ELinks tab menu. If\n\
73 its value is the constant elinks.MENU_LINK then the menu is\n\
74 displayed in the same location as the ELinks link menu and is\n\
75 not displayed unless a link is currently selected.\n");
77 static PyObject *
78 python_menu(PyObject *self, PyObject *args, PyObject *kwargs)
80 PyObject *items;
81 enum python_menu_type menu_type = PYTHON_MENU_DEFAULT;
82 Py_ssize_t length;
83 int i;
84 struct menu_item *menu;
85 struct memory_list *ml = NULL;
86 static char *kwlist[] = {"items", "type", NULL};
88 if (!python_ses) {
89 PyErr_SetString(python_elinks_err, "No session");
90 return NULL;
93 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:menu",
94 kwlist, &items, &menu_type))
95 return NULL;
97 assert(items);
98 if_assert_failed {
99 PyErr_SetString(python_elinks_err, N_("Internal error"));
100 return NULL;
103 if (!PySequence_Check(items)) {
104 PyErr_SetString(PyExc_TypeError, "Argument must be a sequence");
105 return NULL;
107 length = PySequence_Length(items);
108 if (length == -1 || length > INT_MAX) return NULL;
109 else if (length == 0) goto success;
111 if (menu_type < 0 || menu_type >= PYTHON_MENU_MAX) {
112 PyErr_Format(python_elinks_err, "%s %d",
113 N_("Bad number"), menu_type);
114 return NULL;
116 } else if (menu_type == PYTHON_MENU_LINK) {
117 if (!get_current_link(current_frame(python_ses)))
118 goto success;
120 } else if (menu_type == PYTHON_MENU_TAB
121 && python_ses->status.show_tabs_bar) {
122 int y;
124 if (python_ses->status.show_tabs_bar_at_top)
125 y = python_ses->status.show_title_bar;
126 else
127 y = python_ses->tab->term->height - length
128 - python_ses->status.show_status_bar - 2;
130 set_window_ptr(python_ses->tab, python_ses->tab->xpos,
131 int_max(y, 0));
133 } else {
134 set_window_ptr(python_ses->tab, 0, 0);
137 menu = new_menu(FREE_LIST | FREE_TEXT | NO_INTL);
138 if (!menu) return PyErr_NoMemory();
141 * Keep track of all the memory we allocate so we'll be able to free
142 * it in case any error prevents us from displaying the menu.
144 ml = getml(menu, (void *) NULL);
145 if (!ml) {
146 mem_free(menu);
147 return PyErr_NoMemory();
150 for (i = 0; i < length; i++) {
151 PyObject *tuple = PySequence_GetItem(items, i);
152 PyObject *name, *callback;
153 unsigned char *contents;
155 if (!tuple) goto error;
157 if (!PyTuple_Check(tuple)) {
158 Py_DECREF(tuple);
159 PyErr_SetString(PyExc_TypeError,
160 "Argument must be sequence of tuples");
161 goto error;
163 name = PyTuple_GetItem(tuple, 0);
164 callback = PyTuple_GetItem(tuple, 1);
165 Py_DECREF(tuple);
166 if (!name || !callback) goto error;
168 contents = (unsigned char *) PyString_AsString(name);
169 if (!contents) goto error;
171 contents = stracpy(contents);
172 if (!contents) {
173 PyErr_NoMemory();
174 goto error;
176 add_one_to_ml(&ml, contents);
179 * FIXME: We need to increment the reference counts for
180 * callbacks so they won't be garbage-collected by the Python
181 * interpreter before they're called. But for any callback
182 * that isn't called (because the user doesn't select the
183 * corresponding menu item) we'll never have an opportunity
184 * to decrement the reference count again, so this code leaks
185 * references. It probably can't be fixed without changes to
186 * the menu machinery in bfu/menu.c, e.g. to call an arbitrary
187 * clean-up function when a menu is destroyed.
189 * The good news is that in a typical usage case, where the
190 * callback objects wouldn't be garbage-collected anyway until
191 * the Python interpreter exits, this makes no difference at
192 * all. But it's not strictly correct, and it could leak memory
193 * in more elaborate usage where callback objects are created
194 * and thrown away on the fly.
196 Py_INCREF(callback);
197 add_to_menu(&menu, contents, NULL, ACT_MAIN_NONE,
198 invoke_menu_callback, callback, 0);
201 do_menu(python_ses->tab->term, menu, python_ses, 1);
203 success:
204 mem_free_if(ml);
206 Py_INCREF(Py_None);
207 return Py_None;
209 error:
210 freeml(ml);
211 return NULL;
214 static PyMethodDef menu_methods[] = {
215 {"menu", (PyCFunction) python_menu,
216 METH_VARARGS | METH_KEYWORDS,
217 python_menu_doc},
219 {NULL, NULL, 0, NULL}
222 static int
223 add_constant(PyObject *dict, const char *key, int value)
225 PyObject *constant = PyInt_FromLong(value);
226 int result;
228 if (!constant) return -1;
229 result = PyDict_SetItemString(dict, key, constant);
230 Py_DECREF(constant);
232 return result;
236 python_init_menu_interface(PyObject *dict, PyObject *name)
238 if (add_constant(dict, "MENU_LINK", PYTHON_MENU_LINK) != 0) return -1;
239 if (add_constant(dict, "MENU_TAB", PYTHON_MENU_TAB) != 0) return -1;
241 return add_python_methods(dict, name, menu_methods);