1
0
Fork 0

Oh, sorry. I forgot about these.

This commit is contained in:
Witold Filipczyk 2006-10-19 16:20:26 +02:00 committed by Witold Filipczyk
parent ebadc9bf9e
commit 68913c8c7d
13 changed files with 1268 additions and 0 deletions

View File

@ -0,0 +1,129 @@
"""Additional Python code for ELinks maintainers.
This module is intended for ELinks maintainers. If you modify or add to
the Python APIs in src/scripting/python/*.c and/or contrib/python/hooks.py,
you should update the accompanying docstrings to reflect your changes and
then generate a new version of the file doc/python.txt (which serves as a
reference manual for the browser's Python APIs). The embedded interpreter
can use introspection to regenerate the python.txt document for you; just
copy this file into your ~/.elinks directory and add something like the
following to ~/.elinks/hooks.py:
import elinks_maint
elinks.bind_key('F2', elinks_maint.generate_python_txt)
"""
import inspect
import tempfile
import types
preface = """\
Python programmers can customize the behavior of ELinks by creating a Python
hooks module. The embedded Python interpreter provides an internal module
called elinks that can be used by the hooks module to create keystroke
bindings for Python code, obtain information about the document being
viewed, display simple dialog boxes and menus, load documents into the
ELinks cache, or display documents to the user. These two modules are
described below.
"""
module_template = """
MODULE
%s - %s
DESCRIPTION
%s
FUNCTIONS
%s
"""
separator = '-' * 78 + '\n'
def document_modules(*modules):
"""Format the internal documentation found in one or more Python modules."""
output = []
for module in modules:
name, doc, namespace = module.__name__, module.__doc__, module.__dict__
if not name or not namespace:
continue
try:
summary, junk, description = doc.rstrip().split('\n', 2)
except:
summary, description = '?', '(no description available)'
functions = document_functions(namespace)
output.append(module_template % (name, summary, indent(description),
indent(functions)))
return separator.join(output)
def document_functions(namespace):
"""Format the internal documentation for all functions in a namespace."""
objects = namespace.items()
objects.sort()
output = []
for name, object in objects:
if name.startswith('_'):
continue
species = type(object)
if species == types.BuiltinFunctionType:
args = '(...)'
elif species == types.FunctionType:
args = inspect.formatargspec(*inspect.getargspec(object))
else:
continue
description = inspect.getdoc(object)
output.append('%s%s\n%s\n' % (name, args, indent(description)))
return '\n'.join(output)
def generate_python_txt():
"""Generate documentation for the hooks and elinks modules."""
import elinks
import hooks
# Remove anything that doesn't belong in the API documentation.
#
hooks_api_functions = (
'follow_url_hook',
'goto_url_hook',
'pre_format_html_hook',
'proxy_for_hook',
'quit_hook',
)
for key in hooks.__dict__.keys():
if key not in hooks_api_functions and not key.startswith('_'):
del hooks.__dict__[key]
hooks.__doc__ = hooks.__doc__.replace('Example Python', 'Python')
# Generate the documentation.
#
try:
output = separator.join((preface, document_modules(hooks, elinks)))
finally:
# Restore the hooks module to a sane state.
reload(hooks)
# View the documentation.
#
path = write_tempfile(output)
elinks.open(path)
def indent(text):
"""Return indented text."""
indent = ' ' * 4
return '\n'.join([indent + line for line in text.split('\n')])
def write_tempfile(text):
"""Write a string to a temporary file and return the file's name."""
output = tempfile.NamedTemporaryFile(prefix='elinks_maint', suffix='.txt')
output.write(text)
output.flush()
_tempfiles[text] = output
return output.name
# Temp files are stashed in this dictionary to prevent them from being closed
# before ELinks has a chance to read them; they will be automatically deleted
# when the dictionary is garbage-collected at exit time.
#
_tempfiles = {}

View File

