1 /* Simple menus for Python. */
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. */
27 invoke_menu_callback(struct terminal
*term
, void *data
, void *ses
)
29 PyObject
*callback
= data
;
30 struct session
*saved_python_ses
= python_ses
;
35 result
= PyObject_CallFunction(callback
, NULL
);
43 python_ses
= saved_python_ses
;
46 enum python_menu_type
{
53 /* Python interface for displaying simple menus. */
55 static char python_menu_doc
[] =
56 PYTHON_DOCSTRING("menu(items[, type]) -> None\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\
67 Optional arguments:\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");
78 python_menu(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
81 enum python_menu_type menu_type
= PYTHON_MENU_DEFAULT
;
84 struct menu_item
*menu
;
85 struct memory_list
*ml
= NULL
;
86 static char *kwlist
[] = {"items", "type", NULL
};
89 PyErr_SetString(python_elinks_err
, "No session");
93 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "O|i:menu",
94 kwlist
, &items
, &menu_type
))
99 PyErr_SetString(python_elinks_err
, N_("Internal error"));
103 if (!PySequence_Check(items
)) {
104 PyErr_SetString(PyExc_TypeError
, "Argument must be a sequence");
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
);
116 } else if (menu_type
== PYTHON_MENU_LINK
) {
117 if (!get_current_link(current_frame(python_ses
)))
120 } else if (menu_type
== PYTHON_MENU_TAB
121 && python_ses
->status
.show_tabs_bar
) {
124 if (python_ses
->status
.show_tabs_bar_at_top
)
125 y
= python_ses
->status
.show_title_bar
;
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
,
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
);
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
)) {
159 PyErr_SetString(PyExc_TypeError
,
160 "Argument must be sequence of tuples");
163 name
= PyTuple_GetItem(tuple
, 0);
164 callback
= PyTuple_GetItem(tuple
, 1);
166 if (!name
|| !callback
) goto error
;
168 contents
= (unsigned char *) PyString_AsString(name
);
169 if (!contents
) goto error
;
171 contents
= stracpy(contents
);
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.
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);
214 static PyMethodDef menu_methods
[] = {
215 {"menu", (PyCFunction
) python_menu
,
216 METH_VARARGS
| METH_KEYWORDS
,
219 {NULL
, NULL
, 0, NULL
}
223 add_constant(PyObject
*dict
, const char *key
, int value
)
225 PyObject
*constant
= PyInt_FromLong(value
);
228 if (!constant
) return -1;
229 result
= PyDict_SetItemString(dict
, key
, constant
);
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
);