mirror of
https://github.com/rkd77/elinks.git
synced 2025-06-30 22:19:29 -04:00
Likely some new bugs were introduced by this change. The long term goal is clean compilation by g++.
208 lines
5.5 KiB
C
208 lines
5.5 KiB
C
/* Simple menus for Python. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#define PY_SSIZE_T_CLEAN
|
|
#include <Python.h>
|
|
|
|
#include "elinks.h"
|
|
|
|
#include "bfu/menu.h"
|
|
#include "document/document.h"
|
|
#include "document/view.h"
|
|
#include "intl/gettext/libintl.h"
|
|
#include "scripting/python/core.h"
|
|
#include "scripting/python/menu.h"
|
|
#include "session/session.h"
|
|
#include "terminal/window.h"
|
|
#include "util/error.h"
|
|
#include "util/memlist.h"
|
|
#include "util/memory.h"
|
|
#include "util/string.h"
|
|
#include "viewer/text/view.h"
|
|
|
|
/* C wrapper that invokes Python callbacks for menu items. */
|
|
|
|
static void
|
|
invoke_menu_callback(struct terminal *term, void *data, void *ses)
|
|
{
|
|
PyObject *callback = data;
|
|
struct session *saved_python_ses = python_ses;
|
|
PyObject *result;
|
|
|
|
python_ses = ses;
|
|
|
|
result = PyObject_CallFunction(callback, NULL);
|
|
if (result)
|
|
Py_DECREF(result);
|
|
else
|
|
alert_python_error();
|
|
|
|
Py_DECREF(callback);
|
|
|
|
python_ses = saved_python_ses;
|
|
}
|
|
|
|
/* Python interface for displaying simple menus. */
|
|
|
|
char python_menu_doc[] =
|
|
PYTHON_DOCSTRING("menu(items[, type]) -> None\n\
|
|
\n\
|
|
Display a menu.\n\
|
|
\n\
|
|
Arguments:\n\
|
|
\n\
|
|
items -- A sequence of tuples. Each tuple must have two elements: a\n\
|
|
string containing the name of a menu item, and a callable\n\
|
|
object that will be called without any arguments if the user\n\
|
|
selects that menu item.\n\
|
|
\n\
|
|
Optional arguments:\n\
|
|
\n\
|
|
type -- A constant specifying the type of menu to display. By default\n\
|
|
the menu is displayed at the top of the screen, but if this\n\
|
|
argument's value is the constant elinks.MENU_TAB then the menu\n\
|
|
is displayed in the same location as the ELinks tab menu. If\n\
|
|
its value is the constant elinks.MENU_LINK then the menu is\n\
|
|
displayed in the same location as the ELinks link menu and is\n\
|
|
not displayed unless a link is currently selected.\n");
|
|
|
|
PyObject *
|
|
python_menu(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
{
|
|
PyObject *items;
|
|
enum python_menu_type menu_type = PYTHON_MENU_DEFAULT;
|
|
Py_ssize_t length;
|
|
int i;
|
|
struct menu_item *menu;
|
|
struct memory_list *ml = NULL;
|
|
static char *kwlist[] = {"items", "type", NULL};
|
|
|
|
if (!python_ses) {
|
|
PyErr_SetString(python_elinks_err, "No session");
|
|
return NULL;
|
|
}
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:menu",
|
|
kwlist, &items, &menu_type))
|
|
return NULL;
|
|
|
|
assert(items);
|
|
if_assert_failed {
|
|
PyErr_SetString(python_elinks_err, N_("Internal error"));
|
|
return NULL;
|
|
}
|
|
|
|
if (!PySequence_Check(items)) {
|
|
PyErr_SetString(PyExc_TypeError, "Argument must be a sequence");
|
|
return NULL;
|
|
}
|
|
length = PySequence_Length(items);
|
|
if (length == -1 || length > INT_MAX) return NULL;
|
|
else if (length == 0) goto success;
|
|
|
|
if (menu_type < 0 || menu_type >= PYTHON_MENU_MAX) {
|
|
PyErr_Format(python_elinks_err, "%s %d",
|
|
N_("Bad number"), menu_type);
|
|
return NULL;
|
|
|
|
} else if (menu_type == PYTHON_MENU_LINK) {
|
|
if (!get_current_link(current_frame(python_ses)))
|
|
goto success;
|
|
|
|
} else if (menu_type == PYTHON_MENU_TAB
|
|
&& python_ses->status.show_tabs_bar) {
|
|
int y;
|
|
|
|
if (python_ses->status.show_tabs_bar_at_top)
|
|
y = python_ses->status.show_title_bar;
|
|
else
|
|
y = python_ses->tab->term->height - length
|
|
- python_ses->status.show_status_bar - 2;
|
|
|
|
set_window_ptr(python_ses->tab, python_ses->tab->xpos,
|
|
int_max(y, 0));
|
|
|
|
} else {
|
|
set_window_ptr(python_ses->tab, 0, 0);
|
|
}
|
|
|
|
menu = new_menu(FREE_LIST | FREE_TEXT | NO_INTL);
|
|
if (!menu) return PyErr_NoMemory();
|
|
|
|
/*
|
|
* Keep track of all the memory we allocate so we'll be able to free
|
|
* it in case any error prevents us from displaying the menu.
|
|
*/
|
|
ml = getml(menu, (void *) NULL);
|
|
if (!ml) {
|
|
mem_free(menu);
|
|
return PyErr_NoMemory();
|
|
}
|
|
|
|
for (i = 0; i < length; i++) {
|
|
PyObject *tuple = PySequence_GetItem(items, i);
|
|
PyObject *name, *callback;
|
|
char *contents;
|
|
|
|
if (!tuple) goto error;
|
|
|
|
if (!PyTuple_Check(tuple)) {
|
|
Py_DECREF(tuple);
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"Argument must be sequence of tuples");
|
|
goto error;
|
|
}
|
|
name = PyTuple_GetItem(tuple, 0);
|
|
callback = PyTuple_GetItem(tuple, 1);
|
|
Py_DECREF(tuple);
|
|
if (!name || !callback) goto error;
|
|
|
|
contents = (char *) PyUnicode_AsUTF8(name);
|
|
if (!contents) goto error;
|
|
|
|
contents = stracpy(contents);
|
|
if (!contents) {
|
|
PyErr_NoMemory();
|
|
goto error;
|
|
}
|
|
add_one_to_ml(&ml, contents);
|
|
|
|
/*
|
|
* FIXME: We need to increment the reference counts for
|
|
* callbacks so they won't be garbage-collected by the Python
|
|
* interpreter before they're called. But for any callback
|
|
* that isn't called (because the user doesn't select the
|
|
* corresponding menu item) we'll never have an opportunity
|
|
* to decrement the reference count again, so this code leaks
|
|
* references. It probably can't be fixed without changes to
|
|
* the menu machinery in bfu/menu.c, e.g. to call an arbitrary
|
|
* clean-up function when a menu is destroyed.
|
|
*
|
|
* The good news is that in a typical usage case, where the
|
|
* callback objects wouldn't be garbage-collected anyway until
|
|
* the Python interpreter exits, this makes no difference at
|
|
* all. But it's not strictly correct, and it could leak memory
|
|
* in more elaborate usage where callback objects are created
|
|
* and thrown away on the fly.
|
|
*/
|
|
Py_INCREF(callback);
|
|
add_to_menu(&menu, contents, NULL, ACT_MAIN_NONE,
|
|
invoke_menu_callback, callback, 0);
|
|
}
|
|
|
|
do_menu(python_ses->tab->term, menu, python_ses, 1);
|
|
|
|
success:
|
|
mem_free_if(ml);
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
|
|
error:
|
|
freeml(ml);
|
|
return NULL;
|
|
}
|