@ -0,0 +1,248 @@
/* Dialog boxes for Python. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Python.h>
#include "elinks.h"
#include "bfu/inpfield.h"
#include "bfu/msgbox.h"
#include "intl/gettext/libintl.h"
#include "scripting/python/core.h"
#include "session/session.h"
#include "util/error.h"
#include "util/memlist.h"
#include "util/memory.h"
#include "util/string.h"
/* Python interface for displaying information to the user. */
static char python_info_box_doc[] =
PYTHON_DOCSTRING("info_box(text[, title]) -> None\n\
\n\
Display information to the user in a dialog box.\n\
\n\
Arguments:\n\
\n\
text -- The text to be displayed in the dialog box. This argument can\n\
be a string or any object that has a string representation as\n\
returned by str(object).\n\
\n\
Optional arguments:\n\
\n\
title -- A string containing a title for the dialog box. By default\n\
the string \"Info\" is used.\n");
static PyObject *
python_info_box(PyObject *self, PyObject *args, PyObject *kwargs)
{
/* [gettext_accelerator_context(python_info_box)] */
unsigned char *title = N_("Info");
PyObject *object, *string_object;
unsigned char *text;
static char *kwlist[] = {"text", "title", NULL};
if (!python_ses) {
PyErr_SetString(python_elinks_err, "No session");
return NULL;
}
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|s:info_box", kwlist,
&object, &title))
return NULL;
assert(object);
if_assert_failed {
PyErr_SetString(python_elinks_err, N_("Internal error"));
return NULL;
}
/*
* Get a string representation of the object, then copy that string's
* contents.
*/
string_object = PyObject_Str(object);
if (!string_object) return NULL;
text = (unsigned char *) PyString_AS_STRING(string_object);
if (!text) {
Py_DECREF(string_object);
return NULL;
}
text = stracpy(text);
Py_DECREF(string_object);
if (!text) goto mem_error;
title = stracpy(title);
if (!title) goto free_text;
(void) msg_box(python_ses->tab->term, getml(title, NULL),
MSGBOX_NO_INTL | MSGBOX_SCROLLABLE | MSGBOX_FREE_TEXT,
title, ALIGN_LEFT,
text,
NULL, 1,
N_("~OK"), NULL, B_ENTER | B_ESC);
Py_INCREF(Py_None);
return Py_None;
free_text:
mem_free(text);
mem_error:
return PyErr_NoMemory();
}
struct python_input_callback_hop {
struct session *ses;
PyObject *callback;
};
/*
* C wrapper that invokes Python callbacks for input_dialog() OK button.
*
* This is also used indirectly for the Cancel button, with a NULL @text
* argument. See invoke_input_cancel_callback() below.
*/
static void
invoke_input_ok_callback(void *data, unsigned char *text)
{
struct python_input_callback_hop *hop = data;
struct session *saved_python_ses = python_ses;
PyObject *result;
assert(hop && hop->callback);
if_assert_failed return;
python_ses = hop->ses;
/* If @text is NULL, the "s" format will create a None reference. */
result = PyObject_CallFunction(hop->callback, "s", text);
if (result)
Py_DECREF(result);
else
alert_python_error();
Py_DECREF(hop->callback);
mem_free(hop);
python_ses = saved_python_ses;
}
/* C wrapper that invokes Python callbacks for input_dialog() cancel button. */
static void
invoke_input_cancel_callback(void *data)
{
invoke_input_ok_callback(data, NULL);
}
/* Python interface for getting input from the user. */
static char python_input_box_doc[] =
PYTHON_DOCSTRING(
"input_box(prompt, callback, title=\"User dialog\", initial=\"\") -> None\n\
\n\
Display a dialog box to prompt for user input.\n\
\n\
Arguments:\n\
\n\
prompt -- A string containing a prompt for the dialog box.\n\
callback -- A callable object to be called after the dialog is\n\
finished. It will be called with a single argument, which\n\
will be either a string provided by the user or else None\n\
if the user canceled the dialog.\n\
\n\
Optional keyword arguments:\n\
\n\
title -- A string containing a title for the dialog box. By default\n\
the string \"User dialog\" is used.\n\
initial -- A string containing an initial value for the text entry\n\
field. By default the entry field is initially empty.\n");
static PyObject *
python_input_box(PyObject *self, PyObject *args, PyObject *kwargs)
{
unsigned char *prompt;
PyObject *callback;
unsigned char *title = N_("User dialog");
unsigned char *initial = NULL;
struct python_input_callback_hop *hop;
static char *kwlist[] = {"prompt", "callback", "title", "initial", NULL};
if (!python_ses) {
PyErr_SetString(python_elinks_err, "No session");
return NULL;
}
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|ss:input_box",
kwlist, &prompt, &callback, &title,
&initial))
return NULL;
assert(prompt && callback && title);
if_assert_failed {
PyErr_SetString(python_elinks_err, N_("Internal error"));
return NULL;
}
prompt = stracpy(prompt);
if (!prompt) goto mem_error;
title = stracpy(title);
if (!title) goto free_prompt;
if (initial) {
initial = stracpy(initial);
if (!initial) goto free_title;
}
hop = mem_alloc(sizeof(*hop));
if (!hop) goto free_initial;
hop->ses = python_ses;
hop->callback = callback;
Py_INCREF(callback);
input_dialog(python_ses->tab->term, getml(prompt, title, initial, NULL),
title, prompt,
hop, NULL,
MAX_STR_LEN, initial, 0, 0, NULL,
invoke_input_ok_callback,
invoke_input_cancel_callback);
Py_INCREF(Py_None);
return Py_None;
free_initial:
mem_free_if(initial);
free_title:
mem_free(title);
free_prompt:
mem_free(prompt);
mem_error:
return PyErr_NoMemory();
}
static PyMethodDef dialogs_methods[] = {
{"info_box", (PyCFunction) python_info_box,
METH_VARARGS | METH_KEYWORDS,
python_info_box_doc},
{"input_box", (PyCFunction) python_input_box,
METH_VARARGS | METH_KEYWORDS,
python_input_box_doc},
{NULL, NULL, 0, NULL}
};
int
python_init_dialogs_interface(PyObject *dict, PyObject *name)
{
return add_python_methods(dict, name, dialogs_methods);
}

