0
0
mirror of https://github.com/rkd77/elinks.git synced 2025-06-30 22:19:29 -04:00
Witold Filipczyk 0fea79cc8f [cflags] Removed -Wno-pointer-sign
Likely some new bugs were introduced by this change.
The long term goal is clean compilation by g++.
2021-01-02 16:20:27 +01:00

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;
}