View File

@ -0,0 +1,8 @@
#ifndef EL__SCRIPTING_PYTHON_DIALOGS_H
#define EL__SCRIPTING_PYTHON_DIALOGS_H
#include <Python.h>
int python_init_dialogs_interface(PyObject *dict, PyObject *name);
#endif

View File

@ -0,0 +1,144 @@
/* Information about current document and current link for Python. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Python.h>
#include "elinks.h"
#include "cache/cache.h"
#include "scripting/python/core.h"
#include "session/location.h"
#include "session/session.h"
/* Python interface to get the current document's body. */
static char python_current_document_doc[] =
PYTHON_DOCSTRING("current_document() -> string or None\n\
\n\
If a document is being viewed, return its body; otherwise return None.\n");
static PyObject *
python_current_document(PyObject *self, PyObject *args)
{
if (python_ses && have_location(python_ses)) {
struct cache_entry *cached = find_in_cache(cur_loc(python_ses)->vs.uri);
struct fragment *f = cached ? cached->frag.next : NULL;
if (f) return PyString_FromStringAndSize(f->data, f->length);
}
Py_INCREF(Py_None);
return Py_None;
}
/* Python interface to get the current document's header. */
static char python_current_header_doc[] =
PYTHON_DOCSTRING("current_header() -> string or None\n\
\n\
If a document is being viewed and it has a header, return the header;\n\
otherwise return None.\n");
static PyObject *
python_current_header(PyObject *self, PyObject *args)
{
if (python_ses && have_location(python_ses)) {
struct cache_entry *cached = find_in_cache(cur_loc(python_ses)->vs.uri);
if (cached && cached->head)
return PyString_FromString(cached->head);
}
Py_INCREF(Py_None);
return Py_None;
}
/* Python interface to get the currently-selected link's URL. */
static char python_current_link_url_doc[] =
PYTHON_DOCSTRING("current_link_url() -> string or None\n\
\n\
If a link is selected, return its URL; otherwise return None.\n");
static PyObject *
python_current_link_url(PyObject *self, PyObject *args)
{
unsigned char url[MAX_STR_LEN];
if (python_ses && get_current_link_url(python_ses, url, MAX_STR_LEN))
return PyString_FromString(url);
Py_INCREF(Py_None);
return Py_None;
}
/* Python interface to get the current document's title. */
static char python_current_title_doc[] =
PYTHON_DOCSTRING("current_title() -> string or None\n\
\n\
If a document is being viewed, return its title; otherwise return None.\n");
static PyObject *
python_current_title(PyObject *self, PyObject *args)
{
unsigned char title[MAX_STR_LEN];
if (python_ses && get_current_title(python_ses, title, MAX_STR_LEN))
return PyString_FromString(title);
Py_INCREF(Py_None);
return Py_None;
}
/* Python interface to get the current document's URL. */
static char python_current_url_doc[] =
PYTHON_DOCSTRING("current_url() -> string or None\n\
\n\
If a document is being viewed, return its URL; otherwise return None.\n");
static PyObject *
python_current_url(PyObject *self, PyObject *args)
{
unsigned char url[MAX_STR_LEN];
if (python_ses && get_current_url(python_ses, url, MAX_STR_LEN))
return PyString_FromString(url);
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef document_methods[] = {
{"current_document", python_current_document,
METH_NOARGS,
python_current_document_doc},
{"current_header", python_current_header,
METH_NOARGS,
python_current_header_doc},
{"current_link_url", python_current_link_url,
METH_NOARGS,
python_current_link_url_doc},
{"current_title", python_current_title,
METH_NOARGS,
python_current_title_doc},
{"current_url", python_current_url,
METH_NOARGS,
python_current_url_doc},
{NULL, NULL, 0, NULL}
};
int
python_init_document_interface(PyObject *dict, PyObject *name)
{
return add_python_methods(dict, name, document_methods);
}

View File

@ -0,0 +1,8 @@
#ifndef EL__SCRIPTING_PYTHON_DOCUMENT_H
#define EL__SCRIPTING_PYTHON_DOCUMENT_H
#include <Python.h>
int python_init_document_interface(PyObject *dict, PyObject *name);
#endif

View File

@ -0,0 +1,198 @@
/* Keystroke bindings for Python. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Python.h>
#include <stdarg.h>
#include <string.h>
#include "elinks.h"
#include "config/kbdbind.h"
#include "intl/gettext/libintl.h"
#include "main/event.h"
#include "scripting/python/core.h"
#include "session/session.h"
#include "util/error.h"
#include "util/string.h"
static PyObject *keybindings = NULL;
/* C wrapper that invokes Python callbacks for bind_key_to_event_name(). */
static enum evhook_status
invoke_keybinding_callback(va_list ap, void *data)
{
PyObject *callback = data;
struct session *saved_python_ses = python_ses;
PyObject *result;
python_ses = va_arg(ap, struct session *);
result = PyObject_CallFunction(callback, NULL);
if (result)
Py_DECREF(result);
else
alert_python_error();
python_ses = saved_python_ses;
return EVENT_HOOK_STATUS_NEXT;
}
/* Check that a keymap name is valid. */
static int
keymap_is_valid(const unsigned char *keymap)
{
enum keymap_id keymap_id;
for (keymap_id = 0; keymap_id < KEYMAP_MAX; ++keymap_id)
if (!strcmp(keymap, get_keymap_name(keymap_id)))
break;
return (keymap_id != KEYMAP_MAX);
}
/* Python interface for binding keystrokes to callable objects. */
static char python_bind_key_doc[] =
PYTHON_DOCSTRING("bind_key(keystroke, callback[, keymap]) -> None\n\
\n\
Bind a keystroke to a callable object.\n\
\n\
Arguments:\n\
\n\
keystroke -- A string containing a keystroke. The syntax for\n\
keystrokes is described in the elinkskeys(5) man page.\n\
callback -- A callable object to be called when the keystroke is\n\
typed. It will be called without any arguments.\n\
\n\
Optional arguments:\n\
\n\
keymap -- A string containing the name of a keymap. Valid keymap\n\
names can be found in the elinkskeys(5) man page. By\n\
default the \"main\" keymap is used.\n");
static PyObject *
python_bind_key(PyObject *self, PyObject *args, PyObject *kwargs)
{
const unsigned char *keystroke;
PyObject *callback;
unsigned char *keymap = "main";
PyObject *key_tuple;
PyObject *old_callback;
struct string event_name;
int event_id;
unsigned char *error_msg;
static char *kwlist[] = {"keystroke", "callback", "keymap", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|s:bind_key", kwlist,
&keystroke, &callback, &keymap))
return NULL;
assert(keystroke && callback && keymap);
if_assert_failed {
PyErr_SetString(python_elinks_err, N_("Internal error"));
return NULL;
}
if (!keymap_is_valid(keymap)) {
PyErr_Format(python_elinks_err, "%s \"%s\"",
N_("Unrecognised keymap"), keymap);
return NULL;
}
/*
* The callback object needs to be kept alive for as long as the
* keystroke is bound, so we stash a reference to it in a dictionary.
* We don't need to use the dictionary to find callbacks; its sole
* purpose is to prevent these objects from being garbage-collected
* by the Python interpreter.
*
* If binding the key fails for any reason after this point then
* we'll need to restore the dictionary to its previous state, which
* is temporarily preserved in @old_callback.
*/
key_tuple = Py_BuildValue("ss", keymap, keystroke);
if (!key_tuple)
return NULL;
old_callback = PyDict_GetItem(keybindings, key_tuple);
Py_XINCREF(old_callback);
if (PyDict_SetItem(keybindings, key_tuple, callback) != 0) {
Py_DECREF(key_tuple);
Py_XDECREF(old_callback);
return NULL;
}
if (!init_string(&event_name)) {
PyErr_NoMemory();
goto rollback;
}
if (!add_format_to_string(&event_name, "python-func %p", callback)) {
PyErr_SetFromErrno(python_elinks_err);
done_string(&event_name);
goto rollback;
}
event_id = bind_key_to_event_name(keymap, keystroke, event_name.source,
&error_msg);
done_string(&event_name);
if (error_msg) {
PyErr_SetString(python_elinks_err, error_msg);
goto rollback;
}
event_id = register_event_hook(event_id, invoke_keybinding_callback, 0,
callback);
if (event_id == EVENT_NONE) {
PyErr_SetString(python_elinks_err,
N_("Error registering event hook"));
goto rollback;
}
Py_DECREF(key_tuple);
Py_XDECREF(old_callback);
Py_INCREF(Py_None);
return Py_None;
rollback:
/*
* If an error occurred, try to restore the keybindings dictionary
* to its previous state.
*/
if (old_callback) {
(void) PyDict_SetItem(keybindings, key_tuple, old_callback);
Py_DECREF(old_callback);
} else {
(void) PyDict_DelItem(keybindings, key_tuple);
}
Py_DECREF(key_tuple);
return NULL;
}
static PyMethodDef keybinding_methods[] = {
{"bind_key", (PyCFunction) python_bind_key,
METH_VARARGS | METH_KEYWORDS,
python_bind_key_doc},
{NULL, NULL, 0, NULL}
};
int
python_init_keybinding_interface(PyObject *dict, PyObject *name)
{
keybindings = PyDict_New();
if (!keybindings) return -1;
return add_python_methods(dict, name, keybinding_methods);
}
void
python_done_keybinding_interface(void)
{
Py_XDECREF(keybindings);
}

View File

@ -0,0 +1,9 @@
#ifndef EL__SCRIPTING_PYTHON_KEYBINDING_H
#define EL__SCRIPTING_PYTHON_KEYBINDING_H
#include <Python.h>
int python_init_keybinding_interface(PyObject *dict, PyObject *name);
void python_done_keybinding_interface(void);
#endif

167
src/scripting/python/load.c Normal file
View File

@ -0,0 +1,167 @@
/* Document loading for Python. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Python.h>
#include "elinks.h"
#include "cache/cache.h"
#include "intl/gettext/libintl.h"
#include "network/connection.h"
#include "network/state.h"
#include "protocol/uri.h"
#include "scripting/python/core.h"
#include "session/download.h"
#include "session/session.h"
#include "session/task.h"
#include "util/error.h"
#include "util/memory.h"
struct python_load_uri_callback_hop {
struct session *ses;
PyObject *callback;
};
/* C wrapper that invokes Python callbacks for load_uri(). */
static void
invoke_load_uri_callback(struct download *download, void *data)
{
struct python_load_uri_callback_hop *hop = data;
struct session *saved_python_ses = python_ses;
assert(download);
if_assert_failed {
if (hop && hop->callback) {
Py_DECREF(hop->callback);
}
mem_free_if(hop);
return;
}
if (is_in_progress_state(download->state)) return;
assert(hop && hop->callback);
if_assert_failed {
mem_free(download);
mem_free_if(hop);
return;
}
if (download->cached) {
PyObject *result;
struct fragment *f = download->cached->frag.next;
python_ses = hop->ses;
result = PyObject_CallFunction(hop->callback, "ss#",
download->cached->head,
f ? f->data : NULL,
f ? f->length : 0);
if (result)
Py_DECREF(result);
else
alert_python_error();
}
Py_DECREF(hop->callback);
mem_free(hop);
mem_free(download);
python_ses = saved_python_ses;
}
/* Python interface for loading a document. */
static char python_load_doc[] =
PYTHON_DOCSTRING("load(url, callback) -> None\n\
\n\
Load a document into the ELinks cache and pass its contents to a\n\
callable object.\n\
\n\
Arguments:\n\
\n\
url -- A string containing the URL to load.\n\
callback -- A callable object to be called after the document has\n\
been loaded. It will be called with two arguments: the first\n\
will be a string representing the document's header, or None\n\
if it has no header; the second will be a string representing\n\
the document's body, or None if it has no body.\n");
static PyObject *
python_load(PyObject *self, PyObject *args)
{
unsigned char *uristring;
PyObject *callback;
struct uri *uri;
struct download *download;
struct python_load_uri_callback_hop *hop;
if (!python_ses) {
PyErr_SetString(python_elinks_err, "No session");
return NULL;
}
if (!PyArg_ParseTuple(args, "sO:load", &uristring, &callback))
return NULL;
assert(uristring && callback);
if_assert_failed {
PyErr_SetString(python_elinks_err, N_("Internal error"));
return NULL;
}
uri = get_translated_uri(uristring, python_ses->tab->term->cwd);
if (!uri) {
PyErr_SetString(python_elinks_err, N_("Bad URL syntax"));
return NULL;
}
download = mem_alloc(sizeof(*download));
if (!download) goto mem_error;
hop = mem_alloc(sizeof(*hop));
if (!hop) goto free_download;
hop->ses = python_ses;
hop->callback = callback;
Py_INCREF(callback);
download->data = hop;
download->callback = (download_callback_T *) invoke_load_uri_callback;
if (load_uri(uri, NULL, download, PRI_MAIN, CACHE_MODE_NORMAL, -1) != 0) {
PyErr_SetString(python_elinks_err,
get_state_message(download->state,
python_ses->tab->term));
done_uri(uri);
return NULL;
}
done_uri(uri);
Py_INCREF(Py_None);
return Py_None;
free_download:
mem_free(download);
mem_error:
done_uri(uri);
return PyErr_NoMemory();
}
static PyMethodDef load_methods[] = {
{"load", python_load,
METH_VARARGS,
python_load_doc},
{NULL, NULL, 0, NULL}
};
int
python_init_load_interface(PyObject *dict, PyObject *name)
{
return add_python_methods(dict, name, load_methods);
}

View File

@ -0,0 +1,8 @@
#ifndef EL__SCRIPTING_PYTHON_LOAD_H
#define EL__SCRIPTING_PYTHON_LOAD_H
#include <Python.h>
int python_init_load_interface(PyObject *dict, PyObject *name);
#endif

241
src/scripting/python/menu.c Normal file
View File

@ -0,0 +1,241 @@
/* Simple menus for Python. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#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 "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;
}
enum python_menu_type {
PYTHON_MENU_DEFAULT,
PYTHON_MENU_LINK,
PYTHON_MENU_TAB,
PYTHON_MENU_MAX
};
/* Python interface for displaying simple menus. */
static 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");
static PyObject *
python_menu(PyObject *self, PyObject *args, PyObject *kwargs)
{
PyObject *items;
enum python_menu_type menu_type = PYTHON_MENU_DEFAULT;
int length, 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) 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, NULL);
if (!ml) {
mem_free(menu);
return PyErr_NoMemory();
}
for (i = 0; i < length; i++) {
PyObject *tuple = PySequence_GetItem(items, i);
PyObject *name, *callback;
unsigned 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 = (unsigned char *) PyString_AsString(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;
}
static PyMethodDef menu_methods[] = {
{"menu", (PyCFunction) python_menu,
METH_VARARGS | METH_KEYWORDS,
python_menu_doc},
{NULL, NULL, 0, NULL}
};
static int
add_constant(PyObject *dict, const char *key, int value)
{
PyObject *constant = PyInt_FromLong(value);
int result;
if (!constant) return -1;
result = PyDict_SetItemString(dict, key, constant);
Py_DECREF(constant);
return result;
}
int
python_init_menu_interface(PyObject *dict, PyObject *name)
{
if (add_constant(dict, "MENU_LINK", PYTHON_MENU_LINK) != 0) return -1;
if (add_constant(dict, "MENU_TAB", PYTHON_MENU_TAB) != 0) return -1;
return add_python_methods(dict, name, menu_methods);
}

View File

@ -0,0 +1,8 @@
#ifndef EL__SCRIPTING_PYTHON_MENU_H
#define EL__SCRIPTING_PYTHON_MENU_H
#include <Python.h>
int python_init_menu_interface(PyObject *dict, PyObject *name);
#endif

View File

@ -0,0 +1,92 @@
/* Document viewing for Python. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Python.h>
#include "elinks.h"
#include "intl/gettext/libintl.h"
#include "protocol/uri.h"
#include "scripting/python/core.h"
#include "session/task.h"
#include "terminal/tab.h"
#include "util/error.h"
/* Python interface for viewing a document. */
static char python_open_doc[] =
PYTHON_DOCSTRING("open(url, new_tab=False, background=False) -> None\n\
\n\
View a document in either the current tab or a new tab.\n\
\n\
Arguments:\n\
\n\
url -- A string containing the URL to view.\n\
\n\
Optional keyword arguments:\n\
\n\
new_tab -- By default the URL is opened in the current tab. If this\n\
argument's value is the boolean True then the URL is instead\n\
opened in a new tab.\n\
background -- By default a new tab is opened in the foreground. If\n\
this argument's value is the boolean True then a new tab is\n\
instead opened in the background. This argument is ignored\n\
unless new_tab's value is True.\n");
static PyObject *
python_open(PyObject *self, PyObject *args, PyObject *kwargs)
{
unsigned char *url;
int new_tab = 0, background = 0;
struct uri *uri;
static char *kwlist[] = {"url", "new_tab", "background", NULL};
if (!python_ses) {
PyErr_SetString(python_elinks_err, "No session");
return NULL;
}
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ii:open",
kwlist, &url,
&new_tab, &background))
return NULL;
assert(url);
if_assert_failed {
PyErr_SetString(python_elinks_err, N_("Internal error"));
return NULL;
}
uri = get_translated_uri(url, python_ses->tab->term->cwd);
if (!uri) {
PyErr_SetString(python_elinks_err, N_("Bad URL syntax"));
return NULL;
}
if (new_tab)
open_uri_in_new_tab(python_ses, uri, background, 0);
else
goto_uri(python_ses, uri);
done_uri(uri);
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef open_methods[] = {
{"open", (PyCFunction) python_open,
METH_VARARGS | METH_KEYWORDS,
python_open_doc},
{NULL, NULL, 0, NULL}
};
int
python_init_open_interface(PyObject *dict, PyObject *name)
{
return add_python_methods(dict, name, open_methods);
}

View File

@ -0,0 +1,8 @@
#ifndef EL__SCRIPTING_PYTHON_OPEN_H
#define EL__SCRIPTING_PYTHON_OPEN_H
#include <Python.h>
int python_init_open_interface(PyObject *dict, PyObject *name);
#endif